7e59e9c7cfabae5a7dd86eb0721c5322f2d1bec6
[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                 Roo.log('extend!!!!');
210                 Roo.log(sb);
211                 Roo.log(sp);
212                 Roo.log(overrides);
213                 if(typeof sp == 'object'){ // eg. prototype, rather than function constructor..
214                     overrides = sp;
215                     sp = sb;
216                     sb = function(){sp.apply(this, arguments);};
217                 }
218                 var F = function(){}, sbp, spp = sp.prototype;
219                 F.prototype = spp;
220                 sbp = sb.prototype = new F();
221                 sbp.constructor=sb;
222                 sb.superclass=spp;
223                 
224                 if(spp.constructor == Object.prototype.constructor){
225                     spp.constructor=sp;
226                    
227                 }
228                 
229                 sb.override = function(o){
230                     Roo.override(sb, o);
231                 };
232                 sbp.override = io;
233                 Roo.override(sb, overrides);
234                 return sb;
235             };
236         }(),
237
238         /**
239          * Adds a list of functions to the prototype of an existing class, overwriting any existing methods with the same name.
240          * Usage:<pre><code>
241 Roo.override(MyClass, {
242     newMethod1: function(){
243         // etc.
244     },
245     newMethod2: function(foo){
246         // etc.
247     }
248 });
249  </code></pre>
250          * @param {Object} origclass The class to override
251          * @param {Object} overrides The list of functions to add to origClass.  This should be specified as an object literal
252          * containing one or more methods.
253          * @method override
254          */
255         override : function(origclass, overrides){
256             if(overrides){
257                 var p = origclass.prototype;
258                 for(var method in overrides){
259                     p[method] = overrides[method];
260                 }
261             }
262         },
263         /**
264          * Creates namespaces to be used for scoping variables and classes so that they are not global.  Usage:
265          * <pre><code>
266 Roo.namespace('Company', 'Company.data');
267 Company.Widget = function() { ... }
268 Company.data.CustomStore = function(config) { ... }
269 </code></pre>
270          * @param {String} namespace1
271          * @param {String} namespace2
272          * @param {String} etc
273          * @method namespace
274          */
275         namespace : function(){
276             var a=arguments, o=null, i, j, d, rt;
277             for (i=0; i<a.length; ++i) {
278                 d=a[i].split(".");
279                 rt = d[0];
280                 /** eval:var:o */
281                 eval('if (typeof ' + rt + ' == "undefined"){' + rt + ' = {};} o = ' + rt + ';');
282                 for (j=1; j<d.length; ++j) {
283                     o[d[j]]=o[d[j]] || {};
284                     o=o[d[j]];
285                 }
286             }
287         },
288         /**
289          * Creates namespaces to be used for scoping variables and classes so that they are not global.  Usage:
290          * <pre><code>
291 Roo.factory({ xns: Roo.data, xtype : 'Store', .....});
292 Roo.factory(conf, Roo.data);
293 </code></pre>
294          * @param {String} classname
295          * @param {String} namespace (optional)
296          * @method factory
297          */
298          
299         factory : function(c, ns)
300         {
301             // no xtype, no ns or c.xns - or forced off by c.xns
302             if (!c.xtype   || (!ns && !c.xns) ||  (c.xns === false)) { // not enough info...
303                 return c;
304             }
305             ns = c.xns ? c.xns : ns; // if c.xns is set, then use that..
306             if (c.constructor == ns[c.xtype]) {// already created...
307                 return c;
308             }
309             if (ns[c.xtype]) {
310                 if (Roo.debug) Roo.log("Roo.Factory(" + c.xtype + ")");
311                 var ret = new ns[c.xtype](c);
312                 ret.xns = false;
313                 return ret;
314             }
315             c.xns = false; // prevent recursion..
316             return c;
317         },
318          /**
319          * Logs to console if it can.
320          *
321          * @param {String|Object} string
322          * @method log
323          */
324         log : function(s)
325         {
326             if ((typeof(console) == 'undefined') || (typeof(console.log) == 'undefined')) {
327                 return; // alerT?
328             }
329             console.log(s);
330             
331         },
332         /**
333          * 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.
334          * @param {Object} o
335          * @return {String}
336          */
337         urlEncode : function(o){
338             if(!o){
339                 return "";
340             }
341             var buf = [];
342             for(var key in o){
343                 var ov = o[key], k = Roo.encodeURIComponent(key);
344                 var type = typeof ov;
345                 if(type == 'undefined'){
346                     buf.push(k, "=&");
347                 }else if(type != "function" && type != "object"){
348                     buf.push(k, "=", Roo.encodeURIComponent(ov), "&");
349                 }else if(ov instanceof Array){
350                     if (ov.length) {
351                             for(var i = 0, len = ov.length; i < len; i++) {
352                                 buf.push(k, "=", Roo.encodeURIComponent(ov[i] === undefined ? '' : ov[i]), "&");
353                             }
354                         } else {
355                             buf.push(k, "=&");
356                         }
357                 }
358             }
359             buf.pop();
360             return buf.join("");
361         },
362          /**
363          * Safe version of encodeURIComponent
364          * @param {String} data 
365          * @return {String} 
366          */
367         
368         encodeURIComponent : function (data)
369         {
370             try {
371                 return encodeURIComponent(data);
372             } catch(e) {} // should be an uri encode error.
373             
374             if (data == '' || data == null){
375                return '';
376             }
377             // http://stackoverflow.com/questions/2596483/unicode-and-uri-encoding-decoding-and-escaping-in-javascript
378             function nibble_to_hex(nibble){
379                 var chars = '0123456789ABCDEF';
380                 return chars.charAt(nibble);
381             }
382             data = data.toString();
383             var buffer = '';
384             for(var i=0; i<data.length; i++){
385                 var c = data.charCodeAt(i);
386                 var bs = new Array();
387                 if (c > 0x10000){
388                         // 4 bytes
389                     bs[0] = 0xF0 | ((c & 0x1C0000) >>> 18);
390                     bs[1] = 0x80 | ((c & 0x3F000) >>> 12);
391                     bs[2] = 0x80 | ((c & 0xFC0) >>> 6);
392                     bs[3] = 0x80 | (c & 0x3F);
393                 }else if (c > 0x800){
394                          // 3 bytes
395                     bs[0] = 0xE0 | ((c & 0xF000) >>> 12);
396                     bs[1] = 0x80 | ((c & 0xFC0) >>> 6);
397                     bs[2] = 0x80 | (c & 0x3F);
398                 }else if (c > 0x80){
399                        // 2 bytes
400                     bs[0] = 0xC0 | ((c & 0x7C0) >>> 6);
401                     bs[1] = 0x80 | (c & 0x3F);
402                 }else{
403                         // 1 byte
404                     bs[0] = c;
405                 }
406                 for(var j=0; j<bs.length; j++){
407                     var b = bs[j];
408                     var hex = nibble_to_hex((b & 0xF0) >>> 4) 
409                             + nibble_to_hex(b &0x0F);
410                     buffer += '%'+hex;
411                }
412             }
413             return buffer;    
414              
415         },
416
417         /**
418          * 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]}.
419          * @param {String} string
420          * @param {Boolean} overwrite (optional) Items of the same name will overwrite previous values instead of creating an an array (Defaults to false).
421          * @return {Object} A literal with members
422          */
423         urlDecode : function(string, overwrite){
424             if(!string || !string.length){
425                 return {};
426             }
427             var obj = {};
428             var pairs = string.split('&');
429             var pair, name, value;
430             for(var i = 0, len = pairs.length; i < len; i++){
431                 pair = pairs[i].split('=');
432                 name = decodeURIComponent(pair[0]);
433                 value = decodeURIComponent(pair[1]);
434                 if(overwrite !== true){
435                     if(typeof obj[name] == "undefined"){
436                         obj[name] = value;
437                     }else if(typeof obj[name] == "string"){
438                         obj[name] = [obj[name]];
439                         obj[name].push(value);
440                     }else{
441                         obj[name].push(value);
442                     }
443                 }else{
444                     obj[name] = value;
445                 }
446             }
447             return obj;
448         },
449
450         /**
451          * Iterates an array calling the passed function with each item, stopping if your function returns false. If the
452          * passed array is not really an array, your function is called once with it.
453          * The supplied function is called with (Object item, Number index, Array allItems).
454          * @param {Array/NodeList/Mixed} array
455          * @param {Function} fn
456          * @param {Object} scope
457          */
458         each : function(array, fn, scope){
459             if(typeof array.length == "undefined" || typeof array == "string"){
460                 array = [array];
461             }
462             for(var i = 0, len = array.length; i < len; i++){
463                 if(fn.call(scope || array[i], array[i], i, array) === false){ return i; };
464             }
465         },
466
467         // deprecated
468         combine : function(){
469             var as = arguments, l = as.length, r = [];
470             for(var i = 0; i < l; i++){
471                 var a = as[i];
472                 if(a instanceof Array){
473                     r = r.concat(a);
474                 }else if(a.length !== undefined && !a.substr){
475                     r = r.concat(Array.prototype.slice.call(a, 0));
476                 }else{
477                     r.push(a);
478                 }
479             }
480             return r;
481         },
482
483         /**
484          * Escapes the passed string for use in a regular expression
485          * @param {String} str
486          * @return {String}
487          */
488         escapeRe : function(s) {
489             return s.replace(/([.*+?^${}()|[\]\/\\])/g, "\\$1");
490         },
491
492         // internal
493         callback : function(cb, scope, args, delay){
494             if(typeof cb == "function"){
495                 if(delay){
496                     cb.defer(delay, scope, args || []);
497                 }else{
498                     cb.apply(scope, args || []);
499                 }
500             }
501         },
502
503         /**
504          * Return the dom node for the passed string (id), dom node, or Roo.Element
505          * @param {String/HTMLElement/Roo.Element} el
506          * @return HTMLElement
507          */
508         getDom : function(el){
509             if(!el){
510                 return null;
511             }
512             return el.dom ? el.dom : (typeof el == 'string' ? document.getElementById(el) : el);
513         },
514
515         /**
516         * Shorthand for {@link Roo.ComponentMgr#get}
517         * @param {String} id
518         * @return Roo.Component
519         */
520         getCmp : function(id){
521             return Roo.ComponentMgr.get(id);
522         },
523          
524         num : function(v, defaultValue){
525             if(typeof v != 'number'){
526                 return defaultValue;
527             }
528             return v;
529         },
530
531         destroy : function(){
532             for(var i = 0, a = arguments, len = a.length; i < len; i++) {
533                 var as = a[i];
534                 if(as){
535                     if(as.dom){
536                         as.removeAllListeners();
537                         as.remove();
538                         continue;
539                     }
540                     if(typeof as.purgeListeners == 'function'){
541                         as.purgeListeners();
542                     }
543                     if(typeof as.destroy == 'function'){
544                         as.destroy();
545                     }
546                 }
547             }
548         },
549
550         // inpired by a similar function in mootools library
551         /**
552          * Returns the type of object that is passed in. If the object passed in is null or undefined it
553          * return false otherwise it returns one of the following values:<ul>
554          * <li><b>string</b>: If the object passed is a string</li>
555          * <li><b>number</b>: If the object passed is a number</li>
556          * <li><b>boolean</b>: If the object passed is a boolean value</li>
557          * <li><b>function</b>: If the object passed is a function reference</li>
558          * <li><b>object</b>: If the object passed is an object</li>
559          * <li><b>array</b>: If the object passed is an array</li>
560          * <li><b>regexp</b>: If the object passed is a regular expression</li>
561          * <li><b>element</b>: If the object passed is a DOM Element</li>
562          * <li><b>nodelist</b>: If the object passed is a DOM NodeList</li>
563          * <li><b>textnode</b>: If the object passed is a DOM text node and contains something other than whitespace</li>
564          * <li><b>whitespace</b>: If the object passed is a DOM text node and contains only whitespace</li>
565          * @param {Mixed} object
566          * @return {String}
567          */
568         type : function(o){
569             if(o === undefined || o === null){
570                 return false;
571             }
572             if(o.htmlElement){
573                 return 'element';
574             }
575             var t = typeof o;
576             if(t == 'object' && o.nodeName) {
577                 switch(o.nodeType) {
578                     case 1: return 'element';
579                     case 3: return (/\S/).test(o.nodeValue) ? 'textnode' : 'whitespace';
580                 }
581             }
582             if(t == 'object' || t == 'function') {
583                 switch(o.constructor) {
584                     case Array: return 'array';
585                     case RegExp: return 'regexp';
586                 }
587                 if(typeof o.length == 'number' && typeof o.item == 'function') {
588                     return 'nodelist';
589                 }
590             }
591             return t;
592         },
593
594         /**
595          * Returns true if the passed value is null, undefined or an empty string (optional).
596          * @param {Mixed} value The value to test
597          * @param {Boolean} allowBlank (optional) Pass true if an empty string is not considered empty
598          * @return {Boolean}
599          */
600         isEmpty : function(v, allowBlank){
601             return v === null || v === undefined || (!allowBlank ? v === '' : false);
602         },
603         
604         /** @type Boolean */
605         isOpera : isOpera,
606         /** @type Boolean */
607         isSafari : isSafari,
608         /** @type Boolean */
609         isIE : isIE,
610         /** @type Boolean */
611         isIE7 : isIE7,
612         /** @type Boolean */
613         isGecko : isGecko,
614         /** @type Boolean */
615         isBorderBox : isBorderBox,
616         /** @type Boolean */
617         isWindows : isWindows,
618         /** @type Boolean */
619         isLinux : isLinux,
620         /** @type Boolean */
621         isMac : isMac,
622         /** @type Boolean */
623         isTouch : isTouch,
624
625         /**
626          * By default, Ext intelligently decides whether floating elements should be shimmed. If you are using flash,
627          * you may want to set this to true.
628          * @type Boolean
629          */
630         useShims : ((isIE && !isIE7) || (isGecko && isMac)),
631         
632         
633                 
634         /**
635          * Selects a single element as a Roo Element
636          * This is about as close as you can get to jQuery's $('do crazy stuff')
637          * @param {String} selector The selector/xpath query
638          * @param {Node} root (optional) The start of the query (defaults to document).
639          * @return {Roo.Element}
640          */
641         selectNode : function(selector, root) 
642         {
643             var node = Roo.DomQuery.selectNode(selector,root);
644             return node ? Roo.get(node) : new Roo.Element(false);
645         }
646         
647     });
648
649
650 })();
651
652 Roo.namespace("Roo", "Roo.util", "Roo.grid", "Roo.dd", "Roo.tree", "Roo.data",
653                 "Roo.form", "Roo.menu", "Roo.state", "Roo.lib", "Roo.layout", "Roo.app", "Roo.ux");
654 /*
655  * Based on:
656  * Ext JS Library 1.1.1
657  * Copyright(c) 2006-2007, Ext JS, LLC.
658  *
659  * Originally Released Under LGPL - original licence link has changed is not relivant.
660  *
661  * Fork - LGPL
662  * <script type="text/javascript">
663  */
664
665 (function() {    
666     // wrappedn so fnCleanup is not in global scope...
667     if(Roo.isIE) {
668         function fnCleanUp() {
669             var p = Function.prototype;
670             delete p.createSequence;
671             delete p.defer;
672             delete p.createDelegate;
673             delete p.createCallback;
674             delete p.createInterceptor;
675
676             window.detachEvent("onunload", fnCleanUp);
677         }
678         window.attachEvent("onunload", fnCleanUp);
679     }
680 })();
681
682
683 /**
684  * @class Function
685  * These functions are available on every Function object (any JavaScript function).
686  */
687 Roo.apply(Function.prototype, {
688      /**
689      * Creates a callback that passes arguments[0], arguments[1], arguments[2], ...
690      * Call directly on any function. Example: <code>myFunction.createCallback(myarg, myarg2)</code>
691      * Will create a function that is bound to those 2 args.
692      * @return {Function} The new function
693     */
694     createCallback : function(/*args...*/){
695         // make args available, in function below
696         var args = arguments;
697         var method = this;
698         return function() {
699             return method.apply(window, args);
700         };
701     },
702
703     /**
704      * Creates a delegate (callback) that sets the scope to obj.
705      * Call directly on any function. Example: <code>this.myFunction.createDelegate(this)</code>
706      * Will create a function that is automatically scoped to this.
707      * @param {Object} obj (optional) The object for which the scope is set
708      * @param {Array} args (optional) Overrides arguments for the call. (Defaults to the arguments passed by the caller)
709      * @param {Boolean/Number} appendArgs (optional) if True args are appended to call args instead of overriding,
710      *                                             if a number the args are inserted at the specified position
711      * @return {Function} The new function
712      */
713     createDelegate : function(obj, args, appendArgs){
714         var method = this;
715         return function() {
716             var callArgs = args || arguments;
717             if(appendArgs === true){
718                 callArgs = Array.prototype.slice.call(arguments, 0);
719                 callArgs = callArgs.concat(args);
720             }else if(typeof appendArgs == "number"){
721                 callArgs = Array.prototype.slice.call(arguments, 0); // copy arguments first
722                 var applyArgs = [appendArgs, 0].concat(args); // create method call params
723                 Array.prototype.splice.apply(callArgs, applyArgs); // splice them in
724             }
725             return method.apply(obj || window, callArgs);
726         };
727     },
728
729     /**
730      * Calls this function after the number of millseconds specified.
731      * @param {Number} millis The number of milliseconds for the setTimeout call (if 0 the function is executed immediately)
732      * @param {Object} obj (optional) The object for which the scope is set
733      * @param {Array} args (optional) Overrides arguments for the call. (Defaults to the arguments passed by the caller)
734      * @param {Boolean/Number} appendArgs (optional) if True args are appended to call args instead of overriding,
735      *                                             if a number the args are inserted at the specified position
736      * @return {Number} The timeout id that can be used with clearTimeout
737      */
738     defer : function(millis, obj, args, appendArgs){
739         var fn = this.createDelegate(obj, args, appendArgs);
740         if(millis){
741             return setTimeout(fn, millis);
742         }
743         fn();
744         return 0;
745     },
746     /**
747      * Create a combined function call sequence of the original function + the passed function.
748      * The resulting function returns the results of the original function.
749      * The passed fcn is called with the parameters of the original function
750      * @param {Function} fcn The function to sequence
751      * @param {Object} scope (optional) The scope of the passed fcn (Defaults to scope of original function or window)
752      * @return {Function} The new function
753      */
754     createSequence : function(fcn, scope){
755         if(typeof fcn != "function"){
756             return this;
757         }
758         var method = this;
759         return function() {
760             var retval = method.apply(this || window, arguments);
761             fcn.apply(scope || this || window, arguments);
762             return retval;
763         };
764     },
765
766     /**
767      * Creates an interceptor function. The passed fcn is called before the original one. If it returns false, the original one is not called.
768      * The resulting function returns the results of the original function.
769      * The passed fcn is called with the parameters of the original function.
770      * @addon
771      * @param {Function} fcn The function to call before the original
772      * @param {Object} scope (optional) The scope of the passed fcn (Defaults to scope of original function or window)
773      * @return {Function} The new function
774      */
775     createInterceptor : function(fcn, scope){
776         if(typeof fcn != "function"){
777             return this;
778         }
779         var method = this;
780         return function() {
781             fcn.target = this;
782             fcn.method = method;
783             if(fcn.apply(scope || this || window, arguments) === false){
784                 return;
785             }
786             return method.apply(this || window, arguments);
787         };
788     }
789 });
790 /*
791  * Based on:
792  * Ext JS Library 1.1.1
793  * Copyright(c) 2006-2007, Ext JS, LLC.
794  *
795  * Originally Released Under LGPL - original licence link has changed is not relivant.
796  *
797  * Fork - LGPL
798  * <script type="text/javascript">
799  */
800
801 Roo.applyIf(String, {
802     
803     /** @scope String */
804     
805     /**
806      * Escapes the passed string for ' and \
807      * @param {String} string The string to escape
808      * @return {String} The escaped string
809      * @static
810      */
811     escape : function(string) {
812         return string.replace(/('|\\)/g, "\\$1");
813     },
814
815     /**
816      * Pads the left side of a string with a specified character.  This is especially useful
817      * for normalizing number and date strings.  Example usage:
818      * <pre><code>
819 var s = String.leftPad('123', 5, '0');
820 // s now contains the string: '00123'
821 </code></pre>
822      * @param {String} string The original string
823      * @param {Number} size The total length of the output string
824      * @param {String} char (optional) The character with which to pad the original string (defaults to empty string " ")
825      * @return {String} The padded string
826      * @static
827      */
828     leftPad : function (val, size, ch) {
829         var result = new String(val);
830         if(ch === null || ch === undefined || ch === '') {
831             ch = " ";
832         }
833         while (result.length < size) {
834             result = ch + result;
835         }
836         return result;
837     },
838
839     /**
840      * Allows you to define a tokenized string and pass an arbitrary number of arguments to replace the tokens.  Each
841      * token must be unique, and must increment in the format {0}, {1}, etc.  Example usage:
842      * <pre><code>
843 var cls = 'my-class', text = 'Some text';
844 var s = String.format('<div class="{0}">{1}</div>', cls, text);
845 // s now contains the string: '<div class="my-class">Some text</div>'
846 </code></pre>
847      * @param {String} string The tokenized string to be formatted
848      * @param {String} value1 The value to replace token {0}
849      * @param {String} value2 Etc...
850      * @return {String} The formatted string
851      * @static
852      */
853     format : function(format){
854         var args = Array.prototype.slice.call(arguments, 1);
855         return format.replace(/\{(\d+)\}/g, function(m, i){
856             return Roo.util.Format.htmlEncode(args[i]);
857         });
858     }
859 });
860
861 /**
862  * Utility function that allows you to easily switch a string between two alternating values.  The passed value
863  * is compared to the current string, and if they are equal, the other value that was passed in is returned.  If
864  * they are already different, the first value passed in is returned.  Note that this method returns the new value
865  * but does not change the current string.
866  * <pre><code>
867 // alternate sort directions
868 sort = sort.toggle('ASC', 'DESC');
869
870 // instead of conditional logic:
871 sort = (sort == 'ASC' ? 'DESC' : 'ASC');
872 </code></pre>
873  * @param {String} value The value to compare to the current string
874  * @param {String} other The new value to use if the string already equals the first value passed in
875  * @return {String} The new value
876  */
877  
878 String.prototype.toggle = function(value, other){
879     return this == value ? other : value;
880 };/*
881  * Based on:
882  * Ext JS Library 1.1.1
883  * Copyright(c) 2006-2007, Ext JS, LLC.
884  *
885  * Originally Released Under LGPL - original licence link has changed is not relivant.
886  *
887  * Fork - LGPL
888  * <script type="text/javascript">
889  */
890
891  /**
892  * @class Number
893  */
894 Roo.applyIf(Number.prototype, {
895     /**
896      * Checks whether or not the current number is within a desired range.  If the number is already within the
897      * range it is returned, otherwise the min or max value is returned depending on which side of the range is
898      * exceeded.  Note that this method returns the constrained value but does not change the current number.
899      * @param {Number} min The minimum number in the range
900      * @param {Number} max The maximum number in the range
901      * @return {Number} The constrained value if outside the range, otherwise the current value
902      */
903     constrain : function(min, max){
904         return Math.min(Math.max(this, min), max);
905     }
906 });/*
907  * Based on:
908  * Ext JS Library 1.1.1
909  * Copyright(c) 2006-2007, Ext JS, LLC.
910  *
911  * Originally Released Under LGPL - original licence link has changed is not relivant.
912  *
913  * Fork - LGPL
914  * <script type="text/javascript">
915  */
916  /**
917  * @class Array
918  */
919 Roo.applyIf(Array.prototype, {
920     /**
921      * Checks whether or not the specified object exists in the array.
922      * @param {Object} o The object to check for
923      * @return {Number} The index of o in the array (or -1 if it is not found)
924      */
925     indexOf : function(o){
926        for (var i = 0, len = this.length; i < len; i++){
927               if(this[i] == o) return i;
928        }
929            return -1;
930     },
931
932     /**
933      * Removes the specified object from the array.  If the object is not found nothing happens.
934      * @param {Object} o The object to remove
935      */
936     remove : function(o){
937        var index = this.indexOf(o);
938        if(index != -1){
939            this.splice(index, 1);
940        }
941     },
942     /**
943      * Map (JS 1.6 compatibility)
944      * @param {Function} function  to call
945      */
946     map : function(fun )
947     {
948         var len = this.length >>> 0;
949         if (typeof fun != "function")
950             throw new TypeError();
951
952         var res = new Array(len);
953         var thisp = arguments[1];
954         for (var i = 0; i < len; i++)
955         {
956             if (i in this)
957                 res[i] = fun.call(thisp, this[i], i, this);
958         }
959
960         return res;
961     }
962     
963 });
964
965
966  /*
967  * Based on:
968  * Ext JS Library 1.1.1
969  * Copyright(c) 2006-2007, Ext JS, LLC.
970  *
971  * Originally Released Under LGPL - original licence link has changed is not relivant.
972  *
973  * Fork - LGPL
974  * <script type="text/javascript">
975  */
976
977 /**
978  * @class Date
979  *
980  * The date parsing and format syntax is a subset of
981  * <a href="http://www.php.net/date">PHP's date() function</a>, and the formats that are
982  * supported will provide results equivalent to their PHP versions.
983  *
984  * Following is the list of all currently supported formats:
985  *<pre>
986 Sample date:
987 'Wed Jan 10 2007 15:05:01 GMT-0600 (Central Standard Time)'
988
989 Format  Output      Description
990 ------  ----------  --------------------------------------------------------------
991   d      10         Day of the month, 2 digits with leading zeros
992   D      Wed        A textual representation of a day, three letters
993   j      10         Day of the month without leading zeros
994   l      Wednesday  A full textual representation of the day of the week
995   S      th         English ordinal day of month suffix, 2 chars (use with j)
996   w      3          Numeric representation of the day of the week
997   z      9          The julian date, or day of the year (0-365)
998   W      01         ISO-8601 2-digit week number of year, weeks starting on Monday (00-52)
999   F      January    A full textual representation of the month
1000   m      01         Numeric representation of a month, with leading zeros
1001   M      Jan        Month name abbreviation, three letters
1002   n      1          Numeric representation of a month, without leading zeros
1003   t      31         Number of days in the given month
1004   L      0          Whether it's a leap year (1 if it is a leap year, else 0)
1005   Y      2007       A full numeric representation of a year, 4 digits
1006   y      07         A two digit representation of a year
1007   a      pm         Lowercase Ante meridiem and Post meridiem
1008   A      PM         Uppercase Ante meridiem and Post meridiem
1009   g      3          12-hour format of an hour without leading zeros
1010   G      15         24-hour format of an hour without leading zeros
1011   h      03         12-hour format of an hour with leading zeros
1012   H      15         24-hour format of an hour with leading zeros
1013   i      05         Minutes with leading zeros
1014   s      01         Seconds, with leading zeros
1015   O      -0600      Difference to Greenwich time (GMT) in hours (Allows +08, without minutes)
1016   P      -06:00     Difference to Greenwich time (GMT) with colon between hours and minutes
1017   T      CST        Timezone setting of the machine running the code
1018   Z      -21600     Timezone offset in seconds (negative if west of UTC, positive if east)
1019 </pre>
1020  *
1021  * Example usage (note that you must escape format specifiers with '\\' to render them as character literals):
1022  * <pre><code>
1023 var dt = new Date('1/10/2007 03:05:01 PM GMT-0600');
1024 document.write(dt.format('Y-m-d'));                         //2007-01-10
1025 document.write(dt.format('F j, Y, g:i a'));                 //January 10, 2007, 3:05 pm
1026 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
1027  </code></pre>
1028  *
1029  * Here are some standard date/time patterns that you might find helpful.  They
1030  * are not part of the source of Date.js, but to use them you can simply copy this
1031  * block of code into any script that is included after Date.js and they will also become
1032  * globally available on the Date object.  Feel free to add or remove patterns as needed in your code.
1033  * <pre><code>
1034 Date.patterns = {
1035     ISO8601Long:"Y-m-d H:i:s",
1036     ISO8601Short:"Y-m-d",
1037     ShortDate: "n/j/Y",
1038     LongDate: "l, F d, Y",
1039     FullDateTime: "l, F d, Y g:i:s A",
1040     MonthDay: "F d",
1041     ShortTime: "g:i A",
1042     LongTime: "g:i:s A",
1043     SortableDateTime: "Y-m-d\\TH:i:s",
1044     UniversalSortableDateTime: "Y-m-d H:i:sO",
1045     YearMonth: "F, Y"
1046 };
1047 </code></pre>
1048  *
1049  * Example usage:
1050  * <pre><code>
1051 var dt = new Date();
1052 document.write(dt.format(Date.patterns.ShortDate));
1053  </code></pre>
1054  */
1055
1056 /*
1057  * Most of the date-formatting functions below are the excellent work of Baron Schwartz.
1058  * They generate precompiled functions from date formats instead of parsing and
1059  * processing the pattern every time you format a date.  These functions are available
1060  * on every Date object (any javascript function).
1061  *
1062  * The original article and download are here:
1063  * http://www.xaprb.com/blog/2005/12/12/javascript-closures-for-runtime-efficiency/
1064  *
1065  */
1066  
1067  
1068  // was in core
1069 /**
1070  Returns the number of milliseconds between this date and date
1071  @param {Date} date (optional) Defaults to now
1072  @return {Number} The diff in milliseconds
1073  @member Date getElapsed
1074  */
1075 Date.prototype.getElapsed = function(date) {
1076         return Math.abs((date || new Date()).getTime()-this.getTime());
1077 };
1078 // was in date file..
1079
1080
1081 // private
1082 Date.parseFunctions = {count:0};
1083 // private
1084 Date.parseRegexes = [];
1085 // private
1086 Date.formatFunctions = {count:0};
1087
1088 // private
1089 Date.prototype.dateFormat = function(format) {
1090     if (Date.formatFunctions[format] == null) {
1091         Date.createNewFormat(format);
1092     }
1093     var func = Date.formatFunctions[format];
1094     return this[func]();
1095 };
1096
1097
1098 /**
1099  * Formats a date given the supplied format string
1100  * @param {String} format The format string
1101  * @return {String} The formatted date
1102  * @method
1103  */
1104 Date.prototype.format = Date.prototype.dateFormat;
1105
1106 // private
1107 Date.createNewFormat = function(format) {
1108     var funcName = "format" + Date.formatFunctions.count++;
1109     Date.formatFunctions[format] = funcName;
1110     var code = "Date.prototype." + funcName + " = function(){return ";
1111     var special = false;
1112     var ch = '';
1113     for (var i = 0; i < format.length; ++i) {
1114         ch = format.charAt(i);
1115         if (!special && ch == "\\") {
1116             special = true;
1117         }
1118         else if (special) {
1119             special = false;
1120             code += "'" + String.escape(ch) + "' + ";
1121         }
1122         else {
1123             code += Date.getFormatCode(ch);
1124         }
1125     }
1126     /** eval:var:zzzzzzzzzzzzz */
1127     eval(code.substring(0, code.length - 3) + ";}");
1128 };
1129
1130 // private
1131 Date.getFormatCode = function(character) {
1132     switch (character) {
1133     case "d":
1134         return "String.leftPad(this.getDate(), 2, '0') + ";
1135     case "D":
1136         return "Date.dayNames[this.getDay()].substring(0, 3) + ";
1137     case "j":
1138         return "this.getDate() + ";
1139     case "l":
1140         return "Date.dayNames[this.getDay()] + ";
1141     case "S":
1142         return "this.getSuffix() + ";
1143     case "w":
1144         return "this.getDay() + ";
1145     case "z":
1146         return "this.getDayOfYear() + ";
1147     case "W":
1148         return "this.getWeekOfYear() + ";
1149     case "F":
1150         return "Date.monthNames[this.getMonth()] + ";
1151     case "m":
1152         return "String.leftPad(this.getMonth() + 1, 2, '0') + ";
1153     case "M":
1154         return "Date.monthNames[this.getMonth()].substring(0, 3) + ";
1155     case "n":
1156         return "(this.getMonth() + 1) + ";
1157     case "t":
1158         return "this.getDaysInMonth() + ";
1159     case "L":
1160         return "(this.isLeapYear() ? 1 : 0) + ";
1161     case "Y":
1162         return "this.getFullYear() + ";
1163     case "y":
1164         return "('' + this.getFullYear()).substring(2, 4) + ";
1165     case "a":
1166         return "(this.getHours() < 12 ? 'am' : 'pm') + ";
1167     case "A":
1168         return "(this.getHours() < 12 ? 'AM' : 'PM') + ";
1169     case "g":
1170         return "((this.getHours() % 12) ? this.getHours() % 12 : 12) + ";
1171     case "G":
1172         return "this.getHours() + ";
1173     case "h":
1174         return "String.leftPad((this.getHours() % 12) ? this.getHours() % 12 : 12, 2, '0') + ";
1175     case "H":
1176         return "String.leftPad(this.getHours(), 2, '0') + ";
1177     case "i":
1178         return "String.leftPad(this.getMinutes(), 2, '0') + ";
1179     case "s":
1180         return "String.leftPad(this.getSeconds(), 2, '0') + ";
1181     case "O":
1182         return "this.getGMTOffset() + ";
1183     case "P":
1184         return "this.getGMTColonOffset() + ";
1185     case "T":
1186         return "this.getTimezone() + ";
1187     case "Z":
1188         return "(this.getTimezoneOffset() * -60) + ";
1189     default:
1190         return "'" + String.escape(character) + "' + ";
1191     }
1192 };
1193
1194 /**
1195  * Parses the passed string using the specified format. Note that this function expects dates in normal calendar
1196  * format, meaning that months are 1-based (1 = January) and not zero-based like in JavaScript dates.  Any part of
1197  * the date format that is not specified will default to the current date value for that part.  Time parts can also
1198  * be specified, but default to 0.  Keep in mind that the input date string must precisely match the specified format
1199  * string or the parse operation will fail.
1200  * Example Usage:
1201 <pre><code>
1202 //dt = Fri May 25 2007 (current date)
1203 var dt = new Date();
1204
1205 //dt = Thu May 25 2006 (today's month/day in 2006)
1206 dt = Date.parseDate("2006", "Y");
1207
1208 //dt = Sun Jan 15 2006 (all date parts specified)
1209 dt = Date.parseDate("2006-1-15", "Y-m-d");
1210
1211 //dt = Sun Jan 15 2006 15:20:01 GMT-0600 (CST)
1212 dt = Date.parseDate("2006-1-15 3:20:01 PM", "Y-m-d h:i:s A" );
1213 </code></pre>
1214  * @param {String} input The unparsed date as a string
1215  * @param {String} format The format the date is in
1216  * @return {Date} The parsed date
1217  * @static
1218  */
1219 Date.parseDate = function(input, format) {
1220     if (Date.parseFunctions[format] == null) {
1221         Date.createParser(format);
1222     }
1223     var func = Date.parseFunctions[format];
1224     return Date[func](input);
1225 };
1226 /**
1227  * @private
1228  */
1229 Date.createParser = function(format) {
1230     var funcName = "parse" + Date.parseFunctions.count++;
1231     var regexNum = Date.parseRegexes.length;
1232     var currentGroup = 1;
1233     Date.parseFunctions[format] = funcName;
1234
1235     var code = "Date." + funcName + " = function(input){\n"
1236         + "var y = -1, m = -1, d = -1, h = -1, i = -1, s = -1, o, z, v;\n"
1237         + "var d = new Date();\n"
1238         + "y = d.getFullYear();\n"
1239         + "m = d.getMonth();\n"
1240         + "d = d.getDate();\n"
1241         + "var results = input.match(Date.parseRegexes[" + regexNum + "]);\n"
1242         + "if (results && results.length > 0) {";
1243     var regex = "";
1244
1245     var special = false;
1246     var ch = '';
1247     for (var i = 0; i < format.length; ++i) {
1248         ch = format.charAt(i);
1249         if (!special && ch == "\\") {
1250             special = true;
1251         }
1252         else if (special) {
1253             special = false;
1254             regex += String.escape(ch);
1255         }
1256         else {
1257             var obj = Date.formatCodeToRegex(ch, currentGroup);
1258             currentGroup += obj.g;
1259             regex += obj.s;
1260             if (obj.g && obj.c) {
1261                 code += obj.c;
1262             }
1263         }
1264     }
1265
1266     code += "if (y >= 0 && m >= 0 && d > 0 && h >= 0 && i >= 0 && s >= 0)\n"
1267         + "{v = new Date(y, m, d, h, i, s);}\n"
1268         + "else if (y >= 0 && m >= 0 && d > 0 && h >= 0 && i >= 0)\n"
1269         + "{v = new Date(y, m, d, h, i);}\n"
1270         + "else if (y >= 0 && m >= 0 && d > 0 && h >= 0)\n"
1271         + "{v = new Date(y, m, d, h);}\n"
1272         + "else if (y >= 0 && m >= 0 && d > 0)\n"
1273         + "{v = new Date(y, m, d);}\n"
1274         + "else if (y >= 0 && m >= 0)\n"
1275         + "{v = new Date(y, m);}\n"
1276         + "else if (y >= 0)\n"
1277         + "{v = new Date(y);}\n"
1278         + "}return (v && (z || o))?\n" // favour UTC offset over GMT offset
1279         + "    ((z)? v.add(Date.SECOND, (v.getTimezoneOffset() * 60) + (z*1)) :\n" // reset to UTC, then add offset
1280         + "        v.add(Date.HOUR, (v.getGMTOffset() / 100) + (o / -100))) : v\n" // reset to GMT, then add offset
1281         + ";}";
1282
1283     Date.parseRegexes[regexNum] = new RegExp("^" + regex + "$");
1284     /** eval:var:zzzzzzzzzzzzz */
1285     eval(code);
1286 };
1287
1288 // private
1289 Date.formatCodeToRegex = function(character, currentGroup) {
1290     switch (character) {
1291     case "D":
1292         return {g:0,
1293         c:null,
1294         s:"(?:Sun|Mon|Tue|Wed|Thu|Fri|Sat)"};
1295     case "j":
1296         return {g:1,
1297             c:"d = parseInt(results[" + currentGroup + "], 10);\n",
1298             s:"(\\d{1,2})"}; // day of month without leading zeroes
1299     case "d":
1300         return {g:1,
1301             c:"d = parseInt(results[" + currentGroup + "], 10);\n",
1302             s:"(\\d{2})"}; // day of month with leading zeroes
1303     case "l":
1304         return {g:0,
1305             c:null,
1306             s:"(?:" + Date.dayNames.join("|") + ")"};
1307     case "S":
1308         return {g:0,
1309             c:null,
1310             s:"(?:st|nd|rd|th)"};
1311     case "w":
1312         return {g:0,
1313             c:null,
1314             s:"\\d"};
1315     case "z":
1316         return {g:0,
1317             c:null,
1318             s:"(?:\\d{1,3})"};
1319     case "W":
1320         return {g:0,
1321             c:null,
1322             s:"(?:\\d{2})"};
1323     case "F":
1324         return {g:1,
1325             c:"m = parseInt(Date.monthNumbers[results[" + currentGroup + "].substring(0, 3)], 10);\n",
1326             s:"(" + Date.monthNames.join("|") + ")"};
1327     case "M":
1328         return {g:1,
1329             c:"m = parseInt(Date.monthNumbers[results[" + currentGroup + "]], 10);\n",
1330             s:"(Jan|Feb|Mar|Apr|May|Jun|Jul|Aug|Sep|Oct|Nov|Dec)"};
1331     case "n":
1332         return {g:1,
1333             c:"m = parseInt(results[" + currentGroup + "], 10) - 1;\n",
1334             s:"(\\d{1,2})"}; // Numeric representation of a month, without leading zeros
1335     case "m":
1336         return {g:1,
1337             c:"m = parseInt(results[" + currentGroup + "], 10) - 1;\n",
1338             s:"(\\d{2})"}; // Numeric representation of a month, with leading zeros
1339     case "t":
1340         return {g:0,
1341             c:null,
1342             s:"\\d{1,2}"};
1343     case "L":
1344         return {g:0,
1345             c:null,
1346             s:"(?:1|0)"};
1347     case "Y":
1348         return {g:1,
1349             c:"y = parseInt(results[" + currentGroup + "], 10);\n",
1350             s:"(\\d{4})"};
1351     case "y":
1352         return {g:1,
1353             c:"var ty = parseInt(results[" + currentGroup + "], 10);\n"
1354                 + "y = ty > Date.y2kYear ? 1900 + ty : 2000 + ty;\n",
1355             s:"(\\d{1,2})"};
1356     case "a":
1357         return {g:1,
1358             c:"if (results[" + currentGroup + "] == 'am') {\n"
1359                 + "if (h == 12) { h = 0; }\n"
1360                 + "} else { if (h < 12) { h += 12; }}",
1361             s:"(am|pm)"};
1362     case "A":
1363         return {g:1,
1364             c:"if (results[" + currentGroup + "] == 'AM') {\n"
1365                 + "if (h == 12) { h = 0; }\n"
1366                 + "} else { if (h < 12) { h += 12; }}",
1367             s:"(AM|PM)"};
1368     case "g":
1369     case "G":
1370         return {g:1,
1371             c:"h = parseInt(results[" + currentGroup + "], 10);\n",
1372             s:"(\\d{1,2})"}; // 12/24-hr format  format of an hour without leading zeroes
1373     case "h":
1374     case "H":
1375         return {g:1,
1376             c:"h = parseInt(results[" + currentGroup + "], 10);\n",
1377             s:"(\\d{2})"}; //  12/24-hr format  format of an hour with leading zeroes
1378     case "i":
1379         return {g:1,
1380             c:"i = parseInt(results[" + currentGroup + "], 10);\n",
1381             s:"(\\d{2})"};
1382     case "s":
1383         return {g:1,
1384             c:"s = parseInt(results[" + currentGroup + "], 10);\n",
1385             s:"(\\d{2})"};
1386     case "O":
1387         return {g:1,
1388             c:[
1389                 "o = results[", currentGroup, "];\n",
1390                 "var sn = o.substring(0,1);\n", // get + / - sign
1391                 "var hr = o.substring(1,3)*1 + Math.floor(o.substring(3,5) / 60);\n", // get hours (performs minutes-to-hour conversion also)
1392                 "var mn = o.substring(3,5) % 60;\n", // get minutes
1393                 "o = ((-12 <= (hr*60 + mn)/60) && ((hr*60 + mn)/60 <= 14))?\n", // -12hrs <= GMT offset <= 14hrs
1394                 "    (sn + String.leftPad(hr, 2, 0) + String.leftPad(mn, 2, 0)) : null;\n"
1395             ].join(""),
1396             s:"([+\-]\\d{2,4})"};
1397     
1398     
1399     case "P":
1400         return {g:1,
1401                 c:[
1402                    "o = results[", currentGroup, "];\n",
1403                    "var sn = o.substring(0,1);\n",
1404                    "var hr = o.substring(1,3)*1 + Math.floor(o.substring(4,6) / 60);\n",
1405                    "var mn = o.substring(4,6) % 60;\n",
1406                    "o = ((-12 <= (hr*60 + mn)/60) && ((hr*60 + mn)/60 <= 14))?\n",
1407                         "    (sn + String.leftPad(hr, 2, 0) + String.leftPad(mn, 2, 0)) : null;\n"
1408             ].join(""),
1409             s:"([+\-]\\d{4})"};
1410     case "T":
1411         return {g:0,
1412             c:null,
1413             s:"[A-Z]{1,4}"}; // timezone abbrev. may be between 1 - 4 chars
1414     case "Z":
1415         return {g:1,
1416             c:"z = results[" + currentGroup + "];\n" // -43200 <= UTC offset <= 50400
1417                   + "z = (-43200 <= z*1 && z*1 <= 50400)? z : null;\n",
1418             s:"([+\-]?\\d{1,5})"}; // leading '+' sign is optional for UTC offset
1419     default:
1420         return {g:0,
1421             c:null,
1422             s:String.escape(character)};
1423     }
1424 };
1425
1426 /**
1427  * Get the timezone abbreviation of the current date (equivalent to the format specifier 'T').
1428  * @return {String} The abbreviated timezone name (e.g. 'CST')
1429  */
1430 Date.prototype.getTimezone = function() {
1431     return this.toString().replace(/^.*? ([A-Z]{1,4})[\-+][0-9]{4} .*$/, "$1");
1432 };
1433
1434 /**
1435  * Get the offset from GMT of the current date (equivalent to the format specifier 'O').
1436  * @return {String} The 4-character offset string prefixed with + or - (e.g. '-0600')
1437  */
1438 Date.prototype.getGMTOffset = function() {
1439     return (this.getTimezoneOffset() > 0 ? "-" : "+")
1440         + String.leftPad(Math.abs(Math.floor(this.getTimezoneOffset() / 60)), 2, "0")
1441         + String.leftPad(this.getTimezoneOffset() % 60, 2, "0");
1442 };
1443
1444 /**
1445  * Get the offset from GMT of the current date (equivalent to the format specifier 'P').
1446  * @return {String} 2-characters representing hours and 2-characters representing minutes
1447  * seperated by a colon and prefixed with + or - (e.g. '-06:00')
1448  */
1449 Date.prototype.getGMTColonOffset = function() {
1450         return (this.getTimezoneOffset() > 0 ? "-" : "+")
1451                 + String.leftPad(Math.abs(Math.floor(this.getTimezoneOffset() / 60)), 2, "0")
1452                 + ":"
1453                 + String.leftPad(this.getTimezoneOffset() %60, 2, "0");
1454 }
1455
1456 /**
1457  * Get the numeric day number of the year, adjusted for leap year.
1458  * @return {Number} 0 through 364 (365 in leap years)
1459  */
1460 Date.prototype.getDayOfYear = function() {
1461     var num = 0;
1462     Date.daysInMonth[1] = this.isLeapYear() ? 29 : 28;
1463     for (var i = 0; i < this.getMonth(); ++i) {
1464         num += Date.daysInMonth[i];
1465     }
1466     return num + this.getDate() - 1;
1467 };
1468
1469 /**
1470  * Get the string representation of the numeric week number of the year
1471  * (equivalent to the format specifier 'W').
1472  * @return {String} '00' through '52'
1473  */
1474 Date.prototype.getWeekOfYear = function() {
1475     // Skip to Thursday of this week
1476     var now = this.getDayOfYear() + (4 - this.getDay());
1477     // Find the first Thursday of the year
1478     var jan1 = new Date(this.getFullYear(), 0, 1);
1479     var then = (7 - jan1.getDay() + 4);
1480     return String.leftPad(((now - then) / 7) + 1, 2, "0");
1481 };
1482
1483 /**
1484  * Whether or not the current date is in a leap year.
1485  * @return {Boolean} True if the current date is in a leap year, else false
1486  */
1487 Date.prototype.isLeapYear = function() {
1488     var year = this.getFullYear();
1489     return ((year & 3) == 0 && (year % 100 || (year % 400 == 0 && year)));
1490 };
1491
1492 /**
1493  * Get the first day of the current month, adjusted for leap year.  The returned value
1494  * is the numeric day index within the week (0-6) which can be used in conjunction with
1495  * the {@link #monthNames} array to retrieve the textual day name.
1496  * Example:
1497  *<pre><code>
1498 var dt = new Date('1/10/2007');
1499 document.write(Date.dayNames[dt.getFirstDayOfMonth()]); //output: 'Monday'
1500 </code></pre>
1501  * @return {Number} The day number (0-6)
1502  */
1503 Date.prototype.getFirstDayOfMonth = function() {
1504     var day = (this.getDay() - (this.getDate() - 1)) % 7;
1505     return (day < 0) ? (day + 7) : day;
1506 };
1507
1508 /**
1509  * Get the last day of the current month, adjusted for leap year.  The returned value
1510  * is the numeric day index within the week (0-6) which can be used in conjunction with
1511  * the {@link #monthNames} array to retrieve the textual day name.
1512  * Example:
1513  *<pre><code>
1514 var dt = new Date('1/10/2007');
1515 document.write(Date.dayNames[dt.getLastDayOfMonth()]); //output: 'Wednesday'
1516 </code></pre>
1517  * @return {Number} The day number (0-6)
1518  */
1519 Date.prototype.getLastDayOfMonth = function() {
1520     var day = (this.getDay() + (Date.daysInMonth[this.getMonth()] - this.getDate())) % 7;
1521     return (day < 0) ? (day + 7) : day;
1522 };
1523
1524
1525 /**
1526  * Get the first date of this date's month
1527  * @return {Date}
1528  */
1529 Date.prototype.getFirstDateOfMonth = function() {
1530     return new Date(this.getFullYear(), this.getMonth(), 1);
1531 };
1532
1533 /**
1534  * Get the last date of this date's month
1535  * @return {Date}
1536  */
1537 Date.prototype.getLastDateOfMonth = function() {
1538     return new Date(this.getFullYear(), this.getMonth(), this.getDaysInMonth());
1539 };
1540 /**
1541  * Get the number of days in the current month, adjusted for leap year.
1542  * @return {Number} The number of days in the month
1543  */
1544 Date.prototype.getDaysInMonth = function() {
1545     Date.daysInMonth[1] = this.isLeapYear() ? 29 : 28;
1546     return Date.daysInMonth[this.getMonth()];
1547 };
1548
1549 /**
1550  * Get the English ordinal suffix of the current day (equivalent to the format specifier 'S').
1551  * @return {String} 'st, 'nd', 'rd' or 'th'
1552  */
1553 Date.prototype.getSuffix = function() {
1554     switch (this.getDate()) {
1555         case 1:
1556         case 21:
1557         case 31:
1558             return "st";
1559         case 2:
1560         case 22:
1561             return "nd";
1562         case 3:
1563         case 23:
1564             return "rd";
1565         default:
1566             return "th";
1567     }
1568 };
1569
1570 // private
1571 Date.daysInMonth = [31,28,31,30,31,30,31,31,30,31,30,31];
1572
1573 /**
1574  * An array of textual month names.
1575  * Override these values for international dates, for example...
1576  * Date.monthNames = ['JanInYourLang', 'FebInYourLang', ...];
1577  * @type Array
1578  * @static
1579  */
1580 Date.monthNames =
1581    ["January",
1582     "February",
1583     "March",
1584     "April",
1585     "May",
1586     "June",
1587     "July",
1588     "August",
1589     "September",
1590     "October",
1591     "November",
1592     "December"];
1593
1594 /**
1595  * An array of textual day names.
1596  * Override these values for international dates, for example...
1597  * Date.dayNames = ['SundayInYourLang', 'MondayInYourLang', ...];
1598  * @type Array
1599  * @static
1600  */
1601 Date.dayNames =
1602    ["Sunday",
1603     "Monday",
1604     "Tuesday",
1605     "Wednesday",
1606     "Thursday",
1607     "Friday",
1608     "Saturday"];
1609
1610 // private
1611 Date.y2kYear = 50;
1612 // private
1613 Date.monthNumbers = {
1614     Jan:0,
1615     Feb:1,
1616     Mar:2,
1617     Apr:3,
1618     May:4,
1619     Jun:5,
1620     Jul:6,
1621     Aug:7,
1622     Sep:8,
1623     Oct:9,
1624     Nov:10,
1625     Dec:11};
1626
1627 /**
1628  * Creates and returns a new Date instance with the exact same date value as the called instance.
1629  * Dates are copied and passed by reference, so if a copied date variable is modified later, the original
1630  * variable will also be changed.  When the intention is to create a new variable that will not
1631  * modify the original instance, you should create a clone.
1632  *
1633  * Example of correctly cloning a date:
1634  * <pre><code>
1635 //wrong way:
1636 var orig = new Date('10/1/2006');
1637 var copy = orig;
1638 copy.setDate(5);
1639 document.write(orig);  //returns 'Thu Oct 05 2006'!
1640
1641 //correct way:
1642 var orig = new Date('10/1/2006');
1643 var copy = orig.clone();
1644 copy.setDate(5);
1645 document.write(orig);  //returns 'Thu Oct 01 2006'
1646 </code></pre>
1647  * @return {Date} The new Date instance
1648  */
1649 Date.prototype.clone = function() {
1650         return new Date(this.getTime());
1651 };
1652
1653 /**
1654  * Clears any time information from this date
1655  @param {Boolean} clone true to create a clone of this date, clear the time and return it
1656  @return {Date} this or the clone
1657  */
1658 Date.prototype.clearTime = function(clone){
1659     if(clone){
1660         return this.clone().clearTime();
1661     }
1662     this.setHours(0);
1663     this.setMinutes(0);
1664     this.setSeconds(0);
1665     this.setMilliseconds(0);
1666     return this;
1667 };
1668
1669 // private
1670 // safari setMonth is broken
1671 if(Roo.isSafari){
1672     Date.brokenSetMonth = Date.prototype.setMonth;
1673         Date.prototype.setMonth = function(num){
1674                 if(num <= -1){
1675                         var n = Math.ceil(-num);
1676                         var back_year = Math.ceil(n/12);
1677                         var month = (n % 12) ? 12 - n % 12 : 0 ;
1678                         this.setFullYear(this.getFullYear() - back_year);
1679                         return Date.brokenSetMonth.call(this, month);
1680                 } else {
1681                         return Date.brokenSetMonth.apply(this, arguments);
1682                 }
1683         };
1684 }
1685
1686 /** Date interval constant 
1687 * @static 
1688 * @type String */
1689 Date.MILLI = "ms";
1690 /** Date interval constant 
1691 * @static 
1692 * @type String */
1693 Date.SECOND = "s";
1694 /** Date interval constant 
1695 * @static 
1696 * @type String */
1697 Date.MINUTE = "mi";
1698 /** Date interval constant 
1699 * @static 
1700 * @type String */
1701 Date.HOUR = "h";
1702 /** Date interval constant 
1703 * @static 
1704 * @type String */
1705 Date.DAY = "d";
1706 /** Date interval constant 
1707 * @static 
1708 * @type String */
1709 Date.MONTH = "mo";
1710 /** Date interval constant 
1711 * @static 
1712 * @type String */
1713 Date.YEAR = "y";
1714
1715 /**
1716  * Provides a convenient method of performing basic date arithmetic.  This method
1717  * does not modify the Date instance being called - it creates and returns
1718  * a new Date instance containing the resulting date value.
1719  *
1720  * Examples:
1721  * <pre><code>
1722 //Basic usage:
1723 var dt = new Date('10/29/2006').add(Date.DAY, 5);
1724 document.write(dt); //returns 'Fri Oct 06 2006 00:00:00'
1725
1726 //Negative values will subtract correctly:
1727 var dt2 = new Date('10/1/2006').add(Date.DAY, -5);
1728 document.write(dt2); //returns 'Tue Sep 26 2006 00:00:00'
1729
1730 //You can even chain several calls together in one line!
1731 var dt3 = new Date('10/1/2006').add(Date.DAY, 5).add(Date.HOUR, 8).add(Date.MINUTE, -30);
1732 document.write(dt3); //returns 'Fri Oct 06 2006 07:30:00'
1733  </code></pre>
1734  *
1735  * @param {String} interval   A valid date interval enum value
1736  * @param {Number} value      The amount to add to the current date
1737  * @return {Date} The new Date instance
1738  */
1739 Date.prototype.add = function(interval, value){
1740   var d = this.clone();
1741   if (!interval || value === 0) return d;
1742   switch(interval.toLowerCase()){
1743     case Date.MILLI:
1744       d.setMilliseconds(this.getMilliseconds() + value);
1745       break;
1746     case Date.SECOND:
1747       d.setSeconds(this.getSeconds() + value);
1748       break;
1749     case Date.MINUTE:
1750       d.setMinutes(this.getMinutes() + value);
1751       break;
1752     case Date.HOUR:
1753       d.setHours(this.getHours() + value);
1754       break;
1755     case Date.DAY:
1756       d.setDate(this.getDate() + value);
1757       break;
1758     case Date.MONTH:
1759       var day = this.getDate();
1760       if(day > 28){
1761           day = Math.min(day, this.getFirstDateOfMonth().add('mo', value).getLastDateOfMonth().getDate());
1762       }
1763       d.setDate(day);
1764       d.setMonth(this.getMonth() + value);
1765       break;
1766     case Date.YEAR:
1767       d.setFullYear(this.getFullYear() + value);
1768       break;
1769   }
1770   return d;
1771 };
1772 /*
1773  * Based on:
1774  * Ext JS Library 1.1.1
1775  * Copyright(c) 2006-2007, Ext JS, LLC.
1776  *
1777  * Originally Released Under LGPL - original licence link has changed is not relivant.
1778  *
1779  * Fork - LGPL
1780  * <script type="text/javascript">
1781  */
1782
1783 /**
1784  * @class Roo.lib.Dom
1785  * @static
1786  * 
1787  * Dom utils (from YIU afaik)
1788  * 
1789  **/
1790 Roo.lib.Dom = {
1791     /**
1792      * Get the view width
1793      * @param {Boolean} full True will get the full document, otherwise it's the view width
1794      * @return {Number} The width
1795      */
1796      
1797     getViewWidth : function(full) {
1798         return full ? this.getDocumentWidth() : this.getViewportWidth();
1799     },
1800     /**
1801      * Get the view height
1802      * @param {Boolean} full True will get the full document, otherwise it's the view height
1803      * @return {Number} The height
1804      */
1805     getViewHeight : function(full) {
1806         return full ? this.getDocumentHeight() : this.getViewportHeight();
1807     },
1808
1809     getDocumentHeight: function() {
1810         var scrollHeight = (document.compatMode != "CSS1Compat") ? document.body.scrollHeight : document.documentElement.scrollHeight;
1811         return Math.max(scrollHeight, this.getViewportHeight());
1812     },
1813
1814     getDocumentWidth: function() {
1815         var scrollWidth = (document.compatMode != "CSS1Compat") ? document.body.scrollWidth : document.documentElement.scrollWidth;
1816         return Math.max(scrollWidth, this.getViewportWidth());
1817     },
1818
1819     getViewportHeight: function() {
1820         var height = self.innerHeight;
1821         var mode = document.compatMode;
1822
1823         if ((mode || Roo.isIE) && !Roo.isOpera) {
1824             height = (mode == "CSS1Compat") ?
1825                      document.documentElement.clientHeight :
1826                      document.body.clientHeight;
1827         }
1828
1829         return height;
1830     },
1831
1832     getViewportWidth: function() {
1833         var width = self.innerWidth;
1834         var mode = document.compatMode;
1835
1836         if (mode || Roo.isIE) {
1837             width = (mode == "CSS1Compat") ?
1838                     document.documentElement.clientWidth :
1839                     document.body.clientWidth;
1840         }
1841         return width;
1842     },
1843
1844     isAncestor : function(p, c) {
1845         p = Roo.getDom(p);
1846         c = Roo.getDom(c);
1847         if (!p || !c) {
1848             return false;
1849         }
1850
1851         if (p.contains && !Roo.isSafari) {
1852             return p.contains(c);
1853         } else if (p.compareDocumentPosition) {
1854             return !!(p.compareDocumentPosition(c) & 16);
1855         } else {
1856             var parent = c.parentNode;
1857             while (parent) {
1858                 if (parent == p) {
1859                     return true;
1860                 }
1861                 else if (!parent.tagName || parent.tagName.toUpperCase() == "HTML") {
1862                     return false;
1863                 }
1864                 parent = parent.parentNode;
1865             }
1866             return false;
1867         }
1868     },
1869
1870     getRegion : function(el) {
1871         return Roo.lib.Region.getRegion(el);
1872     },
1873
1874     getY : function(el) {
1875         return this.getXY(el)[1];
1876     },
1877
1878     getX : function(el) {
1879         return this.getXY(el)[0];
1880     },
1881
1882     getXY : function(el) {
1883         var p, pe, b, scroll, bd = document.body;
1884         el = Roo.getDom(el);
1885         var fly = Roo.lib.AnimBase.fly;
1886         if (el.getBoundingClientRect) {
1887             b = el.getBoundingClientRect();
1888             scroll = fly(document).getScroll();
1889             return [b.left + scroll.left, b.top + scroll.top];
1890         }
1891         var x = 0, y = 0;
1892
1893         p = el;
1894
1895         var hasAbsolute = fly(el).getStyle("position") == "absolute";
1896
1897         while (p) {
1898
1899             x += p.offsetLeft;
1900             y += p.offsetTop;
1901
1902             if (!hasAbsolute && fly(p).getStyle("position") == "absolute") {
1903                 hasAbsolute = true;
1904             }
1905
1906             if (Roo.isGecko) {
1907                 pe = fly(p);
1908
1909                 var bt = parseInt(pe.getStyle("borderTopWidth"), 10) || 0;
1910                 var bl = parseInt(pe.getStyle("borderLeftWidth"), 10) || 0;
1911
1912
1913                 x += bl;
1914                 y += bt;
1915
1916
1917                 if (p != el && pe.getStyle('overflow') != 'visible') {
1918                     x += bl;
1919                     y += bt;
1920                 }
1921             }
1922             p = p.offsetParent;
1923         }
1924
1925         if (Roo.isSafari && hasAbsolute) {
1926             x -= bd.offsetLeft;
1927             y -= bd.offsetTop;
1928         }
1929
1930         if (Roo.isGecko && !hasAbsolute) {
1931             var dbd = fly(bd);
1932             x += parseInt(dbd.getStyle("borderLeftWidth"), 10) || 0;
1933             y += parseInt(dbd.getStyle("borderTopWidth"), 10) || 0;
1934         }
1935
1936         p = el.parentNode;
1937         while (p && p != bd) {
1938             if (!Roo.isOpera || (p.tagName != 'TR' && fly(p).getStyle("display") != "inline")) {
1939                 x -= p.scrollLeft;
1940                 y -= p.scrollTop;
1941             }
1942             p = p.parentNode;
1943         }
1944         return [x, y];
1945     },
1946  
1947   
1948
1949
1950     setXY : function(el, xy) {
1951         el = Roo.fly(el, '_setXY');
1952         el.position();
1953         var pts = el.translatePoints(xy);
1954         if (xy[0] !== false) {
1955             el.dom.style.left = pts.left + "px";
1956         }
1957         if (xy[1] !== false) {
1958             el.dom.style.top = pts.top + "px";
1959         }
1960     },
1961
1962     setX : function(el, x) {
1963         this.setXY(el, [x, false]);
1964     },
1965
1966     setY : function(el, y) {
1967         this.setXY(el, [false, y]);
1968     }
1969 };
1970 /*
1971  * Portions of this file are based on pieces of Yahoo User Interface Library
1972  * Copyright (c) 2007, Yahoo! Inc. All rights reserved.
1973  * YUI licensed under the BSD License:
1974  * http://developer.yahoo.net/yui/license.txt
1975  * <script type="text/javascript">
1976  *
1977  */
1978
1979 Roo.lib.Event = function() {
1980     var loadComplete = false;
1981     var listeners = [];
1982     var unloadListeners = [];
1983     var retryCount = 0;
1984     var onAvailStack = [];
1985     var counter = 0;
1986     var lastError = null;
1987
1988     return {
1989         POLL_RETRYS: 200,
1990         POLL_INTERVAL: 20,
1991         EL: 0,
1992         TYPE: 1,
1993         FN: 2,
1994         WFN: 3,
1995         OBJ: 3,
1996         ADJ_SCOPE: 4,
1997         _interval: null,
1998
1999         startInterval: function() {
2000             if (!this._interval) {
2001                 var self = this;
2002                 var callback = function() {
2003                     self._tryPreloadAttach();
2004                 };
2005                 this._interval = setInterval(callback, this.POLL_INTERVAL);
2006
2007             }
2008         },
2009
2010         onAvailable: function(p_id, p_fn, p_obj, p_override) {
2011             onAvailStack.push({ id:         p_id,
2012                 fn:         p_fn,
2013                 obj:        p_obj,
2014                 override:   p_override,
2015                 checkReady: false    });
2016
2017             retryCount = this.POLL_RETRYS;
2018             this.startInterval();
2019         },
2020
2021
2022         addListener: function(el, eventName, fn) {
2023             el = Roo.getDom(el);
2024             if (!el || !fn) {
2025                 return false;
2026             }
2027
2028             if ("unload" == eventName) {
2029                 unloadListeners[unloadListeners.length] =
2030                 [el, eventName, fn];
2031                 return true;
2032             }
2033
2034             var wrappedFn = function(e) {
2035                 return fn(Roo.lib.Event.getEvent(e));
2036             };
2037
2038             var li = [el, eventName, fn, wrappedFn];
2039
2040             var index = listeners.length;
2041             listeners[index] = li;
2042
2043             this.doAdd(el, eventName, wrappedFn, false);
2044             return true;
2045
2046         },
2047
2048
2049         removeListener: function(el, eventName, fn) {
2050             var i, len;
2051
2052             el = Roo.getDom(el);
2053
2054             if(!fn) {
2055                 return this.purgeElement(el, false, eventName);
2056             }
2057
2058
2059             if ("unload" == eventName) {
2060
2061                 for (i = 0,len = unloadListeners.length; i < len; i++) {
2062                     var li = unloadListeners[i];
2063                     if (li &&
2064                         li[0] == el &&
2065                         li[1] == eventName &&
2066                         li[2] == fn) {
2067                         unloadListeners.splice(i, 1);
2068                         return true;
2069                     }
2070                 }
2071
2072                 return false;
2073             }
2074
2075             var cacheItem = null;
2076
2077
2078             var index = arguments[3];
2079
2080             if ("undefined" == typeof index) {
2081                 index = this._getCacheIndex(el, eventName, fn);
2082             }
2083
2084             if (index >= 0) {
2085                 cacheItem = listeners[index];
2086             }
2087
2088             if (!el || !cacheItem) {
2089                 return false;
2090             }
2091
2092             this.doRemove(el, eventName, cacheItem[this.WFN], false);
2093
2094             delete listeners[index][this.WFN];
2095             delete listeners[index][this.FN];
2096             listeners.splice(index, 1);
2097
2098             return true;
2099
2100         },
2101
2102
2103         getTarget: function(ev, resolveTextNode) {
2104             ev = ev.browserEvent || ev;
2105             ev = ev.touches ? (ev.touches[0] || ev.changedTouches[0] || ev )  : ev;
2106             var t = ev.target || ev.srcElement;
2107             return this.resolveTextNode(t);
2108         },
2109
2110
2111         resolveTextNode: function(node) {
2112             if (Roo.isSafari && node && 3 == node.nodeType) {
2113                 return node.parentNode;
2114             } else {
2115                 return node;
2116             }
2117         },
2118
2119
2120         getPageX: function(ev) {
2121             ev = ev.browserEvent || ev;
2122             ev = ev.touches ? (ev.touches[0] || ev.changedTouches[0] || ev )  : ev;
2123             var x = ev.pageX;
2124             if (!x && 0 !== x) {
2125                 x = ev.clientX || 0;
2126
2127                 if (Roo.isIE) {
2128                     x += this.getScroll()[1];
2129                 }
2130             }
2131
2132             return x;
2133         },
2134
2135
2136         getPageY: function(ev) {
2137             ev = ev.browserEvent || ev;
2138             ev = ev.touches ? (ev.touches[0] || ev.changedTouches[0] || ev )  : ev;
2139             var y = ev.pageY;
2140             if (!y && 0 !== y) {
2141                 y = ev.clientY || 0;
2142
2143                 if (Roo.isIE) {
2144                     y += this.getScroll()[0];
2145                 }
2146             }
2147
2148
2149             return y;
2150         },
2151
2152
2153         getXY: function(ev) {
2154             ev = ev.browserEvent || ev;
2155             ev = ev.touches ? (ev.touches[0] || ev.changedTouches[0] || ev )  : ev;
2156             return [this.getPageX(ev), this.getPageY(ev)];
2157         },
2158
2159
2160         getRelatedTarget: function(ev) {
2161             ev = ev.browserEvent || ev;
2162             ev = ev.touches ? (ev.touches[0] || ev.changedTouches[0] || ev )  : ev;
2163             var t = ev.relatedTarget;
2164             if (!t) {
2165                 if (ev.type == "mouseout") {
2166                     t = ev.toElement;
2167                 } else if (ev.type == "mouseover") {
2168                     t = ev.fromElement;
2169                 }
2170             }
2171
2172             return this.resolveTextNode(t);
2173         },
2174
2175
2176         getTime: function(ev) {
2177             ev = ev.browserEvent || ev;
2178             ev = ev.touches ? (ev.touches[0] || ev.changedTouches[0] || ev )  : ev;
2179             if (!ev.time) {
2180                 var t = new Date().getTime();
2181                 try {
2182                     ev.time = t;
2183                 } catch(ex) {
2184                     this.lastError = ex;
2185                     return t;
2186                 }
2187             }
2188
2189             return ev.time;
2190         },
2191
2192
2193         stopEvent: function(ev) {
2194             this.stopPropagation(ev);
2195             this.preventDefault(ev);
2196         },
2197
2198
2199         stopPropagation: function(ev) {
2200             ev = ev.browserEvent || ev;
2201             if (ev.stopPropagation) {
2202                 ev.stopPropagation();
2203             } else {
2204                 ev.cancelBubble = true;
2205             }
2206         },
2207
2208
2209         preventDefault: function(ev) {
2210             ev = ev.browserEvent || ev;
2211             if(ev.preventDefault) {
2212                 ev.preventDefault();
2213             } else {
2214                 ev.returnValue = false;
2215             }
2216         },
2217
2218
2219         getEvent: function(e) {
2220             var ev = e || window.event;
2221             if (!ev) {
2222                 var c = this.getEvent.caller;
2223                 while (c) {
2224                     ev = c.arguments[0];
2225                     if (ev && Event == ev.constructor) {
2226                         break;
2227                     }
2228                     c = c.caller;
2229                 }
2230             }
2231             return ev;
2232         },
2233
2234
2235         getCharCode: function(ev) {
2236             ev = ev.browserEvent || ev;
2237             return ev.charCode || ev.keyCode || 0;
2238         },
2239
2240
2241         _getCacheIndex: function(el, eventName, fn) {
2242             for (var i = 0,len = listeners.length; i < len; ++i) {
2243                 var li = listeners[i];
2244                 if (li &&
2245                     li[this.FN] == fn &&
2246                     li[this.EL] == el &&
2247                     li[this.TYPE] == eventName) {
2248                     return i;
2249                 }
2250             }
2251
2252             return -1;
2253         },
2254
2255
2256         elCache: {},
2257
2258
2259         getEl: function(id) {
2260             return document.getElementById(id);
2261         },
2262
2263
2264         clearCache: function() {
2265         },
2266
2267
2268         _load: function(e) {
2269             loadComplete = true;
2270             var EU = Roo.lib.Event;
2271
2272
2273             if (Roo.isIE) {
2274                 EU.doRemove(window, "load", EU._load);
2275             }
2276         },
2277
2278
2279         _tryPreloadAttach: function() {
2280
2281             if (this.locked) {
2282                 return false;
2283             }
2284
2285             this.locked = true;
2286
2287
2288             var tryAgain = !loadComplete;
2289             if (!tryAgain) {
2290                 tryAgain = (retryCount > 0);
2291             }
2292
2293
2294             var notAvail = [];
2295             for (var i = 0,len = onAvailStack.length; i < len; ++i) {
2296                 var item = onAvailStack[i];
2297                 if (item) {
2298                     var el = this.getEl(item.id);
2299
2300                     if (el) {
2301                         if (!item.checkReady ||
2302                             loadComplete ||
2303                             el.nextSibling ||
2304                             (document && document.body)) {
2305
2306                             var scope = el;
2307                             if (item.override) {
2308                                 if (item.override === true) {
2309                                     scope = item.obj;
2310                                 } else {
2311                                     scope = item.override;
2312                                 }
2313                             }
2314                             item.fn.call(scope, item.obj);
2315                             onAvailStack[i] = null;
2316                         }
2317                     } else {
2318                         notAvail.push(item);
2319                     }
2320                 }
2321             }
2322
2323             retryCount = (notAvail.length === 0) ? 0 : retryCount - 1;
2324
2325             if (tryAgain) {
2326
2327                 this.startInterval();
2328             } else {
2329                 clearInterval(this._interval);
2330                 this._interval = null;
2331             }
2332
2333             this.locked = false;
2334
2335             return true;
2336
2337         },
2338
2339
2340         purgeElement: function(el, recurse, eventName) {
2341             var elListeners = this.getListeners(el, eventName);
2342             if (elListeners) {
2343                 for (var i = 0,len = elListeners.length; i < len; ++i) {
2344                     var l = elListeners[i];
2345                     this.removeListener(el, l.type, l.fn);
2346                 }
2347             }
2348
2349             if (recurse && el && el.childNodes) {
2350                 for (i = 0,len = el.childNodes.length; i < len; ++i) {
2351                     this.purgeElement(el.childNodes[i], recurse, eventName);
2352                 }
2353             }
2354         },
2355
2356
2357         getListeners: function(el, eventName) {
2358             var results = [], searchLists;
2359             if (!eventName) {
2360                 searchLists = [listeners, unloadListeners];
2361             } else if (eventName == "unload") {
2362                 searchLists = [unloadListeners];
2363             } else {
2364                 searchLists = [listeners];
2365             }
2366
2367             for (var j = 0; j < searchLists.length; ++j) {
2368                 var searchList = searchLists[j];
2369                 if (searchList && searchList.length > 0) {
2370                     for (var i = 0,len = searchList.length; i < len; ++i) {
2371                         var l = searchList[i];
2372                         if (l && l[this.EL] === el &&
2373                             (!eventName || eventName === l[this.TYPE])) {
2374                             results.push({
2375                                 type:   l[this.TYPE],
2376                                 fn:     l[this.FN],
2377                                 obj:    l[this.OBJ],
2378                                 adjust: l[this.ADJ_SCOPE],
2379                                 index:  i
2380                             });
2381                         }
2382                     }
2383                 }
2384             }
2385
2386             return (results.length) ? results : null;
2387         },
2388
2389
2390         _unload: function(e) {
2391
2392             var EU = Roo.lib.Event, i, j, l, len, index;
2393
2394             for (i = 0,len = unloadListeners.length; i < len; ++i) {
2395                 l = unloadListeners[i];
2396                 if (l) {
2397                     var scope = window;
2398                     if (l[EU.ADJ_SCOPE]) {
2399                         if (l[EU.ADJ_SCOPE] === true) {
2400                             scope = l[EU.OBJ];
2401                         } else {
2402                             scope = l[EU.ADJ_SCOPE];
2403                         }
2404                     }
2405                     l[EU.FN].call(scope, EU.getEvent(e), l[EU.OBJ]);
2406                     unloadListeners[i] = null;
2407                     l = null;
2408                     scope = null;
2409                 }
2410             }
2411
2412             unloadListeners = null;
2413
2414             if (listeners && listeners.length > 0) {
2415                 j = listeners.length;
2416                 while (j) {
2417                     index = j - 1;
2418                     l = listeners[index];
2419                     if (l) {
2420                         EU.removeListener(l[EU.EL], l[EU.TYPE],
2421                                 l[EU.FN], index);
2422                     }
2423                     j = j - 1;
2424                 }
2425                 l = null;
2426
2427                 EU.clearCache();
2428             }
2429
2430             EU.doRemove(window, "unload", EU._unload);
2431
2432         },
2433
2434
2435         getScroll: function() {
2436             var dd = document.documentElement, db = document.body;
2437             if (dd && (dd.scrollTop || dd.scrollLeft)) {
2438                 return [dd.scrollTop, dd.scrollLeft];
2439             } else if (db) {
2440                 return [db.scrollTop, db.scrollLeft];
2441             } else {
2442                 return [0, 0];
2443             }
2444         },
2445
2446
2447         doAdd: function () {
2448             if (window.addEventListener) {
2449                 return function(el, eventName, fn, capture) {
2450                     el.addEventListener(eventName, fn, (capture));
2451                 };
2452             } else if (window.attachEvent) {
2453                 return function(el, eventName, fn, capture) {
2454                     el.attachEvent("on" + eventName, fn);
2455                 };
2456             } else {
2457                 return function() {
2458                 };
2459             }
2460         }(),
2461
2462
2463         doRemove: function() {
2464             if (window.removeEventListener) {
2465                 return function (el, eventName, fn, capture) {
2466                     el.removeEventListener(eventName, fn, (capture));
2467                 };
2468             } else if (window.detachEvent) {
2469                 return function (el, eventName, fn) {
2470                     el.detachEvent("on" + eventName, fn);
2471                 };
2472             } else {
2473                 return function() {
2474                 };
2475             }
2476         }()
2477     };
2478     
2479 }();
2480 (function() {     
2481    
2482     var E = Roo.lib.Event;
2483     E.on = E.addListener;
2484     E.un = E.removeListener;
2485
2486     if (document && document.body) {
2487         E._load();
2488     } else {
2489         E.doAdd(window, "load", E._load);
2490     }
2491     E.doAdd(window, "unload", E._unload);
2492     E._tryPreloadAttach();
2493 })();
2494
2495 /*
2496  * Portions of this file are based on pieces of Yahoo User Interface Library
2497  * Copyright (c) 2007, Yahoo! Inc. All rights reserved.
2498  * YUI licensed under the BSD License:
2499  * http://developer.yahoo.net/yui/license.txt
2500  * <script type="text/javascript">
2501  *
2502  */
2503
2504 (function() {
2505     /**
2506      * @class Roo.lib.Ajax
2507      *
2508      */
2509     Roo.lib.Ajax = {
2510         /**
2511          * @static 
2512          */
2513         request : function(method, uri, cb, data, options) {
2514             if(options){
2515                 var hs = options.headers;
2516                 if(hs){
2517                     for(var h in hs){
2518                         if(hs.hasOwnProperty(h)){
2519                             this.initHeader(h, hs[h], false);
2520                         }
2521                     }
2522                 }
2523                 if(options.xmlData){
2524                     this.initHeader('Content-Type', 'text/xml', false);
2525                     method = 'POST';
2526                     data = options.xmlData;
2527                 }
2528             }
2529
2530             return this.asyncRequest(method, uri, cb, data);
2531         },
2532
2533         serializeForm : function(form) {
2534             if(typeof form == 'string') {
2535                 form = (document.getElementById(form) || document.forms[form]);
2536             }
2537
2538             var el, name, val, disabled, data = '', hasSubmit = false;
2539             for (var i = 0; i < form.elements.length; i++) {
2540                 el = form.elements[i];
2541                 disabled = form.elements[i].disabled;
2542                 name = form.elements[i].name;
2543                 val = form.elements[i].value;
2544
2545                 if (!disabled && name){
2546                     switch (el.type)
2547                             {
2548                         case 'select-one':
2549                         case 'select-multiple':
2550                             for (var j = 0; j < el.options.length; j++) {
2551                                 if (el.options[j].selected) {
2552                                     if (Roo.isIE) {
2553                                         data += Roo.encodeURIComponent(name) + '=' + Roo.encodeURIComponent(el.options[j].attributes['value'].specified ? el.options[j].value : el.options[j].text) + '&';
2554                                     }
2555                                     else {
2556                                         data += Roo.encodeURIComponent(name) + '=' + Roo.encodeURIComponent(el.options[j].hasAttribute('value') ? el.options[j].value : el.options[j].text) + '&';
2557                                     }
2558                                 }
2559                             }
2560                             break;
2561                         case 'radio':
2562                         case 'checkbox':
2563                             if (el.checked) {
2564                                 data += Roo.encodeURIComponent(name) + '=' + Roo.encodeURIComponent(val) + '&';
2565                             }
2566                             break;
2567                         case 'file':
2568
2569                         case undefined:
2570
2571                         case 'reset':
2572
2573                         case 'button':
2574
2575                             break;
2576                         case 'submit':
2577                             if(hasSubmit == false) {
2578                                 data += Roo.encodeURIComponent(name) + '=' + Roo.encodeURIComponent(val) + '&';
2579                                 hasSubmit = true;
2580                             }
2581                             break;
2582                         default:
2583                             data += Roo.encodeURIComponent(name) + '=' + Roo.encodeURIComponent(val) + '&';
2584                             break;
2585                     }
2586                 }
2587             }
2588             data = data.substr(0, data.length - 1);
2589             return data;
2590         },
2591
2592         headers:{},
2593
2594         hasHeaders:false,
2595
2596         useDefaultHeader:true,
2597
2598         defaultPostHeader:'application/x-www-form-urlencoded',
2599
2600         useDefaultXhrHeader:true,
2601
2602         defaultXhrHeader:'XMLHttpRequest',
2603
2604         hasDefaultHeaders:true,
2605
2606         defaultHeaders:{},
2607
2608         poll:{},
2609
2610         timeout:{},
2611
2612         pollInterval:50,
2613
2614         transactionId:0,
2615
2616         setProgId:function(id)
2617         {
2618             this.activeX.unshift(id);
2619         },
2620
2621         setDefaultPostHeader:function(b)
2622         {
2623             this.useDefaultHeader = b;
2624         },
2625
2626         setDefaultXhrHeader:function(b)
2627         {
2628             this.useDefaultXhrHeader = b;
2629         },
2630
2631         setPollingInterval:function(i)
2632         {
2633             if (typeof i == 'number' && isFinite(i)) {
2634                 this.pollInterval = i;
2635             }
2636         },
2637
2638         createXhrObject:function(transactionId)
2639         {
2640             var obj,http;
2641             try
2642             {
2643
2644                 http = new XMLHttpRequest();
2645
2646                 obj = { conn:http, tId:transactionId };
2647             }
2648             catch(e)
2649             {
2650                 for (var i = 0; i < this.activeX.length; ++i) {
2651                     try
2652                     {
2653
2654                         http = new ActiveXObject(this.activeX[i]);
2655
2656                         obj = { conn:http, tId:transactionId };
2657                         break;
2658                     }
2659                     catch(e) {
2660                     }
2661                 }
2662             }
2663             finally
2664             {
2665                 return obj;
2666             }
2667         },
2668
2669         getConnectionObject:function()
2670         {
2671             var o;
2672             var tId = this.transactionId;
2673
2674             try
2675             {
2676                 o = this.createXhrObject(tId);
2677                 if (o) {
2678                     this.transactionId++;
2679                 }
2680             }
2681             catch(e) {
2682             }
2683             finally
2684             {
2685                 return o;
2686             }
2687         },
2688
2689         asyncRequest:function(method, uri, callback, postData)
2690         {
2691             var o = this.getConnectionObject();
2692
2693             if (!o) {
2694                 return null;
2695             }
2696             else {
2697                 o.conn.open(method, uri, true);
2698
2699                 if (this.useDefaultXhrHeader) {
2700                     if (!this.defaultHeaders['X-Requested-With']) {
2701                         this.initHeader('X-Requested-With', this.defaultXhrHeader, true);
2702                     }
2703                 }
2704
2705                 if(postData && this.useDefaultHeader){
2706                     this.initHeader('Content-Type', this.defaultPostHeader);
2707                 }
2708
2709                  if (this.hasDefaultHeaders || this.hasHeaders) {
2710                     this.setHeader(o);
2711                 }
2712
2713                 this.handleReadyState(o, callback);
2714                 o.conn.send(postData || null);
2715
2716                 return o;
2717             }
2718         },
2719
2720         handleReadyState:function(o, callback)
2721         {
2722             var oConn = this;
2723
2724             if (callback && callback.timeout) {
2725                 
2726                 this.timeout[o.tId] = window.setTimeout(function() {
2727                     oConn.abort(o, callback, true);
2728                 }, callback.timeout);
2729             }
2730
2731             this.poll[o.tId] = window.setInterval(
2732                     function() {
2733                         if (o.conn && o.conn.readyState == 4) {
2734                             window.clearInterval(oConn.poll[o.tId]);
2735                             delete oConn.poll[o.tId];
2736
2737                             if(callback && callback.timeout) {
2738                                 window.clearTimeout(oConn.timeout[o.tId]);
2739                                 delete oConn.timeout[o.tId];
2740                             }
2741
2742                             oConn.handleTransactionResponse(o, callback);
2743                         }
2744                     }
2745                     , this.pollInterval);
2746         },
2747
2748         handleTransactionResponse:function(o, callback, isAbort)
2749         {
2750
2751             if (!callback) {
2752                 this.releaseObject(o);
2753                 return;
2754             }
2755
2756             var httpStatus, responseObject;
2757
2758             try
2759             {
2760                 if (o.conn.status !== undefined && o.conn.status != 0) {
2761                     httpStatus = o.conn.status;
2762                 }
2763                 else {
2764                     httpStatus = 13030;
2765                 }
2766             }
2767             catch(e) {
2768
2769
2770                 httpStatus = 13030;
2771             }
2772
2773             if (httpStatus >= 200 && httpStatus < 300) {
2774                 responseObject = this.createResponseObject(o, callback.argument);
2775                 if (callback.success) {
2776                     if (!callback.scope) {
2777                         callback.success(responseObject);
2778                     }
2779                     else {
2780
2781
2782                         callback.success.apply(callback.scope, [responseObject]);
2783                     }
2784                 }
2785             }
2786             else {
2787                 switch (httpStatus) {
2788
2789                     case 12002:
2790                     case 12029:
2791                     case 12030:
2792                     case 12031:
2793                     case 12152:
2794                     case 13030:
2795                         responseObject = this.createExceptionObject(o.tId, callback.argument, (isAbort ? isAbort : false));
2796                         if (callback.failure) {
2797                             if (!callback.scope) {
2798                                 callback.failure(responseObject);
2799                             }
2800                             else {
2801                                 callback.failure.apply(callback.scope, [responseObject]);
2802                             }
2803                         }
2804                         break;
2805                     default:
2806                         responseObject = this.createResponseObject(o, callback.argument);
2807                         if (callback.failure) {
2808                             if (!callback.scope) {
2809                                 callback.failure(responseObject);
2810                             }
2811                             else {
2812                                 callback.failure.apply(callback.scope, [responseObject]);
2813                             }
2814                         }
2815                 }
2816             }
2817
2818             this.releaseObject(o);
2819             responseObject = null;
2820         },
2821
2822         createResponseObject:function(o, callbackArg)
2823         {
2824             var obj = {};
2825             var headerObj = {};
2826
2827             try
2828             {
2829                 var headerStr = o.conn.getAllResponseHeaders();
2830                 var header = headerStr.split('\n');
2831                 for (var i = 0; i < header.length; i++) {
2832                     var delimitPos = header[i].indexOf(':');
2833                     if (delimitPos != -1) {
2834                         headerObj[header[i].substring(0, delimitPos)] = header[i].substring(delimitPos + 2);
2835                     }
2836                 }
2837             }
2838             catch(e) {
2839             }
2840
2841             obj.tId = o.tId;
2842             obj.status = o.conn.status;
2843             obj.statusText = o.conn.statusText;
2844             obj.getResponseHeader = headerObj;
2845             obj.getAllResponseHeaders = headerStr;
2846             obj.responseText = o.conn.responseText;
2847             obj.responseXML = o.conn.responseXML;
2848
2849             if (typeof callbackArg !== undefined) {
2850                 obj.argument = callbackArg;
2851             }
2852
2853             return obj;
2854         },
2855
2856         createExceptionObject:function(tId, callbackArg, isAbort)
2857         {
2858             var COMM_CODE = 0;
2859             var COMM_ERROR = 'communication failure';
2860             var ABORT_CODE = -1;
2861             var ABORT_ERROR = 'transaction aborted';
2862
2863             var obj = {};
2864
2865             obj.tId = tId;
2866             if (isAbort) {
2867                 obj.status = ABORT_CODE;
2868                 obj.statusText = ABORT_ERROR;
2869             }
2870             else {
2871                 obj.status = COMM_CODE;
2872                 obj.statusText = COMM_ERROR;
2873             }
2874
2875             if (callbackArg) {
2876                 obj.argument = callbackArg;
2877             }
2878
2879             return obj;
2880         },
2881
2882         initHeader:function(label, value, isDefault)
2883         {
2884             var headerObj = (isDefault) ? this.defaultHeaders : this.headers;
2885
2886             if (headerObj[label] === undefined) {
2887                 headerObj[label] = value;
2888             }
2889             else {
2890
2891
2892                 headerObj[label] = value + "," + headerObj[label];
2893             }
2894
2895             if (isDefault) {
2896                 this.hasDefaultHeaders = true;
2897             }
2898             else {
2899                 this.hasHeaders = true;
2900             }
2901         },
2902
2903
2904         setHeader:function(o)
2905         {
2906             if (this.hasDefaultHeaders) {
2907                 for (var prop in this.defaultHeaders) {
2908                     if (this.defaultHeaders.hasOwnProperty(prop)) {
2909                         o.conn.setRequestHeader(prop, this.defaultHeaders[prop]);
2910                     }
2911                 }
2912             }
2913
2914             if (this.hasHeaders) {
2915                 for (var prop in this.headers) {
2916                     if (this.headers.hasOwnProperty(prop)) {
2917                         o.conn.setRequestHeader(prop, this.headers[prop]);
2918                     }
2919                 }
2920                 this.headers = {};
2921                 this.hasHeaders = false;
2922             }
2923         },
2924
2925         resetDefaultHeaders:function() {
2926             delete this.defaultHeaders;
2927             this.defaultHeaders = {};
2928             this.hasDefaultHeaders = false;
2929         },
2930
2931         abort:function(o, callback, isTimeout)
2932         {
2933             if(this.isCallInProgress(o)) {
2934                 o.conn.abort();
2935                 window.clearInterval(this.poll[o.tId]);
2936                 delete this.poll[o.tId];
2937                 if (isTimeout) {
2938                     delete this.timeout[o.tId];
2939                 }
2940
2941                 this.handleTransactionResponse(o, callback, true);
2942
2943                 return true;
2944             }
2945             else {
2946                 return false;
2947             }
2948         },
2949
2950
2951         isCallInProgress:function(o)
2952         {
2953             if (o && o.conn) {
2954                 return o.conn.readyState != 4 && o.conn.readyState != 0;
2955             }
2956             else {
2957
2958                 return false;
2959             }
2960         },
2961
2962
2963         releaseObject:function(o)
2964         {
2965
2966             o.conn = null;
2967
2968             o = null;
2969         },
2970
2971         activeX:[
2972         'MSXML2.XMLHTTP.3.0',
2973         'MSXML2.XMLHTTP',
2974         'Microsoft.XMLHTTP'
2975         ]
2976
2977
2978     };
2979 })();/*
2980  * Portions of this file are based on pieces of Yahoo User Interface Library
2981  * Copyright (c) 2007, Yahoo! Inc. All rights reserved.
2982  * YUI licensed under the BSD License:
2983  * http://developer.yahoo.net/yui/license.txt
2984  * <script type="text/javascript">
2985  *
2986  */
2987
2988 Roo.lib.Region = function(t, r, b, l) {
2989     this.top = t;
2990     this[1] = t;
2991     this.right = r;
2992     this.bottom = b;
2993     this.left = l;
2994     this[0] = l;
2995 };
2996
2997
2998 Roo.lib.Region.prototype = {
2999     contains : function(region) {
3000         return ( region.left >= this.left &&
3001                  region.right <= this.right &&
3002                  region.top >= this.top &&
3003                  region.bottom <= this.bottom    );
3004
3005     },
3006
3007     getArea : function() {
3008         return ( (this.bottom - this.top) * (this.right - this.left) );
3009     },
3010
3011     intersect : function(region) {
3012         var t = Math.max(this.top, region.top);
3013         var r = Math.min(this.right, region.right);
3014         var b = Math.min(this.bottom, region.bottom);
3015         var l = Math.max(this.left, region.left);
3016
3017         if (b >= t && r >= l) {
3018             return new Roo.lib.Region(t, r, b, l);
3019         } else {
3020             return null;
3021         }
3022     },
3023     union : function(region) {
3024         var t = Math.min(this.top, region.top);
3025         var r = Math.max(this.right, region.right);
3026         var b = Math.max(this.bottom, region.bottom);
3027         var l = Math.min(this.left, region.left);
3028
3029         return new Roo.lib.Region(t, r, b, l);
3030     },
3031
3032     adjust : function(t, l, b, r) {
3033         this.top += t;
3034         this.left += l;
3035         this.right += r;
3036         this.bottom += b;
3037         return this;
3038     }
3039 };
3040
3041 Roo.lib.Region.getRegion = function(el) {
3042     var p = Roo.lib.Dom.getXY(el);
3043
3044     var t = p[1];
3045     var r = p[0] + el.offsetWidth;
3046     var b = p[1] + el.offsetHeight;
3047     var l = p[0];
3048
3049     return new Roo.lib.Region(t, r, b, l);
3050 };
3051 /*
3052  * Portions of this file are based on pieces of Yahoo User Interface Library
3053  * Copyright (c) 2007, Yahoo! Inc. All rights reserved.
3054  * YUI licensed under the BSD License:
3055  * http://developer.yahoo.net/yui/license.txt
3056  * <script type="text/javascript">
3057  *
3058  */
3059 //@@dep Roo.lib.Region
3060
3061
3062 Roo.lib.Point = function(x, y) {
3063     if (x instanceof Array) {
3064         y = x[1];
3065         x = x[0];
3066     }
3067     this.x = this.right = this.left = this[0] = x;
3068     this.y = this.top = this.bottom = this[1] = y;
3069 };
3070
3071 Roo.lib.Point.prototype = new Roo.lib.Region();
3072 /*
3073  * Portions of this file are based on pieces of Yahoo User Interface Library
3074  * Copyright (c) 2007, Yahoo! Inc. All rights reserved.
3075  * YUI licensed under the BSD License:
3076  * http://developer.yahoo.net/yui/license.txt
3077  * <script type="text/javascript">
3078  *
3079  */
3080  
3081 (function() {   
3082
3083     Roo.lib.Anim = {
3084         scroll : function(el, args, duration, easing, cb, scope) {
3085             this.run(el, args, duration, easing, cb, scope, Roo.lib.Scroll);
3086         },
3087
3088         motion : function(el, args, duration, easing, cb, scope) {
3089             this.run(el, args, duration, easing, cb, scope, Roo.lib.Motion);
3090         },
3091
3092         color : function(el, args, duration, easing, cb, scope) {
3093             this.run(el, args, duration, easing, cb, scope, Roo.lib.ColorAnim);
3094         },
3095
3096         run : function(el, args, duration, easing, cb, scope, type) {
3097             type = type || Roo.lib.AnimBase;
3098             if (typeof easing == "string") {
3099                 easing = Roo.lib.Easing[easing];
3100             }
3101             var anim = new type(el, args, duration, easing);
3102             anim.animateX(function() {
3103                 Roo.callback(cb, scope);
3104             });
3105             return anim;
3106         }
3107     };
3108 })();/*
3109  * Portions of this file are based on pieces of Yahoo User Interface Library
3110  * Copyright (c) 2007, Yahoo! Inc. All rights reserved.
3111  * YUI licensed under the BSD License:
3112  * http://developer.yahoo.net/yui/license.txt
3113  * <script type="text/javascript">
3114  *
3115  */
3116
3117 (function() {    
3118     var libFlyweight;
3119     
3120     function fly(el) {
3121         if (!libFlyweight) {
3122             libFlyweight = new Roo.Element.Flyweight();
3123         }
3124         libFlyweight.dom = el;
3125         return libFlyweight;
3126     }
3127
3128     // since this uses fly! - it cant be in DOM (which does not have fly yet..)
3129     
3130    
3131     
3132     Roo.lib.AnimBase = function(el, attributes, duration, method) {
3133         if (el) {
3134             this.init(el, attributes, duration, method);
3135         }
3136     };
3137
3138     Roo.lib.AnimBase.fly = fly;
3139     
3140     
3141     
3142     Roo.lib.AnimBase.prototype = {
3143
3144         toString: function() {
3145             var el = this.getEl();
3146             var id = el.id || el.tagName;
3147             return ("Anim " + id);
3148         },
3149
3150         patterns: {
3151             noNegatives:        /width|height|opacity|padding/i,
3152             offsetAttribute:  /^((width|height)|(top|left))$/,
3153             defaultUnit:        /width|height|top$|bottom$|left$|right$/i,
3154             offsetUnit:         /\d+(em|%|en|ex|pt|in|cm|mm|pc)$/i
3155         },
3156
3157
3158         doMethod: function(attr, start, end) {
3159             return this.method(this.currentFrame, start, end - start, this.totalFrames);
3160         },
3161
3162
3163         setAttribute: function(attr, val, unit) {
3164             if (this.patterns.noNegatives.test(attr)) {
3165                 val = (val > 0) ? val : 0;
3166             }
3167
3168             Roo.fly(this.getEl(), '_anim').setStyle(attr, val + unit);
3169         },
3170
3171
3172         getAttribute: function(attr) {
3173             var el = this.getEl();
3174             var val = fly(el).getStyle(attr);
3175
3176             if (val !== 'auto' && !this.patterns.offsetUnit.test(val)) {
3177                 return parseFloat(val);
3178             }
3179
3180             var a = this.patterns.offsetAttribute.exec(attr) || [];
3181             var pos = !!( a[3] );
3182             var box = !!( a[2] );
3183
3184
3185             if (box || (fly(el).getStyle('position') == 'absolute' && pos)) {
3186                 val = el['offset' + a[0].charAt(0).toUpperCase() + a[0].substr(1)];
3187             } else {
3188                 val = 0;
3189             }
3190
3191             return val;
3192         },
3193
3194
3195         getDefaultUnit: function(attr) {
3196             if (this.patterns.defaultUnit.test(attr)) {
3197                 return 'px';
3198             }
3199
3200             return '';
3201         },
3202
3203         animateX : function(callback, scope) {
3204             var f = function() {
3205                 this.onComplete.removeListener(f);
3206                 if (typeof callback == "function") {
3207                     callback.call(scope || this, this);
3208                 }
3209             };
3210             this.onComplete.addListener(f, this);
3211             this.animate();
3212         },
3213
3214
3215         setRuntimeAttribute: function(attr) {
3216             var start;
3217             var end;
3218             var attributes = this.attributes;
3219
3220             this.runtimeAttributes[attr] = {};
3221
3222             var isset = function(prop) {
3223                 return (typeof prop !== 'undefined');
3224             };
3225
3226             if (!isset(attributes[attr]['to']) && !isset(attributes[attr]['by'])) {
3227                 return false;
3228             }
3229
3230             start = ( isset(attributes[attr]['from']) ) ? attributes[attr]['from'] : this.getAttribute(attr);
3231
3232
3233             if (isset(attributes[attr]['to'])) {
3234                 end = attributes[attr]['to'];
3235             } else if (isset(attributes[attr]['by'])) {
3236                 if (start.constructor == Array) {
3237                     end = [];
3238                     for (var i = 0, len = start.length; i < len; ++i) {
3239                         end[i] = start[i] + attributes[attr]['by'][i];
3240                     }
3241                 } else {
3242                     end = start + attributes[attr]['by'];
3243                 }
3244             }
3245
3246             this.runtimeAttributes[attr].start = start;
3247             this.runtimeAttributes[attr].end = end;
3248
3249
3250             this.runtimeAttributes[attr].unit = ( isset(attributes[attr].unit) ) ? attributes[attr]['unit'] : this.getDefaultUnit(attr);
3251         },
3252
3253
3254         init: function(el, attributes, duration, method) {
3255
3256             var isAnimated = false;
3257
3258
3259             var startTime = null;
3260
3261
3262             var actualFrames = 0;
3263
3264
3265             el = Roo.getDom(el);
3266
3267
3268             this.attributes = attributes || {};
3269
3270
3271             this.duration = duration || 1;
3272
3273
3274             this.method = method || Roo.lib.Easing.easeNone;
3275
3276
3277             this.useSeconds = true;
3278
3279
3280             this.currentFrame = 0;
3281
3282
3283             this.totalFrames = Roo.lib.AnimMgr.fps;
3284
3285
3286             this.getEl = function() {
3287                 return el;
3288             };
3289
3290
3291             this.isAnimated = function() {
3292                 return isAnimated;
3293             };
3294
3295
3296             this.getStartTime = function() {
3297                 return startTime;
3298             };
3299
3300             this.runtimeAttributes = {};
3301
3302
3303             this.animate = function() {
3304                 if (this.isAnimated()) {
3305                     return false;
3306                 }
3307
3308                 this.currentFrame = 0;
3309
3310                 this.totalFrames = ( this.useSeconds ) ? Math.ceil(Roo.lib.AnimMgr.fps * this.duration) : this.duration;
3311
3312                 Roo.lib.AnimMgr.registerElement(this);
3313             };
3314
3315
3316             this.stop = function(finish) {
3317                 if (finish) {
3318                     this.currentFrame = this.totalFrames;
3319                     this._onTween.fire();
3320                 }
3321                 Roo.lib.AnimMgr.stop(this);
3322             };
3323
3324             var onStart = function() {
3325                 this.onStart.fire();
3326
3327                 this.runtimeAttributes = {};
3328                 for (var attr in this.attributes) {
3329                     this.setRuntimeAttribute(attr);
3330                 }
3331
3332                 isAnimated = true;
3333                 actualFrames = 0;
3334                 startTime = new Date();
3335             };
3336
3337
3338             var onTween = function() {
3339                 var data = {
3340                     duration: new Date() - this.getStartTime(),
3341                     currentFrame: this.currentFrame
3342                 };
3343
3344                 data.toString = function() {
3345                     return (
3346                             'duration: ' + data.duration +
3347                             ', currentFrame: ' + data.currentFrame
3348                             );
3349                 };
3350
3351                 this.onTween.fire(data);
3352
3353                 var runtimeAttributes = this.runtimeAttributes;
3354
3355                 for (var attr in runtimeAttributes) {
3356                     this.setAttribute(attr, this.doMethod(attr, runtimeAttributes[attr].start, runtimeAttributes[attr].end), runtimeAttributes[attr].unit);
3357                 }
3358
3359                 actualFrames += 1;
3360             };
3361
3362             var onComplete = function() {
3363                 var actual_duration = (new Date() - startTime) / 1000 ;
3364
3365                 var data = {
3366                     duration: actual_duration,
3367                     frames: actualFrames,
3368                     fps: actualFrames / actual_duration
3369                 };
3370
3371                 data.toString = function() {
3372                     return (
3373                             'duration: ' + data.duration +
3374                             ', frames: ' + data.frames +
3375                             ', fps: ' + data.fps
3376                             );
3377                 };
3378
3379                 isAnimated = false;
3380                 actualFrames = 0;
3381                 this.onComplete.fire(data);
3382             };
3383
3384
3385             this._onStart = new Roo.util.Event(this);
3386             this.onStart = new Roo.util.Event(this);
3387             this.onTween = new Roo.util.Event(this);
3388             this._onTween = new Roo.util.Event(this);
3389             this.onComplete = new Roo.util.Event(this);
3390             this._onComplete = new Roo.util.Event(this);
3391             this._onStart.addListener(onStart);
3392             this._onTween.addListener(onTween);
3393             this._onComplete.addListener(onComplete);
3394         }
3395     };
3396 })();
3397 /*
3398  * Portions of this file are based on pieces of Yahoo User Interface Library
3399  * Copyright (c) 2007, Yahoo! Inc. All rights reserved.
3400  * YUI licensed under the BSD License:
3401  * http://developer.yahoo.net/yui/license.txt
3402  * <script type="text/javascript">
3403  *
3404  */
3405
3406 Roo.lib.AnimMgr = new function() {
3407
3408     var thread = null;
3409
3410
3411     var queue = [];
3412
3413
3414     var tweenCount = 0;
3415
3416
3417     this.fps = 1000;
3418
3419
3420     this.delay = 1;
3421
3422
3423     this.registerElement = function(tween) {
3424         queue[queue.length] = tween;
3425         tweenCount += 1;
3426         tween._onStart.fire();
3427         this.start();
3428     };
3429
3430
3431     this.unRegister = function(tween, index) {
3432         tween._onComplete.fire();
3433         index = index || getIndex(tween);
3434         if (index != -1) {
3435             queue.splice(index, 1);
3436         }
3437
3438         tweenCount -= 1;
3439         if (tweenCount <= 0) {
3440             this.stop();
3441         }
3442     };
3443
3444
3445     this.start = function() {
3446         if (thread === null) {
3447             thread = setInterval(this.run, this.delay);
3448         }
3449     };
3450
3451
3452     this.stop = function(tween) {
3453         if (!tween) {
3454             clearInterval(thread);
3455
3456             for (var i = 0, len = queue.length; i < len; ++i) {
3457                 if (queue[0].isAnimated()) {
3458                     this.unRegister(queue[0], 0);
3459                 }
3460             }
3461
3462             queue = [];
3463             thread = null;
3464             tweenCount = 0;
3465         }
3466         else {
3467             this.unRegister(tween);
3468         }
3469     };
3470
3471
3472     this.run = function() {
3473         for (var i = 0, len = queue.length; i < len; ++i) {
3474             var tween = queue[i];
3475             if (!tween || !tween.isAnimated()) {
3476                 continue;
3477             }
3478
3479             if (tween.currentFrame < tween.totalFrames || tween.totalFrames === null)
3480             {
3481                 tween.currentFrame += 1;
3482
3483                 if (tween.useSeconds) {
3484                     correctFrame(tween);
3485                 }
3486                 tween._onTween.fire();
3487             }
3488             else {
3489                 Roo.lib.AnimMgr.stop(tween, i);
3490             }
3491         }
3492     };
3493
3494     var getIndex = function(anim) {
3495         for (var i = 0, len = queue.length; i < len; ++i) {
3496             if (queue[i] == anim) {
3497                 return i;
3498             }
3499         }
3500         return -1;
3501     };
3502
3503
3504     var correctFrame = function(tween) {
3505         var frames = tween.totalFrames;
3506         var frame = tween.currentFrame;
3507         var expected = (tween.currentFrame * tween.duration * 1000 / tween.totalFrames);
3508         var elapsed = (new Date() - tween.getStartTime());
3509         var tweak = 0;
3510
3511         if (elapsed < tween.duration * 1000) {
3512             tweak = Math.round((elapsed / expected - 1) * tween.currentFrame);
3513         } else {
3514             tweak = frames - (frame + 1);
3515         }
3516         if (tweak > 0 && isFinite(tweak)) {
3517             if (tween.currentFrame + tweak >= frames) {
3518                 tweak = frames - (frame + 1);
3519             }
3520
3521             tween.currentFrame += tweak;
3522         }
3523     };
3524 };
3525
3526     /*
3527  * Portions of this file are based on pieces of Yahoo User Interface Library
3528  * Copyright (c) 2007, Yahoo! Inc. All rights reserved.
3529  * YUI licensed under the BSD License:
3530  * http://developer.yahoo.net/yui/license.txt
3531  * <script type="text/javascript">
3532  *
3533  */
3534 Roo.lib.Bezier = new function() {
3535
3536         this.getPosition = function(points, t) {
3537             var n = points.length;
3538             var tmp = [];
3539
3540             for (var i = 0; i < n; ++i) {
3541                 tmp[i] = [points[i][0], points[i][1]];
3542             }
3543
3544             for (var j = 1; j < n; ++j) {
3545                 for (i = 0; i < n - j; ++i) {
3546                     tmp[i][0] = (1 - t) * tmp[i][0] + t * tmp[parseInt(i + 1, 10)][0];
3547                     tmp[i][1] = (1 - t) * tmp[i][1] + t * tmp[parseInt(i + 1, 10)][1];
3548                 }
3549             }
3550
3551             return [ tmp[0][0], tmp[0][1] ];
3552
3553         };
3554     };/*
3555  * Portions of this file are based on pieces of Yahoo User Interface Library
3556  * Copyright (c) 2007, Yahoo! Inc. All rights reserved.
3557  * YUI licensed under the BSD License:
3558  * http://developer.yahoo.net/yui/license.txt
3559  * <script type="text/javascript">
3560  *
3561  */
3562 (function() {
3563
3564     Roo.lib.ColorAnim = function(el, attributes, duration, method) {
3565         Roo.lib.ColorAnim.superclass.constructor.call(this, el, attributes, duration, method);
3566     };
3567
3568     Roo.extend(Roo.lib.ColorAnim, Roo.lib.AnimBase);
3569
3570     var fly = Roo.lib.AnimBase.fly;
3571     var Y = Roo.lib;
3572     var superclass = Y.ColorAnim.superclass;
3573     var proto = Y.ColorAnim.prototype;
3574
3575     proto.toString = function() {
3576         var el = this.getEl();
3577         var id = el.id || el.tagName;
3578         return ("ColorAnim " + id);
3579     };
3580
3581     proto.patterns.color = /color$/i;
3582     proto.patterns.rgb = /^rgb\(([0-9]+)\s*,\s*([0-9]+)\s*,\s*([0-9]+)\)$/i;
3583     proto.patterns.hex = /^#?([0-9A-F]{2})([0-9A-F]{2})([0-9A-F]{2})$/i;
3584     proto.patterns.hex3 = /^#?([0-9A-F]{1})([0-9A-F]{1})([0-9A-F]{1})$/i;
3585     proto.patterns.transparent = /^transparent|rgba\(0, 0, 0, 0\)$/;
3586
3587
3588     proto.parseColor = function(s) {
3589         if (s.length == 3) {
3590             return s;
3591         }
3592
3593         var c = this.patterns.hex.exec(s);
3594         if (c && c.length == 4) {
3595             return [ parseInt(c[1], 16), parseInt(c[2], 16), parseInt(c[3], 16) ];
3596         }
3597
3598         c = this.patterns.rgb.exec(s);
3599         if (c && c.length == 4) {
3600             return [ parseInt(c[1], 10), parseInt(c[2], 10), parseInt(c[3], 10) ];
3601         }
3602
3603         c = this.patterns.hex3.exec(s);
3604         if (c && c.length == 4) {
3605             return [ parseInt(c[1] + c[1], 16), parseInt(c[2] + c[2], 16), parseInt(c[3] + c[3], 16) ];
3606         }
3607
3608         return null;
3609     };
3610     // since this uses fly! - it cant be in ColorAnim (which does not have fly yet..)
3611     proto.getAttribute = function(attr) {
3612         var el = this.getEl();
3613         if (this.patterns.color.test(attr)) {
3614             var val = fly(el).getStyle(attr);
3615
3616             if (this.patterns.transparent.test(val)) {
3617                 var parent = el.parentNode;
3618                 val = fly(parent).getStyle(attr);
3619
3620                 while (parent && this.patterns.transparent.test(val)) {
3621                     parent = parent.parentNode;
3622                     val = fly(parent).getStyle(attr);
3623                     if (parent.tagName.toUpperCase() == 'HTML') {
3624                         val = '#fff';
3625                     }
3626                 }
3627             }
3628         } else {
3629             val = superclass.getAttribute.call(this, attr);
3630         }
3631
3632         return val;
3633     };
3634     proto.getAttribute = function(attr) {
3635         var el = this.getEl();
3636         if (this.patterns.color.test(attr)) {
3637             var val = fly(el).getStyle(attr);
3638
3639             if (this.patterns.transparent.test(val)) {
3640                 var parent = el.parentNode;
3641                 val = fly(parent).getStyle(attr);
3642
3643                 while (parent && this.patterns.transparent.test(val)) {
3644                     parent = parent.parentNode;
3645                     val = fly(parent).getStyle(attr);
3646                     if (parent.tagName.toUpperCase() == 'HTML') {
3647                         val = '#fff';
3648                     }
3649                 }
3650             }
3651         } else {
3652             val = superclass.getAttribute.call(this, attr);
3653         }
3654
3655         return val;
3656     };
3657
3658     proto.doMethod = function(attr, start, end) {
3659         var val;
3660
3661         if (this.patterns.color.test(attr)) {
3662             val = [];
3663             for (var i = 0, len = start.length; i < len; ++i) {
3664                 val[i] = superclass.doMethod.call(this, attr, start[i], end[i]);
3665             }
3666
3667             val = 'rgb(' + Math.floor(val[0]) + ',' + Math.floor(val[1]) + ',' + Math.floor(val[2]) + ')';
3668         }
3669         else {
3670             val = superclass.doMethod.call(this, attr, start, end);
3671         }
3672
3673         return val;
3674     };
3675
3676     proto.setRuntimeAttribute = function(attr) {
3677         superclass.setRuntimeAttribute.call(this, attr);
3678
3679         if (this.patterns.color.test(attr)) {
3680             var attributes = this.attributes;
3681             var start = this.parseColor(this.runtimeAttributes[attr].start);
3682             var end = this.parseColor(this.runtimeAttributes[attr].end);
3683
3684             if (typeof attributes[attr]['to'] === 'undefined' && typeof attributes[attr]['by'] !== 'undefined') {
3685                 end = this.parseColor(attributes[attr].by);
3686
3687                 for (var i = 0, len = start.length; i < len; ++i) {
3688                     end[i] = start[i] + end[i];
3689                 }
3690             }
3691
3692             this.runtimeAttributes[attr].start = start;
3693             this.runtimeAttributes[attr].end = end;
3694         }
3695     };
3696 })();
3697
3698 /*
3699  * Portions of this file are based on pieces of Yahoo User Interface Library
3700  * Copyright (c) 2007, Yahoo! Inc. All rights reserved.
3701  * YUI licensed under the BSD License:
3702  * http://developer.yahoo.net/yui/license.txt
3703  * <script type="text/javascript">
3704  *
3705  */
3706 Roo.lib.Easing = {
3707
3708
3709     easeNone: function (t, b, c, d) {
3710         return c * t / d + b;
3711     },
3712
3713
3714     easeIn: function (t, b, c, d) {
3715         return c * (t /= d) * t + b;
3716     },
3717
3718
3719     easeOut: function (t, b, c, d) {
3720         return -c * (t /= d) * (t - 2) + b;
3721     },
3722
3723
3724     easeBoth: function (t, b, c, d) {
3725         if ((t /= d / 2) < 1) {
3726             return c / 2 * t * t + b;
3727         }
3728
3729         return -c / 2 * ((--t) * (t - 2) - 1) + b;
3730     },
3731
3732
3733     easeInStrong: function (t, b, c, d) {
3734         return c * (t /= d) * t * t * t + b;
3735     },
3736
3737
3738     easeOutStrong: function (t, b, c, d) {
3739         return -c * ((t = t / d - 1) * t * t * t - 1) + b;
3740     },
3741
3742
3743     easeBothStrong: function (t, b, c, d) {
3744         if ((t /= d / 2) < 1) {
3745             return c / 2 * t * t * t * t + b;
3746         }
3747
3748         return -c / 2 * ((t -= 2) * t * t * t - 2) + b;
3749     },
3750
3751
3752
3753     elasticIn: function (t, b, c, d, a, p) {
3754         if (t == 0) {
3755             return b;
3756         }
3757         if ((t /= d) == 1) {
3758             return b + c;
3759         }
3760         if (!p) {
3761             p = d * .3;
3762         }
3763
3764         if (!a || a < Math.abs(c)) {
3765             a = c;
3766             var s = p / 4;
3767         }
3768         else {
3769             var s = p / (2 * Math.PI) * Math.asin(c / a);
3770         }
3771
3772         return -(a * Math.pow(2, 10 * (t -= 1)) * Math.sin((t * d - s) * (2 * Math.PI) / p)) + b;
3773     },
3774
3775
3776     elasticOut: function (t, b, c, d, a, p) {
3777         if (t == 0) {
3778             return b;
3779         }
3780         if ((t /= d) == 1) {
3781             return b + c;
3782         }
3783         if (!p) {
3784             p = d * .3;
3785         }
3786
3787         if (!a || a < Math.abs(c)) {
3788             a = c;
3789             var s = p / 4;
3790         }
3791         else {
3792             var s = p / (2 * Math.PI) * Math.asin(c / a);
3793         }
3794
3795         return a * Math.pow(2, -10 * t) * Math.sin((t * d - s) * (2 * Math.PI) / p) + c + b;
3796     },
3797
3798
3799     elasticBoth: function (t, b, c, d, a, p) {
3800         if (t == 0) {
3801             return b;
3802         }
3803
3804         if ((t /= d / 2) == 2) {
3805             return b + c;
3806         }
3807
3808         if (!p) {
3809             p = d * (.3 * 1.5);
3810         }
3811
3812         if (!a || a < Math.abs(c)) {
3813             a = c;
3814             var s = p / 4;
3815         }
3816         else {
3817             var s = p / (2 * Math.PI) * Math.asin(c / a);
3818         }
3819
3820         if (t < 1) {
3821             return -.5 * (a * Math.pow(2, 10 * (t -= 1)) *
3822                           Math.sin((t * d - s) * (2 * Math.PI) / p)) + b;
3823         }
3824         return a * Math.pow(2, -10 * (t -= 1)) *
3825                Math.sin((t * d - s) * (2 * Math.PI) / p) * .5 + c + b;
3826     },
3827
3828
3829
3830     backIn: function (t, b, c, d, s) {
3831         if (typeof s == 'undefined') {
3832             s = 1.70158;
3833         }
3834         return c * (t /= d) * t * ((s + 1) * t - s) + b;
3835     },
3836
3837
3838     backOut: function (t, b, c, d, s) {
3839         if (typeof s == 'undefined') {
3840             s = 1.70158;
3841         }
3842         return c * ((t = t / d - 1) * t * ((s + 1) * t + s) + 1) + b;
3843     },
3844
3845
3846     backBoth: function (t, b, c, d, s) {
3847         if (typeof s == 'undefined') {
3848             s = 1.70158;
3849         }
3850
3851         if ((t /= d / 2 ) < 1) {
3852             return c / 2 * (t * t * (((s *= (1.525)) + 1) * t - s)) + b;
3853         }
3854         return c / 2 * ((t -= 2) * t * (((s *= (1.525)) + 1) * t + s) + 2) + b;
3855     },
3856
3857
3858     bounceIn: function (t, b, c, d) {
3859         return c - Roo.lib.Easing.bounceOut(d - t, 0, c, d) + b;
3860     },
3861
3862
3863     bounceOut: function (t, b, c, d) {
3864         if ((t /= d) < (1 / 2.75)) {
3865             return c * (7.5625 * t * t) + b;
3866         } else if (t < (2 / 2.75)) {
3867             return c * (7.5625 * (t -= (1.5 / 2.75)) * t + .75) + b;
3868         } else if (t < (2.5 / 2.75)) {
3869             return c * (7.5625 * (t -= (2.25 / 2.75)) * t + .9375) + b;
3870         }
3871         return c * (7.5625 * (t -= (2.625 / 2.75)) * t + .984375) + b;
3872     },
3873
3874
3875     bounceBoth: function (t, b, c, d) {
3876         if (t < d / 2) {
3877             return Roo.lib.Easing.bounceIn(t * 2, 0, c, d) * .5 + b;
3878         }
3879         return Roo.lib.Easing.bounceOut(t * 2 - d, 0, c, d) * .5 + c * .5 + b;
3880     }
3881 };/*
3882  * Portions of this file are based on pieces of Yahoo User Interface Library
3883  * Copyright (c) 2007, Yahoo! Inc. All rights reserved.
3884  * YUI licensed under the BSD License:
3885  * http://developer.yahoo.net/yui/license.txt
3886  * <script type="text/javascript">
3887  *
3888  */
3889     (function() {
3890         Roo.lib.Motion = function(el, attributes, duration, method) {
3891             if (el) {
3892                 Roo.lib.Motion.superclass.constructor.call(this, el, attributes, duration, method);
3893             }
3894         };
3895
3896         Roo.extend(Roo.lib.Motion, Roo.lib.ColorAnim);
3897
3898
3899         var Y = Roo.lib;
3900         var superclass = Y.Motion.superclass;
3901         var proto = Y.Motion.prototype;
3902
3903         proto.toString = function() {
3904             var el = this.getEl();
3905             var id = el.id || el.tagName;
3906             return ("Motion " + id);
3907         };
3908
3909         proto.patterns.points = /^points$/i;
3910
3911         proto.setAttribute = function(attr, val, unit) {
3912             if (this.patterns.points.test(attr)) {
3913                 unit = unit || 'px';
3914                 superclass.setAttribute.call(this, 'left', val[0], unit);
3915                 superclass.setAttribute.call(this, 'top', val[1], unit);
3916             } else {
3917                 superclass.setAttribute.call(this, attr, val, unit);
3918             }
3919         };
3920
3921         proto.getAttribute = function(attr) {
3922             if (this.patterns.points.test(attr)) {
3923                 var val = [
3924                         superclass.getAttribute.call(this, 'left'),
3925                         superclass.getAttribute.call(this, 'top')
3926                         ];
3927             } else {
3928                 val = superclass.getAttribute.call(this, attr);
3929             }
3930
3931             return val;
3932         };
3933
3934         proto.doMethod = function(attr, start, end) {
3935             var val = null;
3936
3937             if (this.patterns.points.test(attr)) {
3938                 var t = this.method(this.currentFrame, 0, 100, this.totalFrames) / 100;
3939                 val = Y.Bezier.getPosition(this.runtimeAttributes[attr], t);
3940             } else {
3941                 val = superclass.doMethod.call(this, attr, start, end);
3942             }
3943             return val;
3944         };
3945
3946         proto.setRuntimeAttribute = function(attr) {
3947             if (this.patterns.points.test(attr)) {
3948                 var el = this.getEl();
3949                 var attributes = this.attributes;
3950                 var start;
3951                 var control = attributes['points']['control'] || [];
3952                 var end;
3953                 var i, len;
3954
3955                 if (control.length > 0 && !(control[0] instanceof Array)) {
3956                     control = [control];
3957                 } else {
3958                     var tmp = [];
3959                     for (i = 0,len = control.length; i < len; ++i) {
3960                         tmp[i] = control[i];
3961                     }
3962                     control = tmp;
3963                 }
3964
3965                 Roo.fly(el).position();
3966
3967                 if (isset(attributes['points']['from'])) {
3968                     Roo.lib.Dom.setXY(el, attributes['points']['from']);
3969                 }
3970                 else {
3971                     Roo.lib.Dom.setXY(el, Roo.lib.Dom.getXY(el));
3972                 }
3973
3974                 start = this.getAttribute('points');
3975
3976
3977                 if (isset(attributes['points']['to'])) {
3978                     end = translateValues.call(this, attributes['points']['to'], start);
3979
3980                     var pageXY = Roo.lib.Dom.getXY(this.getEl());
3981                     for (i = 0,len = control.length; i < len; ++i) {
3982                         control[i] = translateValues.call(this, control[i], start);
3983                     }
3984
3985
3986                 } else if (isset(attributes['points']['by'])) {
3987                     end = [ start[0] + attributes['points']['by'][0], start[1] + attributes['points']['by'][1] ];
3988
3989                     for (i = 0,len = control.length; i < len; ++i) {
3990                         control[i] = [ start[0] + control[i][0], start[1] + control[i][1] ];
3991                     }
3992                 }
3993
3994                 this.runtimeAttributes[attr] = [start];
3995
3996                 if (control.length > 0) {
3997                     this.runtimeAttributes[attr] = this.runtimeAttributes[attr].concat(control);
3998                 }
3999
4000                 this.runtimeAttributes[attr][this.runtimeAttributes[attr].length] = end;
4001             }
4002             else {
4003                 superclass.setRuntimeAttribute.call(this, attr);
4004             }
4005         };
4006
4007         var translateValues = function(val, start) {
4008             var pageXY = Roo.lib.Dom.getXY(this.getEl());
4009             val = [ val[0] - pageXY[0] + start[0], val[1] - pageXY[1] + start[1] ];
4010
4011             return val;
4012         };
4013
4014         var isset = function(prop) {
4015             return (typeof prop !== 'undefined');
4016         };
4017     })();
4018 /*
4019  * Portions of this file are based on pieces of Yahoo User Interface Library
4020  * Copyright (c) 2007, Yahoo! Inc. All rights reserved.
4021  * YUI licensed under the BSD License:
4022  * http://developer.yahoo.net/yui/license.txt
4023  * <script type="text/javascript">
4024  *
4025  */
4026     (function() {
4027         Roo.lib.Scroll = function(el, attributes, duration, method) {
4028             if (el) {
4029                 Roo.lib.Scroll.superclass.constructor.call(this, el, attributes, duration, method);
4030             }
4031         };
4032
4033         Roo.extend(Roo.lib.Scroll, Roo.lib.ColorAnim);
4034
4035
4036         var Y = Roo.lib;
4037         var superclass = Y.Scroll.superclass;
4038         var proto = Y.Scroll.prototype;
4039
4040         proto.toString = function() {
4041             var el = this.getEl();
4042             var id = el.id || el.tagName;
4043             return ("Scroll " + id);
4044         };
4045
4046         proto.doMethod = function(attr, start, end) {
4047             var val = null;
4048
4049             if (attr == 'scroll') {
4050                 val = [
4051                         this.method(this.currentFrame, start[0], end[0] - start[0], this.totalFrames),
4052                         this.method(this.currentFrame, start[1], end[1] - start[1], this.totalFrames)
4053                         ];
4054
4055             } else {
4056                 val = superclass.doMethod.call(this, attr, start, end);
4057             }
4058             return val;
4059         };
4060
4061         proto.getAttribute = function(attr) {
4062             var val = null;
4063             var el = this.getEl();
4064
4065             if (attr == 'scroll') {
4066                 val = [ el.scrollLeft, el.scrollTop ];
4067             } else {
4068                 val = superclass.getAttribute.call(this, attr);
4069             }
4070
4071             return val;
4072         };
4073
4074         proto.setAttribute = function(attr, val, unit) {
4075             var el = this.getEl();
4076
4077             if (attr == 'scroll') {
4078                 el.scrollLeft = val[0];
4079                 el.scrollTop = val[1];
4080             } else {
4081                 superclass.setAttribute.call(this, attr, val, unit);
4082             }
4083         };
4084     })();
4085 /*
4086  * Based on:
4087  * Ext JS Library 1.1.1
4088  * Copyright(c) 2006-2007, Ext JS, LLC.
4089  *
4090  * Originally Released Under LGPL - original licence link has changed is not relivant.
4091  *
4092  * Fork - LGPL
4093  * <script type="text/javascript">
4094  */
4095
4096
4097 // nasty IE9 hack - what a pile of crap that is..
4098
4099  if (typeof Range != "undefined" && typeof Range.prototype.createContextualFragment == "undefined") {
4100     Range.prototype.createContextualFragment = function (html) {
4101         var doc = window.document;
4102         var container = doc.createElement("div");
4103         container.innerHTML = html;
4104         var frag = doc.createDocumentFragment(), n;
4105         while ((n = container.firstChild)) {
4106             frag.appendChild(n);
4107         }
4108         return frag;
4109     };
4110 }
4111
4112 /**
4113  * @class Roo.DomHelper
4114  * Utility class for working with DOM and/or Templates. It transparently supports using HTML fragments or DOM.
4115  * 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>.
4116  * @singleton
4117  */
4118 Roo.DomHelper = function(){
4119     var tempTableEl = null;
4120     var emptyTags = /^(?:br|frame|hr|img|input|link|meta|range|spacer|wbr|area|param|col)$/i;
4121     var tableRe = /^table|tbody|tr|td$/i;
4122     var xmlns = {};
4123     // build as innerHTML where available
4124     /** @ignore */
4125     var createHtml = function(o){
4126         if(typeof o == 'string'){
4127             return o;
4128         }
4129         var b = "";
4130         if(!o.tag){
4131             o.tag = "div";
4132         }
4133         b += "<" + o.tag;
4134         for(var attr in o){
4135             if(attr == "tag" || attr == "children" || attr == "cn" || attr == "html" || typeof o[attr] == "function") continue;
4136             if(attr == "style"){
4137                 var s = o["style"];
4138                 if(typeof s == "function"){
4139                     s = s.call();
4140                 }
4141                 if(typeof s == "string"){
4142                     b += ' style="' + s + '"';
4143                 }else if(typeof s == "object"){
4144                     b += ' style="';
4145                     for(var key in s){
4146                         if(typeof s[key] != "function"){
4147                             b += key + ":" + s[key] + ";";
4148                         }
4149                     }
4150                     b += '"';
4151                 }
4152             }else{
4153                 if(attr == "cls"){
4154                     b += ' class="' + o["cls"] + '"';
4155                 }else if(attr == "htmlFor"){
4156                     b += ' for="' + o["htmlFor"] + '"';
4157                 }else{
4158                     b += " " + attr + '="' + o[attr] + '"';
4159                 }
4160             }
4161         }
4162         if(emptyTags.test(o.tag)){
4163             b += "/>";
4164         }else{
4165             b += ">";
4166             var cn = o.children || o.cn;
4167             if(cn){
4168                 //http://bugs.kde.org/show_bug.cgi?id=71506
4169                 if((cn instanceof Array) || (Roo.isSafari && typeof(cn.join) == "function")){
4170                     for(var i = 0, len = cn.length; i < len; i++) {
4171                         b += createHtml(cn[i], b);
4172                     }
4173                 }else{
4174                     b += createHtml(cn, b);
4175                 }
4176             }
4177             if(o.html){
4178                 b += o.html;
4179             }
4180             b += "</" + o.tag + ">";
4181         }
4182         return b;
4183     };
4184
4185     // build as dom
4186     /** @ignore */
4187     var createDom = function(o, parentNode){
4188          
4189         // defininition craeted..
4190         var ns = false;
4191         if (o.ns && o.ns != 'html') {
4192                
4193             if (o.xmlns && typeof(xmlns[o.ns]) == 'undefined') {
4194                 xmlns[o.ns] = o.xmlns;
4195                 ns = o.xmlns;
4196             }
4197             if (typeof(xmlns[o.ns]) == 'undefined') {
4198                 console.log("Trying to create namespace element " + o.ns + ", however no xmlns was sent to builder previously");
4199             }
4200             ns = xmlns[o.ns];
4201         }
4202         
4203         
4204         if (typeof(o) == 'string') {
4205             return parentNode.appendChild(document.createTextNode(o));
4206         }
4207         o.tag = o.tag || div;
4208         if (o.ns && Roo.isIE) {
4209             ns = false;
4210             o.tag = o.ns + ':' + o.tag;
4211             
4212         }
4213         var el = ns ? document.createElementNS( ns, o.tag||'div') :  document.createElement(o.tag||'div');
4214         var useSet = el.setAttribute ? true : false; // In IE some elements don't have setAttribute
4215         for(var attr in o){
4216             
4217             if(attr == "tag" || attr == "ns" ||attr == "xmlns" ||attr == "children" || attr == "cn" || attr == "html" || 
4218                     attr == "style" || typeof o[attr] == "function") continue;
4219                     
4220             if(attr=="cls" && Roo.isIE){
4221                 el.className = o["cls"];
4222             }else{
4223                 if(useSet) el.setAttribute(attr=="cls" ? 'class' : attr, o[attr]);
4224                 else el[attr] = o[attr];
4225             }
4226         }
4227         Roo.DomHelper.applyStyles(el, o.style);
4228         var cn = o.children || o.cn;
4229         if(cn){
4230             //http://bugs.kde.org/show_bug.cgi?id=71506
4231              if((cn instanceof Array) || (Roo.isSafari && typeof(cn.join) == "function")){
4232                 for(var i = 0, len = cn.length; i < len; i++) {
4233                     createDom(cn[i], el);
4234                 }
4235             }else{
4236                 createDom(cn, el);
4237             }
4238         }
4239         if(o.html){
4240             el.innerHTML = o.html;
4241         }
4242         if(parentNode){
4243            parentNode.appendChild(el);
4244         }
4245         return el;
4246     };
4247
4248     var ieTable = function(depth, s, h, e){
4249         tempTableEl.innerHTML = [s, h, e].join('');
4250         var i = -1, el = tempTableEl;
4251         while(++i < depth){
4252             el = el.firstChild;
4253         }
4254         return el;
4255     };
4256
4257     // kill repeat to save bytes
4258     var ts = '<table>',
4259         te = '</table>',
4260         tbs = ts+'<tbody>',
4261         tbe = '</tbody>'+te,
4262         trs = tbs + '<tr>',
4263         tre = '</tr>'+tbe;
4264
4265     /**
4266      * @ignore
4267      * Nasty code for IE's broken table implementation
4268      */
4269     var insertIntoTable = function(tag, where, el, html){
4270         if(!tempTableEl){
4271             tempTableEl = document.createElement('div');
4272         }
4273         var node;
4274         var before = null;
4275         if(tag == 'td'){
4276             if(where == 'afterbegin' || where == 'beforeend'){ // INTO a TD
4277                 return;
4278             }
4279             if(where == 'beforebegin'){
4280                 before = el;
4281                 el = el.parentNode;
4282             } else{
4283                 before = el.nextSibling;
4284                 el = el.parentNode;
4285             }
4286             node = ieTable(4, trs, html, tre);
4287         }
4288         else if(tag == 'tr'){
4289             if(where == 'beforebegin'){
4290                 before = el;
4291                 el = el.parentNode;
4292                 node = ieTable(3, tbs, html, tbe);
4293             } else if(where == 'afterend'){
4294                 before = el.nextSibling;
4295                 el = el.parentNode;
4296                 node = ieTable(3, tbs, html, tbe);
4297             } else{ // INTO a TR
4298                 if(where == 'afterbegin'){
4299                     before = el.firstChild;
4300                 }
4301                 node = ieTable(4, trs, html, tre);
4302             }
4303         } else if(tag == 'tbody'){
4304             if(where == 'beforebegin'){
4305                 before = el;
4306                 el = el.parentNode;
4307                 node = ieTable(2, ts, html, te);
4308             } else if(where == 'afterend'){
4309                 before = el.nextSibling;
4310                 el = el.parentNode;
4311                 node = ieTable(2, ts, html, te);
4312             } else{
4313                 if(where == 'afterbegin'){
4314                     before = el.firstChild;
4315                 }
4316                 node = ieTable(3, tbs, html, tbe);
4317             }
4318         } else{ // TABLE
4319             if(where == 'beforebegin' || where == 'afterend'){ // OUTSIDE the table
4320                 return;
4321             }
4322             if(where == 'afterbegin'){
4323                 before = el.firstChild;
4324             }
4325             node = ieTable(2, ts, html, te);
4326         }
4327         el.insertBefore(node, before);
4328         return node;
4329     };
4330
4331     return {
4332     /** True to force the use of DOM instead of html fragments @type Boolean */
4333     useDom : false,
4334
4335     /**
4336      * Returns the markup for the passed Element(s) config
4337      * @param {Object} o The Dom object spec (and children)
4338      * @return {String}
4339      */
4340     markup : function(o){
4341         return createHtml(o);
4342     },
4343
4344     /**
4345      * Applies a style specification to an element
4346      * @param {String/HTMLElement} el The element to apply styles to
4347      * @param {String/Object/Function} styles A style specification string eg "width:100px", or object in the form {width:"100px"}, or
4348      * a function which returns such a specification.
4349      */
4350     applyStyles : function(el, styles){
4351         if(styles){
4352            el = Roo.fly(el);
4353            if(typeof styles == "string"){
4354                var re = /\s?([a-z\-]*)\:\s?([^;]*);?/gi;
4355                var matches;
4356                while ((matches = re.exec(styles)) != null){
4357                    el.setStyle(matches[1], matches[2]);
4358                }
4359            }else if (typeof styles == "object"){
4360                for (var style in styles){
4361                   el.setStyle(style, styles[style]);
4362                }
4363            }else if (typeof styles == "function"){
4364                 Roo.DomHelper.applyStyles(el, styles.call());
4365            }
4366         }
4367     },
4368
4369     /**
4370      * Inserts an HTML fragment into the Dom
4371      * @param {String} where Where to insert the html in relation to el - beforeBegin, afterBegin, beforeEnd, afterEnd.
4372      * @param {HTMLElement} el The context element
4373      * @param {String} html The HTML fragmenet
4374      * @return {HTMLElement} The new node
4375      */
4376     insertHtml : function(where, el, html){
4377         where = where.toLowerCase();
4378         if(el.insertAdjacentHTML){
4379             if(tableRe.test(el.tagName)){
4380                 var rs;
4381                 if(rs = insertIntoTable(el.tagName.toLowerCase(), where, el, html)){
4382                     return rs;
4383                 }
4384             }
4385             switch(where){
4386                 case "beforebegin":
4387                     el.insertAdjacentHTML('BeforeBegin', html);
4388                     return el.previousSibling;
4389                 case "afterbegin":
4390                     el.insertAdjacentHTML('AfterBegin', html);
4391                     return el.firstChild;
4392                 case "beforeend":
4393                     el.insertAdjacentHTML('BeforeEnd', html);
4394                     return el.lastChild;
4395                 case "afterend":
4396                     el.insertAdjacentHTML('AfterEnd', html);
4397                     return el.nextSibling;
4398             }
4399             throw 'Illegal insertion point -> "' + where + '"';
4400         }
4401         var range = el.ownerDocument.createRange();
4402         var frag;
4403         switch(where){
4404              case "beforebegin":
4405                 range.setStartBefore(el);
4406                 frag = range.createContextualFragment(html);
4407                 el.parentNode.insertBefore(frag, el);
4408                 return el.previousSibling;
4409              case "afterbegin":
4410                 if(el.firstChild){
4411                     range.setStartBefore(el.firstChild);
4412                     frag = range.createContextualFragment(html);
4413                     el.insertBefore(frag, el.firstChild);
4414                     return el.firstChild;
4415                 }else{
4416                     el.innerHTML = html;
4417                     return el.firstChild;
4418                 }
4419             case "beforeend":
4420                 if(el.lastChild){
4421                     range.setStartAfter(el.lastChild);
4422                     frag = range.createContextualFragment(html);
4423                     el.appendChild(frag);
4424                     return el.lastChild;
4425                 }else{
4426                     el.innerHTML = html;
4427                     return el.lastChild;
4428                 }
4429             case "afterend":
4430                 range.setStartAfter(el);
4431                 frag = range.createContextualFragment(html);
4432                 el.parentNode.insertBefore(frag, el.nextSibling);
4433                 return el.nextSibling;
4434             }
4435             throw 'Illegal insertion point -> "' + where + '"';
4436     },
4437
4438     /**
4439      * Creates new Dom element(s) and inserts them before el
4440      * @param {String/HTMLElement/Element} el The context element
4441      * @param {Object/String} o The Dom object spec (and children) or raw HTML blob
4442      * @param {Boolean} returnElement (optional) true to return a Roo.Element
4443      * @return {HTMLElement/Roo.Element} The new node
4444      */
4445     insertBefore : function(el, o, returnElement){
4446         return this.doInsert(el, o, returnElement, "beforeBegin");
4447     },
4448
4449     /**
4450      * Creates new Dom element(s) and inserts them after el
4451      * @param {String/HTMLElement/Element} el The context element
4452      * @param {Object} o The Dom object spec (and children)
4453      * @param {Boolean} returnElement (optional) true to return a Roo.Element
4454      * @return {HTMLElement/Roo.Element} The new node
4455      */
4456     insertAfter : function(el, o, returnElement){
4457         return this.doInsert(el, o, returnElement, "afterEnd", "nextSibling");
4458     },
4459
4460     /**
4461      * Creates new Dom element(s) and inserts them as the first child of el
4462      * @param {String/HTMLElement/Element} el The context element
4463      * @param {Object/String} o The Dom object spec (and children) or raw HTML blob
4464      * @param {Boolean} returnElement (optional) true to return a Roo.Element
4465      * @return {HTMLElement/Roo.Element} The new node
4466      */
4467     insertFirst : function(el, o, returnElement){
4468         return this.doInsert(el, o, returnElement, "afterBegin");
4469     },
4470
4471     // private
4472     doInsert : function(el, o, returnElement, pos, sibling){
4473         el = Roo.getDom(el);
4474         var newNode;
4475         if(this.useDom || o.ns){
4476             newNode = createDom(o, null);
4477             el.parentNode.insertBefore(newNode, sibling ? el[sibling] : el);
4478         }else{
4479             var html = createHtml(o);
4480             newNode = this.insertHtml(pos, el, html);
4481         }
4482         return returnElement ? Roo.get(newNode, true) : newNode;
4483     },
4484
4485     /**
4486      * Creates new Dom element(s) and appends them to el
4487      * @param {String/HTMLElement/Element} el The context element
4488      * @param {Object/String} o The Dom object spec (and children) or raw HTML blob
4489      * @param {Boolean} returnElement (optional) true to return a Roo.Element
4490      * @return {HTMLElement/Roo.Element} The new node
4491      */
4492     append : function(el, o, returnElement){
4493         el = Roo.getDom(el);
4494         var newNode;
4495         if(this.useDom || o.ns){
4496             newNode = createDom(o, null);
4497             el.appendChild(newNode);
4498         }else{
4499             var html = createHtml(o);
4500             newNode = this.insertHtml("beforeEnd", el, html);
4501         }
4502         return returnElement ? Roo.get(newNode, true) : newNode;
4503     },
4504
4505     /**
4506      * Creates new Dom element(s) and overwrites the contents of el with them
4507      * @param {String/HTMLElement/Element} el The context element
4508      * @param {Object/String} o The Dom object spec (and children) or raw HTML blob
4509      * @param {Boolean} returnElement (optional) true to return a Roo.Element
4510      * @return {HTMLElement/Roo.Element} The new node
4511      */
4512     overwrite : function(el, o, returnElement){
4513         el = Roo.getDom(el);
4514         if (o.ns) {
4515           
4516             while (el.childNodes.length) {
4517                 el.removeChild(el.firstChild);
4518             }
4519             createDom(o, el);
4520         } else {
4521             el.innerHTML = createHtml(o);   
4522         }
4523         
4524         return returnElement ? Roo.get(el.firstChild, true) : el.firstChild;
4525     },
4526
4527     /**
4528      * Creates a new Roo.DomHelper.Template from the Dom object spec
4529      * @param {Object} o The Dom object spec (and children)
4530      * @return {Roo.DomHelper.Template} The new template
4531      */
4532     createTemplate : function(o){
4533         var html = createHtml(o);
4534         return new Roo.Template(html);
4535     }
4536     };
4537 }();
4538 /*
4539  * Based on:
4540  * Ext JS Library 1.1.1
4541  * Copyright(c) 2006-2007, Ext JS, LLC.
4542  *
4543  * Originally Released Under LGPL - original licence link has changed is not relivant.
4544  *
4545  * Fork - LGPL
4546  * <script type="text/javascript">
4547  */
4548  
4549 /**
4550 * @class Roo.Template
4551 * Represents an HTML fragment template. Templates can be precompiled for greater performance.
4552 * For a list of available format functions, see {@link Roo.util.Format}.<br />
4553 * Usage:
4554 <pre><code>
4555 var t = new Roo.Template({
4556     html :  '&lt;div name="{id}"&gt;' + 
4557         '&lt;span class="{cls}"&gt;{name:trim} {someval:this.myformat}{value:ellipsis(10)}&lt;/span&gt;' +
4558         '&lt;/div&gt;',
4559     myformat: function (value, allValues) {
4560         return 'XX' + value;
4561     }
4562 });
4563 t.append('some-element', {id: 'myid', cls: 'myclass', name: 'foo', value: 'bar'});
4564 </code></pre>
4565 * For more information see this blog post with examples:
4566 *  <a href="http://www.cnitblog.com/seeyeah/archive/2011/12/30/38728.html/">DomHelper
4567      - Create Elements using DOM, HTML fragments and Templates</a>. 
4568 * @constructor
4569 * @param {Object} cfg - Configuration object.
4570 */
4571 Roo.Template = function(cfg){
4572     // BC!
4573     if(cfg instanceof Array){
4574         cfg = cfg.join("");
4575     }else if(arguments.length > 1){
4576         cfg = Array.prototype.join.call(arguments, "");
4577     }
4578     
4579     
4580     if (typeof(cfg) == 'object') {
4581         Roo.apply(this,cfg)
4582     } else {
4583         // bc
4584         this.html = cfg;
4585     }
4586     if (this.url) {
4587         this.load();
4588     }
4589     
4590 };
4591 Roo.Template.prototype = {
4592     
4593     /**
4594      * @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..
4595      *                    it should be fixed so that template is observable...
4596      */
4597     url : false,
4598     /**
4599      * @cfg {String} html  The HTML fragment or an array of fragments to join("") or multiple arguments to join("")
4600      */
4601     html : '',
4602     /**
4603      * Returns an HTML fragment of this template with the specified values applied.
4604      * @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'})
4605      * @return {String} The HTML fragment
4606      */
4607     applyTemplate : function(values){
4608         try {
4609            
4610             if(this.compiled){
4611                 return this.compiled(values);
4612             }
4613             var useF = this.disableFormats !== true;
4614             var fm = Roo.util.Format, tpl = this;
4615             var fn = function(m, name, format, args){
4616                 if(format && useF){
4617                     if(format.substr(0, 5) == "this."){
4618                         return tpl.call(format.substr(5), values[name], values);
4619                     }else{
4620                         if(args){
4621                             // quoted values are required for strings in compiled templates, 
4622                             // but for non compiled we need to strip them
4623                             // quoted reversed for jsmin
4624                             var re = /^\s*['"](.*)["']\s*$/;
4625                             args = args.split(',');
4626                             for(var i = 0, len = args.length; i < len; i++){
4627                                 args[i] = args[i].replace(re, "$1");
4628                             }
4629                             args = [values[name]].concat(args);
4630                         }else{
4631                             args = [values[name]];
4632                         }
4633                         return fm[format].apply(fm, args);
4634                     }
4635                 }else{
4636                     return values[name] !== undefined ? values[name] : "";
4637                 }
4638             };
4639             return this.html.replace(this.re, fn);
4640         } catch (e) {
4641             Roo.log(e);
4642             throw e;
4643         }
4644          
4645     },
4646     
4647     loading : false,
4648       
4649     load : function ()
4650     {
4651          
4652         if (this.loading) {
4653             return;
4654         }
4655         var _t = this;
4656         
4657         this.loading = true;
4658         this.compiled = false;
4659         
4660         var cx = new Roo.data.Connection();
4661         cx.request({
4662             url : this.url,
4663             method : 'GET',
4664             success : function (response) {
4665                 _t.loading = false;
4666                 _t.html = response.responseText;
4667                 _t.url = false;
4668                 _t.compile();
4669              },
4670             failure : function(response) {
4671                 Roo.log("Template failed to load from " + _t.url);
4672                 _t.loading = false;
4673             }
4674         });
4675     },
4676
4677     /**
4678      * Sets the HTML used as the template and optionally compiles it.
4679      * @param {String} html
4680      * @param {Boolean} compile (optional) True to compile the template (defaults to undefined)
4681      * @return {Roo.Template} this
4682      */
4683     set : function(html, compile){
4684         this.html = html;
4685         this.compiled = null;
4686         if(compile){
4687             this.compile();
4688         }
4689         return this;
4690     },
4691     
4692     /**
4693      * True to disable format functions (defaults to false)
4694      * @type Boolean
4695      */
4696     disableFormats : false,
4697     
4698     /**
4699     * The regular expression used to match template variables 
4700     * @type RegExp
4701     * @property 
4702     */
4703     re : /\{([\w-]+)(?:\:([\w\.]*)(?:\((.*?)?\))?)?\}/g,
4704     
4705     /**
4706      * Compiles the template into an internal function, eliminating the RegEx overhead.
4707      * @return {Roo.Template} this
4708      */
4709     compile : function(){
4710         var fm = Roo.util.Format;
4711         var useF = this.disableFormats !== true;
4712         var sep = Roo.isGecko ? "+" : ",";
4713         var fn = function(m, name, format, args){
4714             if(format && useF){
4715                 args = args ? ',' + args : "";
4716                 if(format.substr(0, 5) != "this."){
4717                     format = "fm." + format + '(';
4718                 }else{
4719                     format = 'this.call("'+ format.substr(5) + '", ';
4720                     args = ", values";
4721                 }
4722             }else{
4723                 args= ''; format = "(values['" + name + "'] == undefined ? '' : ";
4724             }
4725             return "'"+ sep + format + "values['" + name + "']" + args + ")"+sep+"'";
4726         };
4727         var body;
4728         // branched to use + in gecko and [].join() in others
4729         if(Roo.isGecko){
4730             body = "this.compiled = function(values){ return '" +
4731                    this.html.replace(/\\/g, '\\\\').replace(/(\r\n|\n)/g, '\\n').replace(/'/g, "\\'").replace(this.re, fn) +
4732                     "';};";
4733         }else{
4734             body = ["this.compiled = function(values){ return ['"];
4735             body.push(this.html.replace(/\\/g, '\\\\').replace(/(\r\n|\n)/g, '\\n').replace(/'/g, "\\'").replace(this.re, fn));
4736             body.push("'].join('');};");
4737             body = body.join('');
4738         }
4739         /**
4740          * eval:var:values
4741          * eval:var:fm
4742          */
4743         eval(body);
4744         return this;
4745     },
4746     
4747     // private function used to call members
4748     call : function(fnName, value, allValues){
4749         return this[fnName](value, allValues);
4750     },
4751     
4752     /**
4753      * Applies the supplied values to the template and inserts the new node(s) as the first child of el.
4754      * @param {String/HTMLElement/Roo.Element} el The context element
4755      * @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'})
4756      * @param {Boolean} returnElement (optional) true to return a Roo.Element (defaults to undefined)
4757      * @return {HTMLElement/Roo.Element} The new node or Element
4758      */
4759     insertFirst: function(el, values, returnElement){
4760         return this.doInsert('afterBegin', el, values, returnElement);
4761     },
4762
4763     /**
4764      * Applies the supplied values to the template and inserts the new node(s) before el.
4765      * @param {String/HTMLElement/Roo.Element} el The context element
4766      * @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'})
4767      * @param {Boolean} returnElement (optional) true to return a Roo.Element (defaults to undefined)
4768      * @return {HTMLElement/Roo.Element} The new node or Element
4769      */
4770     insertBefore: function(el, values, returnElement){
4771         return this.doInsert('beforeBegin', el, values, returnElement);
4772     },
4773
4774     /**
4775      * Applies the supplied values to the template and inserts the new node(s) after el.
4776      * @param {String/HTMLElement/Roo.Element} el The context element
4777      * @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'})
4778      * @param {Boolean} returnElement (optional) true to return a Roo.Element (defaults to undefined)
4779      * @return {HTMLElement/Roo.Element} The new node or Element
4780      */
4781     insertAfter : function(el, values, returnElement){
4782         return this.doInsert('afterEnd', el, values, returnElement);
4783     },
4784     
4785     /**
4786      * Applies the supplied values to the template and appends the new node(s) to el.
4787      * @param {String/HTMLElement/Roo.Element} el The context element
4788      * @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'})
4789      * @param {Boolean} returnElement (optional) true to return a Roo.Element (defaults to undefined)
4790      * @return {HTMLElement/Roo.Element} The new node or Element
4791      */
4792     append : function(el, values, returnElement){
4793         return this.doInsert('beforeEnd', el, values, returnElement);
4794     },
4795
4796     doInsert : function(where, el, values, returnEl){
4797         el = Roo.getDom(el);
4798         var newNode = Roo.DomHelper.insertHtml(where, el, this.applyTemplate(values));
4799         return returnEl ? Roo.get(newNode, true) : newNode;
4800     },
4801
4802     /**
4803      * Applies the supplied values to the template and overwrites the content of el with the new node(s).
4804      * @param {String/HTMLElement/Roo.Element} el The context element
4805      * @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'})
4806      * @param {Boolean} returnElement (optional) true to return a Roo.Element (defaults to undefined)
4807      * @return {HTMLElement/Roo.Element} The new node or Element
4808      */
4809     overwrite : function(el, values, returnElement){
4810         el = Roo.getDom(el);
4811         el.innerHTML = this.applyTemplate(values);
4812         return returnElement ? Roo.get(el.firstChild, true) : el.firstChild;
4813     }
4814 };
4815 /**
4816  * Alias for {@link #applyTemplate}
4817  * @method
4818  */
4819 Roo.Template.prototype.apply = Roo.Template.prototype.applyTemplate;
4820
4821 // backwards compat
4822 Roo.DomHelper.Template = Roo.Template;
4823
4824 /**
4825  * Creates a template from the passed element's value (<i>display:none</i> textarea, preferred) or innerHTML.
4826  * @param {String/HTMLElement} el A DOM element or its id
4827  * @returns {Roo.Template} The created template
4828  * @static
4829  */
4830 Roo.Template.from = function(el){
4831     el = Roo.getDom(el);
4832     return new Roo.Template(el.value || el.innerHTML);
4833 };/*
4834  * Based on:
4835  * Ext JS Library 1.1.1
4836  * Copyright(c) 2006-2007, Ext JS, LLC.
4837  *
4838  * Originally Released Under LGPL - original licence link has changed is not relivant.
4839  *
4840  * Fork - LGPL
4841  * <script type="text/javascript">
4842  */
4843  
4844
4845 /*
4846  * This is code is also distributed under MIT license for use
4847  * with jQuery and prototype JavaScript libraries.
4848  */
4849 /**
4850  * @class Roo.DomQuery
4851 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).
4852 <p>
4853 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>
4854
4855 <p>
4856 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.
4857 </p>
4858 <h4>Element Selectors:</h4>
4859 <ul class="list">
4860     <li> <b>*</b> any element</li>
4861     <li> <b>E</b> an element with the tag E</li>
4862     <li> <b>E F</b> All descendent elements of E that have the tag F</li>
4863     <li> <b>E > F</b> or <b>E/F</b> all direct children elements of E that have the tag F</li>
4864     <li> <b>E + F</b> all elements with the tag F that are immediately preceded by an element with the tag E</li>
4865     <li> <b>E ~ F</b> all elements with the tag F that are preceded by a sibling element with the tag E</li>
4866 </ul>
4867 <h4>Attribute Selectors:</h4>
4868 <p>The use of @ and quotes are optional. For example, div[@foo='bar'] is also a valid attribute selector.</p>
4869 <ul class="list">
4870     <li> <b>E[foo]</b> has an attribute "foo"</li>
4871     <li> <b>E[foo=bar]</b> has an attribute "foo" that equals "bar"</li>
4872     <li> <b>E[foo^=bar]</b> has an attribute "foo" that starts with "bar"</li>
4873     <li> <b>E[foo$=bar]</b> has an attribute "foo" that ends with "bar"</li>
4874     <li> <b>E[foo*=bar]</b> has an attribute "foo" that contains the substring "bar"</li>
4875     <li> <b>E[foo%=2]</b> has an attribute "foo" that is evenly divisible by 2</li>
4876     <li> <b>E[foo!=bar]</b> has an attribute "foo" that does not equal "bar"</li>
4877 </ul>
4878 <h4>Pseudo Classes:</h4>
4879 <ul class="list">
4880     <li> <b>E:first-child</b> E is the first child of its parent</li>
4881     <li> <b>E:last-child</b> E is the last child of its parent</li>
4882     <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>
4883     <li> <b>E:nth-child(odd)</b> E is an odd child of its parent</li>
4884     <li> <b>E:nth-child(even)</b> E is an even child of its parent</li>
4885     <li> <b>E:only-child</b> E is the only child of its parent</li>
4886     <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>
4887     <li> <b>E:first</b> the first E in the resultset</li>
4888     <li> <b>E:last</b> the last E in the resultset</li>
4889     <li> <b>E:nth(<i>n</i>)</b> the <i>n</i>th E in the resultset (1 based)</li>
4890     <li> <b>E:odd</b> shortcut for :nth-child(odd)</li>
4891     <li> <b>E:even</b> shortcut for :nth-child(even)</li>
4892     <li> <b>E:contains(foo)</b> E's innerHTML contains the substring "foo"</li>
4893     <li> <b>E:nodeValue(foo)</b> E contains a textNode with a nodeValue that equals "foo"</li>
4894     <li> <b>E:not(S)</b> an E element that does not match simple selector S</li>
4895     <li> <b>E:has(S)</b> an E element that has a descendent that matches simple selector S</li>
4896     <li> <b>E:next(S)</b> an E element whose next sibling matches simple selector S</li>
4897     <li> <b>E:prev(S)</b> an E element whose previous sibling matches simple selector S</li>
4898 </ul>
4899 <h4>CSS Value Selectors:</h4>
4900 <ul class="list">
4901     <li> <b>E{display=none}</b> css value "display" that equals "none"</li>
4902     <li> <b>E{display^=none}</b> css value "display" that starts with "none"</li>
4903     <li> <b>E{display$=none}</b> css value "display" that ends with "none"</li>
4904     <li> <b>E{display*=none}</b> css value "display" that contains the substring "none"</li>
4905     <li> <b>E{display%=2}</b> css value "display" that is evenly divisible by 2</li>
4906     <li> <b>E{display!=none}</b> css value "display" that does not equal "none"</li>
4907 </ul>
4908  * @singleton
4909  */
4910 Roo.DomQuery = function(){
4911     var cache = {}, simpleCache = {}, valueCache = {};
4912     var nonSpace = /\S/;
4913     var trimRe = /^\s+|\s+$/g;
4914     var tplRe = /\{(\d+)\}/g;
4915     var modeRe = /^(\s?[\/>+~]\s?|\s|$)/;
4916     var tagTokenRe = /^(#)?([\w-\*]+)/;
4917     var nthRe = /(\d*)n\+?(\d*)/, nthRe2 = /\D/;
4918
4919     function child(p, index){
4920         var i = 0;
4921         var n = p.firstChild;
4922         while(n){
4923             if(n.nodeType == 1){
4924                if(++i == index){
4925                    return n;
4926                }
4927             }
4928             n = n.nextSibling;
4929         }
4930         return null;
4931     };
4932
4933     function next(n){
4934         while((n = n.nextSibling) && n.nodeType != 1);
4935         return n;
4936     };
4937
4938     function prev(n){
4939         while((n = n.previousSibling) && n.nodeType != 1);
4940         return n;
4941     };
4942
4943     function children(d){
4944         var n = d.firstChild, ni = -1;
4945             while(n){
4946                 var nx = n.nextSibling;
4947                 if(n.nodeType == 3 && !nonSpace.test(n.nodeValue)){
4948                     d.removeChild(n);
4949                 }else{
4950                     n.nodeIndex = ++ni;
4951                 }
4952                 n = nx;
4953             }
4954             return this;
4955         };
4956
4957     function byClassName(c, a, v){
4958         if(!v){
4959             return c;
4960         }
4961         var r = [], ri = -1, cn;
4962         for(var i = 0, ci; ci = c[i]; i++){
4963             if((' '+ci.className+' ').indexOf(v) != -1){
4964                 r[++ri] = ci;
4965             }
4966         }
4967         return r;
4968     };
4969
4970     function attrValue(n, attr){
4971         if(!n.tagName && typeof n.length != "undefined"){
4972             n = n[0];
4973         }
4974         if(!n){
4975             return null;
4976         }
4977         if(attr == "for"){
4978             return n.htmlFor;
4979         }
4980         if(attr == "class" || attr == "className"){
4981             return n.className;
4982         }
4983         return n.getAttribute(attr) || n[attr];
4984
4985     };
4986
4987     function getNodes(ns, mode, tagName){
4988         var result = [], ri = -1, cs;
4989         if(!ns){
4990             return result;
4991         }
4992         tagName = tagName || "*";
4993         if(typeof ns.getElementsByTagName != "undefined"){
4994             ns = [ns];
4995         }
4996         if(!mode){
4997             for(var i = 0, ni; ni = ns[i]; i++){
4998                 cs = ni.getElementsByTagName(tagName);
4999                 for(var j = 0, ci; ci = cs[j]; j++){
5000                     result[++ri] = ci;
5001                 }
5002             }
5003         }else if(mode == "/" || mode == ">"){
5004             var utag = tagName.toUpperCase();
5005             for(var i = 0, ni, cn; ni = ns[i]; i++){
5006                 cn = ni.children || ni.childNodes;
5007                 for(var j = 0, cj; cj = cn[j]; j++){
5008                     if(cj.nodeName == utag || cj.nodeName == tagName  || tagName == '*'){
5009                         result[++ri] = cj;
5010                     }
5011                 }
5012             }
5013         }else if(mode == "+"){
5014             var utag = tagName.toUpperCase();
5015             for(var i = 0, n; n = ns[i]; i++){
5016                 while((n = n.nextSibling) && n.nodeType != 1);
5017                 if(n && (n.nodeName == utag || n.nodeName == tagName || tagName == '*')){
5018                     result[++ri] = n;
5019                 }
5020             }
5021         }else if(mode == "~"){
5022             for(var i = 0, n; n = ns[i]; i++){
5023                 while((n = n.nextSibling) && (n.nodeType != 1 || (tagName == '*' || n.tagName.toLowerCase()!=tagName)));
5024                 if(n){
5025                     result[++ri] = n;
5026                 }
5027             }
5028         }
5029         return result;
5030     };
5031
5032     function concat(a, b){
5033         if(b.slice){
5034             return a.concat(b);
5035         }
5036         for(var i = 0, l = b.length; i < l; i++){
5037             a[a.length] = b[i];
5038         }
5039         return a;
5040     }
5041
5042     function byTag(cs, tagName){
5043         if(cs.tagName || cs == document){
5044             cs = [cs];
5045         }
5046         if(!tagName){
5047             return cs;
5048         }
5049         var r = [], ri = -1;
5050         tagName = tagName.toLowerCase();
5051         for(var i = 0, ci; ci = cs[i]; i++){
5052             if(ci.nodeType == 1 && ci.tagName.toLowerCase()==tagName){
5053                 r[++ri] = ci;
5054             }
5055         }
5056         return r;
5057     };
5058
5059     function byId(cs, attr, id){
5060         if(cs.tagName || cs == document){
5061             cs = [cs];
5062         }
5063         if(!id){
5064             return cs;
5065         }
5066         var r = [], ri = -1;
5067         for(var i = 0,ci; ci = cs[i]; i++){
5068             if(ci && ci.id == id){
5069                 r[++ri] = ci;
5070                 return r;
5071             }
5072         }
5073         return r;
5074     };
5075
5076     function byAttribute(cs, attr, value, op, custom){
5077         var r = [], ri = -1, st = custom=="{";
5078         var f = Roo.DomQuery.operators[op];
5079         for(var i = 0, ci; ci = cs[i]; i++){
5080             var a;
5081             if(st){
5082                 a = Roo.DomQuery.getStyle(ci, attr);
5083             }
5084             else if(attr == "class" || attr == "className"){
5085                 a = ci.className;
5086             }else if(attr == "for"){
5087                 a = ci.htmlFor;
5088             }else if(attr == "href"){
5089                 a = ci.getAttribute("href", 2);
5090             }else{
5091                 a = ci.getAttribute(attr);
5092             }
5093             if((f && f(a, value)) || (!f && a)){
5094                 r[++ri] = ci;
5095             }
5096         }
5097         return r;
5098     };
5099
5100     function byPseudo(cs, name, value){
5101         return Roo.DomQuery.pseudos[name](cs, value);
5102     };
5103
5104     // This is for IE MSXML which does not support expandos.
5105     // IE runs the same speed using setAttribute, however FF slows way down
5106     // and Safari completely fails so they need to continue to use expandos.
5107     var isIE = window.ActiveXObject ? true : false;
5108
5109     // this eval is stop the compressor from
5110     // renaming the variable to something shorter
5111     
5112     /** eval:var:batch */
5113     var batch = 30803; 
5114
5115     var key = 30803;
5116
5117     function nodupIEXml(cs){
5118         var d = ++key;
5119         cs[0].setAttribute("_nodup", d);
5120         var r = [cs[0]];
5121         for(var i = 1, len = cs.length; i < len; i++){
5122             var c = cs[i];
5123             if(!c.getAttribute("_nodup") != d){
5124                 c.setAttribute("_nodup", d);
5125                 r[r.length] = c;
5126             }
5127         }
5128         for(var i = 0, len = cs.length; i < len; i++){
5129             cs[i].removeAttribute("_nodup");
5130         }
5131         return r;
5132     }
5133
5134     function nodup(cs){
5135         if(!cs){
5136             return [];
5137         }
5138         var len = cs.length, c, i, r = cs, cj, ri = -1;
5139         if(!len || typeof cs.nodeType != "undefined" || len == 1){
5140             return cs;
5141         }
5142         if(isIE && typeof cs[0].selectSingleNode != "undefined"){
5143             return nodupIEXml(cs);
5144         }
5145         var d = ++key;
5146         cs[0]._nodup = d;
5147         for(i = 1; c = cs[i]; i++){
5148             if(c._nodup != d){
5149                 c._nodup = d;
5150             }else{
5151                 r = [];
5152                 for(var j = 0; j < i; j++){
5153                     r[++ri] = cs[j];
5154                 }
5155                 for(j = i+1; cj = cs[j]; j++){
5156                     if(cj._nodup != d){
5157                         cj._nodup = d;
5158                         r[++ri] = cj;
5159                     }
5160                 }
5161                 return r;
5162             }
5163         }
5164         return r;
5165     }
5166
5167     function quickDiffIEXml(c1, c2){
5168         var d = ++key;
5169         for(var i = 0, len = c1.length; i < len; i++){
5170             c1[i].setAttribute("_qdiff", d);
5171         }
5172         var r = [];
5173         for(var i = 0, len = c2.length; i < len; i++){
5174             if(c2[i].getAttribute("_qdiff") != d){
5175                 r[r.length] = c2[i];
5176             }
5177         }
5178         for(var i = 0, len = c1.length; i < len; i++){
5179            c1[i].removeAttribute("_qdiff");
5180         }
5181         return r;
5182     }
5183
5184     function quickDiff(c1, c2){
5185         var len1 = c1.length;
5186         if(!len1){
5187             return c2;
5188         }
5189         if(isIE && c1[0].selectSingleNode){
5190             return quickDiffIEXml(c1, c2);
5191         }
5192         var d = ++key;
5193         for(var i = 0; i < len1; i++){
5194             c1[i]._qdiff = d;
5195         }
5196         var r = [];
5197         for(var i = 0, len = c2.length; i < len; i++){
5198             if(c2[i]._qdiff != d){
5199                 r[r.length] = c2[i];
5200             }
5201         }
5202         return r;
5203     }
5204
5205     function quickId(ns, mode, root, id){
5206         if(ns == root){
5207            var d = root.ownerDocument || root;
5208            return d.getElementById(id);
5209         }
5210         ns = getNodes(ns, mode, "*");
5211         return byId(ns, null, id);
5212     }
5213
5214     return {
5215         getStyle : function(el, name){
5216             return Roo.fly(el).getStyle(name);
5217         },
5218         /**
5219          * Compiles a selector/xpath query into a reusable function. The returned function
5220          * takes one parameter "root" (optional), which is the context node from where the query should start.
5221          * @param {String} selector The selector/xpath query
5222          * @param {String} type (optional) Either "select" (the default) or "simple" for a simple selector match
5223          * @return {Function}
5224          */
5225         compile : function(path, type){
5226             type = type || "select";
5227             
5228             var fn = ["var f = function(root){\n var mode; ++batch; var n = root || document;\n"];
5229             var q = path, mode, lq;
5230             var tk = Roo.DomQuery.matchers;
5231             var tklen = tk.length;
5232             var mm;
5233
5234             // accept leading mode switch
5235             var lmode = q.match(modeRe);
5236             if(lmode && lmode[1]){
5237                 fn[fn.length] = 'mode="'+lmode[1].replace(trimRe, "")+'";';
5238                 q = q.replace(lmode[1], "");
5239             }
5240             // strip leading slashes
5241             while(path.substr(0, 1)=="/"){
5242                 path = path.substr(1);
5243             }
5244
5245             while(q && lq != q){
5246                 lq = q;
5247                 var tm = q.match(tagTokenRe);
5248                 if(type == "select"){
5249                     if(tm){
5250                         if(tm[1] == "#"){
5251                             fn[fn.length] = 'n = quickId(n, mode, root, "'+tm[2]+'");';
5252                         }else{
5253                             fn[fn.length] = 'n = getNodes(n, mode, "'+tm[2]+'");';
5254                         }
5255                         q = q.replace(tm[0], "");
5256                     }else if(q.substr(0, 1) != '@'){
5257                         fn[fn.length] = 'n = getNodes(n, mode, "*");';
5258                     }
5259                 }else{
5260                     if(tm){
5261                         if(tm[1] == "#"){
5262                             fn[fn.length] = 'n = byId(n, null, "'+tm[2]+'");';
5263                         }else{
5264                             fn[fn.length] = 'n = byTag(n, "'+tm[2]+'");';
5265                         }
5266                         q = q.replace(tm[0], "");
5267                     }
5268                 }
5269                 while(!(mm = q.match(modeRe))){
5270                     var matched = false;
5271                     for(var j = 0; j < tklen; j++){
5272                         var t = tk[j];
5273                         var m = q.match(t.re);
5274                         if(m){
5275                             fn[fn.length] = t.select.replace(tplRe, function(x, i){
5276                                                     return m[i];
5277                                                 });
5278                             q = q.replace(m[0], "");
5279                             matched = true;
5280                             break;
5281                         }
5282                     }
5283                     // prevent infinite loop on bad selector
5284                     if(!matched){
5285                         throw 'Error parsing selector, parsing failed at "' + q + '"';
5286                     }
5287                 }
5288                 if(mm[1]){
5289                     fn[fn.length] = 'mode="'+mm[1].replace(trimRe, "")+'";';
5290                     q = q.replace(mm[1], "");
5291                 }
5292             }
5293             fn[fn.length] = "return nodup(n);\n}";
5294             
5295              /** 
5296               * list of variables that need from compression as they are used by eval.
5297              *  eval:var:batch 
5298              *  eval:var:nodup
5299              *  eval:var:byTag
5300              *  eval:var:ById
5301              *  eval:var:getNodes
5302              *  eval:var:quickId
5303              *  eval:var:mode
5304              *  eval:var:root
5305              *  eval:var:n
5306              *  eval:var:byClassName
5307              *  eval:var:byPseudo
5308              *  eval:var:byAttribute
5309              *  eval:var:attrValue
5310              * 
5311              **/ 
5312             eval(fn.join(""));
5313             return f;
5314         },
5315
5316         /**
5317          * Selects a group of elements.
5318          * @param {String} selector The selector/xpath query (can be a comma separated list of selectors)
5319          * @param {Node} root (optional) The start of the query (defaults to document).
5320          * @return {Array}
5321          */
5322         select : function(path, root, type){
5323             if(!root || root == document){
5324                 root = document;
5325             }
5326             if(typeof root == "string"){
5327                 root = document.getElementById(root);
5328             }
5329             var paths = path.split(",");
5330             var results = [];
5331             for(var i = 0, len = paths.length; i < len; i++){
5332                 var p = paths[i].replace(trimRe, "");
5333                 if(!cache[p]){
5334                     cache[p] = Roo.DomQuery.compile(p);
5335                     if(!cache[p]){
5336                         throw p + " is not a valid selector";
5337                     }
5338                 }
5339                 var result = cache[p](root);
5340                 if(result && result != document){
5341                     results = results.concat(result);
5342                 }
5343             }
5344             if(paths.length > 1){
5345                 return nodup(results);
5346             }
5347             return results;
5348         },
5349
5350         /**
5351          * Selects a single element.
5352          * @param {String} selector The selector/xpath query
5353          * @param {Node} root (optional) The start of the query (defaults to document).
5354          * @return {Element}
5355          */
5356         selectNode : function(path, root){
5357             return Roo.DomQuery.select(path, root)[0];
5358         },
5359
5360         /**
5361          * Selects the value of a node, optionally replacing null with the defaultValue.
5362          * @param {String} selector The selector/xpath query
5363          * @param {Node} root (optional) The start of the query (defaults to document).
5364          * @param {String} defaultValue
5365          */
5366         selectValue : function(path, root, defaultValue){
5367             path = path.replace(trimRe, "");
5368             if(!valueCache[path]){
5369                 valueCache[path] = Roo.DomQuery.compile(path, "select");
5370             }
5371             var n = valueCache[path](root);
5372             n = n[0] ? n[0] : n;
5373             var v = (n && n.firstChild ? n.firstChild.nodeValue : null);
5374             return ((v === null||v === undefined||v==='') ? defaultValue : v);
5375         },
5376
5377         /**
5378          * Selects the value of a node, parsing integers and floats.
5379          * @param {String} selector The selector/xpath query
5380          * @param {Node} root (optional) The start of the query (defaults to document).
5381          * @param {Number} defaultValue
5382          * @return {Number}
5383          */
5384         selectNumber : function(path, root, defaultValue){
5385             var v = Roo.DomQuery.selectValue(path, root, defaultValue || 0);
5386             return parseFloat(v);
5387         },
5388
5389         /**
5390          * Returns true if the passed element(s) match the passed simple selector (e.g. div.some-class or span:first-child)
5391          * @param {String/HTMLElement/Array} el An element id, element or array of elements
5392          * @param {String} selector The simple selector to test
5393          * @return {Boolean}
5394          */
5395         is : function(el, ss){
5396             if(typeof el == "string"){
5397                 el = document.getElementById(el);
5398             }
5399             var isArray = (el instanceof Array);
5400             var result = Roo.DomQuery.filter(isArray ? el : [el], ss);
5401             return isArray ? (result.length == el.length) : (result.length > 0);
5402         },
5403
5404         /**
5405          * Filters an array of elements to only include matches of a simple selector (e.g. div.some-class or span:first-child)
5406          * @param {Array} el An array of elements to filter
5407          * @param {String} selector The simple selector to test
5408          * @param {Boolean} nonMatches If true, it returns the elements that DON'T match
5409          * the selector instead of the ones that match
5410          * @return {Array}
5411          */
5412         filter : function(els, ss, nonMatches){
5413             ss = ss.replace(trimRe, "");
5414             if(!simpleCache[ss]){
5415                 simpleCache[ss] = Roo.DomQuery.compile(ss, "simple");
5416             }
5417             var result = simpleCache[ss](els);
5418             return nonMatches ? quickDiff(result, els) : result;
5419         },
5420
5421         /**
5422          * Collection of matching regular expressions and code snippets.
5423          */
5424         matchers : [{
5425                 re: /^\.([\w-]+)/,
5426                 select: 'n = byClassName(n, null, " {1} ");'
5427             }, {
5428                 re: /^\:([\w-]+)(?:\(((?:[^\s>\/]*|.*?))\))?/,
5429                 select: 'n = byPseudo(n, "{1}", "{2}");'
5430             },{
5431                 re: /^(?:([\[\{])(?:@)?([\w-]+)\s?(?:(=|.=)\s?['"]?(.*?)["']?)?[\]\}])/,
5432                 select: 'n = byAttribute(n, "{2}", "{4}", "{3}", "{1}");'
5433             }, {
5434                 re: /^#([\w-]+)/,
5435                 select: 'n = byId(n, null, "{1}");'
5436             },{
5437                 re: /^@([\w-]+)/,
5438                 select: 'return {firstChild:{nodeValue:attrValue(n, "{1}")}};'
5439             }
5440         ],
5441
5442         /**
5443          * Collection of operator comparison functions. The default operators are =, !=, ^=, $=, *=, %=, |= and ~=.
5444          * 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;.
5445          */
5446         operators : {
5447             "=" : function(a, v){
5448                 return a == v;
5449             },
5450             "!=" : function(a, v){
5451                 return a != v;
5452             },
5453             "^=" : function(a, v){
5454                 return a && a.substr(0, v.length) == v;
5455             },
5456             "$=" : function(a, v){
5457                 return a && a.substr(a.length-v.length) == v;
5458             },
5459             "*=" : function(a, v){
5460                 return a && a.indexOf(v) !== -1;
5461             },
5462             "%=" : function(a, v){
5463                 return (a % v) == 0;
5464             },
5465             "|=" : function(a, v){
5466                 return a && (a == v || a.substr(0, v.length+1) == v+'-');
5467             },
5468             "~=" : function(a, v){
5469                 return a && (' '+a+' ').indexOf(' '+v+' ') != -1;
5470             }
5471         },
5472
5473         /**
5474          * Collection of "pseudo class" processors. Each processor is passed the current nodeset (array)
5475          * and the argument (if any) supplied in the selector.
5476          */
5477         pseudos : {
5478             "first-child" : function(c){
5479                 var r = [], ri = -1, n;
5480                 for(var i = 0, ci; ci = n = c[i]; i++){
5481                     while((n = n.previousSibling) && n.nodeType != 1);
5482                     if(!n){
5483                         r[++ri] = ci;
5484                     }
5485                 }
5486                 return r;
5487             },
5488
5489             "last-child" : function(c){
5490                 var r = [], ri = -1, n;
5491                 for(var i = 0, ci; ci = n = c[i]; i++){
5492                     while((n = n.nextSibling) && n.nodeType != 1);
5493                     if(!n){
5494                         r[++ri] = ci;
5495                     }
5496                 }
5497                 return r;
5498             },
5499
5500             "nth-child" : function(c, a) {
5501                 var r = [], ri = -1;
5502                 var m = nthRe.exec(a == "even" && "2n" || a == "odd" && "2n+1" || !nthRe2.test(a) && "n+" + a || a);
5503                 var f = (m[1] || 1) - 0, l = m[2] - 0;
5504                 for(var i = 0, n; n = c[i]; i++){
5505                     var pn = n.parentNode;
5506                     if (batch != pn._batch) {
5507                         var j = 0;
5508                         for(var cn = pn.firstChild; cn; cn = cn.nextSibling){
5509                             if(cn.nodeType == 1){
5510                                cn.nodeIndex = ++j;
5511                             }
5512                         }
5513                         pn._batch = batch;
5514                     }
5515                     if (f == 1) {
5516                         if (l == 0 || n.nodeIndex == l){
5517                             r[++ri] = n;
5518                         }
5519                     } else if ((n.nodeIndex + l) % f == 0){
5520                         r[++ri] = n;
5521                     }
5522                 }
5523
5524                 return r;
5525             },
5526
5527             "only-child" : function(c){
5528                 var r = [], ri = -1;;
5529                 for(var i = 0, ci; ci = c[i]; i++){
5530                     if(!prev(ci) && !next(ci)){
5531                         r[++ri] = ci;
5532                     }
5533                 }
5534                 return r;
5535             },
5536
5537             "empty" : function(c){
5538                 var r = [], ri = -1;
5539                 for(var i = 0, ci; ci = c[i]; i++){
5540                     var cns = ci.childNodes, j = 0, cn, empty = true;
5541                     while(cn = cns[j]){
5542                         ++j;
5543                         if(cn.nodeType == 1 || cn.nodeType == 3){
5544                             empty = false;
5545                             break;
5546                         }
5547                     }
5548                     if(empty){
5549                         r[++ri] = ci;
5550                     }
5551                 }
5552                 return r;
5553             },
5554
5555             "contains" : function(c, v){
5556                 var r = [], ri = -1;
5557                 for(var i = 0, ci; ci = c[i]; i++){
5558                     if((ci.textContent||ci.innerText||'').indexOf(v) != -1){
5559                         r[++ri] = ci;
5560                     }
5561                 }
5562                 return r;
5563             },
5564
5565             "nodeValue" : function(c, v){
5566                 var r = [], ri = -1;
5567                 for(var i = 0, ci; ci = c[i]; i++){
5568                     if(ci.firstChild && ci.firstChild.nodeValue == v){
5569                         r[++ri] = ci;
5570                     }
5571                 }
5572                 return r;
5573             },
5574
5575             "checked" : function(c){
5576                 var r = [], ri = -1;
5577                 for(var i = 0, ci; ci = c[i]; i++){
5578                     if(ci.checked == true){
5579                         r[++ri] = ci;
5580                     }
5581                 }
5582                 return r;
5583             },
5584
5585             "not" : function(c, ss){
5586                 return Roo.DomQuery.filter(c, ss, true);
5587             },
5588
5589             "odd" : function(c){
5590                 return this["nth-child"](c, "odd");
5591             },
5592
5593             "even" : function(c){
5594                 return this["nth-child"](c, "even");
5595             },
5596
5597             "nth" : function(c, a){
5598                 return c[a-1] || [];
5599             },
5600
5601             "first" : function(c){
5602                 return c[0] || [];
5603             },
5604
5605             "last" : function(c){
5606                 return c[c.length-1] || [];
5607             },
5608
5609             "has" : function(c, ss){
5610                 var s = Roo.DomQuery.select;
5611                 var r = [], ri = -1;
5612                 for(var i = 0, ci; ci = c[i]; i++){
5613                     if(s(ss, ci).length > 0){
5614                         r[++ri] = ci;
5615                     }
5616                 }
5617                 return r;
5618             },
5619
5620             "next" : function(c, ss){
5621                 var is = Roo.DomQuery.is;
5622                 var r = [], ri = -1;
5623                 for(var i = 0, ci; ci = c[i]; i++){
5624                     var n = next(ci);
5625                     if(n && is(n, ss)){
5626                         r[++ri] = ci;
5627                     }
5628                 }
5629                 return r;
5630             },
5631
5632             "prev" : function(c, ss){
5633                 var is = Roo.DomQuery.is;
5634                 var r = [], ri = -1;
5635                 for(var i = 0, ci; ci = c[i]; i++){
5636                     var n = prev(ci);
5637                     if(n && is(n, ss)){
5638                         r[++ri] = ci;
5639                     }
5640                 }
5641                 return r;
5642             }
5643         }
5644     };
5645 }();
5646
5647 /**
5648  * Selects an array of DOM nodes by CSS/XPath selector. Shorthand of {@link Roo.DomQuery#select}
5649  * @param {String} path The selector/xpath query
5650  * @param {Node} root (optional) The start of the query (defaults to document).
5651  * @return {Array}
5652  * @member Roo
5653  * @method query
5654  */
5655 Roo.query = Roo.DomQuery.select;
5656 /*
5657  * Based on:
5658  * Ext JS Library 1.1.1
5659  * Copyright(c) 2006-2007, Ext JS, LLC.
5660  *
5661  * Originally Released Under LGPL - original licence link has changed is not relivant.
5662  *
5663  * Fork - LGPL
5664  * <script type="text/javascript">
5665  */
5666
5667 /**
5668  * @class Roo.util.Observable
5669  * Base class that provides a common interface for publishing events. Subclasses are expected to
5670  * to have a property "events" with all the events defined.<br>
5671  * For example:
5672  * <pre><code>
5673  Employee = function(name){
5674     this.name = name;
5675     this.addEvents({
5676         "fired" : true,
5677         "quit" : true
5678     });
5679  }
5680  Roo.extend(Employee, Roo.util.Observable);
5681 </code></pre>
5682  * @param {Object} config properties to use (incuding events / listeners)
5683  */
5684
5685 Roo.util.Observable = function(cfg){
5686     
5687     cfg = cfg|| {};
5688     this.addEvents(cfg.events || {});
5689     if (cfg.events) {
5690         delete cfg.events; // make sure
5691     }
5692      
5693     Roo.apply(this, cfg);
5694     
5695     if(this.listeners){
5696         this.on(this.listeners);
5697         delete this.listeners;
5698     }
5699 };
5700 Roo.util.Observable.prototype = {
5701     /** 
5702  * @cfg {Object} listeners  list of events and functions to call for this object, 
5703  * For example :
5704  * <pre><code>
5705     listeners :  { 
5706        'click' : function(e) {
5707            ..... 
5708         } ,
5709         .... 
5710     } 
5711   </code></pre>
5712  */
5713     
5714     
5715     /**
5716      * Fires the specified event with the passed parameters (minus the event name).
5717      * @param {String} eventName
5718      * @param {Object...} args Variable number of parameters are passed to handlers
5719      * @return {Boolean} returns false if any of the handlers return false otherwise it returns true
5720      */
5721     fireEvent : function(){
5722         var ce = this.events[arguments[0].toLowerCase()];
5723         if(typeof ce == "object"){
5724             return ce.fire.apply(ce, Array.prototype.slice.call(arguments, 1));
5725         }else{
5726             return true;
5727         }
5728     },
5729
5730     // private
5731     filterOptRe : /^(?:scope|delay|buffer|single)$/,
5732
5733     /**
5734      * Appends an event handler to this component
5735      * @param {String}   eventName The type of event to listen for
5736      * @param {Function} handler The method the event invokes
5737      * @param {Object}   scope (optional) The scope in which to execute the handler
5738      * function. The handler function's "this" context.
5739      * @param {Object}   options (optional) An object containing handler configuration
5740      * properties. This may contain any of the following properties:<ul>
5741      * <li>scope {Object} The scope in which to execute the handler function. The handler function's "this" context.</li>
5742      * <li>delay {Number} The number of milliseconds to delay the invocation of the handler after te event fires.</li>
5743      * <li>single {Boolean} True to add a handler to handle just the next firing of the event, and then remove itself.</li>
5744      * <li>buffer {Number} Causes the handler to be scheduled to run in an {@link Roo.util.DelayedTask} delayed
5745      * by the specified number of milliseconds. If the event fires again within that time, the original
5746      * handler is <em>not</em> invoked, but the new handler is scheduled in its place.</li>
5747      * </ul><br>
5748      * <p>
5749      * <b>Combining Options</b><br>
5750      * Using the options argument, it is possible to combine different types of listeners:<br>
5751      * <br>
5752      * A normalized, delayed, one-time listener that auto stops the event and passes a custom argument (forumId)
5753                 <pre><code>
5754                 el.on('click', this.onClick, this, {
5755                         single: true,
5756                 delay: 100,
5757                 forumId: 4
5758                 });
5759                 </code></pre>
5760      * <p>
5761      * <b>Attaching multiple handlers in 1 call</b><br>
5762      * The method also allows for a single argument to be passed which is a config object containing properties
5763      * which specify multiple handlers.
5764      * <pre><code>
5765                 el.on({
5766                         'click': {
5767                         fn: this.onClick,
5768                         scope: this,
5769                         delay: 100
5770                 }, 
5771                 'mouseover': {
5772                         fn: this.onMouseOver,
5773                         scope: this
5774                 },
5775                 'mouseout': {
5776                         fn: this.onMouseOut,
5777                         scope: this
5778                 }
5779                 });
5780                 </code></pre>
5781      * <p>
5782      * Or a shorthand syntax which passes the same scope object to all handlers:
5783         <pre><code>
5784                 el.on({
5785                         'click': this.onClick,
5786                 'mouseover': this.onMouseOver,
5787                 'mouseout': this.onMouseOut,
5788                 scope: this
5789                 });
5790                 </code></pre>
5791      */
5792     addListener : function(eventName, fn, scope, o){
5793         if(typeof eventName == "object"){
5794             o = eventName;
5795             for(var e in o){
5796                 if(this.filterOptRe.test(e)){
5797                     continue;
5798                 }
5799                 if(typeof o[e] == "function"){
5800                     // shared options
5801                     this.addListener(e, o[e], o.scope,  o);
5802                 }else{
5803                     // individual options
5804                     this.addListener(e, o[e].fn, o[e].scope, o[e]);
5805                 }
5806             }
5807             return;
5808         }
5809         o = (!o || typeof o == "boolean") ? {} : o;
5810         eventName = eventName.toLowerCase();
5811         var ce = this.events[eventName] || true;
5812         if(typeof ce == "boolean"){
5813             ce = new Roo.util.Event(this, eventName);
5814             this.events[eventName] = ce;
5815         }
5816         ce.addListener(fn, scope, o);
5817     },
5818
5819     /**
5820      * Removes a listener
5821      * @param {String}   eventName     The type of event to listen for
5822      * @param {Function} handler        The handler to remove
5823      * @param {Object}   scope  (optional) The scope (this object) for the handler
5824      */
5825     removeListener : function(eventName, fn, scope){
5826         var ce = this.events[eventName.toLowerCase()];
5827         if(typeof ce == "object"){
5828             ce.removeListener(fn, scope);
5829         }
5830     },
5831
5832     /**
5833      * Removes all listeners for this object
5834      */
5835     purgeListeners : function(){
5836         for(var evt in this.events){
5837             if(typeof this.events[evt] == "object"){
5838                  this.events[evt].clearListeners();
5839             }
5840         }
5841     },
5842
5843     relayEvents : function(o, events){
5844         var createHandler = function(ename){
5845             return function(){
5846                 return this.fireEvent.apply(this, Roo.combine(ename, Array.prototype.slice.call(arguments, 0)));
5847             };
5848         };
5849         for(var i = 0, len = events.length; i < len; i++){
5850             var ename = events[i];
5851             if(!this.events[ename]){ this.events[ename] = true; };
5852             o.on(ename, createHandler(ename), this);
5853         }
5854     },
5855
5856     /**
5857      * Used to define events on this Observable
5858      * @param {Object} object The object with the events defined
5859      */
5860     addEvents : function(o){
5861         if(!this.events){
5862             this.events = {};
5863         }
5864         Roo.applyIf(this.events, o);
5865     },
5866
5867     /**
5868      * Checks to see if this object has any listeners for a specified event
5869      * @param {String} eventName The name of the event to check for
5870      * @return {Boolean} True if the event is being listened for, else false
5871      */
5872     hasListener : function(eventName){
5873         var e = this.events[eventName];
5874         return typeof e == "object" && e.listeners.length > 0;
5875     }
5876 };
5877 /**
5878  * Appends an event handler to this element (shorthand for addListener)
5879  * @param {String}   eventName     The type of event to listen for
5880  * @param {Function} handler        The method the event invokes
5881  * @param {Object}   scope (optional) The scope in which to execute the handler
5882  * function. The handler function's "this" context.
5883  * @param {Object}   options  (optional)
5884  * @method
5885  */
5886 Roo.util.Observable.prototype.on = Roo.util.Observable.prototype.addListener;
5887 /**
5888  * Removes a listener (shorthand for removeListener)
5889  * @param {String}   eventName     The type of event to listen for
5890  * @param {Function} handler        The handler to remove
5891  * @param {Object}   scope  (optional) The scope (this object) for the handler
5892  * @method
5893  */
5894 Roo.util.Observable.prototype.un = Roo.util.Observable.prototype.removeListener;
5895
5896 /**
5897  * Starts capture on the specified Observable. All events will be passed
5898  * to the supplied function with the event name + standard signature of the event
5899  * <b>before</b> the event is fired. If the supplied function returns false,
5900  * the event will not fire.
5901  * @param {Observable} o The Observable to capture
5902  * @param {Function} fn The function to call
5903  * @param {Object} scope (optional) The scope (this object) for the fn
5904  * @static
5905  */
5906 Roo.util.Observable.capture = function(o, fn, scope){
5907     o.fireEvent = o.fireEvent.createInterceptor(fn, scope);
5908 };
5909
5910 /**
5911  * Removes <b>all</b> added captures from the Observable.
5912  * @param {Observable} o The Observable to release
5913  * @static
5914  */
5915 Roo.util.Observable.releaseCapture = function(o){
5916     o.fireEvent = Roo.util.Observable.prototype.fireEvent;
5917 };
5918
5919 (function(){
5920
5921     var createBuffered = function(h, o, scope){
5922         var task = new Roo.util.DelayedTask();
5923         return function(){
5924             task.delay(o.buffer, h, scope, Array.prototype.slice.call(arguments, 0));
5925         };
5926     };
5927
5928     var createSingle = function(h, e, fn, scope){
5929         return function(){
5930             e.removeListener(fn, scope);
5931             return h.apply(scope, arguments);
5932         };
5933     };
5934
5935     var createDelayed = function(h, o, scope){
5936         return function(){
5937             var args = Array.prototype.slice.call(arguments, 0);
5938             setTimeout(function(){
5939                 h.apply(scope, args);
5940             }, o.delay || 10);
5941         };
5942     };
5943
5944     Roo.util.Event = function(obj, name){
5945         this.name = name;
5946         this.obj = obj;
5947         this.listeners = [];
5948     };
5949
5950     Roo.util.Event.prototype = {
5951         addListener : function(fn, scope, options){
5952             var o = options || {};
5953             scope = scope || this.obj;
5954             if(!this.isListening(fn, scope)){
5955                 var l = {fn: fn, scope: scope, options: o};
5956                 var h = fn;
5957                 if(o.delay){
5958                     h = createDelayed(h, o, scope);
5959                 }
5960                 if(o.single){
5961                     h = createSingle(h, this, fn, scope);
5962                 }
5963                 if(o.buffer){
5964                     h = createBuffered(h, o, scope);
5965                 }
5966                 l.fireFn = h;
5967                 if(!this.firing){ // if we are currently firing this event, don't disturb the listener loop
5968                     this.listeners.push(l);
5969                 }else{
5970                     this.listeners = this.listeners.slice(0);
5971                     this.listeners.push(l);
5972                 }
5973             }
5974         },
5975
5976         findListener : function(fn, scope){
5977             scope = scope || this.obj;
5978             var ls = this.listeners;
5979             for(var i = 0, len = ls.length; i < len; i++){
5980                 var l = ls[i];
5981                 if(l.fn == fn && l.scope == scope){
5982                     return i;
5983                 }
5984             }
5985             return -1;
5986         },
5987
5988         isListening : function(fn, scope){
5989             return this.findListener(fn, scope) != -1;
5990         },
5991
5992         removeListener : function(fn, scope){
5993             var index;
5994             if((index = this.findListener(fn, scope)) != -1){
5995                 if(!this.firing){
5996                     this.listeners.splice(index, 1);
5997                 }else{
5998                     this.listeners = this.listeners.slice(0);
5999                     this.listeners.splice(index, 1);
6000                 }
6001                 return true;
6002             }
6003             return false;
6004         },
6005
6006         clearListeners : function(){
6007             this.listeners = [];
6008         },
6009
6010         fire : function(){
6011             var ls = this.listeners, scope, len = ls.length;
6012             if(len > 0){
6013                 this.firing = true;
6014                 var args = Array.prototype.slice.call(arguments, 0);
6015                 for(var i = 0; i < len; i++){
6016                     var l = ls[i];
6017                     if(l.fireFn.apply(l.scope||this.obj||window, arguments) === false){
6018                         this.firing = false;
6019                         return false;
6020                     }
6021                 }
6022                 this.firing = false;
6023             }
6024             return true;
6025         }
6026     };
6027 })();/*
6028  * Based on:
6029  * Ext JS Library 1.1.1
6030  * Copyright(c) 2006-2007, Ext JS, LLC.
6031  *
6032  * Originally Released Under LGPL - original licence link has changed is not relivant.
6033  *
6034  * Fork - LGPL
6035  * <script type="text/javascript">
6036  */
6037
6038 /**
6039  * @class Roo.EventManager
6040  * Registers event handlers that want to receive a normalized EventObject instead of the standard browser event and provides 
6041  * several useful events directly.
6042  * See {@link Roo.EventObject} for more details on normalized event objects.
6043  * @singleton
6044  */
6045 Roo.EventManager = function(){
6046     var docReadyEvent, docReadyProcId, docReadyState = false;
6047     var resizeEvent, resizeTask, textEvent, textSize;
6048     var E = Roo.lib.Event;
6049     var D = Roo.lib.Dom;
6050
6051
6052     var fireDocReady = function(){
6053         if(!docReadyState){
6054             docReadyState = true;
6055             Roo.isReady = true;
6056             if(docReadyProcId){
6057                 clearInterval(docReadyProcId);
6058             }
6059             if(Roo.isGecko || Roo.isOpera) {
6060                 document.removeEventListener("DOMContentLoaded", fireDocReady, false);
6061             }
6062             if(Roo.isIE){
6063                 var defer = document.getElementById("ie-deferred-loader");
6064                 if(defer){
6065                     defer.onreadystatechange = null;
6066                     defer.parentNode.removeChild(defer);
6067                 }
6068             }
6069             if(docReadyEvent){
6070                 docReadyEvent.fire();
6071                 docReadyEvent.clearListeners();
6072             }
6073         }
6074     };
6075     
6076     var initDocReady = function(){
6077         docReadyEvent = new Roo.util.Event();
6078         if(Roo.isGecko || Roo.isOpera) {
6079             document.addEventListener("DOMContentLoaded", fireDocReady, false);
6080         }else if(Roo.isIE){
6081             document.write("<s"+'cript id="ie-deferred-loader" defer="defer" src="/'+'/:"></s'+"cript>");
6082             var defer = document.getElementById("ie-deferred-loader");
6083             defer.onreadystatechange = function(){
6084                 if(this.readyState == "complete"){
6085                     fireDocReady();
6086                 }
6087             };
6088         }else if(Roo.isSafari){ 
6089             docReadyProcId = setInterval(function(){
6090                 var rs = document.readyState;
6091                 if(rs == "complete") {
6092                     fireDocReady();     
6093                  }
6094             }, 10);
6095         }
6096         // no matter what, make sure it fires on load
6097         E.on(window, "load", fireDocReady);
6098     };
6099
6100     var createBuffered = function(h, o){
6101         var task = new Roo.util.DelayedTask(h);
6102         return function(e){
6103             // create new event object impl so new events don't wipe out properties
6104             e = new Roo.EventObjectImpl(e);
6105             task.delay(o.buffer, h, null, [e]);
6106         };
6107     };
6108
6109     var createSingle = function(h, el, ename, fn){
6110         return function(e){
6111             Roo.EventManager.removeListener(el, ename, fn);
6112             h(e);
6113         };
6114     };
6115
6116     var createDelayed = function(h, o){
6117         return function(e){
6118             // create new event object impl so new events don't wipe out properties
6119             e = new Roo.EventObjectImpl(e);
6120             setTimeout(function(){
6121                 h(e);
6122             }, o.delay || 10);
6123         };
6124     };
6125
6126     var listen = function(element, ename, opt, fn, scope){
6127         var o = (!opt || typeof opt == "boolean") ? {} : opt;
6128         fn = fn || o.fn; scope = scope || o.scope;
6129         var el = Roo.getDom(element);
6130         if(!el){
6131             throw "Error listening for \"" + ename + '\". Element "' + element + '" doesn\'t exist.';
6132         }
6133         var h = function(e){
6134             e = Roo.EventObject.setEvent(e);
6135             var t;
6136             if(o.delegate){
6137                 t = e.getTarget(o.delegate, el);
6138                 if(!t){
6139                     return;
6140                 }
6141             }else{
6142                 t = e.target;
6143             }
6144             if(o.stopEvent === true){
6145                 e.stopEvent();
6146             }
6147             if(o.preventDefault === true){
6148                e.preventDefault();
6149             }
6150             if(o.stopPropagation === true){
6151                 e.stopPropagation();
6152             }
6153
6154             if(o.normalized === false){
6155                 e = e.browserEvent;
6156             }
6157
6158             fn.call(scope || el, e, t, o);
6159         };
6160         if(o.delay){
6161             h = createDelayed(h, o);
6162         }
6163         if(o.single){
6164             h = createSingle(h, el, ename, fn);
6165         }
6166         if(o.buffer){
6167             h = createBuffered(h, o);
6168         }
6169         fn._handlers = fn._handlers || [];
6170         fn._handlers.push([Roo.id(el), ename, h]);
6171
6172         E.on(el, ename, h);
6173         if(ename == "mousewheel" && el.addEventListener){ // workaround for jQuery
6174             el.addEventListener("DOMMouseScroll", h, false);
6175             E.on(window, 'unload', function(){
6176                 el.removeEventListener("DOMMouseScroll", h, false);
6177             });
6178         }
6179         if(ename == "mousedown" && el == document){ // fix stopped mousedowns on the document
6180             Roo.EventManager.stoppedMouseDownEvent.addListener(h);
6181         }
6182         return h;
6183     };
6184
6185     var stopListening = function(el, ename, fn){
6186         var id = Roo.id(el), hds = fn._handlers, hd = fn;
6187         if(hds){
6188             for(var i = 0, len = hds.length; i < len; i++){
6189                 var h = hds[i];
6190                 if(h[0] == id && h[1] == ename){
6191                     hd = h[2];
6192                     hds.splice(i, 1);
6193                     break;
6194                 }
6195             }
6196         }
6197         E.un(el, ename, hd);
6198         el = Roo.getDom(el);
6199         if(ename == "mousewheel" && el.addEventListener){
6200             el.removeEventListener("DOMMouseScroll", hd, false);
6201         }
6202         if(ename == "mousedown" && el == document){ // fix stopped mousedowns on the document
6203             Roo.EventManager.stoppedMouseDownEvent.removeListener(hd);
6204         }
6205     };
6206
6207     var propRe = /^(?:scope|delay|buffer|single|stopEvent|preventDefault|stopPropagation|normalized|args|delegate)$/;
6208     
6209     var pub = {
6210         
6211         
6212         /** 
6213          * Fix for doc tools
6214          * @scope Roo.EventManager
6215          */
6216         
6217         
6218         /** 
6219          * This is no longer needed and is deprecated. Places a simple wrapper around an event handler to override the browser event
6220          * object with a Roo.EventObject
6221          * @param {Function} fn        The method the event invokes
6222          * @param {Object}   scope    An object that becomes the scope of the handler
6223          * @param {boolean}  override If true, the obj passed in becomes
6224          *                             the execution scope of the listener
6225          * @return {Function} The wrapped function
6226          * @deprecated
6227          */
6228         wrap : function(fn, scope, override){
6229             return function(e){
6230                 Roo.EventObject.setEvent(e);
6231                 fn.call(override ? scope || window : window, Roo.EventObject, scope);
6232             };
6233         },
6234         
6235         /**
6236      * Appends an event handler to an element (shorthand for addListener)
6237      * @param {String/HTMLElement}   element        The html element or id to assign the
6238      * @param {String}   eventName The type of event to listen for
6239      * @param {Function} handler The method the event invokes
6240      * @param {Object}   scope (optional) The scope in which to execute the handler
6241      * function. The handler function's "this" context.
6242      * @param {Object}   options (optional) An object containing handler configuration
6243      * properties. This may contain any of the following properties:<ul>
6244      * <li>scope {Object} The scope in which to execute the handler function. The handler function's "this" context.</li>
6245      * <li>delegate {String} A simple selector to filter the target or look for a descendant of the target</li>
6246      * <li>stopEvent {Boolean} True to stop the event. That is stop propagation, and prevent the default action.</li>
6247      * <li>preventDefault {Boolean} True to prevent the default action</li>
6248      * <li>stopPropagation {Boolean} True to prevent event propagation</li>
6249      * <li>normalized {Boolean} False to pass a browser event to the handler function instead of an Roo.EventObject</li>
6250      * <li>delay {Number} The number of milliseconds to delay the invocation of the handler after te event fires.</li>
6251      * <li>single {Boolean} True to add a handler to handle just the next firing of the event, and then remove itself.</li>
6252      * <li>buffer {Number} Causes the handler to be scheduled to run in an {@link Roo.util.DelayedTask} delayed
6253      * by the specified number of milliseconds. If the event fires again within that time, the original
6254      * handler is <em>not</em> invoked, but the new handler is scheduled in its place.</li>
6255      * </ul><br>
6256      * <p>
6257      * <b>Combining Options</b><br>
6258      * Using the options argument, it is possible to combine different types of listeners:<br>
6259      * <br>
6260      * A normalized, delayed, one-time listener that auto stops the event and passes a custom argument (forumId)<div style="margin: 5px 20px 20px;">
6261      * Code:<pre><code>
6262 el.on('click', this.onClick, this, {
6263     single: true,
6264     delay: 100,
6265     stopEvent : true,
6266     forumId: 4
6267 });</code></pre>
6268      * <p>
6269      * <b>Attaching multiple handlers in 1 call</b><br>
6270       * The method also allows for a single argument to be passed which is a config object containing properties
6271      * which specify multiple handlers.
6272      * <p>
6273      * Code:<pre><code>
6274 el.on({
6275     'click' : {
6276         fn: this.onClick
6277         scope: this,
6278         delay: 100
6279     },
6280     'mouseover' : {
6281         fn: this.onMouseOver
6282         scope: this
6283     },
6284     'mouseout' : {
6285         fn: this.onMouseOut
6286         scope: this
6287     }
6288 });</code></pre>
6289      * <p>
6290      * Or a shorthand syntax:<br>
6291      * Code:<pre><code>
6292 el.on({
6293     'click' : this.onClick,
6294     'mouseover' : this.onMouseOver,
6295     'mouseout' : this.onMouseOut
6296     scope: this
6297 });</code></pre>
6298      */
6299         addListener : function(element, eventName, fn, scope, options){
6300             if(typeof eventName == "object"){
6301                 var o = eventName;
6302                 for(var e in o){
6303                     if(propRe.test(e)){
6304                         continue;
6305                     }
6306                     if(typeof o[e] == "function"){
6307                         // shared options
6308                         listen(element, e, o, o[e], o.scope);
6309                     }else{
6310                         // individual options
6311                         listen(element, e, o[e]);
6312                     }
6313                 }
6314                 return;
6315             }
6316             return listen(element, eventName, options, fn, scope);
6317         },
6318         
6319         /**
6320          * Removes an event handler
6321          *
6322          * @param {String/HTMLElement}   element        The id or html element to remove the 
6323          *                             event from
6324          * @param {String}   eventName     The type of event
6325          * @param {Function} fn
6326          * @return {Boolean} True if a listener was actually removed
6327          */
6328         removeListener : function(element, eventName, fn){
6329             return stopListening(element, eventName, fn);
6330         },
6331         
6332         /**
6333          * Fires when the document is ready (before onload and before images are loaded). Can be 
6334          * accessed shorthanded Roo.onReady().
6335          * @param {Function} fn        The method the event invokes
6336          * @param {Object}   scope    An  object that becomes the scope of the handler
6337          * @param {boolean}  options
6338          */
6339         onDocumentReady : function(fn, scope, options){
6340             if(docReadyState){ // if it already fired
6341                 docReadyEvent.addListener(fn, scope, options);
6342                 docReadyEvent.fire();
6343                 docReadyEvent.clearListeners();
6344                 return;
6345             }
6346             if(!docReadyEvent){
6347                 initDocReady();
6348             }
6349             docReadyEvent.addListener(fn, scope, options);
6350         },
6351         
6352         /**
6353          * Fires when the window is resized and provides resize event buffering (50 milliseconds), passes new viewport width and height to handlers.
6354          * @param {Function} fn        The method the event invokes
6355          * @param {Object}   scope    An object that becomes the scope of the handler
6356          * @param {boolean}  options
6357          */
6358         onWindowResize : function(fn, scope, options){
6359             if(!resizeEvent){
6360                 resizeEvent = new Roo.util.Event();
6361                 resizeTask = new Roo.util.DelayedTask(function(){
6362                     resizeEvent.fire(D.getViewWidth(), D.getViewHeight());
6363                 });
6364                 E.on(window, "resize", function(){
6365                     if(Roo.isIE){
6366                         resizeTask.delay(50);
6367                     }else{
6368                         resizeEvent.fire(D.getViewWidth(), D.getViewHeight());
6369                     }
6370                 });
6371             }
6372             resizeEvent.addListener(fn, scope, options);
6373         },
6374
6375         /**
6376          * Fires when the user changes the active text size. Handler gets called with 2 params, the old size and the new size.
6377          * @param {Function} fn        The method the event invokes
6378          * @param {Object}   scope    An object that becomes the scope of the handler
6379          * @param {boolean}  options
6380          */
6381         onTextResize : function(fn, scope, options){
6382             if(!textEvent){
6383                 textEvent = new Roo.util.Event();
6384                 var textEl = new Roo.Element(document.createElement('div'));
6385                 textEl.dom.className = 'x-text-resize';
6386                 textEl.dom.innerHTML = 'X';
6387                 textEl.appendTo(document.body);
6388                 textSize = textEl.dom.offsetHeight;
6389                 setInterval(function(){
6390                     if(textEl.dom.offsetHeight != textSize){
6391                         textEvent.fire(textSize, textSize = textEl.dom.offsetHeight);
6392                     }
6393                 }, this.textResizeInterval);
6394             }
6395             textEvent.addListener(fn, scope, options);
6396         },
6397
6398         /**
6399          * Removes the passed window resize listener.
6400          * @param {Function} fn        The method the event invokes
6401          * @param {Object}   scope    The scope of handler
6402          */
6403         removeResizeListener : function(fn, scope){
6404             if(resizeEvent){
6405                 resizeEvent.removeListener(fn, scope);
6406             }
6407         },
6408
6409         // private
6410         fireResize : function(){
6411             if(resizeEvent){
6412                 resizeEvent.fire(D.getViewWidth(), D.getViewHeight());
6413             }   
6414         },
6415         /**
6416          * Url used for onDocumentReady with using SSL (defaults to Roo.SSL_SECURE_URL)
6417          */
6418         ieDeferSrc : false,
6419         /**
6420          * The frequency, in milliseconds, to check for text resize events (defaults to 50)
6421          */
6422         textResizeInterval : 50
6423     };
6424     
6425     /**
6426      * Fix for doc tools
6427      * @scopeAlias pub=Roo.EventManager
6428      */
6429     
6430      /**
6431      * Appends an event handler to an element (shorthand for addListener)
6432      * @param {String/HTMLElement}   element        The html element or id to assign the
6433      * @param {String}   eventName The type of event to listen for
6434      * @param {Function} handler The method the event invokes
6435      * @param {Object}   scope (optional) The scope in which to execute the handler
6436      * function. The handler function's "this" context.
6437      * @param {Object}   options (optional) An object containing handler configuration
6438      * properties. This may contain any of the following properties:<ul>
6439      * <li>scope {Object} The scope in which to execute the handler function. The handler function's "this" context.</li>
6440      * <li>delegate {String} A simple selector to filter the target or look for a descendant of the target</li>
6441      * <li>stopEvent {Boolean} True to stop the event. That is stop propagation, and prevent the default action.</li>
6442      * <li>preventDefault {Boolean} True to prevent the default action</li>
6443      * <li>stopPropagation {Boolean} True to prevent event propagation</li>
6444      * <li>normalized {Boolean} False to pass a browser event to the handler function instead of an Roo.EventObject</li>
6445      * <li>delay {Number} The number of milliseconds to delay the invocation of the handler after te event fires.</li>
6446      * <li>single {Boolean} True to add a handler to handle just the next firing of the event, and then remove itself.</li>
6447      * <li>buffer {Number} Causes the handler to be scheduled to run in an {@link Roo.util.DelayedTask} delayed
6448      * by the specified number of milliseconds. If the event fires again within that time, the original
6449      * handler is <em>not</em> invoked, but the new handler is scheduled in its place.</li>
6450      * </ul><br>
6451      * <p>
6452      * <b>Combining Options</b><br>
6453      * Using the options argument, it is possible to combine different types of listeners:<br>
6454      * <br>
6455      * A normalized, delayed, one-time listener that auto stops the event and passes a custom argument (forumId)<div style="margin: 5px 20px 20px;">
6456      * Code:<pre><code>
6457 el.on('click', this.onClick, this, {
6458     single: true,
6459     delay: 100,
6460     stopEvent : true,
6461     forumId: 4
6462 });</code></pre>
6463      * <p>
6464      * <b>Attaching multiple handlers in 1 call</b><br>
6465       * The method also allows for a single argument to be passed which is a config object containing properties
6466      * which specify multiple handlers.
6467      * <p>
6468      * Code:<pre><code>
6469 el.on({
6470     'click' : {
6471         fn: this.onClick
6472         scope: this,
6473         delay: 100
6474     },
6475     'mouseover' : {
6476         fn: this.onMouseOver
6477         scope: this
6478     },
6479     'mouseout' : {
6480         fn: this.onMouseOut
6481         scope: this
6482     }
6483 });</code></pre>
6484      * <p>
6485      * Or a shorthand syntax:<br>
6486      * Code:<pre><code>
6487 el.on({
6488     'click' : this.onClick,
6489     'mouseover' : this.onMouseOver,
6490     'mouseout' : this.onMouseOut
6491     scope: this
6492 });</code></pre>
6493      */
6494     pub.on = pub.addListener;
6495     pub.un = pub.removeListener;
6496
6497     pub.stoppedMouseDownEvent = new Roo.util.Event();
6498     return pub;
6499 }();
6500 /**
6501   * Fires when the document is ready (before onload and before images are loaded).  Shorthand of {@link Roo.EventManager#onDocumentReady}.
6502   * @param {Function} fn        The method the event invokes
6503   * @param {Object}   scope    An  object that becomes the scope of the handler
6504   * @param {boolean}  override If true, the obj passed in becomes
6505   *                             the execution scope of the listener
6506   * @member Roo
6507   * @method onReady
6508  */
6509 Roo.onReady = Roo.EventManager.onDocumentReady;
6510
6511 Roo.onReady(function(){
6512     var bd = Roo.get(document.body);
6513     if(!bd){ return; }
6514
6515     var cls = [
6516             Roo.isIE ? "roo-ie"
6517             : Roo.isGecko ? "roo-gecko"
6518             : Roo.isOpera ? "roo-opera"
6519             : Roo.isSafari ? "roo-safari" : ""];
6520
6521     if(Roo.isMac){
6522         cls.push("roo-mac");
6523     }
6524     if(Roo.isLinux){
6525         cls.push("roo-linux");
6526     }
6527     if(Roo.isBorderBox){
6528         cls.push('roo-border-box');
6529     }
6530     if(Roo.isStrict){ // add to the parent to allow for selectors like ".ext-strict .ext-ie"
6531         var p = bd.dom.parentNode;
6532         if(p){
6533             p.className += ' roo-strict';
6534         }
6535     }
6536     bd.addClass(cls.join(' '));
6537 });
6538
6539 /**
6540  * @class Roo.EventObject
6541  * EventObject exposes the Yahoo! UI Event functionality directly on the object
6542  * passed to your event handler. It exists mostly for convenience. It also fixes the annoying null checks automatically to cleanup your code 
6543  * Example:
6544  * <pre><code>
6545  function handleClick(e){ // e is not a standard event object, it is a Roo.EventObject
6546     e.preventDefault();
6547     var target = e.getTarget();
6548     ...
6549  }
6550  var myDiv = Roo.get("myDiv");
6551  myDiv.on("click", handleClick);
6552  //or
6553  Roo.EventManager.on("myDiv", 'click', handleClick);
6554  Roo.EventManager.addListener("myDiv", 'click', handleClick);
6555  </code></pre>
6556  * @singleton
6557  */
6558 Roo.EventObject = function(){
6559     
6560     var E = Roo.lib.Event;
6561     
6562     // safari keypress events for special keys return bad keycodes
6563     var safariKeys = {
6564         63234 : 37, // left
6565         63235 : 39, // right
6566         63232 : 38, // up
6567         63233 : 40, // down
6568         63276 : 33, // page up
6569         63277 : 34, // page down
6570         63272 : 46, // delete
6571         63273 : 36, // home
6572         63275 : 35  // end
6573     };
6574
6575     // normalize button clicks
6576     var btnMap = Roo.isIE ? {1:0,4:1,2:2} :
6577                 (Roo.isSafari ? {1:0,2:1,3:2} : {0:0,1:1,2:2});
6578
6579     Roo.EventObjectImpl = function(e){
6580         if(e){
6581             this.setEvent(e.browserEvent || e);
6582         }
6583     };
6584     Roo.EventObjectImpl.prototype = {
6585         /**
6586          * Used to fix doc tools.
6587          * @scope Roo.EventObject.prototype
6588          */
6589             
6590
6591         
6592         
6593         /** The normal browser event */
6594         browserEvent : null,
6595         /** The button pressed in a mouse event */
6596         button : -1,
6597         /** True if the shift key was down during the event */
6598         shiftKey : false,
6599         /** True if the control key was down during the event */
6600         ctrlKey : false,
6601         /** True if the alt key was down during the event */
6602         altKey : false,
6603
6604         /** Key constant 
6605         * @type Number */
6606         BACKSPACE : 8,
6607         /** Key constant 
6608         * @type Number */
6609         TAB : 9,
6610         /** Key constant 
6611         * @type Number */
6612         RETURN : 13,
6613         /** Key constant 
6614         * @type Number */
6615         ENTER : 13,
6616         /** Key constant 
6617         * @type Number */
6618         SHIFT : 16,
6619         /** Key constant 
6620         * @type Number */
6621         CONTROL : 17,
6622         /** Key constant 
6623         * @type Number */
6624         ESC : 27,
6625         /** Key constant 
6626         * @type Number */
6627         SPACE : 32,
6628         /** Key constant 
6629         * @type Number */
6630         PAGEUP : 33,
6631         /** Key constant 
6632         * @type Number */
6633         PAGEDOWN : 34,
6634         /** Key constant 
6635         * @type Number */
6636         END : 35,
6637         /** Key constant 
6638         * @type Number */
6639         HOME : 36,
6640         /** Key constant 
6641         * @type Number */
6642         LEFT : 37,
6643         /** Key constant 
6644         * @type Number */
6645         UP : 38,
6646         /** Key constant 
6647         * @type Number */
6648         RIGHT : 39,
6649         /** Key constant 
6650         * @type Number */
6651         DOWN : 40,
6652         /** Key constant 
6653         * @type Number */
6654         DELETE : 46,
6655         /** Key constant 
6656         * @type Number */
6657         F5 : 116,
6658
6659            /** @private */
6660         setEvent : function(e){
6661             if(e == this || (e && e.browserEvent)){ // already wrapped
6662                 return e;
6663             }
6664             this.browserEvent = e;
6665             if(e){
6666                 // normalize buttons
6667                 this.button = e.button ? btnMap[e.button] : (e.which ? e.which-1 : -1);
6668                 if(e.type == 'click' && this.button == -1){
6669                     this.button = 0;
6670                 }
6671                 this.type = e.type;
6672                 this.shiftKey = e.shiftKey;
6673                 // mac metaKey behaves like ctrlKey
6674                 this.ctrlKey = e.ctrlKey || e.metaKey;
6675                 this.altKey = e.altKey;
6676                 // in getKey these will be normalized for the mac
6677                 this.keyCode = e.keyCode;
6678                 // keyup warnings on firefox.
6679                 this.charCode = (e.type == 'keyup' || e.type == 'keydown') ? 0 : e.charCode;
6680                 // cache the target for the delayed and or buffered events
6681                 this.target = E.getTarget(e);
6682                 // same for XY
6683                 this.xy = E.getXY(e);
6684             }else{
6685                 this.button = -1;
6686                 this.shiftKey = false;
6687                 this.ctrlKey = false;
6688                 this.altKey = false;
6689                 this.keyCode = 0;
6690                 this.charCode =0;
6691                 this.target = null;
6692                 this.xy = [0, 0];
6693             }
6694             return this;
6695         },
6696
6697         /**
6698          * Stop the event (preventDefault and stopPropagation)
6699          */
6700         stopEvent : function(){
6701             if(this.browserEvent){
6702                 if(this.browserEvent.type == 'mousedown'){
6703                     Roo.EventManager.stoppedMouseDownEvent.fire(this);
6704                 }
6705                 E.stopEvent(this.browserEvent);
6706             }
6707         },
6708
6709         /**
6710          * Prevents the browsers default handling of the event.
6711          */
6712         preventDefault : function(){
6713             if(this.browserEvent){
6714                 E.preventDefault(this.browserEvent);
6715             }
6716         },
6717
6718         /** @private */
6719         isNavKeyPress : function(){
6720             var k = this.keyCode;
6721             k = Roo.isSafari ? (safariKeys[k] || k) : k;
6722             return (k >= 33 && k <= 40) || k == this.RETURN || k == this.TAB || k == this.ESC;
6723         },
6724
6725         isSpecialKey : function(){
6726             var k = this.keyCode;
6727             return (this.type == 'keypress' && this.ctrlKey) || k == 9 || k == 13  || k == 40 || k == 27 ||
6728             (k == 16) || (k == 17) ||
6729             (k >= 18 && k <= 20) ||
6730             (k >= 33 && k <= 35) ||
6731             (k >= 36 && k <= 39) ||
6732             (k >= 44 && k <= 45);
6733         },
6734         /**
6735          * Cancels bubbling of the event.
6736          */
6737         stopPropagation : function(){
6738             if(this.browserEvent){
6739                 if(this.type == 'mousedown'){
6740                     Roo.EventManager.stoppedMouseDownEvent.fire(this);
6741                 }
6742                 E.stopPropagation(this.browserEvent);
6743             }
6744         },
6745
6746         /**
6747          * Gets the key code for the event.
6748          * @return {Number}
6749          */
6750         getCharCode : function(){
6751             return this.charCode || this.keyCode;
6752         },
6753
6754         /**
6755          * Returns a normalized keyCode for the event.
6756          * @return {Number} The key code
6757          */
6758         getKey : function(){
6759             var k = this.keyCode || this.charCode;
6760             return Roo.isSafari ? (safariKeys[k] || k) : k;
6761         },
6762
6763         /**
6764          * Gets the x coordinate of the event.
6765          * @return {Number}
6766          */
6767         getPageX : function(){
6768             return this.xy[0];
6769         },
6770
6771         /**
6772          * Gets the y coordinate of the event.
6773          * @return {Number}
6774          */
6775         getPageY : function(){
6776             return this.xy[1];
6777         },
6778
6779         /**
6780          * Gets the time of the event.
6781          * @return {Number}
6782          */
6783         getTime : function(){
6784             if(this.browserEvent){
6785                 return E.getTime(this.browserEvent);
6786             }
6787             return null;
6788         },
6789
6790         /**
6791          * Gets the page coordinates of the event.
6792          * @return {Array} The xy values like [x, y]
6793          */
6794         getXY : function(){
6795             return this.xy;
6796         },
6797
6798         /**
6799          * Gets the target for the event.
6800          * @param {String} selector (optional) A simple selector to filter the target or look for an ancestor of the target
6801          * @param {Number/String/HTMLElement/Element} maxDepth (optional) The max depth to
6802                 search as a number or element (defaults to 10 || document.body)
6803          * @param {Boolean} returnEl (optional) True to return a Roo.Element object instead of DOM node
6804          * @return {HTMLelement}
6805          */
6806         getTarget : function(selector, maxDepth, returnEl){
6807             return selector ? Roo.fly(this.target).findParent(selector, maxDepth, returnEl) : this.target;
6808         },
6809         /**
6810          * Gets the related target.
6811          * @return {HTMLElement}
6812          */
6813         getRelatedTarget : function(){
6814             if(this.browserEvent){
6815                 return E.getRelatedTarget(this.browserEvent);
6816             }
6817             return null;
6818         },
6819
6820         /**
6821          * Normalizes mouse wheel delta across browsers
6822          * @return {Number} The delta
6823          */
6824         getWheelDelta : function(){
6825             var e = this.browserEvent;
6826             var delta = 0;
6827             if(e.wheelDelta){ /* IE/Opera. */
6828                 delta = e.wheelDelta/120;
6829             }else if(e.detail){ /* Mozilla case. */
6830                 delta = -e.detail/3;
6831             }
6832             return delta;
6833         },
6834
6835         /**
6836          * Returns true if the control, meta, shift or alt key was pressed during this event.
6837          * @return {Boolean}
6838          */
6839         hasModifier : function(){
6840             return !!((this.ctrlKey || this.altKey) || this.shiftKey);
6841         },
6842
6843         /**
6844          * Returns true if the target of this event equals el or is a child of el
6845          * @param {String/HTMLElement/Element} el
6846          * @param {Boolean} related (optional) true to test if the related target is within el instead of the target
6847          * @return {Boolean}
6848          */
6849         within : function(el, related){
6850             var t = this[related ? "getRelatedTarget" : "getTarget"]();
6851             return t && Roo.fly(el).contains(t);
6852         },
6853
6854         getPoint : function(){
6855             return new Roo.lib.Point(this.xy[0], this.xy[1]);
6856         }
6857     };
6858
6859     return new Roo.EventObjectImpl();
6860 }();
6861             
6862     /*
6863  * Based on:
6864  * Ext JS Library 1.1.1
6865  * Copyright(c) 2006-2007, Ext JS, LLC.
6866  *
6867  * Originally Released Under LGPL - original licence link has changed is not relivant.
6868  *
6869  * Fork - LGPL
6870  * <script type="text/javascript">
6871  */
6872
6873  
6874 // was in Composite Element!??!?!
6875  
6876 (function(){
6877     var D = Roo.lib.Dom;
6878     var E = Roo.lib.Event;
6879     var A = Roo.lib.Anim;
6880
6881     // local style camelizing for speed
6882     var propCache = {};
6883     var camelRe = /(-[a-z])/gi;
6884     var camelFn = function(m, a){ return a.charAt(1).toUpperCase(); };
6885     var view = document.defaultView;
6886
6887 /**
6888  * @class Roo.Element
6889  * Represents an Element in the DOM.<br><br>
6890  * Usage:<br>
6891 <pre><code>
6892 var el = Roo.get("my-div");
6893
6894 // or with getEl
6895 var el = getEl("my-div");
6896
6897 // or with a DOM element
6898 var el = Roo.get(myDivElement);
6899 </code></pre>
6900  * Using Roo.get() or getEl() instead of calling the constructor directly ensures you get the same object
6901  * each call instead of constructing a new one.<br><br>
6902  * <b>Animations</b><br />
6903  * Many of the functions for manipulating an element have an optional "animate" parameter. The animate parameter
6904  * should either be a boolean (true) or an object literal with animation options. The animation options are:
6905 <pre>
6906 Option    Default   Description
6907 --------- --------  ---------------------------------------------
6908 duration  .35       The duration of the animation in seconds
6909 easing    easeOut   The YUI easing method
6910 callback  none      A function to execute when the anim completes
6911 scope     this      The scope (this) of the callback function
6912 </pre>
6913 * Also, the Anim object being used for the animation will be set on your options object as "anim", which allows you to stop or
6914 * manipulate the animation. Here's an example:
6915 <pre><code>
6916 var el = Roo.get("my-div");
6917
6918 // no animation
6919 el.setWidth(100);
6920
6921 // default animation
6922 el.setWidth(100, true);
6923
6924 // animation with some options set
6925 el.setWidth(100, {
6926     duration: 1,
6927     callback: this.foo,
6928     scope: this
6929 });
6930
6931 // using the "anim" property to get the Anim object
6932 var opt = {
6933     duration: 1,
6934     callback: this.foo,
6935     scope: this
6936 };
6937 el.setWidth(100, opt);
6938 ...
6939 if(opt.anim.isAnimated()){
6940     opt.anim.stop();
6941 }
6942 </code></pre>
6943 * <b> Composite (Collections of) Elements</b><br />
6944  * For working with collections of Elements, see <a href="Roo.CompositeElement.html">Roo.CompositeElement</a>
6945  * @constructor Create a new Element directly.
6946  * @param {String/HTMLElement} element
6947  * @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).
6948  */
6949     Roo.Element = function(element, forceNew){
6950         var dom = typeof element == "string" ?
6951                 document.getElementById(element) : element;
6952         if(!dom){ // invalid id/element
6953             return null;
6954         }
6955         var id = dom.id;
6956         if(forceNew !== true && id && Roo.Element.cache[id]){ // element object already exists
6957             return Roo.Element.cache[id];
6958         }
6959
6960         /**
6961          * The DOM element
6962          * @type HTMLElement
6963          */
6964         this.dom = dom;
6965
6966         /**
6967          * The DOM element ID
6968          * @type String
6969          */
6970         this.id = id || Roo.id(dom);
6971     };
6972
6973     var El = Roo.Element;
6974
6975     El.prototype = {
6976         /**
6977          * The element's default display mode  (defaults to "")
6978          * @type String
6979          */
6980         originalDisplay : "",
6981
6982         visibilityMode : 1,
6983         /**
6984          * The default unit to append to CSS values where a unit isn't provided (defaults to px).
6985          * @type String
6986          */
6987         defaultUnit : "px",
6988         /**
6989          * Sets the element's visibility mode. When setVisible() is called it
6990          * will use this to determine whether to set the visibility or the display property.
6991          * @param visMode Element.VISIBILITY or Element.DISPLAY
6992          * @return {Roo.Element} this
6993          */
6994         setVisibilityMode : function(visMode){
6995             this.visibilityMode = visMode;
6996             return this;
6997         },
6998         /**
6999          * Convenience method for setVisibilityMode(Element.DISPLAY)
7000          * @param {String} display (optional) What to set display to when visible
7001          * @return {Roo.Element} this
7002          */
7003         enableDisplayMode : function(display){
7004             this.setVisibilityMode(El.DISPLAY);
7005             if(typeof display != "undefined") this.originalDisplay = display;
7006             return this;
7007         },
7008
7009         /**
7010          * 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)
7011          * @param {String} selector The simple selector to test
7012          * @param {Number/String/HTMLElement/Element} maxDepth (optional) The max depth to
7013                 search as a number or element (defaults to 10 || document.body)
7014          * @param {Boolean} returnEl (optional) True to return a Roo.Element object instead of DOM node
7015          * @return {HTMLElement} The matching DOM node (or null if no match was found)
7016          */
7017         findParent : function(simpleSelector, maxDepth, returnEl){
7018             var p = this.dom, b = document.body, depth = 0, dq = Roo.DomQuery, stopEl;
7019             maxDepth = maxDepth || 50;
7020             if(typeof maxDepth != "number"){
7021                 stopEl = Roo.getDom(maxDepth);
7022                 maxDepth = 10;
7023             }
7024             while(p && p.nodeType == 1 && depth < maxDepth && p != b && p != stopEl){
7025                 if(dq.is(p, simpleSelector)){
7026                     return returnEl ? Roo.get(p) : p;
7027                 }
7028                 depth++;
7029                 p = p.parentNode;
7030             }
7031             return null;
7032         },
7033
7034
7035         /**
7036          * Looks at parent nodes for a match of the passed simple selector (e.g. div.some-class or span:first-child)
7037          * @param {String} selector The simple selector to test
7038          * @param {Number/String/HTMLElement/Element} maxDepth (optional) The max depth to
7039                 search as a number or element (defaults to 10 || document.body)
7040          * @param {Boolean} returnEl (optional) True to return a Roo.Element object instead of DOM node
7041          * @return {HTMLElement} The matching DOM node (or null if no match was found)
7042          */
7043         findParentNode : function(simpleSelector, maxDepth, returnEl){
7044             var p = Roo.fly(this.dom.parentNode, '_internal');
7045             return p ? p.findParent(simpleSelector, maxDepth, returnEl) : null;
7046         },
7047
7048         /**
7049          * Walks up the dom looking for a parent node that matches the passed simple selector (e.g. div.some-class or span:first-child).
7050          * This is a shortcut for findParentNode() that always returns an Roo.Element.
7051          * @param {String} selector The simple selector to test
7052          * @param {Number/String/HTMLElement/Element} maxDepth (optional) The max depth to
7053                 search as a number or element (defaults to 10 || document.body)
7054          * @return {Roo.Element} The matching DOM node (or null if no match was found)
7055          */
7056         up : function(simpleSelector, maxDepth){
7057             return this.findParentNode(simpleSelector, maxDepth, true);
7058         },
7059
7060
7061
7062         /**
7063          * Returns true if this element matches the passed simple selector (e.g. div.some-class or span:first-child)
7064          * @param {String} selector The simple selector to test
7065          * @return {Boolean} True if this element matches the selector, else false
7066          */
7067         is : function(simpleSelector){
7068             return Roo.DomQuery.is(this.dom, simpleSelector);
7069         },
7070
7071         /**
7072          * Perform animation on this element.
7073          * @param {Object} args The YUI animation control args
7074          * @param {Float} duration (optional) How long the animation lasts in seconds (defaults to .35)
7075          * @param {Function} onComplete (optional) Function to call when animation completes
7076          * @param {String} easing (optional) Easing method to use (defaults to 'easeOut')
7077          * @param {String} animType (optional) 'run' is the default. Can also be 'color', 'motion', or 'scroll'
7078          * @return {Roo.Element} this
7079          */
7080         animate : function(args, duration, onComplete, easing, animType){
7081             this.anim(args, {duration: duration, callback: onComplete, easing: easing}, animType);
7082             return this;
7083         },
7084
7085         /*
7086          * @private Internal animation call
7087          */
7088         anim : function(args, opt, animType, defaultDur, defaultEase, cb){
7089             animType = animType || 'run';
7090             opt = opt || {};
7091             var anim = Roo.lib.Anim[animType](
7092                 this.dom, args,
7093                 (opt.duration || defaultDur) || .35,
7094                 (opt.easing || defaultEase) || 'easeOut',
7095                 function(){
7096                     Roo.callback(cb, this);
7097                     Roo.callback(opt.callback, opt.scope || this, [this, opt]);
7098                 },
7099                 this
7100             );
7101             opt.anim = anim;
7102             return anim;
7103         },
7104
7105         // private legacy anim prep
7106         preanim : function(a, i){
7107             return !a[i] ? false : (typeof a[i] == "object" ? a[i]: {duration: a[i+1], callback: a[i+2], easing: a[i+3]});
7108         },
7109
7110         /**
7111          * Removes worthless text nodes
7112          * @param {Boolean} forceReclean (optional) By default the element
7113          * keeps track if it has been cleaned already so
7114          * you can call this over and over. However, if you update the element and
7115          * need to force a reclean, you can pass true.
7116          */
7117         clean : function(forceReclean){
7118             if(this.isCleaned && forceReclean !== true){
7119                 return this;
7120             }
7121             var ns = /\S/;
7122             var d = this.dom, n = d.firstChild, ni = -1;
7123             while(n){
7124                 var nx = n.nextSibling;
7125                 if(n.nodeType == 3 && !ns.test(n.nodeValue)){
7126                     d.removeChild(n);
7127                 }else{
7128                     n.nodeIndex = ++ni;
7129                 }
7130                 n = nx;
7131             }
7132             this.isCleaned = true;
7133             return this;
7134         },
7135
7136         // private
7137         calcOffsetsTo : function(el){
7138             el = Roo.get(el);
7139             var d = el.dom;
7140             var restorePos = false;
7141             if(el.getStyle('position') == 'static'){
7142                 el.position('relative');
7143                 restorePos = true;
7144             }
7145             var x = 0, y =0;
7146             var op = this.dom;
7147             while(op && op != d && op.tagName != 'HTML'){
7148                 x+= op.offsetLeft;
7149                 y+= op.offsetTop;
7150                 op = op.offsetParent;
7151             }
7152             if(restorePos){
7153                 el.position('static');
7154             }
7155             return [x, y];
7156         },
7157
7158         /**
7159          * Scrolls this element into view within the passed container.
7160          * @param {String/HTMLElement/Element} container (optional) The container element to scroll (defaults to document.body)
7161          * @param {Boolean} hscroll (optional) False to disable horizontal scroll (defaults to true)
7162          * @return {Roo.Element} this
7163          */
7164         scrollIntoView : function(container, hscroll){
7165             var c = Roo.getDom(container) || document.body;
7166             var el = this.dom;
7167
7168             var o = this.calcOffsetsTo(c),
7169                 l = o[0],
7170                 t = o[1],
7171                 b = t+el.offsetHeight,
7172                 r = l+el.offsetWidth;
7173
7174             var ch = c.clientHeight;
7175             var ct = parseInt(c.scrollTop, 10);
7176             var cl = parseInt(c.scrollLeft, 10);
7177             var cb = ct + ch;
7178             var cr = cl + c.clientWidth;
7179
7180             if(t < ct){
7181                 c.scrollTop = t;
7182             }else if(b > cb){
7183                 c.scrollTop = b-ch;
7184             }
7185
7186             if(hscroll !== false){
7187                 if(l < cl){
7188                     c.scrollLeft = l;
7189                 }else if(r > cr){
7190                     c.scrollLeft = r-c.clientWidth;
7191                 }
7192             }
7193             return this;
7194         },
7195
7196         // private
7197         scrollChildIntoView : function(child, hscroll){
7198             Roo.fly(child, '_scrollChildIntoView').scrollIntoView(this, hscroll);
7199         },
7200
7201         /**
7202          * Measures the element's content height and updates height to match. Note: this function uses setTimeout so
7203          * the new height may not be available immediately.
7204          * @param {Boolean} animate (optional) Animate the transition (defaults to false)
7205          * @param {Float} duration (optional) Length of the animation in seconds (defaults to .35)
7206          * @param {Function} onComplete (optional) Function to call when animation completes
7207          * @param {String} easing (optional) Easing method to use (defaults to easeOut)
7208          * @return {Roo.Element} this
7209          */
7210         autoHeight : function(animate, duration, onComplete, easing){
7211             var oldHeight = this.getHeight();
7212             this.clip();
7213             this.setHeight(1); // force clipping
7214             setTimeout(function(){
7215                 var height = parseInt(this.dom.scrollHeight, 10); // parseInt for Safari
7216                 if(!animate){
7217                     this.setHeight(height);
7218                     this.unclip();
7219                     if(typeof onComplete == "function"){
7220                         onComplete();
7221                     }
7222                 }else{
7223                     this.setHeight(oldHeight); // restore original height
7224                     this.setHeight(height, animate, duration, function(){
7225                         this.unclip();
7226                         if(typeof onComplete == "function") onComplete();
7227                     }.createDelegate(this), easing);
7228                 }
7229             }.createDelegate(this), 0);
7230             return this;
7231         },
7232
7233         /**
7234          * Returns true if this element is an ancestor of the passed element
7235          * @param {HTMLElement/String} el The element to check
7236          * @return {Boolean} True if this element is an ancestor of el, else false
7237          */
7238         contains : function(el){
7239             if(!el){return false;}
7240             return D.isAncestor(this.dom, el.dom ? el.dom : el);
7241         },
7242
7243         /**
7244          * Checks whether the element is currently visible using both visibility and display properties.
7245          * @param {Boolean} deep (optional) True to walk the dom and see if parent elements are hidden (defaults to false)
7246          * @return {Boolean} True if the element is currently visible, else false
7247          */
7248         isVisible : function(deep) {
7249             var vis = !(this.getStyle("visibility") == "hidden" || this.getStyle("display") == "none");
7250             if(deep !== true || !vis){
7251                 return vis;
7252             }
7253             var p = this.dom.parentNode;
7254             while(p && p.tagName.toLowerCase() != "body"){
7255                 if(!Roo.fly(p, '_isVisible').isVisible()){
7256                     return false;
7257                 }
7258                 p = p.parentNode;
7259             }
7260             return true;
7261         },
7262
7263         /**
7264          * Creates a {@link Roo.CompositeElement} for child nodes based on the passed CSS selector (the selector should not contain an id).
7265          * @param {String} selector The CSS selector
7266          * @param {Boolean} unique (optional) True to create a unique Roo.Element for each child (defaults to false, which creates a single shared flyweight object)
7267          * @return {CompositeElement/CompositeElementLite} The composite element
7268          */
7269         select : function(selector, unique){
7270             return El.select(selector, unique, this.dom);
7271         },
7272
7273         /**
7274          * Selects child nodes based on the passed CSS selector (the selector should not contain an id).
7275          * @param {String} selector The CSS selector
7276          * @return {Array} An array of the matched nodes
7277          */
7278         query : function(selector, unique){
7279             return Roo.DomQuery.select(selector, this.dom);
7280         },
7281
7282         /**
7283          * Selects a single child at any depth below this element based on the passed CSS selector (the selector should not contain an id).
7284          * @param {String} selector The CSS selector
7285          * @param {Boolean} returnDom (optional) True to return the DOM node instead of Roo.Element (defaults to false)
7286          * @return {HTMLElement/Roo.Element} The child Roo.Element (or DOM node if returnDom = true)
7287          */
7288         child : function(selector, returnDom){
7289             var n = Roo.DomQuery.selectNode(selector, this.dom);
7290             return returnDom ? n : Roo.get(n);
7291         },
7292
7293         /**
7294          * Selects a single *direct* child based on the passed CSS selector (the selector should not contain an id).
7295          * @param {String} selector The CSS selector
7296          * @param {Boolean} returnDom (optional) True to return the DOM node instead of Roo.Element (defaults to false)
7297          * @return {HTMLElement/Roo.Element} The child Roo.Element (or DOM node if returnDom = true)
7298          */
7299         down : function(selector, returnDom){
7300             var n = Roo.DomQuery.selectNode(" > " + selector, this.dom);
7301             return returnDom ? n : Roo.get(n);
7302         },
7303
7304         /**
7305          * Initializes a {@link Roo.dd.DD} drag drop object for this element.
7306          * @param {String} group The group the DD object is member of
7307          * @param {Object} config The DD config object
7308          * @param {Object} overrides An object containing methods to override/implement on the DD object
7309          * @return {Roo.dd.DD} The DD object
7310          */
7311         initDD : function(group, config, overrides){
7312             var dd = new Roo.dd.DD(Roo.id(this.dom), group, config);
7313             return Roo.apply(dd, overrides);
7314         },
7315
7316         /**
7317          * Initializes a {@link Roo.dd.DDProxy} object for this element.
7318          * @param {String} group The group the DDProxy object is member of
7319          * @param {Object} config The DDProxy config object
7320          * @param {Object} overrides An object containing methods to override/implement on the DDProxy object
7321          * @return {Roo.dd.DDProxy} The DDProxy object
7322          */
7323         initDDProxy : function(group, config, overrides){
7324             var dd = new Roo.dd.DDProxy(Roo.id(this.dom), group, config);
7325             return Roo.apply(dd, overrides);
7326         },
7327
7328         /**
7329          * Initializes a {@link Roo.dd.DDTarget} object for this element.
7330          * @param {String} group The group the DDTarget object is member of
7331          * @param {Object} config The DDTarget config object
7332          * @param {Object} overrides An object containing methods to override/implement on the DDTarget object
7333          * @return {Roo.dd.DDTarget} The DDTarget object
7334          */
7335         initDDTarget : function(group, config, overrides){
7336             var dd = new Roo.dd.DDTarget(Roo.id(this.dom), group, config);
7337             return Roo.apply(dd, overrides);
7338         },
7339
7340         /**
7341          * Sets the visibility of the element (see details). If the visibilityMode is set to Element.DISPLAY, it will use
7342          * the display property to hide the element, otherwise it uses visibility. The default is to hide and show using the visibility property.
7343          * @param {Boolean} visible Whether the element is visible
7344          * @param {Boolean/Object} animate (optional) True for the default animation, or a standard Element animation config object
7345          * @return {Roo.Element} this
7346          */
7347          setVisible : function(visible, animate){
7348             if(!animate || !A){
7349                 if(this.visibilityMode == El.DISPLAY){
7350                     this.setDisplayed(visible);
7351                 }else{
7352                     this.fixDisplay();
7353                     this.dom.style.visibility = visible ? "visible" : "hidden";
7354                 }
7355             }else{
7356                 // closure for composites
7357                 var dom = this.dom;
7358                 var visMode = this.visibilityMode;
7359                 if(visible){
7360                     this.setOpacity(.01);
7361                     this.setVisible(true);
7362                 }
7363                 this.anim({opacity: { to: (visible?1:0) }},
7364                       this.preanim(arguments, 1),
7365                       null, .35, 'easeIn', function(){
7366                          if(!visible){
7367                              if(visMode == El.DISPLAY){
7368                                  dom.style.display = "none";
7369                              }else{
7370                                  dom.style.visibility = "hidden";
7371                              }
7372                              Roo.get(dom).setOpacity(1);
7373                          }
7374                      });
7375             }
7376             return this;
7377         },
7378
7379         /**
7380          * Returns true if display is not "none"
7381          * @return {Boolean}
7382          */
7383         isDisplayed : function() {
7384             return this.getStyle("display") != "none";
7385         },
7386
7387         /**
7388          * Toggles the element's visibility or display, depending on visibility mode.
7389          * @param {Boolean/Object} animate (optional) True for the default animation, or a standard Element animation config object
7390          * @return {Roo.Element} this
7391          */
7392         toggle : function(animate){
7393             this.setVisible(!this.isVisible(), this.preanim(arguments, 0));
7394             return this;
7395         },
7396
7397         /**
7398          * Sets the CSS display property. Uses originalDisplay if the specified value is a boolean true.
7399          * @param {Boolean} value Boolean value to display the element using its default display, or a string to set the display directly
7400          * @return {Roo.Element} this
7401          */
7402         setDisplayed : function(value) {
7403             if(typeof value == "boolean"){
7404                value = value ? this.originalDisplay : "none";
7405             }
7406             this.setStyle("display", value);
7407             return this;
7408         },
7409
7410         /**
7411          * Tries to focus the element. Any exceptions are caught and ignored.
7412          * @return {Roo.Element} this
7413          */
7414         focus : function() {
7415             try{
7416                 this.dom.focus();
7417             }catch(e){}
7418             return this;
7419         },
7420
7421         /**
7422          * Tries to blur the element. Any exceptions are caught and ignored.
7423          * @return {Roo.Element} this
7424          */
7425         blur : function() {
7426             try{
7427                 this.dom.blur();
7428             }catch(e){}
7429             return this;
7430         },
7431
7432         /**
7433          * Adds one or more CSS classes to the element. Duplicate classes are automatically filtered out.
7434          * @param {String/Array} className The CSS class to add, or an array of classes
7435          * @return {Roo.Element} this
7436          */
7437         addClass : function(className){
7438             if(className instanceof Array){
7439                 for(var i = 0, len = className.length; i < len; i++) {
7440                     this.addClass(className[i]);
7441                 }
7442             }else{
7443                 if(className && !this.hasClass(className)){
7444                     this.dom.className = this.dom.className + " " + className;
7445                 }
7446             }
7447             return this;
7448         },
7449
7450         /**
7451          * Adds one or more CSS classes to this element and removes the same class(es) from all siblings.
7452          * @param {String/Array} className The CSS class to add, or an array of classes
7453          * @return {Roo.Element} this
7454          */
7455         radioClass : function(className){
7456             var siblings = this.dom.parentNode.childNodes;
7457             for(var i = 0; i < siblings.length; i++) {
7458                 var s = siblings[i];
7459                 if(s.nodeType == 1){
7460                     Roo.get(s).removeClass(className);
7461                 }
7462             }
7463             this.addClass(className);
7464             return this;
7465         },
7466
7467         /**
7468          * Removes one or more CSS classes from the element.
7469          * @param {String/Array} className The CSS class to remove, or an array of classes
7470          * @return {Roo.Element} this
7471          */
7472         removeClass : function(className){
7473             if(!className || !this.dom.className){
7474                 return this;
7475             }
7476             if(className instanceof Array){
7477                 for(var i = 0, len = className.length; i < len; i++) {
7478                     this.removeClass(className[i]);
7479                 }
7480             }else{
7481                 if(this.hasClass(className)){
7482                     var re = this.classReCache[className];
7483                     if (!re) {
7484                        re = new RegExp('(?:^|\\s+)' + className + '(?:\\s+|$)', "g");
7485                        this.classReCache[className] = re;
7486                     }
7487                     this.dom.className =
7488                         this.dom.className.replace(re, " ");
7489                 }
7490             }
7491             return this;
7492         },
7493
7494         // private
7495         classReCache: {},
7496
7497         /**
7498          * Toggles the specified CSS class on this element (removes it if it already exists, otherwise adds it).
7499          * @param {String} className The CSS class to toggle
7500          * @return {Roo.Element} this
7501          */
7502         toggleClass : function(className){
7503             if(this.hasClass(className)){
7504                 this.removeClass(className);
7505             }else{
7506                 this.addClass(className);
7507             }
7508             return this;
7509         },
7510
7511         /**
7512          * Checks if the specified CSS class exists on this element's DOM node.
7513          * @param {String} className The CSS class to check for
7514          * @return {Boolean} True if the class exists, else false
7515          */
7516         hasClass : function(className){
7517             return className && (' '+this.dom.className+' ').indexOf(' '+className+' ') != -1;
7518         },
7519
7520         /**
7521          * Replaces a CSS class on the element with another.  If the old name does not exist, the new name will simply be added.
7522          * @param {String} oldClassName The CSS class to replace
7523          * @param {String} newClassName The replacement CSS class
7524          * @return {Roo.Element} this
7525          */
7526         replaceClass : function(oldClassName, newClassName){
7527             this.removeClass(oldClassName);
7528             this.addClass(newClassName);
7529             return this;
7530         },
7531
7532         /**
7533          * Returns an object with properties matching the styles requested.
7534          * For example, el.getStyles('color', 'font-size', 'width') might return
7535          * {'color': '#FFFFFF', 'font-size': '13px', 'width': '100px'}.
7536          * @param {String} style1 A style name
7537          * @param {String} style2 A style name
7538          * @param {String} etc.
7539          * @return {Object} The style object
7540          */
7541         getStyles : function(){
7542             var a = arguments, len = a.length, r = {};
7543             for(var i = 0; i < len; i++){
7544                 r[a[i]] = this.getStyle(a[i]);
7545             }
7546             return r;
7547         },
7548
7549         /**
7550          * Normalizes currentStyle and computedStyle. This is not YUI getStyle, it is an optimised version.
7551          * @param {String} property The style property whose value is returned.
7552          * @return {String} The current value of the style property for this element.
7553          */
7554         getStyle : function(){
7555             return view && view.getComputedStyle ?
7556                 function(prop){
7557                     var el = this.dom, v, cs, camel;
7558                     if(prop == 'float'){
7559                         prop = "cssFloat";
7560                     }
7561                     if(el.style && (v = el.style[prop])){
7562                         return v;
7563                     }
7564                     if(cs = view.getComputedStyle(el, "")){
7565                         if(!(camel = propCache[prop])){
7566                             camel = propCache[prop] = prop.replace(camelRe, camelFn);
7567                         }
7568                         return cs[camel];
7569                     }
7570                     return null;
7571                 } :
7572                 function(prop){
7573                     var el = this.dom, v, cs, camel;
7574                     if(prop == 'opacity'){
7575                         if(typeof el.style.filter == 'string'){
7576                             var m = el.style.filter.match(/alpha\(opacity=(.*)\)/i);
7577                             if(m){
7578                                 var fv = parseFloat(m[1]);
7579                                 if(!isNaN(fv)){
7580                                     return fv ? fv / 100 : 0;
7581                                 }
7582                             }
7583                         }
7584                         return 1;
7585                     }else if(prop == 'float'){
7586                         prop = "styleFloat";
7587                     }
7588                     if(!(camel = propCache[prop])){
7589                         camel = propCache[prop] = prop.replace(camelRe, camelFn);
7590                     }
7591                     if(v = el.style[camel]){
7592                         return v;
7593                     }
7594                     if(cs = el.currentStyle){
7595                         return cs[camel];
7596                     }
7597                     return null;
7598                 };
7599         }(),
7600
7601         /**
7602          * Wrapper for setting style properties, also takes single object parameter of multiple styles.
7603          * @param {String/Object} property The style property to be set, or an object of multiple styles.
7604          * @param {String} value (optional) The value to apply to the given property, or null if an object was passed.
7605          * @return {Roo.Element} this
7606          */
7607         setStyle : function(prop, value){
7608             if(typeof prop == "string"){
7609                 
7610                 if (prop == 'float') {
7611                     this.setStyle(Roo.isIE ? 'styleFloat'  : 'cssFloat', value);
7612                     return this;
7613                 }
7614                 
7615                 var camel;
7616                 if(!(camel = propCache[prop])){
7617                     camel = propCache[prop] = prop.replace(camelRe, camelFn);
7618                 }
7619                 
7620                 if(camel == 'opacity') {
7621                     this.setOpacity(value);
7622                 }else{
7623                     this.dom.style[camel] = value;
7624                 }
7625             }else{
7626                 for(var style in prop){
7627                     if(typeof prop[style] != "function"){
7628                        this.setStyle(style, prop[style]);
7629                     }
7630                 }
7631             }
7632             return this;
7633         },
7634
7635         /**
7636          * More flexible version of {@link #setStyle} for setting style properties.
7637          * @param {String/Object/Function} styles A style specification string, e.g. "width:100px", or object in the form {width:"100px"}, or
7638          * a function which returns such a specification.
7639          * @return {Roo.Element} this
7640          */
7641         applyStyles : function(style){
7642             Roo.DomHelper.applyStyles(this.dom, style);
7643             return this;
7644         },
7645
7646         /**
7647           * 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).
7648           * @return {Number} The X position of the element
7649           */
7650         getX : function(){
7651             return D.getX(this.dom);
7652         },
7653
7654         /**
7655           * 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).
7656           * @return {Number} The Y position of the element
7657           */
7658         getY : function(){
7659             return D.getY(this.dom);
7660         },
7661
7662         /**
7663           * 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).
7664           * @return {Array} The XY position of the element
7665           */
7666         getXY : function(){
7667             return D.getXY(this.dom);
7668         },
7669
7670         /**
7671          * 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).
7672          * @param {Number} The X position of the element
7673          * @param {Boolean/Object} animate (optional) True for the default animation, or a standard Element animation config object
7674          * @return {Roo.Element} this
7675          */
7676         setX : function(x, animate){
7677             if(!animate || !A){
7678                 D.setX(this.dom, x);
7679             }else{
7680                 this.setXY([x, this.getY()], this.preanim(arguments, 1));
7681             }
7682             return this;
7683         },
7684
7685         /**
7686          * 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).
7687          * @param {Number} The Y position of the element
7688          * @param {Boolean/Object} animate (optional) True for the default animation, or a standard Element animation config object
7689          * @return {Roo.Element} this
7690          */
7691         setY : function(y, animate){
7692             if(!animate || !A){
7693                 D.setY(this.dom, y);
7694             }else{
7695                 this.setXY([this.getX(), y], this.preanim(arguments, 1));
7696             }
7697             return this;
7698         },
7699
7700         /**
7701          * Sets the element's left position directly using CSS style (instead of {@link #setX}).
7702          * @param {String} left The left CSS property value
7703          * @return {Roo.Element} this
7704          */
7705         setLeft : function(left){
7706             this.setStyle("left", this.addUnits(left));
7707             return this;
7708         },
7709
7710         /**
7711          * Sets the element's top position directly using CSS style (instead of {@link #setY}).
7712          * @param {String} top The top CSS property value
7713          * @return {Roo.Element} this
7714          */
7715         setTop : function(top){
7716             this.setStyle("top", this.addUnits(top));
7717             return this;
7718         },
7719
7720         /**
7721          * Sets the element's CSS right style.
7722          * @param {String} right The right CSS property value
7723          * @return {Roo.Element} this
7724          */
7725         setRight : function(right){
7726             this.setStyle("right", this.addUnits(right));
7727             return this;
7728         },
7729
7730         /**
7731          * Sets the element's CSS bottom style.
7732          * @param {String} bottom The bottom CSS property value
7733          * @return {Roo.Element} this
7734          */
7735         setBottom : function(bottom){
7736             this.setStyle("bottom", this.addUnits(bottom));
7737             return this;
7738         },
7739
7740         /**
7741          * Sets the position of the element in page coordinates, regardless of how the element is positioned.
7742          * The element must be part of the DOM tree to have page coordinates (display:none or elements not appended return false).
7743          * @param {Array} pos Contains X & Y [x, y] values for new position (coordinates are page-based)
7744          * @param {Boolean/Object} animate (optional) True for the default animation, or a standard Element animation config object
7745          * @return {Roo.Element} this
7746          */
7747         setXY : function(pos, animate){
7748             if(!animate || !A){
7749                 D.setXY(this.dom, pos);
7750             }else{
7751                 this.anim({points: {to: pos}}, this.preanim(arguments, 1), 'motion');
7752             }
7753             return this;
7754         },
7755
7756         /**
7757          * Sets the position of the element in page coordinates, regardless of how the element is positioned.
7758          * The element must be part of the DOM tree to have page coordinates (display:none or elements not appended return false).
7759          * @param {Number} x X value for new position (coordinates are page-based)
7760          * @param {Number} y Y value for new position (coordinates are page-based)
7761          * @param {Boolean/Object} animate (optional) True for the default animation, or a standard Element animation config object
7762          * @return {Roo.Element} this
7763          */
7764         setLocation : function(x, y, animate){
7765             this.setXY([x, y], this.preanim(arguments, 2));
7766             return this;
7767         },
7768
7769         /**
7770          * Sets the position of the element in page coordinates, regardless of how the element is positioned.
7771          * The element must be part of the DOM tree to have page coordinates (display:none or elements not appended return false).
7772          * @param {Number} x X value for new position (coordinates are page-based)
7773          * @param {Number} y Y value for new position (coordinates are page-based)
7774          * @param {Boolean/Object} animate (optional) True for the default animation, or a standard Element animation config object
7775          * @return {Roo.Element} this
7776          */
7777         moveTo : function(x, y, animate){
7778             this.setXY([x, y], this.preanim(arguments, 2));
7779             return this;
7780         },
7781
7782         /**
7783          * Returns the region of the given element.
7784          * The element must be part of the DOM tree to have a region (display:none or elements not appended return false).
7785          * @return {Region} A Roo.lib.Region containing "top, left, bottom, right" member data.
7786          */
7787         getRegion : function(){
7788             return D.getRegion(this.dom);
7789         },
7790
7791         /**
7792          * Returns the offset height of the element
7793          * @param {Boolean} contentHeight (optional) true to get the height minus borders and padding
7794          * @return {Number} The element's height
7795          */
7796         getHeight : function(contentHeight){
7797             var h = this.dom.offsetHeight || 0;
7798             return contentHeight !== true ? h : h-this.getBorderWidth("tb")-this.getPadding("tb");
7799         },
7800
7801         /**
7802          * Returns the offset width of the element
7803          * @param {Boolean} contentWidth (optional) true to get the width minus borders and padding
7804          * @return {Number} The element's width
7805          */
7806         getWidth : function(contentWidth){
7807             var w = this.dom.offsetWidth || 0;
7808             return contentWidth !== true ? w : w-this.getBorderWidth("lr")-this.getPadding("lr");
7809         },
7810
7811         /**
7812          * Returns either the offsetHeight or the height of this element based on CSS height adjusted by padding or borders
7813          * when needed to simulate offsetHeight when offsets aren't available. This may not work on display:none elements
7814          * if a height has not been set using CSS.
7815          * @return {Number}
7816          */
7817         getComputedHeight : function(){
7818             var h = Math.max(this.dom.offsetHeight, this.dom.clientHeight);
7819             if(!h){
7820                 h = parseInt(this.getStyle('height'), 10) || 0;
7821                 if(!this.isBorderBox()){
7822                     h += this.getFrameWidth('tb');
7823                 }
7824             }
7825             return h;
7826         },
7827
7828         /**
7829          * Returns either the offsetWidth or the width of this element based on CSS width adjusted by padding or borders
7830          * when needed to simulate offsetWidth when offsets aren't available. This may not work on display:none elements
7831          * if a width has not been set using CSS.
7832          * @return {Number}
7833          */
7834         getComputedWidth : function(){
7835             var w = Math.max(this.dom.offsetWidth, this.dom.clientWidth);
7836             if(!w){
7837                 w = parseInt(this.getStyle('width'), 10) || 0;
7838                 if(!this.isBorderBox()){
7839                     w += this.getFrameWidth('lr');
7840                 }
7841             }
7842             return w;
7843         },
7844
7845         /**
7846          * Returns the size of the element.
7847          * @param {Boolean} contentSize (optional) true to get the width/size minus borders and padding
7848          * @return {Object} An object containing the element's size {width: (element width), height: (element height)}
7849          */
7850         getSize : function(contentSize){
7851             return {width: this.getWidth(contentSize), height: this.getHeight(contentSize)};
7852         },
7853
7854         /**
7855          * Returns the width and height of the viewport.
7856          * @return {Object} An object containing the viewport's size {width: (viewport width), height: (viewport height)}
7857          */
7858         getViewSize : function(){
7859             var d = this.dom, doc = document, aw = 0, ah = 0;
7860             if(d == doc || d == doc.body){
7861                 return {width : D.getViewWidth(), height: D.getViewHeight()};
7862             }else{
7863                 return {
7864                     width : d.clientWidth,
7865                     height: d.clientHeight
7866                 };
7867             }
7868         },
7869
7870         /**
7871          * Returns the value of the "value" attribute
7872          * @param {Boolean} asNumber true to parse the value as a number
7873          * @return {String/Number}
7874          */
7875         getValue : function(asNumber){
7876             return asNumber ? parseInt(this.dom.value, 10) : this.dom.value;
7877         },
7878
7879         // private
7880         adjustWidth : function(width){
7881             if(typeof width == "number"){
7882                 if(this.autoBoxAdjust && !this.isBorderBox()){
7883                    width -= (this.getBorderWidth("lr") + this.getPadding("lr"));
7884                 }
7885                 if(width < 0){
7886                     width = 0;
7887                 }
7888             }
7889             return width;
7890         },
7891
7892         // private
7893         adjustHeight : function(height){
7894             if(typeof height == "number"){
7895                if(this.autoBoxAdjust && !this.isBorderBox()){
7896                    height -= (this.getBorderWidth("tb") + this.getPadding("tb"));
7897                }
7898                if(height < 0){
7899                    height = 0;
7900                }
7901             }
7902             return height;
7903         },
7904
7905         /**
7906          * Set the width of the element
7907          * @param {Number} width The new width
7908          * @param {Boolean/Object} animate (optional) true for the default animation or a standard Element animation config object
7909          * @return {Roo.Element} this
7910          */
7911         setWidth : function(width, animate){
7912             width = this.adjustWidth(width);
7913             if(!animate || !A){
7914                 this.dom.style.width = this.addUnits(width);
7915             }else{
7916                 this.anim({width: {to: width}}, this.preanim(arguments, 1));
7917             }
7918             return this;
7919         },
7920
7921         /**
7922          * Set the height of the element
7923          * @param {Number} height The new height
7924          * @param {Boolean/Object} animate (optional) true for the default animation or a standard Element animation config object
7925          * @return {Roo.Element} this
7926          */
7927          setHeight : function(height, animate){
7928             height = this.adjustHeight(height);
7929             if(!animate || !A){
7930                 this.dom.style.height = this.addUnits(height);
7931             }else{
7932                 this.anim({height: {to: height}}, this.preanim(arguments, 1));
7933             }
7934             return this;
7935         },
7936
7937         /**
7938          * Set the size of the element. If animation is true, both width an height will be animated concurrently.
7939          * @param {Number} width The new width
7940          * @param {Number} height The new height
7941          * @param {Boolean/Object} animate (optional) true for the default animation or a standard Element animation config object
7942          * @return {Roo.Element} this
7943          */
7944          setSize : function(width, height, animate){
7945             if(typeof width == "object"){ // in case of object from getSize()
7946                 height = width.height; width = width.width;
7947             }
7948             width = this.adjustWidth(width); height = this.adjustHeight(height);
7949             if(!animate || !A){
7950                 this.dom.style.width = this.addUnits(width);
7951                 this.dom.style.height = this.addUnits(height);
7952             }else{
7953                 this.anim({width: {to: width}, height: {to: height}}, this.preanim(arguments, 2));
7954             }
7955             return this;
7956         },
7957
7958         /**
7959          * Sets the element's position and size in one shot. If animation is true then width, height, x and y will be animated concurrently.
7960          * @param {Number} x X value for new position (coordinates are page-based)
7961          * @param {Number} y Y value for new position (coordinates are page-based)
7962          * @param {Number} width The new width
7963          * @param {Number} height The new height
7964          * @param {Boolean/Object} animate (optional) true for the default animation or a standard Element animation config object
7965          * @return {Roo.Element} this
7966          */
7967         setBounds : function(x, y, width, height, animate){
7968             if(!animate || !A){
7969                 this.setSize(width, height);
7970                 this.setLocation(x, y);
7971             }else{
7972                 width = this.adjustWidth(width); height = this.adjustHeight(height);
7973                 this.anim({points: {to: [x, y]}, width: {to: width}, height: {to: height}},
7974                               this.preanim(arguments, 4), 'motion');
7975             }
7976             return this;
7977         },
7978
7979         /**
7980          * 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.
7981          * @param {Roo.lib.Region} region The region to fill
7982          * @param {Boolean/Object} animate (optional) true for the default animation or a standard Element animation config object
7983          * @return {Roo.Element} this
7984          */
7985         setRegion : function(region, animate){
7986             this.setBounds(region.left, region.top, region.right-region.left, region.bottom-region.top, this.preanim(arguments, 1));
7987             return this;
7988         },
7989
7990         /**
7991          * Appends an event handler
7992          *
7993          * @param {String}   eventName     The type of event to append
7994          * @param {Function} fn        The method the event invokes
7995          * @param {Object} scope       (optional) The scope (this object) of the fn
7996          * @param {Object}   options   (optional)An object with standard {@link Roo.EventManager#addListener} options
7997          */
7998         addListener : function(eventName, fn, scope, options){
7999             if (this.dom) {
8000                 Roo.EventManager.on(this.dom,  eventName, fn, scope || this, options);
8001             }
8002         },
8003
8004         /**
8005          * Removes an event handler from this element
8006          * @param {String} eventName the type of event to remove
8007          * @param {Function} fn the method the event invokes
8008          * @return {Roo.Element} this
8009          */
8010         removeListener : function(eventName, fn){
8011             Roo.EventManager.removeListener(this.dom,  eventName, fn);
8012             return this;
8013         },
8014
8015         /**
8016          * Removes all previous added listeners from this element
8017          * @return {Roo.Element} this
8018          */
8019         removeAllListeners : function(){
8020             E.purgeElement(this.dom);
8021             return this;
8022         },
8023
8024         relayEvent : function(eventName, observable){
8025             this.on(eventName, function(e){
8026                 observable.fireEvent(eventName, e);
8027             });
8028         },
8029
8030         /**
8031          * Set the opacity of the element
8032          * @param {Float} opacity The new opacity. 0 = transparent, .5 = 50% visibile, 1 = fully visible, etc
8033          * @param {Boolean/Object} animate (optional) true for the default animation or a standard Element animation config object
8034          * @return {Roo.Element} this
8035          */
8036          setOpacity : function(opacity, animate){
8037             if(!animate || !A){
8038                 var s = this.dom.style;
8039                 if(Roo.isIE){
8040                     s.zoom = 1;
8041                     s.filter = (s.filter || '').replace(/alpha\([^\)]*\)/gi,"") +
8042                                (opacity == 1 ? "" : "alpha(opacity=" + opacity * 100 + ")");
8043                 }else{
8044                     s.opacity = opacity;
8045                 }
8046             }else{
8047                 this.anim({opacity: {to: opacity}}, this.preanim(arguments, 1), null, .35, 'easeIn');
8048             }
8049             return this;
8050         },
8051
8052         /**
8053          * Gets the left X coordinate
8054          * @param {Boolean} local True to get the local css position instead of page coordinate
8055          * @return {Number}
8056          */
8057         getLeft : function(local){
8058             if(!local){
8059                 return this.getX();
8060             }else{
8061                 return parseInt(this.getStyle("left"), 10) || 0;
8062             }
8063         },
8064
8065         /**
8066          * Gets the right X coordinate of the element (element X position + element width)
8067          * @param {Boolean} local True to get the local css position instead of page coordinate
8068          * @return {Number}
8069          */
8070         getRight : function(local){
8071             if(!local){
8072                 return this.getX() + this.getWidth();
8073             }else{
8074                 return (this.getLeft(true) + this.getWidth()) || 0;
8075             }
8076         },
8077
8078         /**
8079          * Gets the top Y coordinate
8080          * @param {Boolean} local True to get the local css position instead of page coordinate
8081          * @return {Number}
8082          */
8083         getTop : function(local) {
8084             if(!local){
8085                 return this.getY();
8086             }else{
8087                 return parseInt(this.getStyle("top"), 10) || 0;
8088             }
8089         },
8090
8091         /**
8092          * Gets the bottom Y coordinate of the element (element Y position + element height)
8093          * @param {Boolean} local True to get the local css position instead of page coordinate
8094          * @return {Number}
8095          */
8096         getBottom : function(local){
8097             if(!local){
8098                 return this.getY() + this.getHeight();
8099             }else{
8100                 return (this.getTop(true) + this.getHeight()) || 0;
8101             }
8102         },
8103
8104         /**
8105         * Initializes positioning on this element. If a desired position is not passed, it will make the
8106         * the element positioned relative IF it is not already positioned.
8107         * @param {String} pos (optional) Positioning to use "relative", "absolute" or "fixed"
8108         * @param {Number} zIndex (optional) The zIndex to apply
8109         * @param {Number} x (optional) Set the page X position
8110         * @param {Number} y (optional) Set the page Y position
8111         */
8112         position : function(pos, zIndex, x, y){
8113             if(!pos){
8114                if(this.getStyle('position') == 'static'){
8115                    this.setStyle('position', 'relative');
8116                }
8117             }else{
8118                 this.setStyle("position", pos);
8119             }
8120             if(zIndex){
8121                 this.setStyle("z-index", zIndex);
8122             }
8123             if(x !== undefined && y !== undefined){
8124                 this.setXY([x, y]);
8125             }else if(x !== undefined){
8126                 this.setX(x);
8127             }else if(y !== undefined){
8128                 this.setY(y);
8129             }
8130         },
8131
8132         /**
8133         * Clear positioning back to the default when the document was loaded
8134         * @param {String} value (optional) The value to use for the left,right,top,bottom, defaults to '' (empty string). You could use 'auto'.
8135         * @return {Roo.Element} this
8136          */
8137         clearPositioning : function(value){
8138             value = value ||'';
8139             this.setStyle({
8140                 "left": value,
8141                 "right": value,
8142                 "top": value,
8143                 "bottom": value,
8144                 "z-index": "",
8145                 "position" : "static"
8146             });
8147             return this;
8148         },
8149
8150         /**
8151         * Gets an object with all CSS positioning properties. Useful along with setPostioning to get
8152         * snapshot before performing an update and then restoring the element.
8153         * @return {Object}
8154         */
8155         getPositioning : function(){
8156             var l = this.getStyle("left");
8157             var t = this.getStyle("top");
8158             return {
8159                 "position" : this.getStyle("position"),
8160                 "left" : l,
8161                 "right" : l ? "" : this.getStyle("right"),
8162                 "top" : t,
8163                 "bottom" : t ? "" : this.getStyle("bottom"),
8164                 "z-index" : this.getStyle("z-index")
8165             };
8166         },
8167
8168         /**
8169          * Gets the width of the border(s) for the specified side(s)
8170          * @param {String} side Can be t, l, r, b or any combination of those to add multiple values. For example,
8171          * passing lr would get the border (l)eft width + the border (r)ight width.
8172          * @return {Number} The width of the sides passed added together
8173          */
8174         getBorderWidth : function(side){
8175             return this.addStyles(side, El.borders);
8176         },
8177
8178         /**
8179          * Gets the width of the padding(s) for the specified side(s)
8180          * @param {String} side Can be t, l, r, b or any combination of those to add multiple values. For example,
8181          * passing lr would get the padding (l)eft + the padding (r)ight.
8182          * @return {Number} The padding of the sides passed added together
8183          */
8184         getPadding : function(side){
8185             return this.addStyles(side, El.paddings);
8186         },
8187
8188         /**
8189         * Set positioning with an object returned by getPositioning().
8190         * @param {Object} posCfg
8191         * @return {Roo.Element} this
8192          */
8193         setPositioning : function(pc){
8194             this.applyStyles(pc);
8195             if(pc.right == "auto"){
8196                 this.dom.style.right = "";
8197             }
8198             if(pc.bottom == "auto"){
8199                 this.dom.style.bottom = "";
8200             }
8201             return this;
8202         },
8203
8204         // private
8205         fixDisplay : function(){
8206             if(this.getStyle("display") == "none"){
8207                 this.setStyle("visibility", "hidden");
8208                 this.setStyle("display", this.originalDisplay); // first try reverting to default
8209                 if(this.getStyle("display") == "none"){ // if that fails, default to block
8210                     this.setStyle("display", "block");
8211                 }
8212             }
8213         },
8214
8215         /**
8216          * Quick set left and top adding default units
8217          * @param {String} left The left CSS property value
8218          * @param {String} top The top CSS property value
8219          * @return {Roo.Element} this
8220          */
8221          setLeftTop : function(left, top){
8222             this.dom.style.left = this.addUnits(left);
8223             this.dom.style.top = this.addUnits(top);
8224             return this;
8225         },
8226
8227         /**
8228          * Move this element relative to its current position.
8229          * @param {String} direction Possible values are: "l","left" - "r","right" - "t","top","up" - "b","bottom","down".
8230          * @param {Number} distance How far to move the element in pixels
8231          * @param {Boolean/Object} animate (optional) true for the default animation or a standard Element animation config object
8232          * @return {Roo.Element} this
8233          */
8234          move : function(direction, distance, animate){
8235             var xy = this.getXY();
8236             direction = direction.toLowerCase();
8237             switch(direction){
8238                 case "l":
8239                 case "left":
8240                     this.moveTo(xy[0]-distance, xy[1], this.preanim(arguments, 2));
8241                     break;
8242                case "r":
8243                case "right":
8244                     this.moveTo(xy[0]+distance, xy[1], this.preanim(arguments, 2));
8245                     break;
8246                case "t":
8247                case "top":
8248                case "up":
8249                     this.moveTo(xy[0], xy[1]-distance, this.preanim(arguments, 2));
8250                     break;
8251                case "b":
8252                case "bottom":
8253                case "down":
8254                     this.moveTo(xy[0], xy[1]+distance, this.preanim(arguments, 2));
8255                     break;
8256             }
8257             return this;
8258         },
8259
8260         /**
8261          *  Store the current overflow setting and clip overflow on the element - use {@link #unclip} to remove
8262          * @return {Roo.Element} this
8263          */
8264         clip : function(){
8265             if(!this.isClipped){
8266                this.isClipped = true;
8267                this.originalClip = {
8268                    "o": this.getStyle("overflow"),
8269                    "x": this.getStyle("overflow-x"),
8270                    "y": this.getStyle("overflow-y")
8271                };
8272                this.setStyle("overflow", "hidden");
8273                this.setStyle("overflow-x", "hidden");
8274                this.setStyle("overflow-y", "hidden");
8275             }
8276             return this;
8277         },
8278
8279         /**
8280          *  Return clipping (overflow) to original clipping before clip() was called
8281          * @return {Roo.Element} this
8282          */
8283         unclip : function(){
8284             if(this.isClipped){
8285                 this.isClipped = false;
8286                 var o = this.originalClip;
8287                 if(o.o){this.setStyle("overflow", o.o);}
8288                 if(o.x){this.setStyle("overflow-x", o.x);}
8289                 if(o.y){this.setStyle("overflow-y", o.y);}
8290             }
8291             return this;
8292         },
8293
8294
8295         /**
8296          * Gets the x,y coordinates specified by the anchor position on the element.
8297          * @param {String} anchor (optional) The specified anchor position (defaults to "c").  See {@link #alignTo} for details on supported anchor positions.
8298          * @param {Object} size (optional) An object containing the size to use for calculating anchor position
8299          *                       {width: (target width), height: (target height)} (defaults to the element's current size)
8300          * @param {Boolean} local (optional) True to get the local (element top/left-relative) anchor position instead of page coordinates
8301          * @return {Array} [x, y] An array containing the element's x and y coordinates
8302          */
8303         getAnchorXY : function(anchor, local, s){
8304             //Passing a different size is useful for pre-calculating anchors,
8305             //especially for anchored animations that change the el size.
8306
8307             var w, h, vp = false;
8308             if(!s){
8309                 var d = this.dom;
8310                 if(d == document.body || d == document){
8311                     vp = true;
8312                     w = D.getViewWidth(); h = D.getViewHeight();
8313                 }else{
8314                     w = this.getWidth(); h = this.getHeight();
8315                 }
8316             }else{
8317                 w = s.width;  h = s.height;
8318             }
8319             var x = 0, y = 0, r = Math.round;
8320             switch((anchor || "tl").toLowerCase()){
8321                 case "c":
8322                     x = r(w*.5);
8323                     y = r(h*.5);
8324                 break;
8325                 case "t":
8326                     x = r(w*.5);
8327                     y = 0;
8328                 break;
8329                 case "l":
8330                     x = 0;
8331                     y = r(h*.5);
8332                 break;
8333                 case "r":
8334                     x = w;
8335                     y = r(h*.5);
8336                 break;
8337                 case "b":
8338                     x = r(w*.5);
8339                     y = h;
8340                 break;
8341                 case "tl":
8342                     x = 0;
8343                     y = 0;
8344                 break;
8345                 case "bl":
8346                     x = 0;
8347                     y = h;
8348                 break;
8349                 case "br":
8350                     x = w;
8351                     y = h;
8352                 break;
8353                 case "tr":
8354                     x = w;
8355                     y = 0;
8356                 break;
8357             }
8358             if(local === true){
8359                 return [x, y];
8360             }
8361             if(vp){
8362                 var sc = this.getScroll();
8363                 return [x + sc.left, y + sc.top];
8364             }
8365             //Add the element's offset xy
8366             var o = this.getXY();
8367             return [x+o[0], y+o[1]];
8368         },
8369
8370         /**
8371          * Gets the x,y coordinates to align this element with another element. See {@link #alignTo} for more info on the
8372          * supported position values.
8373          * @param {String/HTMLElement/Roo.Element} element The element to align to.
8374          * @param {String} position The position to align to.
8375          * @param {Array} offsets (optional) Offset the positioning by [x, y]
8376          * @return {Array} [x, y]
8377          */
8378         getAlignToXY : function(el, p, o){
8379             el = Roo.get(el);
8380             var d = this.dom;
8381             if(!el.dom){
8382                 throw "Element.alignTo with an element that doesn't exist";
8383             }
8384             var c = false; //constrain to viewport
8385             var p1 = "", p2 = "";
8386             o = o || [0,0];
8387
8388             if(!p){
8389                 p = "tl-bl";
8390             }else if(p == "?"){
8391                 p = "tl-bl?";
8392             }else if(p.indexOf("-") == -1){
8393                 p = "tl-" + p;
8394             }
8395             p = p.toLowerCase();
8396             var m = p.match(/^([a-z]+)-([a-z]+)(\?)?$/);
8397             if(!m){
8398                throw "Element.alignTo with an invalid alignment " + p;
8399             }
8400             p1 = m[1]; p2 = m[2]; c = !!m[3];
8401
8402             //Subtract the aligned el's internal xy from the target's offset xy
8403             //plus custom offset to get the aligned el's new offset xy
8404             var a1 = this.getAnchorXY(p1, true);
8405             var a2 = el.getAnchorXY(p2, false);
8406             var x = a2[0] - a1[0] + o[0];
8407             var y = a2[1] - a1[1] + o[1];
8408             if(c){
8409                 //constrain the aligned el to viewport if necessary
8410                 var w = this.getWidth(), h = this.getHeight(), r = el.getRegion();
8411                 // 5px of margin for ie
8412                 var dw = D.getViewWidth()-5, dh = D.getViewHeight()-5;
8413
8414                 //If we are at a viewport boundary and the aligned el is anchored on a target border that is
8415                 //perpendicular to the vp border, allow the aligned el to slide on that border,
8416                 //otherwise swap the aligned el to the opposite border of the target.
8417                 var p1y = p1.charAt(0), p1x = p1.charAt(p1.length-1);
8418                var p2y = p2.charAt(0), p2x = p2.charAt(p2.length-1);
8419                var swapY = ((p1y=="t" && p2y=="b") || (p1y=="b" && p2y=="t"));
8420                var swapX = ((p1x=="r" && p2x=="l") || (p1x=="l" && p2x=="r"));
8421
8422                var doc = document;
8423                var scrollX = (doc.documentElement.scrollLeft || doc.body.scrollLeft || 0)+5;
8424                var scrollY = (doc.documentElement.scrollTop || doc.body.scrollTop || 0)+5;
8425
8426                if((x+w) > dw + scrollX){
8427                     x = swapX ? r.left-w : dw+scrollX-w;
8428                 }
8429                if(x < scrollX){
8430                    x = swapX ? r.right : scrollX;
8431                }
8432                if((y+h) > dh + scrollY){
8433                     y = swapY ? r.top-h : dh+scrollY-h;
8434                 }
8435                if (y < scrollY){
8436                    y = swapY ? r.bottom : scrollY;
8437                }
8438             }
8439             return [x,y];
8440         },
8441
8442         // private
8443         getConstrainToXY : function(){
8444             var os = {top:0, left:0, bottom:0, right: 0};
8445
8446             return function(el, local, offsets, proposedXY){
8447                 el = Roo.get(el);
8448                 offsets = offsets ? Roo.applyIf(offsets, os) : os;
8449
8450                 var vw, vh, vx = 0, vy = 0;
8451                 if(el.dom == document.body || el.dom == document){
8452                     vw = Roo.lib.Dom.getViewWidth();
8453                     vh = Roo.lib.Dom.getViewHeight();
8454                 }else{
8455                     vw = el.dom.clientWidth;
8456                     vh = el.dom.clientHeight;
8457                     if(!local){
8458                         var vxy = el.getXY();
8459                         vx = vxy[0];
8460                         vy = vxy[1];
8461                     }
8462                 }
8463
8464                 var s = el.getScroll();
8465
8466                 vx += offsets.left + s.left;
8467                 vy += offsets.top + s.top;
8468
8469                 vw -= offsets.right;
8470                 vh -= offsets.bottom;
8471
8472                 var vr = vx+vw;
8473                 var vb = vy+vh;
8474
8475                 var xy = proposedXY || (!local ? this.getXY() : [this.getLeft(true), this.getTop(true)]);
8476                 var x = xy[0], y = xy[1];
8477                 var w = this.dom.offsetWidth, h = this.dom.offsetHeight;
8478
8479                 // only move it if it needs it
8480                 var moved = false;
8481
8482                 // first validate right/bottom
8483                 if((x + w) > vr){
8484                     x = vr - w;
8485                     moved = true;
8486                 }
8487                 if((y + h) > vb){
8488                     y = vb - h;
8489                     moved = true;
8490                 }
8491                 // then make sure top/left isn't negative
8492                 if(x < vx){
8493                     x = vx;
8494                     moved = true;
8495                 }
8496                 if(y < vy){
8497                     y = vy;
8498                     moved = true;
8499                 }
8500                 return moved ? [x, y] : false;
8501             };
8502         }(),
8503
8504         // private
8505         adjustForConstraints : function(xy, parent, offsets){
8506             return this.getConstrainToXY(parent || document, false, offsets, xy) ||  xy;
8507         },
8508
8509         /**
8510          * Aligns this element with another element relative to the specified anchor points. If the other element is the
8511          * document it aligns it to the viewport.
8512          * The position parameter is optional, and can be specified in any one of the following formats:
8513          * <ul>
8514          *   <li><b>Blank</b>: Defaults to aligning the element's top-left corner to the target's bottom-left corner ("tl-bl").</li>
8515          *   <li><b>One anchor (deprecated)</b>: The passed anchor position is used as the target element's anchor point.
8516          *       The element being aligned will position its top-left corner (tl) to that point.  <i>This method has been
8517          *       deprecated in favor of the newer two anchor syntax below</i>.</li>
8518          *   <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
8519          *       element's anchor point, and the second value is used as the target's anchor point.</li>
8520          * </ul>
8521          * In addition to the anchor points, the position parameter also supports the "?" character.  If "?" is passed at the end of
8522          * the position string, the element will attempt to align as specified, but the position will be adjusted to constrain to
8523          * the viewport if necessary.  Note that the element being aligned might be swapped to align to a different position than
8524          * that specified in order to enforce the viewport constraints.
8525          * Following are all of the supported anchor positions:
8526     <pre>
8527     Value  Description
8528     -----  -----------------------------
8529     tl     The top left corner (default)
8530     t      The center of the top edge
8531     tr     The top right corner
8532     l      The center of the left edge
8533     c      In the center of the element
8534     r      The center of the right edge
8535     bl     The bottom left corner
8536     b      The center of the bottom edge
8537     br     The bottom right corner
8538     </pre>
8539     Example Usage:
8540     <pre><code>
8541     // align el to other-el using the default positioning ("tl-bl", non-constrained)
8542     el.alignTo("other-el");
8543
8544     // align the top left corner of el with the top right corner of other-el (constrained to viewport)
8545     el.alignTo("other-el", "tr?");
8546
8547     // align the bottom right corner of el with the center left edge of other-el
8548     el.alignTo("other-el", "br-l?");
8549
8550     // align the center of el with the bottom left corner of other-el and
8551     // adjust the x position by -6 pixels (and the y position by 0)
8552     el.alignTo("other-el", "c-bl", [-6, 0]);
8553     </code></pre>
8554          * @param {String/HTMLElement/Roo.Element} element The element to align to.
8555          * @param {String} position The position to align to.
8556          * @param {Array} offsets (optional) Offset the positioning by [x, y]
8557          * @param {Boolean/Object} animate (optional) true for the default animation or a standard Element animation config object
8558          * @return {Roo.Element} this
8559          */
8560         alignTo : function(element, position, offsets, animate){
8561             var xy = this.getAlignToXY(element, position, offsets);
8562             this.setXY(xy, this.preanim(arguments, 3));
8563             return this;
8564         },
8565
8566         /**
8567          * Anchors an element to another element and realigns it when the window is resized.
8568          * @param {String/HTMLElement/Roo.Element} element The element to align to.
8569          * @param {String} position The position to align to.
8570          * @param {Array} offsets (optional) Offset the positioning by [x, y]
8571          * @param {Boolean/Object} animate (optional) True for the default animation or a standard Element animation config object
8572          * @param {Boolean/Number} monitorScroll (optional) True to monitor body scroll and reposition. If this parameter
8573          * is a number, it is used as the buffer delay (defaults to 50ms).
8574          * @param {Function} callback The function to call after the animation finishes
8575          * @return {Roo.Element} this
8576          */
8577         anchorTo : function(el, alignment, offsets, animate, monitorScroll, callback){
8578             var action = function(){
8579                 this.alignTo(el, alignment, offsets, animate);
8580                 Roo.callback(callback, this);
8581             };
8582             Roo.EventManager.onWindowResize(action, this);
8583             var tm = typeof monitorScroll;
8584             if(tm != 'undefined'){
8585                 Roo.EventManager.on(window, 'scroll', action, this,
8586                     {buffer: tm == 'number' ? monitorScroll : 50});
8587             }
8588             action.call(this); // align immediately
8589             return this;
8590         },
8591         /**
8592          * Clears any opacity settings from this element. Required in some cases for IE.
8593          * @return {Roo.Element} this
8594          */
8595         clearOpacity : function(){
8596             if (window.ActiveXObject) {
8597                 if(typeof this.dom.style.filter == 'string' && (/alpha/i).test(this.dom.style.filter)){
8598                     this.dom.style.filter = "";
8599                 }
8600             } else {
8601                 this.dom.style.opacity = "";
8602                 this.dom.style["-moz-opacity"] = "";
8603                 this.dom.style["-khtml-opacity"] = "";
8604             }
8605             return this;
8606         },
8607
8608         /**
8609          * Hide this element - Uses display mode to determine whether to use "display" or "visibility". See {@link #setVisible}.
8610          * @param {Boolean/Object} animate (optional) true for the default animation or a standard Element animation config object
8611          * @return {Roo.Element} this
8612          */
8613         hide : function(animate){
8614             this.setVisible(false, this.preanim(arguments, 0));
8615             return this;
8616         },
8617
8618         /**
8619         * Show this element - Uses display mode to determine whether to use "display" or "visibility". See {@link #setVisible}.
8620         * @param {Boolean/Object} animate (optional) true for the default animation or a standard Element animation config object
8621          * @return {Roo.Element} this
8622          */
8623         show : function(animate){
8624             this.setVisible(true, this.preanim(arguments, 0));
8625             return this;
8626         },
8627
8628         /**
8629          * @private Test if size has a unit, otherwise appends the default
8630          */
8631         addUnits : function(size){
8632             return Roo.Element.addUnits(size, this.defaultUnit);
8633         },
8634
8635         /**
8636          * Temporarily enables offsets (width,height,x,y) for an element with display:none, use endMeasure() when done.
8637          * @return {Roo.Element} this
8638          */
8639         beginMeasure : function(){
8640             var el = this.dom;
8641             if(el.offsetWidth || el.offsetHeight){
8642                 return this; // offsets work already
8643             }
8644             var changed = [];
8645             var p = this.dom, b = document.body; // start with this element
8646             while((!el.offsetWidth && !el.offsetHeight) && p && p.tagName && p != b){
8647                 var pe = Roo.get(p);
8648                 if(pe.getStyle('display') == 'none'){
8649                     changed.push({el: p, visibility: pe.getStyle("visibility")});
8650                     p.style.visibility = "hidden";
8651                     p.style.display = "block";
8652                 }
8653                 p = p.parentNode;
8654             }
8655             this._measureChanged = changed;
8656             return this;
8657
8658         },
8659
8660         /**
8661          * Restores displays to before beginMeasure was called
8662          * @return {Roo.Element} this
8663          */
8664         endMeasure : function(){
8665             var changed = this._measureChanged;
8666             if(changed){
8667                 for(var i = 0, len = changed.length; i < len; i++) {
8668                     var r = changed[i];
8669                     r.el.style.visibility = r.visibility;
8670                     r.el.style.display = "none";
8671                 }
8672                 this._measureChanged = null;
8673             }
8674             return this;
8675         },
8676
8677         /**
8678         * Update the innerHTML of this element, optionally searching for and processing scripts
8679         * @param {String} html The new HTML
8680         * @param {Boolean} loadScripts (optional) true to look for and process scripts
8681         * @param {Function} callback For async script loading you can be noticed when the update completes
8682         * @return {Roo.Element} this
8683          */
8684         update : function(html, loadScripts, callback){
8685             if(typeof html == "undefined"){
8686                 html = "";
8687             }
8688             if(loadScripts !== true){
8689                 this.dom.innerHTML = html;
8690                 if(typeof callback == "function"){
8691                     callback();
8692                 }
8693                 return this;
8694             }
8695             var id = Roo.id();
8696             var dom = this.dom;
8697
8698             html += '<span id="' + id + '"></span>';
8699
8700             E.onAvailable(id, function(){
8701                 var hd = document.getElementsByTagName("head")[0];
8702                 var re = /(?:<script([^>]*)?>)((\n|\r|.)*?)(?:<\/script>)/ig;
8703                 var srcRe = /\ssrc=([\'\"])(.*?)\1/i;
8704                 var typeRe = /\stype=([\'\"])(.*?)\1/i;
8705
8706                 var match;
8707                 while(match = re.exec(html)){
8708                     var attrs = match[1];
8709                     var srcMatch = attrs ? attrs.match(srcRe) : false;
8710                     if(srcMatch && srcMatch[2]){
8711                        var s = document.createElement("script");
8712                        s.src = srcMatch[2];
8713                        var typeMatch = attrs.match(typeRe);
8714                        if(typeMatch && typeMatch[2]){
8715                            s.type = typeMatch[2];
8716                        }
8717                        hd.appendChild(s);
8718                     }else if(match[2] && match[2].length > 0){
8719                         if(window.execScript) {
8720                            window.execScript(match[2]);
8721                         } else {
8722                             /**
8723                              * eval:var:id
8724                              * eval:var:dom
8725                              * eval:var:html
8726                              * 
8727                              */
8728                            window.eval(match[2]);
8729                         }
8730                     }
8731                 }
8732                 var el = document.getElementById(id);
8733                 if(el){el.parentNode.removeChild(el);}
8734                 if(typeof callback == "function"){
8735                     callback();
8736                 }
8737             });
8738             dom.innerHTML = html.replace(/(?:<script.*?>)((\n|\r|.)*?)(?:<\/script>)/ig, "");
8739             return this;
8740         },
8741
8742         /**
8743          * Direct access to the UpdateManager update() method (takes the same parameters).
8744          * @param {String/Function} url The url for this request or a function to call to get the url
8745          * @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}
8746          * @param {Function} callback (optional) Callback when transaction is complete - called with signature (oElement, bSuccess)
8747          * @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.
8748          * @return {Roo.Element} this
8749          */
8750         load : function(){
8751             var um = this.getUpdateManager();
8752             um.update.apply(um, arguments);
8753             return this;
8754         },
8755
8756         /**
8757         * Gets this element's UpdateManager
8758         * @return {Roo.UpdateManager} The UpdateManager
8759         */
8760         getUpdateManager : function(){
8761             if(!this.updateManager){
8762                 this.updateManager = new Roo.UpdateManager(this);
8763             }
8764             return this.updateManager;
8765         },
8766
8767         /**
8768          * Disables text selection for this element (normalized across browsers)
8769          * @return {Roo.Element} this
8770          */
8771         unselectable : function(){
8772             this.dom.unselectable = "on";
8773             this.swallowEvent("selectstart", true);
8774             this.applyStyles("-moz-user-select:none;-khtml-user-select:none;");
8775             this.addClass("x-unselectable");
8776             return this;
8777         },
8778
8779         /**
8780         * Calculates the x, y to center this element on the screen
8781         * @return {Array} The x, y values [x, y]
8782         */
8783         getCenterXY : function(){
8784             return this.getAlignToXY(document, 'c-c');
8785         },
8786
8787         /**
8788         * Centers the Element in either the viewport, or another Element.
8789         * @param {String/HTMLElement/Roo.Element} centerIn (optional) The element in which to center the element.
8790         */
8791         center : function(centerIn){
8792             this.alignTo(centerIn || document, 'c-c');
8793             return this;
8794         },
8795
8796         /**
8797          * Tests various css rules/browsers to determine if this element uses a border box
8798          * @return {Boolean}
8799          */
8800         isBorderBox : function(){
8801             return noBoxAdjust[this.dom.tagName.toLowerCase()] || Roo.isBorderBox;
8802         },
8803
8804         /**
8805          * Return a box {x, y, width, height} that can be used to set another elements
8806          * size/location to match this element.
8807          * @param {Boolean} contentBox (optional) If true a box for the content of the element is returned.
8808          * @param {Boolean} local (optional) If true the element's left and top are returned instead of page x/y.
8809          * @return {Object} box An object in the format {x, y, width, height}
8810          */
8811         getBox : function(contentBox, local){
8812             var xy;
8813             if(!local){
8814                 xy = this.getXY();
8815             }else{
8816                 var left = parseInt(this.getStyle("left"), 10) || 0;
8817                 var top = parseInt(this.getStyle("top"), 10) || 0;
8818                 xy = [left, top];
8819             }
8820             var el = this.dom, w = el.offsetWidth, h = el.offsetHeight, bx;
8821             if(!contentBox){
8822                 bx = {x: xy[0], y: xy[1], 0: xy[0], 1: xy[1], width: w, height: h};
8823             }else{
8824                 var l = this.getBorderWidth("l")+this.getPadding("l");
8825                 var r = this.getBorderWidth("r")+this.getPadding("r");
8826                 var t = this.getBorderWidth("t")+this.getPadding("t");
8827                 var b = this.getBorderWidth("b")+this.getPadding("b");
8828                 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)};
8829             }
8830             bx.right = bx.x + bx.width;
8831             bx.bottom = bx.y + bx.height;
8832             return bx;
8833         },
8834
8835         /**
8836          * Returns the sum width of the padding and borders for the passed "sides". See getBorderWidth()
8837          for more information about the sides.
8838          * @param {String} sides
8839          * @return {Number}
8840          */
8841         getFrameWidth : function(sides, onlyContentBox){
8842             return onlyContentBox && Roo.isBorderBox ? 0 : (this.getPadding(sides) + this.getBorderWidth(sides));
8843         },
8844
8845         /**
8846          * 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.
8847          * @param {Object} box The box to fill {x, y, width, height}
8848          * @param {Boolean} adjust (optional) Whether to adjust for box-model issues automatically
8849          * @param {Boolean/Object} animate (optional) true for the default animation or a standard Element animation config object
8850          * @return {Roo.Element} this
8851          */
8852         setBox : function(box, adjust, animate){
8853             var w = box.width, h = box.height;
8854             if((adjust && !this.autoBoxAdjust) && !this.isBorderBox()){
8855                w -= (this.getBorderWidth("lr") + this.getPadding("lr"));
8856                h -= (this.getBorderWidth("tb") + this.getPadding("tb"));
8857             }
8858             this.setBounds(box.x, box.y, w, h, this.preanim(arguments, 2));
8859             return this;
8860         },
8861
8862         /**
8863          * Forces the browser to repaint this element
8864          * @return {Roo.Element} this
8865          */
8866          repaint : function(){
8867             var dom = this.dom;
8868             this.addClass("x-repaint");
8869             setTimeout(function(){
8870                 Roo.get(dom).removeClass("x-repaint");
8871             }, 1);
8872             return this;
8873         },
8874
8875         /**
8876          * Returns an object with properties top, left, right and bottom representing the margins of this element unless sides is passed,
8877          * then it returns the calculated width of the sides (see getPadding)
8878          * @param {String} sides (optional) Any combination of l, r, t, b to get the sum of those sides
8879          * @return {Object/Number}
8880          */
8881         getMargins : function(side){
8882             if(!side){
8883                 return {
8884                     top: parseInt(this.getStyle("margin-top"), 10) || 0,
8885                     left: parseInt(this.getStyle("margin-left"), 10) || 0,
8886                     bottom: parseInt(this.getStyle("margin-bottom"), 10) || 0,
8887                     right: parseInt(this.getStyle("margin-right"), 10) || 0
8888                 };
8889             }else{
8890                 return this.addStyles(side, El.margins);
8891              }
8892         },
8893
8894         // private
8895         addStyles : function(sides, styles){
8896             var val = 0, v, w;
8897             for(var i = 0, len = sides.length; i < len; i++){
8898                 v = this.getStyle(styles[sides.charAt(i)]);
8899                 if(v){
8900                      w = parseInt(v, 10);
8901                      if(w){ val += w; }
8902                 }
8903             }
8904             return val;
8905         },
8906
8907         /**
8908          * Creates a proxy element of this element
8909          * @param {String/Object} config The class name of the proxy element or a DomHelper config object
8910          * @param {String/HTMLElement} renderTo (optional) The element or element id to render the proxy to (defaults to document.body)
8911          * @param {Boolean} matchBox (optional) True to align and size the proxy to this element now (defaults to false)
8912          * @return {Roo.Element} The new proxy element
8913          */
8914         createProxy : function(config, renderTo, matchBox){
8915             if(renderTo){
8916                 renderTo = Roo.getDom(renderTo);
8917             }else{
8918                 renderTo = document.body;
8919             }
8920             config = typeof config == "object" ?
8921                 config : {tag : "div", cls: config};
8922             var proxy = Roo.DomHelper.append(renderTo, config, true);
8923             if(matchBox){
8924                proxy.setBox(this.getBox());
8925             }
8926             return proxy;
8927         },
8928
8929         /**
8930          * Puts a mask over this element to disable user interaction. Requires core.css.
8931          * This method can only be applied to elements which accept child nodes.
8932          * @param {String} msg (optional) A message to display in the mask
8933          * @param {String} msgCls (optional) A css class to apply to the msg element
8934          * @return {Element} The mask  element
8935          */
8936         mask : function(msg, msgCls)
8937         {
8938             if(this.getStyle("position") == "static"){
8939                 this.setStyle("position", "relative");
8940             }
8941             if(!this._mask){
8942                 this._mask = Roo.DomHelper.append(this.dom, {cls:"roo-el-mask"}, true);
8943             }
8944             this.addClass("x-masked");
8945             this._mask.setDisplayed(true);
8946             
8947             // we wander
8948             var z = 0;
8949             var dom = this.dom
8950             while (dom && dom.style) {
8951                 if (!isNaN(parseInt(dom.style.zIndex))) {
8952                     z = Math.max(z, parseInt(dom.style.zIndex));
8953                 }
8954                 dom = dom.parentNode;
8955             }
8956             // if we are masking the body - then it hides everything..
8957             if (this.dom == document.body) {
8958                 z = 1000000;
8959                 this._mask.setWidth(Roo.lib.Dom.getDocumentWidth());
8960                 this._mask.setHeight(Roo.lib.Dom.getDocumentHeight());
8961             }
8962            
8963             if(typeof msg == 'string'){
8964                 if(!this._maskMsg){
8965                     this._maskMsg = Roo.DomHelper.append(this.dom, {cls:"roo-el-mask-msg", cn:{tag:'div'}}, true);
8966                 }
8967                 var mm = this._maskMsg;
8968                 mm.dom.className = msgCls ? "roo-el-mask-msg " + msgCls : "roo-el-mask-msg";
8969                 mm.dom.firstChild.innerHTML = msg;
8970                 mm.setDisplayed(true);
8971                 mm.center(this);
8972                 mm.setStyle('z-index', z + 102);
8973             }
8974             if(Roo.isIE && !(Roo.isIE7 && Roo.isStrict) && this.getStyle('height') == 'auto'){ // ie will not expand full height automatically
8975                 this._mask.setHeight(this.getHeight());
8976             }
8977             this._mask.setStyle('z-index', z + 100);
8978             
8979             return this._mask;
8980         },
8981
8982         /**
8983          * Removes a previously applied mask. If removeEl is true the mask overlay is destroyed, otherwise
8984          * it is cached for reuse.
8985          */
8986         unmask : function(removeEl){
8987             if(this._mask){
8988                 if(removeEl === true){
8989                     this._mask.remove();
8990                     delete this._mask;
8991                     if(this._maskMsg){
8992                         this._maskMsg.remove();
8993                         delete this._maskMsg;
8994                     }
8995                 }else{
8996                     this._mask.setDisplayed(false);
8997                     if(this._maskMsg){
8998                         this._maskMsg.setDisplayed(false);
8999                     }
9000                 }
9001             }
9002             this.removeClass("x-masked");
9003         },
9004
9005         /**
9006          * Returns true if this element is masked
9007          * @return {Boolean}
9008          */
9009         isMasked : function(){
9010             return this._mask && this._mask.isVisible();
9011         },
9012
9013         /**
9014          * Creates an iframe shim for this element to keep selects and other windowed objects from
9015          * showing through.
9016          * @return {Roo.Element} The new shim element
9017          */
9018         createShim : function(){
9019             var el = document.createElement('iframe');
9020             el.frameBorder = 'no';
9021             el.className = 'roo-shim';
9022             if(Roo.isIE && Roo.isSecure){
9023                 el.src = Roo.SSL_SECURE_URL;
9024             }
9025             var shim = Roo.get(this.dom.parentNode.insertBefore(el, this.dom));
9026             shim.autoBoxAdjust = false;
9027             return shim;
9028         },
9029
9030         /**
9031          * Removes this element from the DOM and deletes it from the cache
9032          */
9033         remove : function(){
9034             if(this.dom.parentNode){
9035                 this.dom.parentNode.removeChild(this.dom);
9036             }
9037             delete El.cache[this.dom.id];
9038         },
9039
9040         /**
9041          * Sets up event handlers to add and remove a css class when the mouse is over this element
9042          * @param {String} className
9043          * @param {Boolean} preventFlicker (optional) If set to true, it prevents flickering by filtering
9044          * mouseout events for children elements
9045          * @return {Roo.Element} this
9046          */
9047         addClassOnOver : function(className, preventFlicker){
9048             this.on("mouseover", function(){
9049                 Roo.fly(this, '_internal').addClass(className);
9050             }, this.dom);
9051             var removeFn = function(e){
9052                 if(preventFlicker !== true || !e.within(this, true)){
9053                     Roo.fly(this, '_internal').removeClass(className);
9054                 }
9055             };
9056             this.on("mouseout", removeFn, this.dom);
9057             return this;
9058         },
9059
9060         /**
9061          * Sets up event handlers to add and remove a css class when this element has the focus
9062          * @param {String} className
9063          * @return {Roo.Element} this
9064          */
9065         addClassOnFocus : function(className){
9066             this.on("focus", function(){
9067                 Roo.fly(this, '_internal').addClass(className);
9068             }, this.dom);
9069             this.on("blur", function(){
9070                 Roo.fly(this, '_internal').removeClass(className);
9071             }, this.dom);
9072             return this;
9073         },
9074         /**
9075          * 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)
9076          * @param {String} className
9077          * @return {Roo.Element} this
9078          */
9079         addClassOnClick : function(className){
9080             var dom = this.dom;
9081             this.on("mousedown", function(){
9082                 Roo.fly(dom, '_internal').addClass(className);
9083                 var d = Roo.get(document);
9084                 var fn = function(){
9085                     Roo.fly(dom, '_internal').removeClass(className);
9086                     d.removeListener("mouseup", fn);
9087                 };
9088                 d.on("mouseup", fn);
9089             });
9090             return this;
9091         },
9092
9093         /**
9094          * Stops the specified event from bubbling and optionally prevents the default action
9095          * @param {String} eventName
9096          * @param {Boolean} preventDefault (optional) true to prevent the default action too
9097          * @return {Roo.Element} this
9098          */
9099         swallowEvent : function(eventName, preventDefault){
9100             var fn = function(e){
9101                 e.stopPropagation();
9102                 if(preventDefault){
9103                     e.preventDefault();
9104                 }
9105             };
9106             if(eventName instanceof Array){
9107                 for(var i = 0, len = eventName.length; i < len; i++){
9108                      this.on(eventName[i], fn);
9109                 }
9110                 return this;
9111             }
9112             this.on(eventName, fn);
9113             return this;
9114         },
9115
9116         /**
9117          * @private
9118          */
9119       fitToParentDelegate : Roo.emptyFn, // keep a reference to the fitToParent delegate
9120
9121         /**
9122          * Sizes this element to its parent element's dimensions performing
9123          * neccessary box adjustments.
9124          * @param {Boolean} monitorResize (optional) If true maintains the fit when the browser window is resized.
9125          * @param {String/HTMLElment/Element} targetParent (optional) The target parent, default to the parentNode.
9126          * @return {Roo.Element} this
9127          */
9128         fitToParent : function(monitorResize, targetParent) {
9129           Roo.EventManager.removeResizeListener(this.fitToParentDelegate); // always remove previous fitToParent delegate from onWindowResize
9130           this.fitToParentDelegate = Roo.emptyFn; // remove reference to previous delegate
9131           if (monitorResize === true && !this.dom.parentNode) { // check if this Element still exists
9132             return;
9133           }
9134           var p = Roo.get(targetParent || this.dom.parentNode);
9135           this.setSize(p.getComputedWidth() - p.getFrameWidth('lr'), p.getComputedHeight() - p.getFrameWidth('tb'));
9136           if (monitorResize === true) {
9137             this.fitToParentDelegate = this.fitToParent.createDelegate(this, [true, targetParent]);
9138             Roo.EventManager.onWindowResize(this.fitToParentDelegate);
9139           }
9140           return this;
9141         },
9142
9143         /**
9144          * Gets the next sibling, skipping text nodes
9145          * @return {HTMLElement} The next sibling or null
9146          */
9147         getNextSibling : function(){
9148             var n = this.dom.nextSibling;
9149             while(n && n.nodeType != 1){
9150                 n = n.nextSibling;
9151             }
9152             return n;
9153         },
9154
9155         /**
9156          * Gets the previous sibling, skipping text nodes
9157          * @return {HTMLElement} The previous sibling or null
9158          */
9159         getPrevSibling : function(){
9160             var n = this.dom.previousSibling;
9161             while(n && n.nodeType != 1){
9162                 n = n.previousSibling;
9163             }
9164             return n;
9165         },
9166
9167
9168         /**
9169          * Appends the passed element(s) to this element
9170          * @param {String/HTMLElement/Array/Element/CompositeElement} el
9171          * @return {Roo.Element} this
9172          */
9173         appendChild: function(el){
9174             el = Roo.get(el);
9175             el.appendTo(this);
9176             return this;
9177         },
9178
9179         /**
9180          * Creates the passed DomHelper config and appends it to this element or optionally inserts it before the passed child element.
9181          * @param {Object} config DomHelper element config object.  If no tag is specified (e.g., {tag:'input'}) then a div will be
9182          * automatically generated with the specified attributes.
9183          * @param {HTMLElement} insertBefore (optional) a child element of this element
9184          * @param {Boolean} returnDom (optional) true to return the dom node instead of creating an Element
9185          * @return {Roo.Element} The new child element
9186          */
9187         createChild: function(config, insertBefore, returnDom){
9188             config = config || {tag:'div'};
9189             if(insertBefore){
9190                 return Roo.DomHelper.insertBefore(insertBefore, config, returnDom !== true);
9191             }
9192             return Roo.DomHelper[!this.dom.firstChild ? 'overwrite' : 'append'](this.dom, config,  returnDom !== true);
9193         },
9194
9195         /**
9196          * Appends this element to the passed element
9197          * @param {String/HTMLElement/Element} el The new parent element
9198          * @return {Roo.Element} this
9199          */
9200         appendTo: function(el){
9201             el = Roo.getDom(el);
9202             el.appendChild(this.dom);
9203             return this;
9204         },
9205
9206         /**
9207          * Inserts this element before the passed element in the DOM
9208          * @param {String/HTMLElement/Element} el The element to insert before
9209          * @return {Roo.Element} this
9210          */
9211         insertBefore: function(el){
9212             el = Roo.getDom(el);
9213             el.parentNode.insertBefore(this.dom, el);
9214             return this;
9215         },
9216
9217         /**
9218          * Inserts this element after the passed element in the DOM
9219          * @param {String/HTMLElement/Element} el The element to insert after
9220          * @return {Roo.Element} this
9221          */
9222         insertAfter: function(el){
9223             el = Roo.getDom(el);
9224             el.parentNode.insertBefore(this.dom, el.nextSibling);
9225             return this;
9226         },
9227
9228         /**
9229          * Inserts (or creates) an element (or DomHelper config) as the first child of the this element
9230          * @param {String/HTMLElement/Element/Object} el The id or element to insert or a DomHelper config to create and insert
9231          * @return {Roo.Element} The new child
9232          */
9233         insertFirst: function(el, returnDom){
9234             el = el || {};
9235             if(typeof el == 'object' && !el.nodeType){ // dh config
9236                 return this.createChild(el, this.dom.firstChild, returnDom);
9237             }else{
9238                 el = Roo.getDom(el);
9239                 this.dom.insertBefore(el, this.dom.firstChild);
9240                 return !returnDom ? Roo.get(el) : el;
9241             }
9242         },
9243
9244         /**
9245          * Inserts (or creates) the passed element (or DomHelper config) as a sibling of this element
9246          * @param {String/HTMLElement/Element/Object} el The id or element to insert or a DomHelper config to create and insert
9247          * @param {String} where (optional) 'before' or 'after' defaults to before
9248          * @param {Boolean} returnDom (optional) True to return the raw DOM element instead of Roo.Element
9249          * @return {Roo.Element} the inserted Element
9250          */
9251         insertSibling: function(el, where, returnDom){
9252             where = where ? where.toLowerCase() : 'before';
9253             el = el || {};
9254             var rt, refNode = where == 'before' ? this.dom : this.dom.nextSibling;
9255
9256             if(typeof el == 'object' && !el.nodeType){ // dh config
9257                 if(where == 'after' && !this.dom.nextSibling){
9258                     rt = Roo.DomHelper.append(this.dom.parentNode, el, !returnDom);
9259                 }else{
9260                     rt = Roo.DomHelper[where == 'after' ? 'insertAfter' : 'insertBefore'](this.dom, el, !returnDom);
9261                 }
9262
9263             }else{
9264                 rt = this.dom.parentNode.insertBefore(Roo.getDom(el),
9265                             where == 'before' ? this.dom : this.dom.nextSibling);
9266                 if(!returnDom){
9267                     rt = Roo.get(rt);
9268                 }
9269             }
9270             return rt;
9271         },
9272
9273         /**
9274          * Creates and wraps this element with another element
9275          * @param {Object} config (optional) DomHelper element config object for the wrapper element or null for an empty div
9276          * @param {Boolean} returnDom (optional) True to return the raw DOM element instead of Roo.Element
9277          * @return {HTMLElement/Element} The newly created wrapper element
9278          */
9279         wrap: function(config, returnDom){
9280             if(!config){
9281                 config = {tag: "div"};
9282             }
9283             var newEl = Roo.DomHelper.insertBefore(this.dom, config, !returnDom);
9284             newEl.dom ? newEl.dom.appendChild(this.dom) : newEl.appendChild(this.dom);
9285             return newEl;
9286         },
9287
9288         /**
9289          * Replaces the passed element with this element
9290          * @param {String/HTMLElement/Element} el The element to replace
9291          * @return {Roo.Element} this
9292          */
9293         replace: function(el){
9294             el = Roo.get(el);
9295             this.insertBefore(el);
9296             el.remove();
9297             return this;
9298         },
9299
9300         /**
9301          * Inserts an html fragment into this element
9302          * @param {String} where Where to insert the html in relation to the this element - beforeBegin, afterBegin, beforeEnd, afterEnd.
9303          * @param {String} html The HTML fragment
9304          * @param {Boolean} returnEl True to return an Roo.Element
9305          * @return {HTMLElement/Roo.Element} The inserted node (or nearest related if more than 1 inserted)
9306          */
9307         insertHtml : function(where, html, returnEl){
9308             var el = Roo.DomHelper.insertHtml(where, this.dom, html);
9309             return returnEl ? Roo.get(el) : el;
9310         },
9311
9312         /**
9313          * Sets the passed attributes as attributes of this element (a style attribute can be a string, object or function)
9314          * @param {Object} o The object with the attributes
9315          * @param {Boolean} useSet (optional) false to override the default setAttribute to use expandos.
9316          * @return {Roo.Element} this
9317          */
9318         set : function(o, useSet){
9319             var el = this.dom;
9320             useSet = typeof useSet == 'undefined' ? (el.setAttribute ? true : false) : useSet;
9321             for(var attr in o){
9322                 if(attr == "style" || typeof o[attr] == "function") continue;
9323                 if(attr=="cls"){
9324                     el.className = o["cls"];
9325                 }else{
9326                     if(useSet) el.setAttribute(attr, o[attr]);
9327                     else el[attr] = o[attr];
9328                 }
9329             }
9330             if(o.style){
9331                 Roo.DomHelper.applyStyles(el, o.style);
9332             }
9333             return this;
9334         },
9335
9336         /**
9337          * Convenience method for constructing a KeyMap
9338          * @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:
9339          *                                  {key: (number or array), shift: (true/false), ctrl: (true/false), alt: (true/false)}
9340          * @param {Function} fn The function to call
9341          * @param {Object} scope (optional) The scope of the function
9342          * @return {Roo.KeyMap} The KeyMap created
9343          */
9344         addKeyListener : function(key, fn, scope){
9345             var config;
9346             if(typeof key != "object" || key instanceof Array){
9347                 config = {
9348                     key: key,
9349                     fn: fn,
9350                     scope: scope
9351                 };
9352             }else{
9353                 config = {
9354                     key : key.key,
9355                     shift : key.shift,
9356                     ctrl : key.ctrl,
9357                     alt : key.alt,
9358                     fn: fn,
9359                     scope: scope
9360                 };
9361             }
9362             return new Roo.KeyMap(this, config);
9363         },
9364
9365         /**
9366          * Creates a KeyMap for this element
9367          * @param {Object} config The KeyMap config. See {@link Roo.KeyMap} for more details
9368          * @return {Roo.KeyMap} The KeyMap created
9369          */
9370         addKeyMap : function(config){
9371             return new Roo.KeyMap(this, config);
9372         },
9373
9374         /**
9375          * Returns true if this element is scrollable.
9376          * @return {Boolean}
9377          */
9378          isScrollable : function(){
9379             var dom = this.dom;
9380             return dom.scrollHeight > dom.clientHeight || dom.scrollWidth > dom.clientWidth;
9381         },
9382
9383         /**
9384          * 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().
9385          * @param {String} side Either "left" for scrollLeft values or "top" for scrollTop values.
9386          * @param {Number} value The new scroll value
9387          * @param {Boolean/Object} animate (optional) true for the default animation or a standard Element animation config object
9388          * @return {Element} this
9389          */
9390
9391         scrollTo : function(side, value, animate){
9392             var prop = side.toLowerCase() == "left" ? "scrollLeft" : "scrollTop";
9393             if(!animate || !A){
9394                 this.dom[prop] = value;
9395             }else{
9396                 var to = prop == "scrollLeft" ? [value, this.dom.scrollTop] : [this.dom.scrollLeft, value];
9397                 this.anim({scroll: {"to": to}}, this.preanim(arguments, 2), 'scroll');
9398             }
9399             return this;
9400         },
9401
9402         /**
9403          * Scrolls this element the specified direction. Does bounds checking to make sure the scroll is
9404          * within this element's scrollable range.
9405          * @param {String} direction Possible values are: "l","left" - "r","right" - "t","top","up" - "b","bottom","down".
9406          * @param {Number} distance How far to scroll the element in pixels
9407          * @param {Boolean/Object} animate (optional) true for the default animation or a standard Element animation config object
9408          * @return {Boolean} Returns true if a scroll was triggered or false if the element
9409          * was scrolled as far as it could go.
9410          */
9411          scroll : function(direction, distance, animate){
9412              if(!this.isScrollable()){
9413                  return;
9414              }
9415              var el = this.dom;
9416              var l = el.scrollLeft, t = el.scrollTop;
9417              var w = el.scrollWidth, h = el.scrollHeight;
9418              var cw = el.clientWidth, ch = el.clientHeight;
9419              direction = direction.toLowerCase();
9420              var scrolled = false;
9421              var a = this.preanim(arguments, 2);
9422              switch(direction){
9423                  case "l":
9424                  case "left":
9425                      if(w - l > cw){
9426                          var v = Math.min(l + distance, w-cw);
9427                          this.scrollTo("left", v, a);
9428                          scrolled = true;
9429                      }
9430                      break;
9431                 case "r":
9432                 case "right":
9433                      if(l > 0){
9434                          var v = Math.max(l - distance, 0);
9435                          this.scrollTo("left", v, a);
9436                          scrolled = true;
9437                      }
9438                      break;
9439                 case "t":
9440                 case "top":
9441                 case "up":
9442                      if(t > 0){
9443                          var v = Math.max(t - distance, 0);
9444                          this.scrollTo("top", v, a);
9445                          scrolled = true;
9446                      }
9447                      break;
9448                 case "b":
9449                 case "bottom":
9450                 case "down":
9451                      if(h - t > ch){
9452                          var v = Math.min(t + distance, h-ch);
9453                          this.scrollTo("top", v, a);
9454                          scrolled = true;
9455                      }
9456                      break;
9457              }
9458              return scrolled;
9459         },
9460
9461         /**
9462          * Translates the passed page coordinates into left/top css values for this element
9463          * @param {Number/Array} x The page x or an array containing [x, y]
9464          * @param {Number} y The page y
9465          * @return {Object} An object with left and top properties. e.g. {left: (value), top: (value)}
9466          */
9467         translatePoints : function(x, y){
9468             if(typeof x == 'object' || x instanceof Array){
9469                 y = x[1]; x = x[0];
9470             }
9471             var p = this.getStyle('position');
9472             var o = this.getXY();
9473
9474             var l = parseInt(this.getStyle('left'), 10);
9475             var t = parseInt(this.getStyle('top'), 10);
9476
9477             if(isNaN(l)){
9478                 l = (p == "relative") ? 0 : this.dom.offsetLeft;
9479             }
9480             if(isNaN(t)){
9481                 t = (p == "relative") ? 0 : this.dom.offsetTop;
9482             }
9483
9484             return {left: (x - o[0] + l), top: (y - o[1] + t)};
9485         },
9486
9487         /**
9488          * Returns the current scroll position of the element.
9489          * @return {Object} An object containing the scroll position in the format {left: (scrollLeft), top: (scrollTop)}
9490          */
9491         getScroll : function(){
9492             var d = this.dom, doc = document;
9493             if(d == doc || d == doc.body){
9494                 var l = window.pageXOffset || doc.documentElement.scrollLeft || doc.body.scrollLeft || 0;
9495                 var t = window.pageYOffset || doc.documentElement.scrollTop || doc.body.scrollTop || 0;
9496                 return {left: l, top: t};
9497             }else{
9498                 return {left: d.scrollLeft, top: d.scrollTop};
9499             }
9500         },
9501
9502         /**
9503          * Return the CSS color for the specified CSS attribute. rgb, 3 digit (like #fff) and valid values
9504          * are convert to standard 6 digit hex color.
9505          * @param {String} attr The css attribute
9506          * @param {String} defaultValue The default value to use when a valid color isn't found
9507          * @param {String} prefix (optional) defaults to #. Use an empty string when working with
9508          * YUI color anims.
9509          */
9510         getColor : function(attr, defaultValue, prefix){
9511             var v = this.getStyle(attr);
9512             if(!v || v == "transparent" || v == "inherit") {
9513                 return defaultValue;
9514             }
9515             var color = typeof prefix == "undefined" ? "#" : prefix;
9516             if(v.substr(0, 4) == "rgb("){
9517                 var rvs = v.slice(4, v.length -1).split(",");
9518                 for(var i = 0; i < 3; i++){
9519                     var h = parseInt(rvs[i]).toString(16);
9520                     if(h < 16){
9521                         h = "0" + h;
9522                     }
9523                     color += h;
9524                 }
9525             } else {
9526                 if(v.substr(0, 1) == "#"){
9527                     if(v.length == 4) {
9528                         for(var i = 1; i < 4; i++){
9529                             var c = v.charAt(i);
9530                             color +=  c + c;
9531                         }
9532                     }else if(v.length == 7){
9533                         color += v.substr(1);
9534                     }
9535                 }
9536             }
9537             return(color.length > 5 ? color.toLowerCase() : defaultValue);
9538         },
9539
9540         /**
9541          * Wraps the specified element with a special markup/CSS block that renders by default as a gray container with a
9542          * gradient background, rounded corners and a 4-way shadow.
9543          * @param {String} class (optional) A base CSS class to apply to the containing wrapper element (defaults to 'x-box').
9544          * Note that there are a number of CSS rules that are dependent on this name to make the overall effect work,
9545          * so if you supply an alternate base class, make sure you also supply all of the necessary rules.
9546          * @return {Roo.Element} this
9547          */
9548         boxWrap : function(cls){
9549             cls = cls || 'x-box';
9550             var el = Roo.get(this.insertHtml('beforeBegin', String.format('<div class="{0}">'+El.boxMarkup+'</div>', cls)));
9551             el.child('.'+cls+'-mc').dom.appendChild(this.dom);
9552             return el;
9553         },
9554
9555         /**
9556          * Returns the value of a namespaced attribute from the element's underlying DOM node.
9557          * @param {String} namespace The namespace in which to look for the attribute
9558          * @param {String} name The attribute name
9559          * @return {String} The attribute value
9560          */
9561         getAttributeNS : Roo.isIE ? function(ns, name){
9562             var d = this.dom;
9563             var type = typeof d[ns+":"+name];
9564             if(type != 'undefined' && type != 'unknown'){
9565                 return d[ns+":"+name];
9566             }
9567             return d[name];
9568         } : function(ns, name){
9569             var d = this.dom;
9570             return d.getAttributeNS(ns, name) || d.getAttribute(ns+":"+name) || d.getAttribute(name) || d[name];
9571         },
9572         
9573         
9574         /**
9575          * Sets or Returns the value the dom attribute value
9576          * @param {String} name The attribute name
9577          * @param {String} value (optional) The value to set the attribute to
9578          * @return {String} The attribute value
9579          */
9580         attr : function(name){
9581             if (arguments.length > 1) {
9582                 this.dom.setAttribute(name, arguments[1]);
9583                 return arguments[1];
9584             }
9585             if (!this.dom.hasAttribute(name)) {
9586                 return undefined;
9587             }
9588             return this.dom.getAttribute(name);
9589         }
9590         
9591         
9592         
9593     };
9594
9595     var ep = El.prototype;
9596
9597     /**
9598      * Appends an event handler (Shorthand for addListener)
9599      * @param {String}   eventName     The type of event to append
9600      * @param {Function} fn        The method the event invokes
9601      * @param {Object} scope       (optional) The scope (this object) of the fn
9602      * @param {Object}   options   (optional)An object with standard {@link Roo.EventManager#addListener} options
9603      * @method
9604      */
9605     ep.on = ep.addListener;
9606         // backwards compat
9607     ep.mon = ep.addListener;
9608
9609     /**
9610      * Removes an event handler from this element (shorthand for removeListener)
9611      * @param {String} eventName the type of event to remove
9612      * @param {Function} fn the method the event invokes
9613      * @return {Roo.Element} this
9614      * @method
9615      */
9616     ep.un = ep.removeListener;
9617
9618     /**
9619      * true to automatically adjust width and height settings for box-model issues (default to true)
9620      */
9621     ep.autoBoxAdjust = true;
9622
9623     // private
9624     El.unitPattern = /\d+(px|em|%|en|ex|pt|in|cm|mm|pc)$/i;
9625
9626     // private
9627     El.addUnits = function(v, defaultUnit){
9628         if(v === "" || v == "auto"){
9629             return v;
9630         }
9631         if(v === undefined){
9632             return '';
9633         }
9634         if(typeof v == "number" || !El.unitPattern.test(v)){
9635             return v + (defaultUnit || 'px');
9636         }
9637         return v;
9638     };
9639
9640     // special markup used throughout Roo when box wrapping elements
9641     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>';
9642     /**
9643      * Visibility mode constant - Use visibility to hide element
9644      * @static
9645      * @type Number
9646      */
9647     El.VISIBILITY = 1;
9648     /**
9649      * Visibility mode constant - Use display to hide element
9650      * @static
9651      * @type Number
9652      */
9653     El.DISPLAY = 2;
9654
9655     El.borders = {l: "border-left-width", r: "border-right-width", t: "border-top-width", b: "border-bottom-width"};
9656     El.paddings = {l: "padding-left", r: "padding-right", t: "padding-top", b: "padding-bottom"};
9657     El.margins = {l: "margin-left", r: "margin-right", t: "margin-top", b: "margin-bottom"};
9658
9659
9660
9661     /**
9662      * @private
9663      */
9664     El.cache = {};
9665
9666     var docEl;
9667
9668     /**
9669      * Static method to retrieve Element objects. Uses simple caching to consistently return the same object.
9670      * Automatically fixes if an object was recreated with the same id via AJAX or DOM.
9671      * @param {String/HTMLElement/Element} el The id of the node, a DOM Node or an existing Element.
9672      * @return {Element} The Element object
9673      * @static
9674      */
9675     El.get = function(el){
9676         var ex, elm, id;
9677         if(!el){ return null; }
9678         if(typeof el == "string"){ // element id
9679             if(!(elm = document.getElementById(el))){
9680                 return null;
9681             }
9682             if(ex = El.cache[el]){
9683                 ex.dom = elm;
9684             }else{
9685                 ex = El.cache[el] = new El(elm);
9686             }
9687             return ex;
9688         }else if(el.tagName){ // dom element
9689             if(!(id = el.id)){
9690                 id = Roo.id(el);
9691             }
9692             if(ex = El.cache[id]){
9693                 ex.dom = el;
9694             }else{
9695                 ex = El.cache[id] = new El(el);
9696             }
9697             return ex;
9698         }else if(el instanceof El){
9699             if(el != docEl){
9700                 el.dom = document.getElementById(el.id) || el.dom; // refresh dom element in case no longer valid,
9701                                                               // catch case where it hasn't been appended
9702                 El.cache[el.id] = el; // in case it was created directly with Element(), let's cache it
9703             }
9704             return el;
9705         }else if(el.isComposite){
9706             return el;
9707         }else if(el instanceof Array){
9708             return El.select(el);
9709         }else if(el == document){
9710             // create a bogus element object representing the document object
9711             if(!docEl){
9712                 var f = function(){};
9713                 f.prototype = El.prototype;
9714                 docEl = new f();
9715                 docEl.dom = document;
9716             }
9717             return docEl;
9718         }
9719         return null;
9720     };
9721
9722     // private
9723     El.uncache = function(el){
9724         for(var i = 0, a = arguments, len = a.length; i < len; i++) {
9725             if(a[i]){
9726                 delete El.cache[a[i].id || a[i]];
9727             }
9728         }
9729     };
9730
9731     // private
9732     // Garbage collection - uncache elements/purge listeners on orphaned elements
9733     // so we don't hold a reference and cause the browser to retain them
9734     El.garbageCollect = function(){
9735         if(!Roo.enableGarbageCollector){
9736             clearInterval(El.collectorThread);
9737             return;
9738         }
9739         for(var eid in El.cache){
9740             var el = El.cache[eid], d = el.dom;
9741             // -------------------------------------------------------
9742             // Determining what is garbage:
9743             // -------------------------------------------------------
9744             // !d
9745             // dom node is null, definitely garbage
9746             // -------------------------------------------------------
9747             // !d.parentNode
9748             // no parentNode == direct orphan, definitely garbage
9749             // -------------------------------------------------------
9750             // !d.offsetParent && !document.getElementById(eid)
9751             // display none elements have no offsetParent so we will
9752             // also try to look it up by it's id. However, check
9753             // offsetParent first so we don't do unneeded lookups.
9754             // This enables collection of elements that are not orphans
9755             // directly, but somewhere up the line they have an orphan
9756             // parent.
9757             // -------------------------------------------------------
9758             if(!d || !d.parentNode || (!d.offsetParent && !document.getElementById(eid))){
9759                 delete El.cache[eid];
9760                 if(d && Roo.enableListenerCollection){
9761                     E.purgeElement(d);
9762                 }
9763             }
9764         }
9765     }
9766     El.collectorThreadId = setInterval(El.garbageCollect, 30000);
9767
9768
9769     // dom is optional
9770     El.Flyweight = function(dom){
9771         this.dom = dom;
9772     };
9773     El.Flyweight.prototype = El.prototype;
9774
9775     El._flyweights = {};
9776     /**
9777      * Gets the globally shared flyweight Element, with the passed node as the active element. Do not store a reference to this element -
9778      * the dom node can be overwritten by other code.
9779      * @param {String/HTMLElement} el The dom node or id
9780      * @param {String} named (optional) Allows for creation of named reusable flyweights to
9781      *                                  prevent conflicts (e.g. internally Roo uses "_internal")
9782      * @static
9783      * @return {Element} The shared Element object
9784      */
9785     El.fly = function(el, named){
9786         named = named || '_global';
9787         el = Roo.getDom(el);
9788         if(!el){
9789             return null;
9790         }
9791         if(!El._flyweights[named]){
9792             El._flyweights[named] = new El.Flyweight();
9793         }
9794         El._flyweights[named].dom = el;
9795         return El._flyweights[named];
9796     };
9797
9798     /**
9799      * Static method to retrieve Element objects. Uses simple caching to consistently return the same object.
9800      * Automatically fixes if an object was recreated with the same id via AJAX or DOM.
9801      * Shorthand of {@link Roo.Element#get}
9802      * @param {String/HTMLElement/Element} el The id of the node, a DOM Node or an existing Element.
9803      * @return {Element} The Element object
9804      * @member Roo
9805      * @method get
9806      */
9807     Roo.get = El.get;
9808     /**
9809      * Gets the globally shared flyweight Element, with the passed node as the active element. Do not store a reference to this element -
9810      * the dom node can be overwritten by other code.
9811      * Shorthand of {@link Roo.Element#fly}
9812      * @param {String/HTMLElement} el The dom node or id
9813      * @param {String} named (optional) Allows for creation of named reusable flyweights to
9814      *                                  prevent conflicts (e.g. internally Roo uses "_internal")
9815      * @static
9816      * @return {Element} The shared Element object
9817      * @member Roo
9818      * @method fly
9819      */
9820     Roo.fly = El.fly;
9821
9822     // speedy lookup for elements never to box adjust
9823     var noBoxAdjust = Roo.isStrict ? {
9824         select:1
9825     } : {
9826         input:1, select:1, textarea:1
9827     };
9828     if(Roo.isIE || Roo.isGecko){
9829         noBoxAdjust['button'] = 1;
9830     }
9831
9832
9833     Roo.EventManager.on(window, 'unload', function(){
9834         delete El.cache;
9835         delete El._flyweights;
9836     });
9837 })();
9838
9839
9840
9841
9842 if(Roo.DomQuery){
9843     Roo.Element.selectorFunction = Roo.DomQuery.select;
9844 }
9845
9846 Roo.Element.select = function(selector, unique, root){
9847     var els;
9848     if(typeof selector == "string"){
9849         els = Roo.Element.selectorFunction(selector, root);
9850     }else if(selector.length !== undefined){
9851         els = selector;
9852     }else{
9853         throw "Invalid selector";
9854     }
9855     if(unique === true){
9856         return new Roo.CompositeElement(els);
9857     }else{
9858         return new Roo.CompositeElementLite(els);
9859     }
9860 };
9861 /**
9862  * Selects elements based on the passed CSS selector to enable working on them as 1.
9863  * @param {String/Array} selector The CSS selector or an array of elements
9864  * @param {Boolean} unique (optional) true to create a unique Roo.Element for each element (defaults to a shared flyweight object)
9865  * @param {HTMLElement/String} root (optional) The root element of the query or id of the root
9866  * @return {CompositeElementLite/CompositeElement}
9867  * @member Roo
9868  * @method select
9869  */
9870 Roo.select = Roo.Element.select;
9871
9872
9873
9874
9875
9876
9877
9878
9879
9880
9881
9882
9883
9884
9885 /*
9886  * Based on:
9887  * Ext JS Library 1.1.1
9888  * Copyright(c) 2006-2007, Ext JS, LLC.
9889  *
9890  * Originally Released Under LGPL - original licence link has changed is not relivant.
9891  *
9892  * Fork - LGPL
9893  * <script type="text/javascript">
9894  */
9895
9896
9897
9898 //Notifies Element that fx methods are available
9899 Roo.enableFx = true;
9900
9901 /**
9902  * @class Roo.Fx
9903  * <p>A class to provide basic animation and visual effects support.  <b>Note:</b> This class is automatically applied
9904  * to the {@link Roo.Element} interface when included, so all effects calls should be performed via Element.
9905  * Conversely, since the effects are not actually defined in Element, Roo.Fx <b>must</b> be included in order for the 
9906  * Element effects to work.</p><br/>
9907  *
9908  * <p>It is important to note that although the Fx methods and many non-Fx Element methods support "method chaining" in that
9909  * they return the Element object itself as the method return value, it is not always possible to mix the two in a single
9910  * method chain.  The Fx methods use an internal effects queue so that each effect can be properly timed and sequenced.
9911  * Non-Fx methods, on the other hand, have no such internal queueing and will always execute immediately.  For this reason,
9912  * while it may be possible to mix certain Fx and non-Fx method calls in a single chain, it may not always provide the
9913  * expected results and should be done with care.</p><br/>
9914  *
9915  * <p>Motion effects support 8-way anchoring, meaning that you can choose one of 8 different anchor points on the Element
9916  * that will serve as either the start or end point of the animation.  Following are all of the supported anchor positions:</p>
9917 <pre>
9918 Value  Description
9919 -----  -----------------------------
9920 tl     The top left corner
9921 t      The center of the top edge
9922 tr     The top right corner
9923 l      The center of the left edge
9924 r      The center of the right edge
9925 bl     The bottom left corner
9926 b      The center of the bottom edge
9927 br     The bottom right corner
9928 </pre>
9929  * <b>Although some Fx methods accept specific custom config parameters, the ones shown in the Config Options section
9930  * below are common options that can be passed to any Fx method.</b>
9931  * @cfg {Function} callback A function called when the effect is finished
9932  * @cfg {Object} scope The scope of the effect function
9933  * @cfg {String} easing A valid Easing value for the effect
9934  * @cfg {String} afterCls A css class to apply after the effect
9935  * @cfg {Number} duration The length of time (in seconds) that the effect should last
9936  * @cfg {Boolean} remove Whether the Element should be removed from the DOM and destroyed after the effect finishes
9937  * @cfg {Boolean} useDisplay Whether to use the <i>display</i> CSS property instead of <i>visibility</i> when hiding Elements (only applies to 
9938  * effects that end with the element being visually hidden, ignored otherwise)
9939  * @cfg {String/Object/Function} afterStyle A style specification string, e.g. "width:100px", or an object in the form {width:"100px"}, or
9940  * a function which returns such a specification that will be applied to the Element after the effect finishes
9941  * @cfg {Boolean} block Whether the effect should block other effects from queueing while it runs
9942  * @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
9943  * @cfg {Boolean} stopFx Whether subsequent effects should be stopped and removed after the current effect finishes
9944  */
9945 Roo.Fx = {
9946         /**
9947          * Slides the element into view.  An anchor point can be optionally passed to set the point of
9948          * origin for the slide effect.  This function automatically handles wrapping the element with
9949          * a fixed-size container if needed.  See the Fx class overview for valid anchor point options.
9950          * Usage:
9951          *<pre><code>
9952 // default: slide the element in from the top
9953 el.slideIn();
9954
9955 // custom: slide the element in from the right with a 2-second duration
9956 el.slideIn('r', { duration: 2 });
9957
9958 // common config options shown with default values
9959 el.slideIn('t', {
9960     easing: 'easeOut',
9961     duration: .5
9962 });
9963 </code></pre>
9964          * @param {String} anchor (optional) One of the valid Fx anchor positions (defaults to top: 't')
9965          * @param {Object} options (optional) Object literal with any of the Fx config options
9966          * @return {Roo.Element} The Element
9967          */
9968     slideIn : function(anchor, o){
9969         var el = this.getFxEl();
9970         o = o || {};
9971
9972         el.queueFx(o, function(){
9973
9974             anchor = anchor || "t";
9975
9976             // fix display to visibility
9977             this.fixDisplay();
9978
9979             // restore values after effect
9980             var r = this.getFxRestore();
9981             var b = this.getBox();
9982             // fixed size for slide
9983             this.setSize(b);
9984
9985             // wrap if needed
9986             var wrap = this.fxWrap(r.pos, o, "hidden");
9987
9988             var st = this.dom.style;
9989             st.visibility = "visible";
9990             st.position = "absolute";
9991
9992             // clear out temp styles after slide and unwrap
9993             var after = function(){
9994                 el.fxUnwrap(wrap, r.pos, o);
9995                 st.width = r.width;
9996                 st.height = r.height;
9997                 el.afterFx(o);
9998             };
9999             // time to calc the positions
10000             var a, pt = {to: [b.x, b.y]}, bw = {to: b.width}, bh = {to: b.height};
10001
10002             switch(anchor.toLowerCase()){
10003                 case "t":
10004                     wrap.setSize(b.width, 0);
10005                     st.left = st.bottom = "0";
10006                     a = {height: bh};
10007                 break;
10008                 case "l":
10009                     wrap.setSize(0, b.height);
10010                     st.right = st.top = "0";
10011                     a = {width: bw};
10012                 break;
10013                 case "r":
10014                     wrap.setSize(0, b.height);
10015                     wrap.setX(b.right);
10016                     st.left = st.top = "0";
10017                     a = {width: bw, points: pt};
10018                 break;
10019                 case "b":
10020                     wrap.setSize(b.width, 0);
10021                     wrap.setY(b.bottom);
10022                     st.left = st.top = "0";
10023                     a = {height: bh, points: pt};
10024                 break;
10025                 case "tl":
10026                     wrap.setSize(0, 0);
10027                     st.right = st.bottom = "0";
10028                     a = {width: bw, height: bh};
10029                 break;
10030                 case "bl":
10031                     wrap.setSize(0, 0);
10032                     wrap.setY(b.y+b.height);
10033                     st.right = st.top = "0";
10034                     a = {width: bw, height: bh, points: pt};
10035                 break;
10036                 case "br":
10037                     wrap.setSize(0, 0);
10038                     wrap.setXY([b.right, b.bottom]);
10039                     st.left = st.top = "0";
10040                     a = {width: bw, height: bh, points: pt};
10041                 break;
10042                 case "tr":
10043                     wrap.setSize(0, 0);
10044                     wrap.setX(b.x+b.width);
10045                     st.left = st.bottom = "0";
10046                     a = {width: bw, height: bh, points: pt};
10047                 break;
10048             }
10049             this.dom.style.visibility = "visible";
10050             wrap.show();
10051
10052             arguments.callee.anim = wrap.fxanim(a,
10053                 o,
10054                 'motion',
10055                 .5,
10056                 'easeOut', after);
10057         });
10058         return this;
10059     },
10060     
10061         /**
10062          * Slides the element out of view.  An anchor point can be optionally passed to set the end point
10063          * for the slide effect.  When the effect is completed, the element will be hidden (visibility = 
10064          * 'hidden') but block elements will still take up space in the document.  The element must be removed
10065          * from the DOM using the 'remove' config option if desired.  This function automatically handles 
10066          * wrapping the element with a fixed-size container if needed.  See the Fx class overview for valid anchor point options.
10067          * Usage:
10068          *<pre><code>
10069 // default: slide the element out to the top
10070 el.slideOut();
10071
10072 // custom: slide the element out to the right with a 2-second duration
10073 el.slideOut('r', { duration: 2 });
10074
10075 // common config options shown with default values
10076 el.slideOut('t', {
10077     easing: 'easeOut',
10078     duration: .5,
10079     remove: false,
10080     useDisplay: false
10081 });
10082 </code></pre>
10083          * @param {String} anchor (optional) One of the valid Fx anchor positions (defaults to top: 't')
10084          * @param {Object} options (optional) Object literal with any of the Fx config options
10085          * @return {Roo.Element} The Element
10086          */
10087     slideOut : function(anchor, o){
10088         var el = this.getFxEl();
10089         o = o || {};
10090
10091         el.queueFx(o, function(){
10092
10093             anchor = anchor || "t";
10094
10095             // restore values after effect
10096             var r = this.getFxRestore();
10097             
10098             var b = this.getBox();
10099             // fixed size for slide
10100             this.setSize(b);
10101
10102             // wrap if needed
10103             var wrap = this.fxWrap(r.pos, o, "visible");
10104
10105             var st = this.dom.style;
10106             st.visibility = "visible";
10107             st.position = "absolute";
10108
10109             wrap.setSize(b);
10110
10111             var after = function(){
10112                 if(o.useDisplay){
10113                     el.setDisplayed(false);
10114                 }else{
10115                     el.hide();
10116                 }
10117
10118                 el.fxUnwrap(wrap, r.pos, o);
10119
10120                 st.width = r.width;
10121                 st.height = r.height;
10122
10123                 el.afterFx(o);
10124             };
10125
10126             var a, zero = {to: 0};
10127             switch(anchor.toLowerCase()){
10128                 case "t":
10129                     st.left = st.bottom = "0";
10130                     a = {height: zero};
10131                 break;
10132                 case "l":
10133                     st.right = st.top = "0";
10134                     a = {width: zero};
10135                 break;
10136                 case "r":
10137                     st.left = st.top = "0";
10138                     a = {width: zero, points: {to:[b.right, b.y]}};
10139                 break;
10140                 case "b":
10141                     st.left = st.top = "0";
10142                     a = {height: zero, points: {to:[b.x, b.bottom]}};
10143                 break;
10144                 case "tl":
10145                     st.right = st.bottom = "0";
10146                     a = {width: zero, height: zero};
10147                 break;
10148                 case "bl":
10149                     st.right = st.top = "0";
10150                     a = {width: zero, height: zero, points: {to:[b.x, b.bottom]}};
10151                 break;
10152                 case "br":
10153                     st.left = st.top = "0";
10154                     a = {width: zero, height: zero, points: {to:[b.x+b.width, b.bottom]}};
10155                 break;
10156                 case "tr":
10157                     st.left = st.bottom = "0";
10158                     a = {width: zero, height: zero, points: {to:[b.right, b.y]}};
10159                 break;
10160             }
10161
10162             arguments.callee.anim = wrap.fxanim(a,
10163                 o,
10164                 'motion',
10165                 .5,
10166                 "easeOut", after);
10167         });
10168         return this;
10169     },
10170
10171         /**
10172          * Fades the element out while slowly expanding it in all directions.  When the effect is completed, the 
10173          * element will be hidden (visibility = 'hidden') but block elements will still take up space in the document. 
10174          * The element must be removed from the DOM using the 'remove' config option if desired.
10175          * Usage:
10176          *<pre><code>
10177 // default
10178 el.puff();
10179
10180 // common config options shown with default values
10181 el.puff({
10182     easing: 'easeOut',
10183     duration: .5,
10184     remove: false,
10185     useDisplay: false
10186 });
10187 </code></pre>
10188          * @param {Object} options (optional) Object literal with any of the Fx config options
10189          * @return {Roo.Element} The Element
10190          */
10191     puff : function(o){
10192         var el = this.getFxEl();
10193         o = o || {};
10194
10195         el.queueFx(o, function(){
10196             this.clearOpacity();
10197             this.show();
10198
10199             // restore values after effect
10200             var r = this.getFxRestore();
10201             var st = this.dom.style;
10202
10203             var after = function(){
10204                 if(o.useDisplay){
10205                     el.setDisplayed(false);
10206                 }else{
10207                     el.hide();
10208                 }
10209
10210                 el.clearOpacity();
10211
10212                 el.setPositioning(r.pos);
10213                 st.width = r.width;
10214                 st.height = r.height;
10215                 st.fontSize = '';
10216                 el.afterFx(o);
10217             };
10218
10219             var width = this.getWidth();
10220             var height = this.getHeight();
10221
10222             arguments.callee.anim = this.fxanim({
10223                     width : {to: this.adjustWidth(width * 2)},
10224                     height : {to: this.adjustHeight(height * 2)},
10225                     points : {by: [-(width * .5), -(height * .5)]},
10226                     opacity : {to: 0},
10227                     fontSize: {to:200, unit: "%"}
10228                 },
10229                 o,
10230                 'motion',
10231                 .5,
10232                 "easeOut", after);
10233         });
10234         return this;
10235     },
10236
10237         /**
10238          * Blinks the element as if it was clicked and then collapses on its center (similar to switching off a television).
10239          * When the effect is completed, the element will be hidden (visibility = 'hidden') but block elements will still 
10240          * take up space in the document. The element must be removed from the DOM using the 'remove' config option if desired.
10241          * Usage:
10242          *<pre><code>
10243 // default
10244 el.switchOff();
10245
10246 // all config options shown with default values
10247 el.switchOff({
10248     easing: 'easeIn',
10249     duration: .3,
10250     remove: false,
10251     useDisplay: false
10252 });
10253 </code></pre>
10254          * @param {Object} options (optional) Object literal with any of the Fx config options
10255          * @return {Roo.Element} The Element
10256          */
10257     switchOff : function(o){
10258         var el = this.getFxEl();
10259         o = o || {};
10260
10261         el.queueFx(o, function(){
10262             this.clearOpacity();
10263             this.clip();
10264
10265             // restore values after effect
10266             var r = this.getFxRestore();
10267             var st = this.dom.style;
10268
10269             var after = function(){
10270                 if(o.useDisplay){
10271                     el.setDisplayed(false);
10272                 }else{
10273                     el.hide();
10274                 }
10275
10276                 el.clearOpacity();
10277                 el.setPositioning(r.pos);
10278                 st.width = r.width;
10279                 st.height = r.height;
10280
10281                 el.afterFx(o);
10282             };
10283
10284             this.fxanim({opacity:{to:0.3}}, null, null, .1, null, function(){
10285                 this.clearOpacity();
10286                 (function(){
10287                     this.fxanim({
10288                         height:{to:1},
10289                         points:{by:[0, this.getHeight() * .5]}
10290                     }, o, 'motion', 0.3, 'easeIn', after);
10291                 }).defer(100, this);
10292             });
10293         });
10294         return this;
10295     },
10296
10297     /**
10298      * Highlights the Element by setting a color (applies to the background-color by default, but can be
10299      * changed using the "attr" config option) and then fading back to the original color. If no original
10300      * color is available, you should provide the "endColor" config option which will be cleared after the animation.
10301      * Usage:
10302 <pre><code>
10303 // default: highlight background to yellow
10304 el.highlight();
10305
10306 // custom: highlight foreground text to blue for 2 seconds
10307 el.highlight("0000ff", { attr: 'color', duration: 2 });
10308
10309 // common config options shown with default values
10310 el.highlight("ffff9c", {
10311     attr: "background-color", //can be any valid CSS property (attribute) that supports a color value
10312     endColor: (current color) or "ffffff",
10313     easing: 'easeIn',
10314     duration: 1
10315 });
10316 </code></pre>
10317      * @param {String} color (optional) The highlight color. Should be a 6 char hex color without the leading # (defaults to yellow: 'ffff9c')
10318      * @param {Object} options (optional) Object literal with any of the Fx config options
10319      * @return {Roo.Element} The Element
10320      */ 
10321     highlight : function(color, o){
10322         var el = this.getFxEl();
10323         o = o || {};
10324
10325         el.queueFx(o, function(){
10326             color = color || "ffff9c";
10327             attr = o.attr || "backgroundColor";
10328
10329             this.clearOpacity();
10330             this.show();
10331
10332             var origColor = this.getColor(attr);
10333             var restoreColor = this.dom.style[attr];
10334             endColor = (o.endColor || origColor) || "ffffff";
10335
10336             var after = function(){
10337                 el.dom.style[attr] = restoreColor;
10338                 el.afterFx(o);
10339             };
10340
10341             var a = {};
10342             a[attr] = {from: color, to: endColor};
10343             arguments.callee.anim = this.fxanim(a,
10344                 o,
10345                 'color',
10346                 1,
10347                 'easeIn', after);
10348         });
10349         return this;
10350     },
10351
10352    /**
10353     * Shows a ripple of exploding, attenuating borders to draw attention to an Element.
10354     * Usage:
10355 <pre><code>
10356 // default: a single light blue ripple
10357 el.frame();
10358
10359 // custom: 3 red ripples lasting 3 seconds total
10360 el.frame("ff0000", 3, { duration: 3 });
10361
10362 // common config options shown with default values
10363 el.frame("C3DAF9", 1, {
10364     duration: 1 //duration of entire animation (not each individual ripple)
10365     // Note: Easing is not configurable and will be ignored if included
10366 });
10367 </code></pre>
10368     * @param {String} color (optional) The color of the border.  Should be a 6 char hex color without the leading # (defaults to light blue: 'C3DAF9').
10369     * @param {Number} count (optional) The number of ripples to display (defaults to 1)
10370     * @param {Object} options (optional) Object literal with any of the Fx config options
10371     * @return {Roo.Element} The Element
10372     */
10373     frame : function(color, count, o){
10374         var el = this.getFxEl();
10375         o = o || {};
10376
10377         el.queueFx(o, function(){
10378             color = color || "#C3DAF9";
10379             if(color.length == 6){
10380                 color = "#" + color;
10381             }
10382             count = count || 1;
10383             duration = o.duration || 1;
10384             this.show();
10385
10386             var b = this.getBox();
10387             var animFn = function(){
10388                 var proxy = this.createProxy({
10389
10390                      style:{
10391                         visbility:"hidden",
10392                         position:"absolute",
10393                         "z-index":"35000", // yee haw
10394                         border:"0px solid " + color
10395                      }
10396                   });
10397                 var scale = Roo.isBorderBox ? 2 : 1;
10398                 proxy.animate({
10399                     top:{from:b.y, to:b.y - 20},
10400                     left:{from:b.x, to:b.x - 20},
10401                     borderWidth:{from:0, to:10},
10402                     opacity:{from:1, to:0},
10403                     height:{from:b.height, to:(b.height + (20*scale))},
10404                     width:{from:b.width, to:(b.width + (20*scale))}
10405                 }, duration, function(){
10406                     proxy.remove();
10407                 });
10408                 if(--count > 0){
10409                      animFn.defer((duration/2)*1000, this);
10410                 }else{
10411                     el.afterFx(o);
10412                 }
10413             };
10414             animFn.call(this);
10415         });
10416         return this;
10417     },
10418
10419    /**
10420     * Creates a pause before any subsequent queued effects begin.  If there are
10421     * no effects queued after the pause it will have no effect.
10422     * Usage:
10423 <pre><code>
10424 el.pause(1);
10425 </code></pre>
10426     * @param {Number} seconds The length of time to pause (in seconds)
10427     * @return {Roo.Element} The Element
10428     */
10429     pause : function(seconds){
10430         var el = this.getFxEl();
10431         var o = {};
10432
10433         el.queueFx(o, function(){
10434             setTimeout(function(){
10435                 el.afterFx(o);
10436             }, seconds * 1000);
10437         });
10438         return this;
10439     },
10440
10441    /**
10442     * Fade an element in (from transparent to opaque).  The ending opacity can be specified
10443     * using the "endOpacity" config option.
10444     * Usage:
10445 <pre><code>
10446 // default: fade in from opacity 0 to 100%
10447 el.fadeIn();
10448
10449 // custom: fade in from opacity 0 to 75% over 2 seconds
10450 el.fadeIn({ endOpacity: .75, duration: 2});
10451
10452 // common config options shown with default values
10453 el.fadeIn({
10454     endOpacity: 1, //can be any value between 0 and 1 (e.g. .5)
10455     easing: 'easeOut',
10456     duration: .5
10457 });
10458 </code></pre>
10459     * @param {Object} options (optional) Object literal with any of the Fx config options
10460     * @return {Roo.Element} The Element
10461     */
10462     fadeIn : function(o){
10463         var el = this.getFxEl();
10464         o = o || {};
10465         el.queueFx(o, function(){
10466             this.setOpacity(0);
10467             this.fixDisplay();
10468             this.dom.style.visibility = 'visible';
10469             var to = o.endOpacity || 1;
10470             arguments.callee.anim = this.fxanim({opacity:{to:to}},
10471                 o, null, .5, "easeOut", function(){
10472                 if(to == 1){
10473                     this.clearOpacity();
10474                 }
10475                 el.afterFx(o);
10476             });
10477         });
10478         return this;
10479     },
10480
10481    /**
10482     * Fade an element out (from opaque to transparent).  The ending opacity can be specified
10483     * using the "endOpacity" config option.
10484     * Usage:
10485 <pre><code>
10486 // default: fade out from the element's current opacity to 0
10487 el.fadeOut();
10488
10489 // custom: fade out from the element's current opacity to 25% over 2 seconds
10490 el.fadeOut({ endOpacity: .25, duration: 2});
10491
10492 // common config options shown with default values
10493 el.fadeOut({
10494     endOpacity: 0, //can be any value between 0 and 1 (e.g. .5)
10495     easing: 'easeOut',
10496     duration: .5
10497     remove: false,
10498     useDisplay: false
10499 });
10500 </code></pre>
10501     * @param {Object} options (optional) Object literal with any of the Fx config options
10502     * @return {Roo.Element} The Element
10503     */
10504     fadeOut : function(o){
10505         var el = this.getFxEl();
10506         o = o || {};
10507         el.queueFx(o, function(){
10508             arguments.callee.anim = this.fxanim({opacity:{to:o.endOpacity || 0}},
10509                 o, null, .5, "easeOut", function(){
10510                 if(this.visibilityMode == Roo.Element.DISPLAY || o.useDisplay){
10511                      this.dom.style.display = "none";
10512                 }else{
10513                      this.dom.style.visibility = "hidden";
10514                 }
10515                 this.clearOpacity();
10516                 el.afterFx(o);
10517             });
10518         });
10519         return this;
10520     },
10521
10522    /**
10523     * Animates the transition of an element's dimensions from a starting height/width
10524     * to an ending height/width.
10525     * Usage:
10526 <pre><code>
10527 // change height and width to 100x100 pixels
10528 el.scale(100, 100);
10529
10530 // common config options shown with default values.  The height and width will default to
10531 // the element's existing values if passed as null.
10532 el.scale(
10533     [element's width],
10534     [element's height], {
10535     easing: 'easeOut',
10536     duration: .35
10537 });
10538 </code></pre>
10539     * @param {Number} width  The new width (pass undefined to keep the original width)
10540     * @param {Number} height  The new height (pass undefined to keep the original height)
10541     * @param {Object} options (optional) Object literal with any of the Fx config options
10542     * @return {Roo.Element} The Element
10543     */
10544     scale : function(w, h, o){
10545         this.shift(Roo.apply({}, o, {
10546             width: w,
10547             height: h
10548         }));
10549         return this;
10550     },
10551
10552    /**
10553     * Animates the transition of any combination of an element's dimensions, xy position and/or opacity.
10554     * Any of these properties not specified in the config object will not be changed.  This effect 
10555     * requires that at least one new dimension, position or opacity setting must be passed in on
10556     * the config object in order for the function to have any effect.
10557     * Usage:
10558 <pre><code>
10559 // slide the element horizontally to x position 200 while changing the height and opacity
10560 el.shift({ x: 200, height: 50, opacity: .8 });
10561
10562 // common config options shown with default values.
10563 el.shift({
10564     width: [element's width],
10565     height: [element's height],
10566     x: [element's x position],
10567     y: [element's y position],
10568     opacity: [element's opacity],
10569     easing: 'easeOut',
10570     duration: .35
10571 });
10572 </code></pre>
10573     * @param {Object} options  Object literal with any of the Fx config options
10574     * @return {Roo.Element} The Element
10575     */
10576     shift : function(o){
10577         var el = this.getFxEl();
10578         o = o || {};
10579         el.queueFx(o, function(){
10580             var a = {}, w = o.width, h = o.height, x = o.x, y = o.y,  op = o.opacity;
10581             if(w !== undefined){
10582                 a.width = {to: this.adjustWidth(w)};
10583             }
10584             if(h !== undefined){
10585                 a.height = {to: this.adjustHeight(h)};
10586             }
10587             if(x !== undefined || y !== undefined){
10588                 a.points = {to: [
10589                     x !== undefined ? x : this.getX(),
10590                     y !== undefined ? y : this.getY()
10591                 ]};
10592             }
10593             if(op !== undefined){
10594                 a.opacity = {to: op};
10595             }
10596             if(o.xy !== undefined){
10597                 a.points = {to: o.xy};
10598             }
10599             arguments.callee.anim = this.fxanim(a,
10600                 o, 'motion', .35, "easeOut", function(){
10601                 el.afterFx(o);
10602             });
10603         });
10604         return this;
10605     },
10606
10607         /**
10608          * Slides the element while fading it out of view.  An anchor point can be optionally passed to set the 
10609          * ending point of the effect.
10610          * Usage:
10611          *<pre><code>
10612 // default: slide the element downward while fading out
10613 el.ghost();
10614
10615 // custom: slide the element out to the right with a 2-second duration
10616 el.ghost('r', { duration: 2 });
10617
10618 // common config options shown with default values
10619 el.ghost('b', {
10620     easing: 'easeOut',
10621     duration: .5
10622     remove: false,
10623     useDisplay: false
10624 });
10625 </code></pre>
10626          * @param {String} anchor (optional) One of the valid Fx anchor positions (defaults to bottom: 'b')
10627          * @param {Object} options (optional) Object literal with any of the Fx config options
10628          * @return {Roo.Element} The Element
10629          */
10630     ghost : function(anchor, o){
10631         var el = this.getFxEl();
10632         o = o || {};
10633
10634         el.queueFx(o, function(){
10635             anchor = anchor || "b";
10636
10637             // restore values after effect
10638             var r = this.getFxRestore();
10639             var w = this.getWidth(),
10640                 h = this.getHeight();
10641
10642             var st = this.dom.style;
10643
10644             var after = function(){
10645                 if(o.useDisplay){
10646                     el.setDisplayed(false);
10647                 }else{
10648                     el.hide();
10649                 }
10650
10651                 el.clearOpacity();
10652                 el.setPositioning(r.pos);
10653                 st.width = r.width;
10654                 st.height = r.height;
10655
10656                 el.afterFx(o);
10657             };
10658
10659             var a = {opacity: {to: 0}, points: {}}, pt = a.points;
10660             switch(anchor.toLowerCase()){
10661                 case "t":
10662                     pt.by = [0, -h];
10663                 break;
10664                 case "l":
10665                     pt.by = [-w, 0];
10666                 break;
10667                 case "r":
10668                     pt.by = [w, 0];
10669                 break;
10670                 case "b":
10671                     pt.by = [0, h];
10672                 break;
10673                 case "tl":
10674                     pt.by = [-w, -h];
10675                 break;
10676                 case "bl":
10677                     pt.by = [-w, h];
10678                 break;
10679                 case "br":
10680                     pt.by = [w, h];
10681                 break;
10682                 case "tr":
10683                     pt.by = [w, -h];
10684                 break;
10685             }
10686
10687             arguments.callee.anim = this.fxanim(a,
10688                 o,
10689                 'motion',
10690                 .5,
10691                 "easeOut", after);
10692         });
10693         return this;
10694     },
10695
10696         /**
10697          * Ensures that all effects queued after syncFx is called on the element are
10698          * run concurrently.  This is the opposite of {@link #sequenceFx}.
10699          * @return {Roo.Element} The Element
10700          */
10701     syncFx : function(){
10702         this.fxDefaults = Roo.apply(this.fxDefaults || {}, {
10703             block : false,
10704             concurrent : true,
10705             stopFx : false
10706         });
10707         return this;
10708     },
10709
10710         /**
10711          * Ensures that all effects queued after sequenceFx is called on the element are
10712          * run in sequence.  This is the opposite of {@link #syncFx}.
10713          * @return {Roo.Element} The Element
10714          */
10715     sequenceFx : function(){
10716         this.fxDefaults = Roo.apply(this.fxDefaults || {}, {
10717             block : false,
10718             concurrent : false,
10719             stopFx : false
10720         });
10721         return this;
10722     },
10723
10724         /* @private */
10725     nextFx : function(){
10726         var ef = this.fxQueue[0];
10727         if(ef){
10728             ef.call(this);
10729         }
10730     },
10731
10732         /**
10733          * Returns true if the element has any effects actively running or queued, else returns false.
10734          * @return {Boolean} True if element has active effects, else false
10735          */
10736     hasActiveFx : function(){
10737         return this.fxQueue && this.fxQueue[0];
10738     },
10739
10740         /**
10741          * Stops any running effects and clears the element's internal effects queue if it contains
10742          * any additional effects that haven't started yet.
10743          * @return {Roo.Element} The Element
10744          */
10745     stopFx : function(){
10746         if(this.hasActiveFx()){
10747             var cur = this.fxQueue[0];
10748             if(cur && cur.anim && cur.anim.isAnimated()){
10749                 this.fxQueue = [cur]; // clear out others
10750                 cur.anim.stop(true);
10751             }
10752         }
10753         return this;
10754     },
10755
10756         /* @private */
10757     beforeFx : function(o){
10758         if(this.hasActiveFx() && !o.concurrent){
10759            if(o.stopFx){
10760                this.stopFx();
10761                return true;
10762            }
10763            return false;
10764         }
10765         return true;
10766     },
10767
10768         /**
10769          * Returns true if the element is currently blocking so that no other effect can be queued
10770          * until this effect is finished, else returns false if blocking is not set.  This is commonly
10771          * used to ensure that an effect initiated by a user action runs to completion prior to the
10772          * same effect being restarted (e.g., firing only one effect even if the user clicks several times).
10773          * @return {Boolean} True if blocking, else false
10774          */
10775     hasFxBlock : function(){
10776         var q = this.fxQueue;
10777         return q && q[0] && q[0].block;
10778     },
10779
10780         /* @private */
10781     queueFx : function(o, fn){
10782         if(!this.fxQueue){
10783             this.fxQueue = [];
10784         }
10785         if(!this.hasFxBlock()){
10786             Roo.applyIf(o, this.fxDefaults);
10787             if(!o.concurrent){
10788                 var run = this.beforeFx(o);
10789                 fn.block = o.block;
10790                 this.fxQueue.push(fn);
10791                 if(run){
10792                     this.nextFx();
10793                 }
10794             }else{
10795                 fn.call(this);
10796             }
10797         }
10798         return this;
10799     },
10800
10801         /* @private */
10802     fxWrap : function(pos, o, vis){
10803         var wrap;
10804         if(!o.wrap || !(wrap = Roo.get(o.wrap))){
10805             var wrapXY;
10806             if(o.fixPosition){
10807                 wrapXY = this.getXY();
10808             }
10809             var div = document.createElement("div");
10810             div.style.visibility = vis;
10811             wrap = Roo.get(this.dom.parentNode.insertBefore(div, this.dom));
10812             wrap.setPositioning(pos);
10813             if(wrap.getStyle("position") == "static"){
10814                 wrap.position("relative");
10815             }
10816             this.clearPositioning('auto');
10817             wrap.clip();
10818             wrap.dom.appendChild(this.dom);
10819             if(wrapXY){
10820                 wrap.setXY(wrapXY);
10821             }
10822         }
10823         return wrap;
10824     },
10825
10826         /* @private */
10827     fxUnwrap : function(wrap, pos, o){
10828         this.clearPositioning();
10829         this.setPositioning(pos);
10830         if(!o.wrap){
10831             wrap.dom.parentNode.insertBefore(this.dom, wrap.dom);
10832             wrap.remove();
10833         }
10834     },
10835
10836         /* @private */
10837     getFxRestore : function(){
10838         var st = this.dom.style;
10839         return {pos: this.getPositioning(), width: st.width, height : st.height};
10840     },
10841
10842         /* @private */
10843     afterFx : function(o){
10844         if(o.afterStyle){
10845             this.applyStyles(o.afterStyle);
10846         }
10847         if(o.afterCls){
10848             this.addClass(o.afterCls);
10849         }
10850         if(o.remove === true){
10851             this.remove();
10852         }
10853         Roo.callback(o.callback, o.scope, [this]);
10854         if(!o.concurrent){
10855             this.fxQueue.shift();
10856             this.nextFx();
10857         }
10858     },
10859
10860         /* @private */
10861     getFxEl : function(){ // support for composite element fx
10862         return Roo.get(this.dom);
10863     },
10864
10865         /* @private */
10866     fxanim : function(args, opt, animType, defaultDur, defaultEase, cb){
10867         animType = animType || 'run';
10868         opt = opt || {};
10869         var anim = Roo.lib.Anim[animType](
10870             this.dom, args,
10871             (opt.duration || defaultDur) || .35,
10872             (opt.easing || defaultEase) || 'easeOut',
10873             function(){
10874                 Roo.callback(cb, this);
10875             },
10876             this
10877         );
10878         opt.anim = anim;
10879         return anim;
10880     }
10881 };
10882
10883 // backwords compat
10884 Roo.Fx.resize = Roo.Fx.scale;
10885
10886 //When included, Roo.Fx is automatically applied to Element so that all basic
10887 //effects are available directly via the Element API
10888 Roo.apply(Roo.Element.prototype, Roo.Fx);/*
10889  * Based on:
10890  * Ext JS Library 1.1.1
10891  * Copyright(c) 2006-2007, Ext JS, LLC.
10892  *
10893  * Originally Released Under LGPL - original licence link has changed is not relivant.
10894  *
10895  * Fork - LGPL
10896  * <script type="text/javascript">
10897  */
10898
10899
10900 /**
10901  * @class Roo.CompositeElement
10902  * Standard composite class. Creates a Roo.Element for every element in the collection.
10903  * <br><br>
10904  * <b>NOTE: Although they are not listed, this class supports all of the set/update methods of Roo.Element. All Roo.Element
10905  * actions will be performed on all the elements in this collection.</b>
10906  * <br><br>
10907  * All methods return <i>this</i> and can be chained.
10908  <pre><code>
10909  var els = Roo.select("#some-el div.some-class", true);
10910  // or select directly from an existing element
10911  var el = Roo.get('some-el');
10912  el.select('div.some-class', true);
10913
10914  els.setWidth(100); // all elements become 100 width
10915  els.hide(true); // all elements fade out and hide
10916  // or
10917  els.setWidth(100).hide(true);
10918  </code></pre>
10919  */
10920 Roo.CompositeElement = function(els){
10921     this.elements = [];
10922     this.addElements(els);
10923 };
10924 Roo.CompositeElement.prototype = {
10925     isComposite: true,
10926     addElements : function(els){
10927         if(!els) return this;
10928         if(typeof els == "string"){
10929             els = Roo.Element.selectorFunction(els);
10930         }
10931         var yels = this.elements;
10932         var index = yels.length-1;
10933         for(var i = 0, len = els.length; i < len; i++) {
10934                 yels[++index] = Roo.get(els[i]);
10935         }
10936         return this;
10937     },
10938
10939     /**
10940     * Clears this composite and adds the elements returned by the passed selector.
10941     * @param {String/Array} els A string CSS selector, an array of elements or an element
10942     * @return {CompositeElement} this
10943     */
10944     fill : function(els){
10945         this.elements = [];
10946         this.add(els);
10947         return this;
10948     },
10949
10950     /**
10951     * Filters this composite to only elements that match the passed selector.
10952     * @param {String} selector A string CSS selector
10953     * @return {CompositeElement} this
10954     */
10955     filter : function(selector){
10956         var els = [];
10957         this.each(function(el){
10958             if(el.is(selector)){
10959                 els[els.length] = el.dom;
10960             }
10961         });
10962         this.fill(els);
10963         return this;
10964     },
10965
10966     invoke : function(fn, args){
10967         var els = this.elements;
10968         for(var i = 0, len = els.length; i < len; i++) {
10969                 Roo.Element.prototype[fn].apply(els[i], args);
10970         }
10971         return this;
10972     },
10973     /**
10974     * Adds elements to this composite.
10975     * @param {String/Array} els A string CSS selector, an array of elements or an element
10976     * @return {CompositeElement} this
10977     */
10978     add : function(els){
10979         if(typeof els == "string"){
10980             this.addElements(Roo.Element.selectorFunction(els));
10981         }else if(els.length !== undefined){
10982             this.addElements(els);
10983         }else{
10984             this.addElements([els]);
10985         }
10986         return this;
10987     },
10988     /**
10989     * Calls the passed function passing (el, this, index) for each element in this composite.
10990     * @param {Function} fn The function to call
10991     * @param {Object} scope (optional) The <i>this</i> object (defaults to the element)
10992     * @return {CompositeElement} this
10993     */
10994     each : function(fn, scope){
10995         var els = this.elements;
10996         for(var i = 0, len = els.length; i < len; i++){
10997             if(fn.call(scope || els[i], els[i], this, i) === false) {
10998                 break;
10999             }
11000         }
11001         return this;
11002     },
11003
11004     /**
11005      * Returns the Element object at the specified index
11006      * @param {Number} index
11007      * @return {Roo.Element}
11008      */
11009     item : function(index){
11010         return this.elements[index] || null;
11011     },
11012
11013     /**
11014      * Returns the first Element
11015      * @return {Roo.Element}
11016      */
11017     first : function(){
11018         return this.item(0);
11019     },
11020
11021     /**
11022      * Returns the last Element
11023      * @return {Roo.Element}
11024      */
11025     last : function(){
11026         return this.item(this.elements.length-1);
11027     },
11028
11029     /**
11030      * Returns the number of elements in this composite
11031      * @return Number
11032      */
11033     getCount : function(){
11034         return this.elements.length;
11035     },
11036
11037     /**
11038      * Returns true if this composite contains the passed element
11039      * @return Boolean
11040      */
11041     contains : function(el){
11042         return this.indexOf(el) !== -1;
11043     },
11044
11045     /**
11046      * Returns true if this composite contains the passed element
11047      * @return Boolean
11048      */
11049     indexOf : function(el){
11050         return this.elements.indexOf(Roo.get(el));
11051     },
11052
11053
11054     /**
11055     * Removes the specified element(s).
11056     * @param {Mixed} el The id of an element, the Element itself, the index of the element in this composite
11057     * or an array of any of those.
11058     * @param {Boolean} removeDom (optional) True to also remove the element from the document
11059     * @return {CompositeElement} this
11060     */
11061     removeElement : function(el, removeDom){
11062         if(el instanceof Array){
11063             for(var i = 0, len = el.length; i < len; i++){
11064                 this.removeElement(el[i]);
11065             }
11066             return this;
11067         }
11068         var index = typeof el == 'number' ? el : this.indexOf(el);
11069         if(index !== -1){
11070             if(removeDom){
11071                 var d = this.elements[index];
11072                 if(d.dom){
11073                     d.remove();
11074                 }else{
11075                     d.parentNode.removeChild(d);
11076                 }
11077             }
11078             this.elements.splice(index, 1);
11079         }
11080         return this;
11081     },
11082
11083     /**
11084     * Replaces the specified element with the passed element.
11085     * @param {String/HTMLElement/Element/Number} el The id of an element, the Element itself, the index of the element in this composite
11086     * to replace.
11087     * @param {String/HTMLElement/Element} replacement The id of an element or the Element itself.
11088     * @param {Boolean} domReplace (Optional) True to remove and replace the element in the document too.
11089     * @return {CompositeElement} this
11090     */
11091     replaceElement : function(el, replacement, domReplace){
11092         var index = typeof el == 'number' ? el : this.indexOf(el);
11093         if(index !== -1){
11094             if(domReplace){
11095                 this.elements[index].replaceWith(replacement);
11096             }else{
11097                 this.elements.splice(index, 1, Roo.get(replacement))
11098             }
11099         }
11100         return this;
11101     },
11102
11103     /**
11104      * Removes all elements.
11105      */
11106     clear : function(){
11107         this.elements = [];
11108     }
11109 };
11110 (function(){
11111     Roo.CompositeElement.createCall = function(proto, fnName){
11112         if(!proto[fnName]){
11113             proto[fnName] = function(){
11114                 return this.invoke(fnName, arguments);
11115             };
11116         }
11117     };
11118     for(var fnName in Roo.Element.prototype){
11119         if(typeof Roo.Element.prototype[fnName] == "function"){
11120             Roo.CompositeElement.createCall(Roo.CompositeElement.prototype, fnName);
11121         }
11122     };
11123 })();
11124 /*
11125  * Based on:
11126  * Ext JS Library 1.1.1
11127  * Copyright(c) 2006-2007, Ext JS, LLC.
11128  *
11129  * Originally Released Under LGPL - original licence link has changed is not relivant.
11130  *
11131  * Fork - LGPL
11132  * <script type="text/javascript">
11133  */
11134
11135 /**
11136  * @class Roo.CompositeElementLite
11137  * @extends Roo.CompositeElement
11138  * Flyweight composite class. Reuses the same Roo.Element for element operations.
11139  <pre><code>
11140  var els = Roo.select("#some-el div.some-class");
11141  // or select directly from an existing element
11142  var el = Roo.get('some-el');
11143  el.select('div.some-class');
11144
11145  els.setWidth(100); // all elements become 100 width
11146  els.hide(true); // all elements fade out and hide
11147  // or
11148  els.setWidth(100).hide(true);
11149  </code></pre><br><br>
11150  * <b>NOTE: Although they are not listed, this class supports all of the set/update methods of Roo.Element. All Roo.Element
11151  * actions will be performed on all the elements in this collection.</b>
11152  */
11153 Roo.CompositeElementLite = function(els){
11154     Roo.CompositeElementLite.superclass.constructor.call(this, els);
11155     this.el = new Roo.Element.Flyweight();
11156 };
11157 Roo.extend(Roo.CompositeElementLite, Roo.CompositeElement, {
11158     addElements : function(els){
11159         if(els){
11160             if(els instanceof Array){
11161                 this.elements = this.elements.concat(els);
11162             }else{
11163                 var yels = this.elements;
11164                 var index = yels.length-1;
11165                 for(var i = 0, len = els.length; i < len; i++) {
11166                     yels[++index] = els[i];
11167                 }
11168             }
11169         }
11170         return this;
11171     },
11172     invoke : function(fn, args){
11173         var els = this.elements;
11174         var el = this.el;
11175         for(var i = 0, len = els.length; i < len; i++) {
11176             el.dom = els[i];
11177                 Roo.Element.prototype[fn].apply(el, args);
11178         }
11179         return this;
11180     },
11181     /**
11182      * Returns a flyweight Element of the dom element object at the specified index
11183      * @param {Number} index
11184      * @return {Roo.Element}
11185      */
11186     item : function(index){
11187         if(!this.elements[index]){
11188             return null;
11189         }
11190         this.el.dom = this.elements[index];
11191         return this.el;
11192     },
11193
11194     // fixes scope with flyweight
11195     addListener : function(eventName, handler, scope, opt){
11196         var els = this.elements;
11197         for(var i = 0, len = els.length; i < len; i++) {
11198             Roo.EventManager.on(els[i], eventName, handler, scope || els[i], opt);
11199         }
11200         return this;
11201     },
11202
11203     /**
11204     * Calls the passed function passing (el, this, index) for each element in this composite. <b>The element
11205     * passed is the flyweight (shared) Roo.Element instance, so if you require a
11206     * a reference to the dom node, use el.dom.</b>
11207     * @param {Function} fn The function to call
11208     * @param {Object} scope (optional) The <i>this</i> object (defaults to the element)
11209     * @return {CompositeElement} this
11210     */
11211     each : function(fn, scope){
11212         var els = this.elements;
11213         var el = this.el;
11214         for(var i = 0, len = els.length; i < len; i++){
11215             el.dom = els[i];
11216                 if(fn.call(scope || el, el, this, i) === false){
11217                 break;
11218             }
11219         }
11220         return this;
11221     },
11222
11223     indexOf : function(el){
11224         return this.elements.indexOf(Roo.getDom(el));
11225     },
11226
11227     replaceElement : function(el, replacement, domReplace){
11228         var index = typeof el == 'number' ? el : this.indexOf(el);
11229         if(index !== -1){
11230             replacement = Roo.getDom(replacement);
11231             if(domReplace){
11232                 var d = this.elements[index];
11233                 d.parentNode.insertBefore(replacement, d);
11234                 d.parentNode.removeChild(d);
11235             }
11236             this.elements.splice(index, 1, replacement);
11237         }
11238         return this;
11239     }
11240 });
11241 Roo.CompositeElementLite.prototype.on = Roo.CompositeElementLite.prototype.addListener;
11242
11243 /*
11244  * Based on:
11245  * Ext JS Library 1.1.1
11246  * Copyright(c) 2006-2007, Ext JS, LLC.
11247  *
11248  * Originally Released Under LGPL - original licence link has changed is not relivant.
11249  *
11250  * Fork - LGPL
11251  * <script type="text/javascript">
11252  */
11253
11254  
11255
11256 /**
11257  * @class Roo.data.Connection
11258  * @extends Roo.util.Observable
11259  * The class encapsulates a connection to the page's originating domain, allowing requests to be made
11260  * either to a configured URL, or to a URL specified at request time.<br><br>
11261  * <p>
11262  * Requests made by this class are asynchronous, and will return immediately. No data from
11263  * the server will be available to the statement immediately following the {@link #request} call.
11264  * To process returned data, use a callback in the request options object, or an event listener.</p><br>
11265  * <p>
11266  * Note: If you are doing a file upload, you will not get a normal response object sent back to
11267  * your callback or event handler.  Since the upload is handled via in IFRAME, there is no XMLHttpRequest.
11268  * The response object is created using the innerHTML of the IFRAME's document as the responseText
11269  * property and, if present, the IFRAME's XML document as the responseXML property.</p><br>
11270  * This means that a valid XML or HTML document must be returned. If JSON data is required, it is suggested
11271  * that it be placed either inside a &lt;textarea> in an HTML document and retrieved from the responseText
11272  * using a regex, or inside a CDATA section in an XML document and retrieved from the responseXML using
11273  * standard DOM methods.
11274  * @constructor
11275  * @param {Object} config a configuration object.
11276  */
11277 Roo.data.Connection = function(config){
11278     Roo.apply(this, config);
11279     this.addEvents({
11280         /**
11281          * @event beforerequest
11282          * Fires before a network request is made to retrieve a data object.
11283          * @param {Connection} conn This Connection object.
11284          * @param {Object} options The options config object passed to the {@link #request} method.
11285          */
11286         "beforerequest" : true,
11287         /**
11288          * @event requestcomplete
11289          * Fires if the request was successfully completed.
11290          * @param {Connection} conn This Connection object.
11291          * @param {Object} response The XHR object containing the response data.
11292          * See {@link http://www.w3.org/TR/XMLHttpRequest/} for details.
11293          * @param {Object} options The options config object passed to the {@link #request} method.
11294          */
11295         "requestcomplete" : true,
11296         /**
11297          * @event requestexception
11298          * Fires if an error HTTP status was returned from the server.
11299          * See {@link http://www.w3.org/Protocols/rfc2616/rfc2616-sec10.html} for details of HTTP status codes.
11300          * @param {Connection} conn This Connection object.
11301          * @param {Object} response The XHR object containing the response data.
11302          * See {@link http://www.w3.org/TR/XMLHttpRequest/} for details.
11303          * @param {Object} options The options config object passed to the {@link #request} method.
11304          */
11305         "requestexception" : true
11306     });
11307     Roo.data.Connection.superclass.constructor.call(this);
11308 };
11309
11310 Roo.extend(Roo.data.Connection, Roo.util.Observable, {
11311     /**
11312      * @cfg {String} url (Optional) The default URL to be used for requests to the server. (defaults to undefined)
11313      */
11314     /**
11315      * @cfg {Object} extraParams (Optional) An object containing properties which are used as
11316      * extra parameters to each request made by this object. (defaults to undefined)
11317      */
11318     /**
11319      * @cfg {Object} defaultHeaders (Optional) An object containing request headers which are added
11320      *  to each request made by this object. (defaults to undefined)
11321      */
11322     /**
11323      * @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)
11324      */
11325     /**
11326      * @cfg {Number} timeout (Optional) The timeout in milliseconds to be used for requests. (defaults to 30000)
11327      */
11328     timeout : 30000,
11329     /**
11330      * @cfg {Boolean} autoAbort (Optional) Whether this request should abort any pending requests. (defaults to false)
11331      * @type Boolean
11332      */
11333     autoAbort:false,
11334
11335     /**
11336      * @cfg {Boolean} disableCaching (Optional) True to add a unique cache-buster param to GET requests. (defaults to true)
11337      * @type Boolean
11338      */
11339     disableCaching: true,
11340
11341     /**
11342      * Sends an HTTP request to a remote server.
11343      * @param {Object} options An object which may contain the following properties:<ul>
11344      * <li><b>url</b> {String} (Optional) The URL to which to send the request. Defaults to configured URL</li>
11345      * <li><b>params</b> {Object/String/Function} (Optional) An object containing properties which are used as parameters to the
11346      * request, a url encoded string or a function to call to get either.</li>
11347      * <li><b>method</b> {String} (Optional) The HTTP method to use for the request. Defaults to the configured method, or
11348      * if no method was configured, "GET" if no parameters are being sent, and "POST" if parameters are being sent.</li>
11349      * <li><b>callback</b> {Function} (Optional) The function to be called upon receipt of the HTTP response.
11350      * The callback is called regardless of success or failure and is passed the following parameters:<ul>
11351      * <li>options {Object} The parameter to the request call.</li>
11352      * <li>success {Boolean} True if the request succeeded.</li>
11353      * <li>response {Object} The XMLHttpRequest object containing the response data.</li>
11354      * </ul></li>
11355      * <li><b>success</b> {Function} (Optional) The function to be called upon success of the request.
11356      * The callback is passed the following parameters:<ul>
11357      * <li>response {Object} The XMLHttpRequest object containing the response data.</li>
11358      * <li>options {Object} The parameter to the request call.</li>
11359      * </ul></li>
11360      * <li><b>failure</b> {Function} (Optional) The function to be called upon failure of the request.
11361      * The callback is passed the following parameters:<ul>
11362      * <li>response {Object} The XMLHttpRequest object containing the response data.</li>
11363      * <li>options {Object} The parameter to the request call.</li>
11364      * </ul></li>
11365      * <li><b>scope</b> {Object} (Optional) The scope in which to execute the callbacks: The "this" object
11366      * for the callback function. Defaults to the browser window.</li>
11367      * <li><b>form</b> {Object/String} (Optional) A form object or id to pull parameters from.</li>
11368      * <li><b>isUpload</b> {Boolean} (Optional) True if the form object is a file upload (will usually be automatically detected).</li>
11369      * <li><b>headers</b> {Object} (Optional) Request headers to set for the request.</li>
11370      * <li><b>xmlData</b> {Object} (Optional) XML document to use for the post. Note: This will be used instead of
11371      * params for the post data. Any params will be appended to the URL.</li>
11372      * <li><b>disableCaching</b> {Boolean} (Optional) True to add a unique cache-buster param to GET requests.</li>
11373      * </ul>
11374      * @return {Number} transactionId
11375      */
11376     request : function(o){
11377         if(this.fireEvent("beforerequest", this, o) !== false){
11378             var p = o.params;
11379
11380             if(typeof p == "function"){
11381                 p = p.call(o.scope||window, o);
11382             }
11383             if(typeof p == "object"){
11384                 p = Roo.urlEncode(o.params);
11385             }
11386             if(this.extraParams){
11387                 var extras = Roo.urlEncode(this.extraParams);
11388                 p = p ? (p + '&' + extras) : extras;
11389             }
11390
11391             var url = o.url || this.url;
11392             if(typeof url == 'function'){
11393                 url = url.call(o.scope||window, o);
11394             }
11395
11396             if(o.form){
11397                 var form = Roo.getDom(o.form);
11398                 url = url || form.action;
11399
11400                 var enctype = form.getAttribute("enctype");
11401                 if(o.isUpload || (enctype && enctype.toLowerCase() == 'multipart/form-data')){
11402                     return this.doFormUpload(o, p, url);
11403                 }
11404                 var f = Roo.lib.Ajax.serializeForm(form);
11405                 p = p ? (p + '&' + f) : f;
11406             }
11407
11408             var hs = o.headers;
11409             if(this.defaultHeaders){
11410                 hs = Roo.apply(hs || {}, this.defaultHeaders);
11411                 if(!o.headers){
11412                     o.headers = hs;
11413                 }
11414             }
11415
11416             var cb = {
11417                 success: this.handleResponse,
11418                 failure: this.handleFailure,
11419                 scope: this,
11420                 argument: {options: o},
11421                 timeout : o.timeout || this.timeout
11422             };
11423
11424             var method = o.method||this.method||(p ? "POST" : "GET");
11425
11426             if(method == 'GET' && (this.disableCaching && o.disableCaching !== false) || o.disableCaching === true){
11427                 url += (url.indexOf('?') != -1 ? '&' : '?') + '_dc=' + (new Date().getTime());
11428             }
11429
11430             if(typeof o.autoAbort == 'boolean'){ // options gets top priority
11431                 if(o.autoAbort){
11432                     this.abort();
11433                 }
11434             }else if(this.autoAbort !== false){
11435                 this.abort();
11436             }
11437
11438             if((method == 'GET' && p) || o.xmlData){
11439                 url += (url.indexOf('?') != -1 ? '&' : '?') + p;
11440                 p = '';
11441             }
11442             this.transId = Roo.lib.Ajax.request(method, url, cb, p, o);
11443             return this.transId;
11444         }else{
11445             Roo.callback(o.callback, o.scope, [o, null, null]);
11446             return null;
11447         }
11448     },
11449
11450     /**
11451      * Determine whether this object has a request outstanding.
11452      * @param {Number} transactionId (Optional) defaults to the last transaction
11453      * @return {Boolean} True if there is an outstanding request.
11454      */
11455     isLoading : function(transId){
11456         if(transId){
11457             return Roo.lib.Ajax.isCallInProgress(transId);
11458         }else{
11459             return this.transId ? true : false;
11460         }
11461     },
11462
11463     /**
11464      * Aborts any outstanding request.
11465      * @param {Number} transactionId (Optional) defaults to the last transaction
11466      */
11467     abort : function(transId){
11468         if(transId || this.isLoading()){
11469             Roo.lib.Ajax.abort(transId || this.transId);
11470         }
11471     },
11472
11473     // private
11474     handleResponse : function(response){
11475         this.transId = false;
11476         var options = response.argument.options;
11477         response.argument = options ? options.argument : null;
11478         this.fireEvent("requestcomplete", this, response, options);
11479         Roo.callback(options.success, options.scope, [response, options]);
11480         Roo.callback(options.callback, options.scope, [options, true, response]);
11481     },
11482
11483     // private
11484     handleFailure : function(response, e){
11485         this.transId = false;
11486         var options = response.argument.options;
11487         response.argument = options ? options.argument : null;
11488         this.fireEvent("requestexception", this, response, options, e);
11489         Roo.callback(options.failure, options.scope, [response, options]);
11490         Roo.callback(options.callback, options.scope, [options, false, response]);
11491     },
11492
11493     // private
11494     doFormUpload : function(o, ps, url){
11495         var id = Roo.id();
11496         var frame = document.createElement('iframe');
11497         frame.id = id;
11498         frame.name = id;
11499         frame.className = 'x-hidden';
11500         if(Roo.isIE){
11501             frame.src = Roo.SSL_SECURE_URL;
11502         }
11503         document.body.appendChild(frame);
11504
11505         if(Roo.isIE){
11506            document.frames[id].name = id;
11507         }
11508
11509         var form = Roo.getDom(o.form);
11510         form.target = id;
11511         form.method = 'POST';
11512         form.enctype = form.encoding = 'multipart/form-data';
11513         if(url){
11514             form.action = url;
11515         }
11516
11517         var hiddens, hd;
11518         if(ps){ // add dynamic params
11519             hiddens = [];
11520             ps = Roo.urlDecode(ps, false);
11521             for(var k in ps){
11522                 if(ps.hasOwnProperty(k)){
11523                     hd = document.createElement('input');
11524                     hd.type = 'hidden';
11525                     hd.name = k;
11526                     hd.value = ps[k];
11527                     form.appendChild(hd);
11528                     hiddens.push(hd);
11529                 }
11530             }
11531         }
11532
11533         function cb(){
11534             var r = {  // bogus response object
11535                 responseText : '',
11536                 responseXML : null
11537             };
11538
11539             r.argument = o ? o.argument : null;
11540
11541             try { //
11542                 var doc;
11543                 if(Roo.isIE){
11544                     doc = frame.contentWindow.document;
11545                 }else {
11546                     doc = (frame.contentDocument || window.frames[id].document);
11547                 }
11548                 if(doc && doc.body){
11549                     r.responseText = doc.body.innerHTML;
11550                 }
11551                 if(doc && doc.XMLDocument){
11552                     r.responseXML = doc.XMLDocument;
11553                 }else {
11554                     r.responseXML = doc;
11555                 }
11556             }
11557             catch(e) {
11558                 // ignore
11559             }
11560
11561             Roo.EventManager.removeListener(frame, 'load', cb, this);
11562
11563             this.fireEvent("requestcomplete", this, r, o);
11564             Roo.callback(o.success, o.scope, [r, o]);
11565             Roo.callback(o.callback, o.scope, [o, true, r]);
11566
11567             setTimeout(function(){document.body.removeChild(frame);}, 100);
11568         }
11569
11570         Roo.EventManager.on(frame, 'load', cb, this);
11571         form.submit();
11572
11573         if(hiddens){ // remove dynamic params
11574             for(var i = 0, len = hiddens.length; i < len; i++){
11575                 form.removeChild(hiddens[i]);
11576             }
11577         }
11578     }
11579 });
11580 /*
11581  * Based on:
11582  * Ext JS Library 1.1.1
11583  * Copyright(c) 2006-2007, Ext JS, LLC.
11584  *
11585  * Originally Released Under LGPL - original licence link has changed is not relivant.
11586  *
11587  * Fork - LGPL
11588  * <script type="text/javascript">
11589  */
11590  
11591 /**
11592  * Global Ajax request class.
11593  * 
11594  * @class Roo.Ajax
11595  * @extends Roo.data.Connection
11596  * @static
11597  * 
11598  * @cfg {String} url  The default URL to be used for requests to the server. (defaults to undefined)
11599  * @cfg {Object} extraParams  An object containing properties which are used as extra parameters to each request made by this object. (defaults to undefined)
11600  * @cfg {Object} defaultHeaders  An object containing request headers which are added to each request made by this object. (defaults to undefined)
11601  * @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)
11602  * @cfg {Number} timeout (Optional) The timeout in milliseconds to be used for requests. (defaults to 30000)
11603  * @cfg {Boolean} autoAbort (Optional) Whether a new request should abort any pending requests. (defaults to false)
11604  * @cfg {Boolean} disableCaching (Optional)   True to add a unique cache-buster param to GET requests. (defaults to true)
11605  */
11606 Roo.Ajax = new Roo.data.Connection({
11607     // fix up the docs
11608     /**
11609      * @scope Roo.Ajax
11610      * @type {Boolear} 
11611      */
11612     autoAbort : false,
11613
11614     /**
11615      * Serialize the passed form into a url encoded string
11616      * @scope Roo.Ajax
11617      * @param {String/HTMLElement} form
11618      * @return {String}
11619      */
11620     serializeForm : function(form){
11621         return Roo.lib.Ajax.serializeForm(form);
11622     }
11623 });/*
11624  * Based on:
11625  * Ext JS Library 1.1.1
11626  * Copyright(c) 2006-2007, Ext JS, LLC.
11627  *
11628  * Originally Released Under LGPL - original licence link has changed is not relivant.
11629  *
11630  * Fork - LGPL
11631  * <script type="text/javascript">
11632  */
11633
11634  
11635 /**
11636  * @class Roo.UpdateManager
11637  * @extends Roo.util.Observable
11638  * Provides AJAX-style update for Element object.<br><br>
11639  * Usage:<br>
11640  * <pre><code>
11641  * // Get it from a Roo.Element object
11642  * var el = Roo.get("foo");
11643  * var mgr = el.getUpdateManager();
11644  * mgr.update("http://myserver.com/index.php", "param1=1&amp;param2=2");
11645  * ...
11646  * mgr.formUpdate("myFormId", "http://myserver.com/index.php");
11647  * <br>
11648  * // or directly (returns the same UpdateManager instance)
11649  * var mgr = new Roo.UpdateManager("myElementId");
11650  * mgr.startAutoRefresh(60, "http://myserver.com/index.php");
11651  * mgr.on("update", myFcnNeedsToKnow);
11652  * <br>
11653    // short handed call directly from the element object
11654    Roo.get("foo").load({
11655         url: "bar.php",
11656         scripts:true,
11657         params: "for=bar",
11658         text: "Loading Foo..."
11659    });
11660  * </code></pre>
11661  * @constructor
11662  * Create new UpdateManager directly.
11663  * @param {String/HTMLElement/Roo.Element} el The element to update
11664  * @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).
11665  */
11666 Roo.UpdateManager = function(el, forceNew){
11667     el = Roo.get(el);
11668     if(!forceNew && el.updateManager){
11669         return el.updateManager;
11670     }
11671     /**
11672      * The Element object
11673      * @type Roo.Element
11674      */
11675     this.el = el;
11676     /**
11677      * Cached url to use for refreshes. Overwritten every time update() is called unless "discardUrl" param is set to true.
11678      * @type String
11679      */
11680     this.defaultUrl = null;
11681
11682     this.addEvents({
11683         /**
11684          * @event beforeupdate
11685          * Fired before an update is made, return false from your handler and the update is cancelled.
11686          * @param {Roo.Element} el
11687          * @param {String/Object/Function} url
11688          * @param {String/Object} params
11689          */
11690         "beforeupdate": true,
11691         /**
11692          * @event update
11693          * Fired after successful update is made.
11694          * @param {Roo.Element} el
11695          * @param {Object} oResponseObject The response Object
11696          */
11697         "update": true,
11698         /**
11699          * @event failure
11700          * Fired on update failure.
11701          * @param {Roo.Element} el
11702          * @param {Object} oResponseObject The response Object
11703          */
11704         "failure": true
11705     });
11706     var d = Roo.UpdateManager.defaults;
11707     /**
11708      * Blank page URL to use with SSL file uploads (Defaults to Roo.UpdateManager.defaults.sslBlankUrl or "about:blank").
11709      * @type String
11710      */
11711     this.sslBlankUrl = d.sslBlankUrl;
11712     /**
11713      * Whether to append unique parameter on get request to disable caching (Defaults to Roo.UpdateManager.defaults.disableCaching or false).
11714      * @type Boolean
11715      */
11716     this.disableCaching = d.disableCaching;
11717     /**
11718      * Text for loading indicator (Defaults to Roo.UpdateManager.defaults.indicatorText or '&lt;div class="loading-indicator"&gt;Loading...&lt;/div&gt;').
11719      * @type String
11720      */
11721     this.indicatorText = d.indicatorText;
11722     /**
11723      * Whether to show indicatorText when loading (Defaults to Roo.UpdateManager.defaults.showLoadIndicator or true).
11724      * @type String
11725      */
11726     this.showLoadIndicator = d.showLoadIndicator;
11727     /**
11728      * Timeout for requests or form posts in seconds (Defaults to Roo.UpdateManager.defaults.timeout or 30 seconds).
11729      * @type Number
11730      */
11731     this.timeout = d.timeout;
11732
11733     /**
11734      * True to process scripts in the output (Defaults to Roo.UpdateManager.defaults.loadScripts (false)).
11735      * @type Boolean
11736      */
11737     this.loadScripts = d.loadScripts;
11738
11739     /**
11740      * Transaction object of current executing transaction
11741      */
11742     this.transaction = null;
11743
11744     /**
11745      * @private
11746      */
11747     this.autoRefreshProcId = null;
11748     /**
11749      * Delegate for refresh() prebound to "this", use myUpdater.refreshDelegate.createCallback(arg1, arg2) to bind arguments
11750      * @type Function
11751      */
11752     this.refreshDelegate = this.refresh.createDelegate(this);
11753     /**
11754      * Delegate for update() prebound to "this", use myUpdater.updateDelegate.createCallback(arg1, arg2) to bind arguments
11755      * @type Function
11756      */
11757     this.updateDelegate = this.update.createDelegate(this);
11758     /**
11759      * Delegate for formUpdate() prebound to "this", use myUpdater.formUpdateDelegate.createCallback(arg1, arg2) to bind arguments
11760      * @type Function
11761      */
11762     this.formUpdateDelegate = this.formUpdate.createDelegate(this);
11763     /**
11764      * @private
11765      */
11766     this.successDelegate = this.processSuccess.createDelegate(this);
11767     /**
11768      * @private
11769      */
11770     this.failureDelegate = this.processFailure.createDelegate(this);
11771
11772     if(!this.renderer){
11773      /**
11774       * The renderer for this UpdateManager. Defaults to {@link Roo.UpdateManager.BasicRenderer}.
11775       */
11776     this.renderer = new Roo.UpdateManager.BasicRenderer();
11777     }
11778     
11779     Roo.UpdateManager.superclass.constructor.call(this);
11780 };
11781
11782 Roo.extend(Roo.UpdateManager, Roo.util.Observable, {
11783     /**
11784      * Get the Element this UpdateManager is bound to
11785      * @return {Roo.Element} The element
11786      */
11787     getEl : function(){
11788         return this.el;
11789     },
11790     /**
11791      * Performs an async request, updating this element with the response. If params are specified it uses POST, otherwise it uses GET.
11792      * @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:
11793 <pre><code>
11794 um.update({<br/>
11795     url: "your-url.php",<br/>
11796     params: {param1: "foo", param2: "bar"}, // or a URL encoded string<br/>
11797     callback: yourFunction,<br/>
11798     scope: yourObject, //(optional scope)  <br/>
11799     discardUrl: false, <br/>
11800     nocache: false,<br/>
11801     text: "Loading...",<br/>
11802     timeout: 30,<br/>
11803     scripts: false<br/>
11804 });
11805 </code></pre>
11806      * The only required property is url. The optional properties nocache, text and scripts
11807      * are shorthand for disableCaching, indicatorText and loadScripts and are used to set their associated property on this UpdateManager instance.
11808      * @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}
11809      * @param {Function} callback (optional) Callback when transaction is complete - called with signature (oElement, bSuccess, oResponse)
11810      * @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.
11811      */
11812     update : function(url, params, callback, discardUrl){
11813         if(this.fireEvent("beforeupdate", this.el, url, params) !== false){
11814             var method = this.method,
11815                 cfg;
11816             if(typeof url == "object"){ // must be config object
11817                 cfg = url;
11818                 url = cfg.url;
11819                 params = params || cfg.params;
11820                 callback = callback || cfg.callback;
11821                 discardUrl = discardUrl || cfg.discardUrl;
11822                 if(callback && cfg.scope){
11823                     callback = callback.createDelegate(cfg.scope);
11824                 }
11825                 if(typeof cfg.method != "undefined"){method = cfg.method;};
11826                 if(typeof cfg.nocache != "undefined"){this.disableCaching = cfg.nocache;};
11827                 if(typeof cfg.text != "undefined"){this.indicatorText = '<div class="loading-indicator">'+cfg.text+"</div>";};
11828                 if(typeof cfg.scripts != "undefined"){this.loadScripts = cfg.scripts;};
11829                 if(typeof cfg.timeout != "undefined"){this.timeout = cfg.timeout;};
11830             }
11831             this.showLoading();
11832             if(!discardUrl){
11833                 this.defaultUrl = url;
11834             }
11835             if(typeof url == "function"){
11836                 url = url.call(this);
11837             }
11838
11839             method = method || (params ? "POST" : "GET");
11840             if(method == "GET"){
11841                 url = this.prepareUrl(url);
11842             }
11843
11844             var o = Roo.apply(cfg ||{}, {
11845                 url : url,
11846                 params: params,
11847                 success: this.successDelegate,
11848                 failure: this.failureDelegate,
11849                 callback: undefined,
11850                 timeout: (this.timeout*1000),
11851                 argument: {"url": url, "form": null, "callback": callback, "params": params}
11852             });
11853             Roo.log("updated manager called with timeout of " + o.timeout);
11854             this.transaction = Roo.Ajax.request(o);
11855         }
11856     },
11857
11858     /**
11859      * 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.
11860      * Uses this.sslBlankUrl for SSL file uploads to prevent IE security warning.
11861      * @param {String/HTMLElement} form The form Id or form element
11862      * @param {String} url (optional) The url to pass the form to. If omitted the action attribute on the form will be used.
11863      * @param {Boolean} reset (optional) Whether to try to reset the form after the update
11864      * @param {Function} callback (optional) Callback when transaction is complete - called with signature (oElement, bSuccess, oResponse)
11865      */
11866     formUpdate : function(form, url, reset, callback){
11867         if(this.fireEvent("beforeupdate", this.el, form, url) !== false){
11868             if(typeof url == "function"){
11869                 url = url.call(this);
11870             }
11871             form = Roo.getDom(form);
11872             this.transaction = Roo.Ajax.request({
11873                 form: form,
11874                 url:url,
11875                 success: this.successDelegate,
11876                 failure: this.failureDelegate,
11877                 timeout: (this.timeout*1000),
11878                 argument: {"url": url, "form": form, "callback": callback, "reset": reset}
11879             });
11880             this.showLoading.defer(1, this);
11881         }
11882     },
11883
11884     /**
11885      * Refresh the element with the last used url or defaultUrl. If there is no url, it returns immediately
11886      * @param {Function} callback (optional) Callback when transaction is complete - called with signature (oElement, bSuccess)
11887      */
11888     refresh : function(callback){
11889         if(this.defaultUrl == null){
11890             return;
11891         }
11892         this.update(this.defaultUrl, null, callback, true);
11893     },
11894
11895     /**
11896      * Set this element to auto refresh.
11897      * @param {Number} interval How often to update (in seconds).
11898      * @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)
11899      * @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}
11900      * @param {Function} callback (optional) Callback when transaction is complete - called with signature (oElement, bSuccess)
11901      * @param {Boolean} refreshNow (optional) Whether to execute the refresh now, or wait the interval
11902      */
11903     startAutoRefresh : function(interval, url, params, callback, refreshNow){
11904         if(refreshNow){
11905             this.update(url || this.defaultUrl, params, callback, true);
11906         }
11907         if(this.autoRefreshProcId){
11908             clearInterval(this.autoRefreshProcId);
11909         }
11910         this.autoRefreshProcId = setInterval(this.update.createDelegate(this, [url || this.defaultUrl, params, callback, true]), interval*1000);
11911     },
11912
11913     /**
11914      * Stop auto refresh on this element.
11915      */
11916      stopAutoRefresh : function(){
11917         if(this.autoRefreshProcId){
11918             clearInterval(this.autoRefreshProcId);
11919             delete this.autoRefreshProcId;
11920         }
11921     },
11922
11923     isAutoRefreshing : function(){
11924        return this.autoRefreshProcId ? true : false;
11925     },
11926     /**
11927      * Called to update the element to "Loading" state. Override to perform custom action.
11928      */
11929     showLoading : function(){
11930         if(this.showLoadIndicator){
11931             this.el.update(this.indicatorText);
11932         }
11933     },
11934
11935     /**
11936      * Adds unique parameter to query string if disableCaching = true
11937      * @private
11938      */
11939     prepareUrl : function(url){
11940         if(this.disableCaching){
11941             var append = "_dc=" + (new Date().getTime());
11942             if(url.indexOf("?") !== -1){
11943                 url += "&" + append;
11944             }else{
11945                 url += "?" + append;
11946             }
11947         }
11948         return url;
11949     },
11950
11951     /**
11952      * @private
11953      */
11954     processSuccess : function(response){
11955         this.transaction = null;
11956         if(response.argument.form && response.argument.reset){
11957             try{ // put in try/catch since some older FF releases had problems with this
11958                 response.argument.form.reset();
11959             }catch(e){}
11960         }
11961         if(this.loadScripts){
11962             this.renderer.render(this.el, response, this,
11963                 this.updateComplete.createDelegate(this, [response]));
11964         }else{
11965             this.renderer.render(this.el, response, this);
11966             this.updateComplete(response);
11967         }
11968     },
11969
11970     updateComplete : function(response){
11971         this.fireEvent("update", this.el, response);
11972         if(typeof response.argument.callback == "function"){
11973             response.argument.callback(this.el, true, response);
11974         }
11975     },
11976
11977     /**
11978      * @private
11979      */
11980     processFailure : function(response){
11981         this.transaction = null;
11982         this.fireEvent("failure", this.el, response);
11983         if(typeof response.argument.callback == "function"){
11984             response.argument.callback(this.el, false, response);
11985         }
11986     },
11987
11988     /**
11989      * Set the content renderer for this UpdateManager. See {@link Roo.UpdateManager.BasicRenderer#render} for more details.
11990      * @param {Object} renderer The object implementing the render() method
11991      */
11992     setRenderer : function(renderer){
11993         this.renderer = renderer;
11994     },
11995
11996     getRenderer : function(){
11997        return this.renderer;
11998     },
11999
12000     /**
12001      * Set the defaultUrl used for updates
12002      * @param {String/Function} defaultUrl The url or a function to call to get the url
12003      */
12004     setDefaultUrl : function(defaultUrl){
12005         this.defaultUrl = defaultUrl;
12006     },
12007
12008     /**
12009      * Aborts the executing transaction
12010      */
12011     abort : function(){
12012         if(this.transaction){
12013             Roo.Ajax.abort(this.transaction);
12014         }
12015     },
12016
12017     /**
12018      * Returns true if an update is in progress
12019      * @return {Boolean}
12020      */
12021     isUpdating : function(){
12022         if(this.transaction){
12023             return Roo.Ajax.isLoading(this.transaction);
12024         }
12025         return false;
12026     }
12027 });
12028
12029 /**
12030  * @class Roo.UpdateManager.defaults
12031  * @static (not really - but it helps the doc tool)
12032  * The defaults collection enables customizing the default properties of UpdateManager
12033  */
12034    Roo.UpdateManager.defaults = {
12035        /**
12036          * Timeout for requests or form posts in seconds (Defaults 30 seconds).
12037          * @type Number
12038          */
12039          timeout : 30,
12040
12041          /**
12042          * True to process scripts by default (Defaults to false).
12043          * @type Boolean
12044          */
12045         loadScripts : false,
12046
12047         /**
12048         * Blank page URL to use with SSL file uploads (Defaults to "javascript:false").
12049         * @type String
12050         */
12051         sslBlankUrl : (Roo.SSL_SECURE_URL || "javascript:false"),
12052         /**
12053          * Whether to append unique parameter on get request to disable caching (Defaults to false).
12054          * @type Boolean
12055          */
12056         disableCaching : false,
12057         /**
12058          * Whether to show indicatorText when loading (Defaults to true).
12059          * @type Boolean
12060          */
12061         showLoadIndicator : true,
12062         /**
12063          * Text for loading indicator (Defaults to '&lt;div class="loading-indicator"&gt;Loading...&lt;/div&gt;').
12064          * @type String
12065          */
12066         indicatorText : '<div class="loading-indicator">Loading...</div>'
12067    };
12068
12069 /**
12070  * Static convenience method. This method is deprecated in favor of el.load({url:'foo.php', ...}).
12071  *Usage:
12072  * <pre><code>Roo.UpdateManager.updateElement("my-div", "stuff.php");</code></pre>
12073  * @param {String/HTMLElement/Roo.Element} el The element to update
12074  * @param {String} url The url
12075  * @param {String/Object} params (optional) Url encoded param string or an object of name/value pairs
12076  * @param {Object} options (optional) A config object with any of the UpdateManager properties you want to set - for example: {disableCaching:true, indicatorText: "Loading data..."}
12077  * @static
12078  * @deprecated
12079  * @member Roo.UpdateManager
12080  */
12081 Roo.UpdateManager.updateElement = function(el, url, params, options){
12082     var um = Roo.get(el, true).getUpdateManager();
12083     Roo.apply(um, options);
12084     um.update(url, params, options ? options.callback : null);
12085 };
12086 // alias for backwards compat
12087 Roo.UpdateManager.update = Roo.UpdateManager.updateElement;
12088 /**
12089  * @class Roo.UpdateManager.BasicRenderer
12090  * Default Content renderer. Updates the elements innerHTML with the responseText.
12091  */
12092 Roo.UpdateManager.BasicRenderer = function(){};
12093
12094 Roo.UpdateManager.BasicRenderer.prototype = {
12095     /**
12096      * This is called when the transaction is completed and it's time to update the element - The BasicRenderer
12097      * updates the elements innerHTML with the responseText - To perform a custom render (i.e. XML or JSON processing),
12098      * create an object with a "render(el, response)" method and pass it to setRenderer on the UpdateManager.
12099      * @param {Roo.Element} el The element being rendered
12100      * @param {Object} response The YUI Connect response object
12101      * @param {UpdateManager} updateManager The calling update manager
12102      * @param {Function} callback A callback that will need to be called if loadScripts is true on the UpdateManager
12103      */
12104      render : function(el, response, updateManager, callback){
12105         el.update(response.responseText, updateManager.loadScripts, callback);
12106     }
12107 };
12108 /*
12109  * Based on:
12110  * Roo JS
12111  * (c)) Alan Knowles
12112  * Licence : LGPL
12113  */
12114
12115
12116 /**
12117  * @class Roo.DomTemplate
12118  * @extends Roo.Template
12119  * An effort at a dom based template engine..
12120  *
12121  * Similar to XTemplate, except it uses dom parsing to create the template..
12122  *
12123  * Supported features:
12124  *
12125  *  Tags:
12126
12127 <pre><code>
12128       {a_variable} - output encoded.
12129       {a_variable.format:("Y-m-d")} - call a method on the variable
12130       {a_variable:raw} - unencoded output
12131       {a_variable:toFixed(1,2)} - Roo.util.Format."toFixed"
12132       {a_variable:this.method_on_template(...)} - call a method on the template object.
12133  
12134 </code></pre>
12135  *  The tpl tag:
12136 <pre><code>
12137         &lt;div roo-for="a_variable or condition.."&gt;&lt;/div&gt;
12138         &lt;div roo-if="a_variable or condition"&gt;&lt;/div&gt;
12139         &lt;div roo-exec="some javascript"&gt;&lt;/div&gt;
12140         &lt;div roo-name="named_template"&gt;&lt;/div&gt; 
12141   
12142 </code></pre>
12143  *      
12144  */
12145 Roo.DomTemplate = function()
12146 {
12147      Roo.DomTemplate.superclass.constructor.apply(this, arguments);
12148      if (this.html) {
12149         this.compile();
12150      }
12151 };
12152
12153
12154 Roo.extend(Roo.DomTemplate, Roo.Template, {
12155     /**
12156      * id counter for sub templates.
12157      */
12158     id : 0,
12159     /**
12160      * flag to indicate if dom parser is inside a pre,
12161      * it will strip whitespace if not.
12162      */
12163     inPre : false,
12164     
12165     /**
12166      * The various sub templates
12167      */
12168     tpls : false,
12169     
12170     
12171     
12172     /**
12173      *
12174      * basic tag replacing syntax
12175      * WORD:WORD()
12176      *
12177      * // you can fake an object call by doing this
12178      *  x.t:(test,tesT) 
12179      * 
12180      */
12181     re : /(\{|\%7B)([\w-\.]+)(?:\:([\w\.]*)(?:\(([^)]*?)?\))?)?(\}|\%7D)/g,
12182     //re : /\{([\w-\.]+)(?:\:([\w\.]*)(?:\((.*?)?\))?)?\}/g,
12183     
12184     iterChild : function (node, method) {
12185         
12186         var oldPre = this.inPre;
12187         if (node.tagName == 'PRE') {
12188             this.inPre = true;
12189         }
12190         for( var i = 0; i < node.childNodes.length; i++) {
12191             method.call(this, node.childNodes[i]);
12192         }
12193         this.inPre = oldPre;
12194     },
12195     
12196     
12197     
12198     /**
12199      * compile the template
12200      *
12201      * This is not recursive, so I'm not sure how nested templates are really going to be handled..
12202      *
12203      */
12204     compile: function()
12205     {
12206         var s = this.html;
12207         
12208         // covert the html into DOM...
12209         var doc = false;
12210         var div =false;
12211         try {
12212             doc = document.implementation.createHTMLDocument("");
12213             doc.documentElement.innerHTML =   this.html  ;
12214             div = doc.documentElement;
12215         } catch (e) {
12216             // old IE... - nasty -- it causes all sorts of issues.. with
12217             // images getting pulled from server..
12218             div = document.createElement('div');
12219             div.innerHTML = this.html;
12220         }
12221         //doc.documentElement.innerHTML = htmlBody
12222          
12223         
12224         
12225         this.tpls = [];
12226         var _t = this;
12227         this.iterChild(div, function(n) {_t.compileNode(n, true); });
12228         
12229         var tpls = this.tpls;
12230         
12231         // create a top level template from the snippet..
12232         
12233         //Roo.log(div.innerHTML);
12234         
12235         var tpl = {
12236             uid : 'master',
12237             id : this.id++,
12238             attr : false,
12239             value : false,
12240             body : div.innerHTML,
12241             
12242             forCall : false,
12243             execCall : false,
12244             dom : div,
12245             isTop : true
12246             
12247         };
12248         tpls.unshift(tpl);
12249         
12250         
12251         // compile them...
12252         this.tpls = [];
12253         Roo.each(tpls, function(tp){
12254             this.compileTpl(tp);
12255             this.tpls[tp.id] = tp;
12256         }, this);
12257         
12258         this.master = tpls[0];
12259         return this;
12260         
12261         
12262     },
12263     
12264     compileNode : function(node, istop) {
12265         // test for
12266         //Roo.log(node);
12267         
12268         
12269         // skip anything not a tag..
12270         if (node.nodeType != 1) {
12271             if (node.nodeType == 3 && !this.inPre) {
12272                 // reduce white space..
12273                 node.nodeValue = node.nodeValue.replace(/\s+/g, ' '); 
12274                 
12275             }
12276             return;
12277         }
12278         
12279         var tpl = {
12280             uid : false,
12281             id : false,
12282             attr : false,
12283             value : false,
12284             body : '',
12285             
12286             forCall : false,
12287             execCall : false,
12288             dom : false,
12289             isTop : istop
12290             
12291             
12292         };
12293         
12294         
12295         switch(true) {
12296             case (node.hasAttribute('roo-for')): tpl.attr = 'for'; break;
12297             case (node.hasAttribute('roo-if')): tpl.attr = 'if'; break;
12298             case (node.hasAttribute('roo-name')): tpl.attr = 'name'; break;
12299             case (node.hasAttribute('roo-exec')): tpl.attr = 'exec'; break;
12300             // no default..
12301         }
12302         
12303         
12304         if (!tpl.attr) {
12305             // just itterate children..
12306             this.iterChild(node,this.compileNode);
12307             return;
12308         }
12309         tpl.uid = this.id++;
12310         tpl.value = node.getAttribute('roo-' +  tpl.attr);
12311         node.removeAttribute('roo-'+ tpl.attr);
12312         if (tpl.attr != 'name') {
12313             var placeholder = document.createTextNode('{domtpl' + tpl.uid + '}');
12314             node.parentNode.replaceChild(placeholder,  node);
12315         } else {
12316             
12317             var placeholder =  document.createElement('span');
12318             placeholder.className = 'roo-tpl-' + tpl.value;
12319             node.parentNode.replaceChild(placeholder,  node);
12320         }
12321         
12322         // parent now sees '{domtplXXXX}
12323         this.iterChild(node,this.compileNode);
12324         
12325         // we should now have node body...
12326         var div = document.createElement('div');
12327         div.appendChild(node);
12328         tpl.dom = node;
12329         // this has the unfortunate side effect of converting tagged attributes
12330         // eg. href="{...}" into %7C...%7D
12331         // this has been fixed by searching for those combo's although it's a bit hacky..
12332         
12333         
12334         tpl.body = div.innerHTML;
12335         
12336         
12337          
12338         tpl.id = tpl.uid;
12339         switch(tpl.attr) {
12340             case 'for' :
12341                 switch (tpl.value) {
12342                     case '.':  tpl.forCall = new Function('values', 'parent', 'with(values){ return values; }'); break;
12343                     case '..': tpl.forCall= new Function('values', 'parent', 'with(values){ return parent; }'); break;
12344                     default:   tpl.forCall= new Function('values', 'parent', 'with(values){ return '+tpl.value+'; }');
12345                 }
12346                 break;
12347             
12348             case 'exec':
12349                 tpl.execCall = new Function('values', 'parent', 'with(values){ '+(Roo.util.Format.htmlDecode(tpl.value))+'; }');
12350                 break;
12351             
12352             case 'if':     
12353                 tpl.ifCall = new Function('values', 'parent', 'with(values){ return '+(Roo.util.Format.htmlDecode(tpl.value))+'; }');
12354                 break;
12355             
12356             case 'name':
12357                 tpl.id  = tpl.value; // replace non characters???
12358                 break;
12359             
12360         }
12361         
12362         
12363         this.tpls.push(tpl);
12364         
12365         
12366         
12367     },
12368     
12369     
12370     
12371     
12372     /**
12373      * Compile a segment of the template into a 'sub-template'
12374      *
12375      * 
12376      * 
12377      *
12378      */
12379     compileTpl : function(tpl)
12380     {
12381         var fm = Roo.util.Format;
12382         var useF = this.disableFormats !== true;
12383         
12384         var sep = Roo.isGecko ? "+\n" : ",\n";
12385         
12386         var undef = function(str) {
12387             Roo.debug && Roo.log("Property not found :"  + str);
12388             return '';
12389         };
12390           
12391         //Roo.log(tpl.body);
12392         
12393         
12394         
12395         var fn = function(m, lbrace, name, format, args)
12396         {
12397             //Roo.log("ARGS");
12398             //Roo.log(arguments);
12399             args = args ? args.replace(/\\'/g,"'") : args;
12400             //["{TEST:(a,b,c)}", "TEST", "", "a,b,c", 0, "{TEST:(a,b,c)}"]
12401             if (typeof(format) == 'undefined') {
12402                 format =  'htmlEncode'; 
12403             }
12404             if (format == 'raw' ) {
12405                 format = false;
12406             }
12407             
12408             if(name.substr(0, 6) == 'domtpl'){
12409                 return "'"+ sep +'this.applySubTemplate('+name.substr(6)+', values, parent)'+sep+"'";
12410             }
12411             
12412             // build an array of options to determine if value is undefined..
12413             
12414             // basically get 'xxxx.yyyy' then do
12415             // (typeof(xxxx) == 'undefined' || typeof(xxx.yyyy) == 'undefined') ?
12416             //    (function () { Roo.log("Property not found"); return ''; })() :
12417             //    ......
12418             
12419             var udef_ar = [];
12420             var lookfor = '';
12421             Roo.each(name.split('.'), function(st) {
12422                 lookfor += (lookfor.length ? '.': '') + st;
12423                 udef_ar.push(  "(typeof(" + lookfor + ") == 'undefined')"  );
12424             });
12425             
12426             var udef_st = '((' + udef_ar.join(" || ") +") ? undef('" + name + "') : "; // .. needs )
12427             
12428             
12429             if(format && useF){
12430                 
12431                 args = args ? ',' + args : "";
12432                  
12433                 if(format.substr(0, 5) != "this."){
12434                     format = "fm." + format + '(';
12435                 }else{
12436                     format = 'this.call("'+ format.substr(5) + '", ';
12437                     args = ", values";
12438                 }
12439                 
12440                 return "'"+ sep +   udef_st   +    format + name + args + "))"+sep+"'";
12441             }
12442              
12443             if (args && args.length) {
12444                 // called with xxyx.yuu:(test,test)
12445                 // change to ()
12446                 return "'"+ sep + udef_st  + name + '(' +  args + "))"+sep+"'";
12447             }
12448             // raw.. - :raw modifier..
12449             return "'"+ sep + udef_st  + name + ")"+sep+"'";
12450             
12451         };
12452         var body;
12453         // branched to use + in gecko and [].join() in others
12454         if(Roo.isGecko){
12455             body = "tpl.compiled = function(values, parent){  with(values) { return '" +
12456                    tpl.body.replace(/(\r\n|\n)/g, '\\n').replace(/'/g, "\\'").replace(this.re, fn) +
12457                     "';};};";
12458         }else{
12459             body = ["tpl.compiled = function(values, parent){  with (values) { return ['"];
12460             body.push(tpl.body.replace(/(\r\n|\n)/g,
12461                             '\\n').replace(/'/g, "\\'").replace(this.re, fn));
12462             body.push("'].join('');};};");
12463             body = body.join('');
12464         }
12465         
12466         Roo.debug && Roo.log(body.replace(/\\n/,'\n'));
12467        
12468         /** eval:var:tpl eval:var:fm eval:var:useF eval:var:undef  */
12469         eval(body);
12470         
12471         return this;
12472     },
12473      
12474     /**
12475      * same as applyTemplate, except it's done to one of the subTemplates
12476      * when using named templates, you can do:
12477      *
12478      * var str = pl.applySubTemplate('your-name', values);
12479      *
12480      * 
12481      * @param {Number} id of the template
12482      * @param {Object} values to apply to template
12483      * @param {Object} parent (normaly the instance of this object)
12484      */
12485     applySubTemplate : function(id, values, parent)
12486     {
12487         
12488         
12489         var t = this.tpls[id];
12490         
12491         
12492         try { 
12493             if(t.ifCall && !t.ifCall.call(this, values, parent)){
12494                 Roo.debug && Roo.log('if call on ' + t.value + ' return false');
12495                 return '';
12496             }
12497         } catch(e) {
12498             Roo.log('Xtemplate.applySubTemplate('+ id+ '): Exception thrown on roo-if="' + t.value + '" - ' + e.toString());
12499             Roo.log(values);
12500           
12501             return '';
12502         }
12503         try { 
12504             
12505             if(t.execCall && t.execCall.call(this, values, parent)){
12506                 return '';
12507             }
12508         } catch(e) {
12509             Roo.log('Xtemplate.applySubTemplate('+ id+ '): Exception thrown on roo-for="' + t.value + '" - ' + e.toString());
12510             Roo.log(values);
12511             return '';
12512         }
12513         
12514         try {
12515             var vs = t.forCall ? t.forCall.call(this, values, parent) : values;
12516             parent = t.target ? values : parent;
12517             if(t.forCall && vs instanceof Array){
12518                 var buf = [];
12519                 for(var i = 0, len = vs.length; i < len; i++){
12520                     try {
12521                         buf[buf.length] = t.compiled.call(this, vs[i], parent);
12522                     } catch (e) {
12523                         Roo.log('Xtemplate.applySubTemplate('+ id+ '): Exception thrown on body="' + t.value + '" - ' + e.toString());
12524                         Roo.log(e.body);
12525                         //Roo.log(t.compiled);
12526                         Roo.log(vs[i]);
12527                     }   
12528                 }
12529                 return buf.join('');
12530             }
12531         } catch (e) {
12532             Roo.log('Xtemplate.applySubTemplate('+ id+ '): Exception thrown on roo-for="' + t.value + '" - ' + e.toString());
12533             Roo.log(values);
12534             return '';
12535         }
12536         try {
12537             return t.compiled.call(this, vs, parent);
12538         } catch (e) {
12539             Roo.log('Xtemplate.applySubTemplate('+ id+ '): Exception thrown on body="' + t.value + '" - ' + e.toString());
12540             Roo.log(e.body);
12541             //Roo.log(t.compiled);
12542             Roo.log(values);
12543             return '';
12544         }
12545     },
12546
12547    
12548
12549     applyTemplate : function(values){
12550         return this.master.compiled.call(this, values, {});
12551         //var s = this.subs;
12552     },
12553
12554     apply : function(){
12555         return this.applyTemplate.apply(this, arguments);
12556     }
12557
12558  });
12559
12560 Roo.DomTemplate.from = function(el){
12561     el = Roo.getDom(el);
12562     return new Roo.Domtemplate(el.value || el.innerHTML);
12563 };/*
12564  * Based on:
12565  * Ext JS Library 1.1.1
12566  * Copyright(c) 2006-2007, Ext JS, LLC.
12567  *
12568  * Originally Released Under LGPL - original licence link has changed is not relivant.
12569  *
12570  * Fork - LGPL
12571  * <script type="text/javascript">
12572  */
12573
12574 /**
12575  * @class Roo.util.DelayedTask
12576  * Provides a convenient method of performing setTimeout where a new
12577  * timeout cancels the old timeout. An example would be performing validation on a keypress.
12578  * You can use this class to buffer
12579  * the keypress events for a certain number of milliseconds, and perform only if they stop
12580  * for that amount of time.
12581  * @constructor The parameters to this constructor serve as defaults and are not required.
12582  * @param {Function} fn (optional) The default function to timeout
12583  * @param {Object} scope (optional) The default scope of that timeout
12584  * @param {Array} args (optional) The default Array of arguments
12585  */
12586 Roo.util.DelayedTask = function(fn, scope, args){
12587     var id = null, d, t;
12588
12589     var call = function(){
12590         var now = new Date().getTime();
12591         if(now - t >= d){
12592             clearInterval(id);
12593             id = null;
12594             fn.apply(scope, args || []);
12595         }
12596     };
12597     /**
12598      * Cancels any pending timeout and queues a new one
12599      * @param {Number} delay The milliseconds to delay
12600      * @param {Function} newFn (optional) Overrides function passed to constructor
12601      * @param {Object} newScope (optional) Overrides scope passed to constructor
12602      * @param {Array} newArgs (optional) Overrides args passed to constructor
12603      */
12604     this.delay = function(delay, newFn, newScope, newArgs){
12605         if(id && delay != d){
12606             this.cancel();
12607         }
12608         d = delay;
12609         t = new Date().getTime();
12610         fn = newFn || fn;
12611         scope = newScope || scope;
12612         args = newArgs || args;
12613         if(!id){
12614             id = setInterval(call, d);
12615         }
12616     };
12617
12618     /**
12619      * Cancel the last queued timeout
12620      */
12621     this.cancel = function(){
12622         if(id){
12623             clearInterval(id);
12624             id = null;
12625         }
12626     };
12627 };/*
12628  * Based on:
12629  * Ext JS Library 1.1.1
12630  * Copyright(c) 2006-2007, Ext JS, LLC.
12631  *
12632  * Originally Released Under LGPL - original licence link has changed is not relivant.
12633  *
12634  * Fork - LGPL
12635  * <script type="text/javascript">
12636  */
12637  
12638  
12639 Roo.util.TaskRunner = function(interval){
12640     interval = interval || 10;
12641     var tasks = [], removeQueue = [];
12642     var id = 0;
12643     var running = false;
12644
12645     var stopThread = function(){
12646         running = false;
12647         clearInterval(id);
12648         id = 0;
12649     };
12650
12651     var startThread = function(){
12652         if(!running){
12653             running = true;
12654             id = setInterval(runTasks, interval);
12655         }
12656     };
12657
12658     var removeTask = function(task){
12659         removeQueue.push(task);
12660         if(task.onStop){
12661             task.onStop();
12662         }
12663     };
12664
12665     var runTasks = function(){
12666         if(removeQueue.length > 0){
12667             for(var i = 0, len = removeQueue.length; i < len; i++){
12668                 tasks.remove(removeQueue[i]);
12669             }
12670             removeQueue = [];
12671             if(tasks.length < 1){
12672                 stopThread();
12673                 return;
12674             }
12675         }
12676         var now = new Date().getTime();
12677         for(var i = 0, len = tasks.length; i < len; ++i){
12678             var t = tasks[i];
12679             var itime = now - t.taskRunTime;
12680             if(t.interval <= itime){
12681                 var rt = t.run.apply(t.scope || t, t.args || [++t.taskRunCount]);
12682                 t.taskRunTime = now;
12683                 if(rt === false || t.taskRunCount === t.repeat){
12684                     removeTask(t);
12685                     return;
12686                 }
12687             }
12688             if(t.duration && t.duration <= (now - t.taskStartTime)){
12689                 removeTask(t);
12690             }
12691         }
12692     };
12693
12694     /**
12695      * Queues a new task.
12696      * @param {Object} task
12697      */
12698     this.start = function(task){
12699         tasks.push(task);
12700         task.taskStartTime = new Date().getTime();
12701         task.taskRunTime = 0;
12702         task.taskRunCount = 0;
12703         startThread();
12704         return task;
12705     };
12706
12707     this.stop = function(task){
12708         removeTask(task);
12709         return task;
12710     };
12711
12712     this.stopAll = function(){
12713         stopThread();
12714         for(var i = 0, len = tasks.length; i < len; i++){
12715             if(tasks[i].onStop){
12716                 tasks[i].onStop();
12717             }
12718         }
12719         tasks = [];
12720         removeQueue = [];
12721     };
12722 };
12723
12724 Roo.TaskMgr = new Roo.util.TaskRunner();/*
12725  * Based on:
12726  * Ext JS Library 1.1.1
12727  * Copyright(c) 2006-2007, Ext JS, LLC.
12728  *
12729  * Originally Released Under LGPL - original licence link has changed is not relivant.
12730  *
12731  * Fork - LGPL
12732  * <script type="text/javascript">
12733  */
12734
12735  
12736 /**
12737  * @class Roo.util.MixedCollection
12738  * @extends Roo.util.Observable
12739  * A Collection class that maintains both numeric indexes and keys and exposes events.
12740  * @constructor
12741  * @param {Boolean} allowFunctions True if the addAll function should add function references to the
12742  * collection (defaults to false)
12743  * @param {Function} keyFn A function that can accept an item of the type(s) stored in this MixedCollection
12744  * and return the key value for that item.  This is used when available to look up the key on items that
12745  * were passed without an explicit key parameter to a MixedCollection method.  Passing this parameter is
12746  * equivalent to providing an implementation for the {@link #getKey} method.
12747  */
12748 Roo.util.MixedCollection = function(allowFunctions, keyFn){
12749     this.items = [];
12750     this.map = {};
12751     this.keys = [];
12752     this.length = 0;
12753     this.addEvents({
12754         /**
12755          * @event clear
12756          * Fires when the collection is cleared.
12757          */
12758         "clear" : true,
12759         /**
12760          * @event add
12761          * Fires when an item is added to the collection.
12762          * @param {Number} index The index at which the item was added.
12763          * @param {Object} o The item added.
12764          * @param {String} key The key associated with the added item.
12765          */
12766         "add" : true,
12767         /**
12768          * @event replace
12769          * Fires when an item is replaced in the collection.
12770          * @param {String} key he key associated with the new added.
12771          * @param {Object} old The item being replaced.
12772          * @param {Object} new The new item.
12773          */
12774         "replace" : true,
12775         /**
12776          * @event remove
12777          * Fires when an item is removed from the collection.
12778          * @param {Object} o The item being removed.
12779          * @param {String} key (optional) The key associated with the removed item.
12780          */
12781         "remove" : true,
12782         "sort" : true
12783     });
12784     this.allowFunctions = allowFunctions === true;
12785     if(keyFn){
12786         this.getKey = keyFn;
12787     }
12788     Roo.util.MixedCollection.superclass.constructor.call(this);
12789 };
12790
12791 Roo.extend(Roo.util.MixedCollection, Roo.util.Observable, {
12792     allowFunctions : false,
12793     
12794 /**
12795  * Adds an item to the collection.
12796  * @param {String} key The key to associate with the item
12797  * @param {Object} o The item to add.
12798  * @return {Object} The item added.
12799  */
12800     add : function(key, o){
12801         if(arguments.length == 1){
12802             o = arguments[0];
12803             key = this.getKey(o);
12804         }
12805         if(typeof key == "undefined" || key === null){
12806             this.length++;
12807             this.items.push(o);
12808             this.keys.push(null);
12809         }else{
12810             var old = this.map[key];
12811             if(old){
12812                 return this.replace(key, o);
12813             }
12814             this.length++;
12815             this.items.push(o);
12816             this.map[key] = o;
12817             this.keys.push(key);
12818         }
12819         this.fireEvent("add", this.length-1, o, key);
12820         return o;
12821     },
12822        
12823 /**
12824   * MixedCollection has a generic way to fetch keys if you implement getKey.
12825 <pre><code>
12826 // normal way
12827 var mc = new Roo.util.MixedCollection();
12828 mc.add(someEl.dom.id, someEl);
12829 mc.add(otherEl.dom.id, otherEl);
12830 //and so on
12831
12832 // using getKey
12833 var mc = new Roo.util.MixedCollection();
12834 mc.getKey = function(el){
12835    return el.dom.id;
12836 };
12837 mc.add(someEl);
12838 mc.add(otherEl);
12839
12840 // or via the constructor
12841 var mc = new Roo.util.MixedCollection(false, function(el){
12842    return el.dom.id;
12843 });
12844 mc.add(someEl);
12845 mc.add(otherEl);
12846 </code></pre>
12847  * @param o {Object} The item for which to find the key.
12848  * @return {Object} The key for the passed item.
12849  */
12850     getKey : function(o){
12851          return o.id; 
12852     },
12853    
12854 /**
12855  * Replaces an item in the collection.
12856  * @param {String} key The key associated with the item to replace, or the item to replace.
12857  * @param o {Object} o (optional) If the first parameter passed was a key, the item to associate with that key.
12858  * @return {Object}  The new item.
12859  */
12860     replace : function(key, o){
12861         if(arguments.length == 1){
12862             o = arguments[0];
12863             key = this.getKey(o);
12864         }
12865         var old = this.item(key);
12866         if(typeof key == "undefined" || key === null || typeof old == "undefined"){
12867              return this.add(key, o);
12868         }
12869         var index = this.indexOfKey(key);
12870         this.items[index] = o;
12871         this.map[key] = o;
12872         this.fireEvent("replace", key, old, o);
12873         return o;
12874     },
12875    
12876 /**
12877  * Adds all elements of an Array or an Object to the collection.
12878  * @param {Object/Array} objs An Object containing properties which will be added to the collection, or
12879  * an Array of values, each of which are added to the collection.
12880  */
12881     addAll : function(objs){
12882         if(arguments.length > 1 || objs instanceof Array){
12883             var args = arguments.length > 1 ? arguments : objs;
12884             for(var i = 0, len = args.length; i < len; i++){
12885                 this.add(args[i]);
12886             }
12887         }else{
12888             for(var key in objs){
12889                 if(this.allowFunctions || typeof objs[key] != "function"){
12890                     this.add(key, objs[key]);
12891                 }
12892             }
12893         }
12894     },
12895    
12896 /**
12897  * Executes the specified function once for every item in the collection, passing each
12898  * item as the first and only parameter. returning false from the function will stop the iteration.
12899  * @param {Function} fn The function to execute for each item.
12900  * @param {Object} scope (optional) The scope in which to execute the function.
12901  */
12902     each : function(fn, scope){
12903         var items = [].concat(this.items); // each safe for removal
12904         for(var i = 0, len = items.length; i < len; i++){
12905             if(fn.call(scope || items[i], items[i], i, len) === false){
12906                 break;
12907             }
12908         }
12909     },
12910    
12911 /**
12912  * Executes the specified function once for every key in the collection, passing each
12913  * key, and its associated item as the first two parameters.
12914  * @param {Function} fn The function to execute for each item.
12915  * @param {Object} scope (optional) The scope in which to execute the function.
12916  */
12917     eachKey : function(fn, scope){
12918         for(var i = 0, len = this.keys.length; i < len; i++){
12919             fn.call(scope || window, this.keys[i], this.items[i], i, len);
12920         }
12921     },
12922    
12923 /**
12924  * Returns the first item in the collection which elicits a true return value from the
12925  * passed selection function.
12926  * @param {Function} fn The selection function to execute for each item.
12927  * @param {Object} scope (optional) The scope in which to execute the function.
12928  * @return {Object} The first item in the collection which returned true from the selection function.
12929  */
12930     find : function(fn, scope){
12931         for(var i = 0, len = this.items.length; i < len; i++){
12932             if(fn.call(scope || window, this.items[i], this.keys[i])){
12933                 return this.items[i];
12934             }
12935         }
12936         return null;
12937     },
12938    
12939 /**
12940  * Inserts an item at the specified index in the collection.
12941  * @param {Number} index The index to insert the item at.
12942  * @param {String} key The key to associate with the new item, or the item itself.
12943  * @param {Object} o  (optional) If the second parameter was a key, the new item.
12944  * @return {Object} The item inserted.
12945  */
12946     insert : function(index, key, o){
12947         if(arguments.length == 2){
12948             o = arguments[1];
12949             key = this.getKey(o);
12950         }
12951         if(index >= this.length){
12952             return this.add(key, o);
12953         }
12954         this.length++;
12955         this.items.splice(index, 0, o);
12956         if(typeof key != "undefined" && key != null){
12957             this.map[key] = o;
12958         }
12959         this.keys.splice(index, 0, key);
12960         this.fireEvent("add", index, o, key);
12961         return o;
12962     },
12963    
12964 /**
12965  * Removed an item from the collection.
12966  * @param {Object} o The item to remove.
12967  * @return {Object} The item removed.
12968  */
12969     remove : function(o){
12970         return this.removeAt(this.indexOf(o));
12971     },
12972    
12973 /**
12974  * Remove an item from a specified index in the collection.
12975  * @param {Number} index The index within the collection of the item to remove.
12976  */
12977     removeAt : function(index){
12978         if(index < this.length && index >= 0){
12979             this.length--;
12980             var o = this.items[index];
12981             this.items.splice(index, 1);
12982             var key = this.keys[index];
12983             if(typeof key != "undefined"){
12984                 delete this.map[key];
12985             }
12986             this.keys.splice(index, 1);
12987             this.fireEvent("remove", o, key);
12988         }
12989     },
12990    
12991 /**
12992  * Removed an item associated with the passed key fom the collection.
12993  * @param {String} key The key of the item to remove.
12994  */
12995     removeKey : function(key){
12996         return this.removeAt(this.indexOfKey(key));
12997     },
12998    
12999 /**
13000  * Returns the number of items in the collection.
13001  * @return {Number} the number of items in the collection.
13002  */
13003     getCount : function(){
13004         return this.length; 
13005     },
13006    
13007 /**
13008  * Returns index within the collection of the passed Object.
13009  * @param {Object} o The item to find the index of.
13010  * @return {Number} index of the item.
13011  */
13012     indexOf : function(o){
13013         if(!this.items.indexOf){
13014             for(var i = 0, len = this.items.length; i < len; i++){
13015                 if(this.items[i] == o) return i;
13016             }
13017             return -1;
13018         }else{
13019             return this.items.indexOf(o);
13020         }
13021     },
13022    
13023 /**
13024  * Returns index within the collection of the passed key.
13025  * @param {String} key The key to find the index of.
13026  * @return {Number} index of the key.
13027  */
13028     indexOfKey : function(key){
13029         if(!this.keys.indexOf){
13030             for(var i = 0, len = this.keys.length; i < len; i++){
13031                 if(this.keys[i] == key) return i;
13032             }
13033             return -1;
13034         }else{
13035             return this.keys.indexOf(key);
13036         }
13037     },
13038    
13039 /**
13040  * Returns the item associated with the passed key OR index. Key has priority over index.
13041  * @param {String/Number} key The key or index of the item.
13042  * @return {Object} The item associated with the passed key.
13043  */
13044     item : function(key){
13045         var item = typeof this.map[key] != "undefined" ? this.map[key] : this.items[key];
13046         return typeof item != 'function' || this.allowFunctions ? item : null; // for prototype!
13047     },
13048     
13049 /**
13050  * Returns the item at the specified index.
13051  * @param {Number} index The index of the item.
13052  * @return {Object}
13053  */
13054     itemAt : function(index){
13055         return this.items[index];
13056     },
13057     
13058 /**
13059  * Returns the item associated with the passed key.
13060  * @param {String/Number} key The key of the item.
13061  * @return {Object} The item associated with the passed key.
13062  */
13063     key : function(key){
13064         return this.map[key];
13065     },
13066    
13067 /**
13068  * Returns true if the collection contains the passed Object as an item.
13069  * @param {Object} o  The Object to look for in the collection.
13070  * @return {Boolean} True if the collection contains the Object as an item.
13071  */
13072     contains : function(o){
13073         return this.indexOf(o) != -1;
13074     },
13075    
13076 /**
13077  * Returns true if the collection contains the passed Object as a key.
13078  * @param {String} key The key to look for in the collection.
13079  * @return {Boolean} True if the collection contains the Object as a key.
13080  */
13081     containsKey : function(key){
13082         return typeof this.map[key] != "undefined";
13083     },
13084    
13085 /**
13086  * Removes all items from the collection.
13087  */
13088     clear : function(){
13089         this.length = 0;
13090         this.items = [];
13091         this.keys = [];
13092         this.map = {};
13093         this.fireEvent("clear");
13094     },
13095    
13096 /**
13097  * Returns the first item in the collection.
13098  * @return {Object} the first item in the collection..
13099  */
13100     first : function(){
13101         return this.items[0]; 
13102     },
13103    
13104 /**
13105  * Returns the last item in the collection.
13106  * @return {Object} the last item in the collection..
13107  */
13108     last : function(){
13109         return this.items[this.length-1];   
13110     },
13111     
13112     _sort : function(property, dir, fn){
13113         var dsc = String(dir).toUpperCase() == "DESC" ? -1 : 1;
13114         fn = fn || function(a, b){
13115             return a-b;
13116         };
13117         var c = [], k = this.keys, items = this.items;
13118         for(var i = 0, len = items.length; i < len; i++){
13119             c[c.length] = {key: k[i], value: items[i], index: i};
13120         }
13121         c.sort(function(a, b){
13122             var v = fn(a[property], b[property]) * dsc;
13123             if(v == 0){
13124                 v = (a.index < b.index ? -1 : 1);
13125             }
13126             return v;
13127         });
13128         for(var i = 0, len = c.length; i < len; i++){
13129             items[i] = c[i].value;
13130             k[i] = c[i].key;
13131         }
13132         this.fireEvent("sort", this);
13133     },
13134     
13135     /**
13136      * Sorts this collection with the passed comparison function
13137      * @param {String} direction (optional) "ASC" or "DESC"
13138      * @param {Function} fn (optional) comparison function
13139      */
13140     sort : function(dir, fn){
13141         this._sort("value", dir, fn);
13142     },
13143     
13144     /**
13145      * Sorts this collection by keys
13146      * @param {String} direction (optional) "ASC" or "DESC"
13147      * @param {Function} fn (optional) a comparison function (defaults to case insensitive string)
13148      */
13149     keySort : function(dir, fn){
13150         this._sort("key", dir, fn || function(a, b){
13151             return String(a).toUpperCase()-String(b).toUpperCase();
13152         });
13153     },
13154     
13155     /**
13156      * Returns a range of items in this collection
13157      * @param {Number} startIndex (optional) defaults to 0
13158      * @param {Number} endIndex (optional) default to the last item
13159      * @return {Array} An array of items
13160      */
13161     getRange : function(start, end){
13162         var items = this.items;
13163         if(items.length < 1){
13164             return [];
13165         }
13166         start = start || 0;
13167         end = Math.min(typeof end == "undefined" ? this.length-1 : end, this.length-1);
13168         var r = [];
13169         if(start <= end){
13170             for(var i = start; i <= end; i++) {
13171                     r[r.length] = items[i];
13172             }
13173         }else{
13174             for(var i = start; i >= end; i--) {
13175                     r[r.length] = items[i];
13176             }
13177         }
13178         return r;
13179     },
13180         
13181     /**
13182      * Filter the <i>objects</i> in this collection by a specific property. 
13183      * Returns a new collection that has been filtered.
13184      * @param {String} property A property on your objects
13185      * @param {String/RegExp} value Either string that the property values 
13186      * should start with or a RegExp to test against the property
13187      * @return {MixedCollection} The new filtered collection
13188      */
13189     filter : function(property, value){
13190         if(!value.exec){ // not a regex
13191             value = String(value);
13192             if(value.length == 0){
13193                 return this.clone();
13194             }
13195             value = new RegExp("^" + Roo.escapeRe(value), "i");
13196         }
13197         return this.filterBy(function(o){
13198             return o && value.test(o[property]);
13199         });
13200         },
13201     
13202     /**
13203      * Filter by a function. * Returns a new collection that has been filtered.
13204      * The passed function will be called with each 
13205      * object in the collection. If the function returns true, the value is included 
13206      * otherwise it is filtered.
13207      * @param {Function} fn The function to be called, it will receive the args o (the object), k (the key)
13208      * @param {Object} scope (optional) The scope of the function (defaults to this) 
13209      * @return {MixedCollection} The new filtered collection
13210      */
13211     filterBy : function(fn, scope){
13212         var r = new Roo.util.MixedCollection();
13213         r.getKey = this.getKey;
13214         var k = this.keys, it = this.items;
13215         for(var i = 0, len = it.length; i < len; i++){
13216             if(fn.call(scope||this, it[i], k[i])){
13217                                 r.add(k[i], it[i]);
13218                         }
13219         }
13220         return r;
13221     },
13222     
13223     /**
13224      * Creates a duplicate of this collection
13225      * @return {MixedCollection}
13226      */
13227     clone : function(){
13228         var r = new Roo.util.MixedCollection();
13229         var k = this.keys, it = this.items;
13230         for(var i = 0, len = it.length; i < len; i++){
13231             r.add(k[i], it[i]);
13232         }
13233         r.getKey = this.getKey;
13234         return r;
13235     }
13236 });
13237 /**
13238  * Returns the item associated with the passed key or index.
13239  * @method
13240  * @param {String/Number} key The key or index of the item.
13241  * @return {Object} The item associated with the passed key.
13242  */
13243 Roo.util.MixedCollection.prototype.get = Roo.util.MixedCollection.prototype.item;/*
13244  * Based on:
13245  * Ext JS Library 1.1.1
13246  * Copyright(c) 2006-2007, Ext JS, LLC.
13247  *
13248  * Originally Released Under LGPL - original licence link has changed is not relivant.
13249  *
13250  * Fork - LGPL
13251  * <script type="text/javascript">
13252  */
13253 /**
13254  * @class Roo.util.JSON
13255  * Modified version of Douglas Crockford"s json.js that doesn"t
13256  * mess with the Object prototype 
13257  * http://www.json.org/js.html
13258  * @singleton
13259  */
13260 Roo.util.JSON = new (function(){
13261     var useHasOwn = {}.hasOwnProperty ? true : false;
13262     
13263     // crashes Safari in some instances
13264     //var validRE = /^("(\\.|[^"\\\n\r])*?"|[,:{}\[\]0-9.\-+Eaeflnr-u \n\r\t])+?$/;
13265     
13266     var pad = function(n) {
13267         return n < 10 ? "0" + n : n;
13268     };
13269     
13270     var m = {
13271         "\b": '\\b',
13272         "\t": '\\t',
13273         "\n": '\\n',
13274         "\f": '\\f',
13275         "\r": '\\r',
13276         '"' : '\\"',
13277         "\\": '\\\\'
13278     };
13279
13280     var encodeString = function(s){
13281         if (/["\\\x00-\x1f]/.test(s)) {
13282             return '"' + s.replace(/([\x00-\x1f\\"])/g, function(a, b) {
13283                 var c = m[b];
13284                 if(c){
13285                     return c;
13286                 }
13287                 c = b.charCodeAt();
13288                 return "\\u00" +
13289                     Math.floor(c / 16).toString(16) +
13290                     (c % 16).toString(16);
13291             }) + '"';
13292         }
13293         return '"' + s + '"';
13294     };
13295     
13296     var encodeArray = function(o){
13297         var a = ["["], b, i, l = o.length, v;
13298             for (i = 0; i < l; i += 1) {
13299                 v = o[i];
13300                 switch (typeof v) {
13301                     case "undefined":
13302                     case "function":
13303                     case "unknown":
13304                         break;
13305                     default:
13306                         if (b) {
13307                             a.push(',');
13308                         }
13309                         a.push(v === null ? "null" : Roo.util.JSON.encode(v));
13310                         b = true;
13311                 }
13312             }
13313             a.push("]");
13314             return a.join("");
13315     };
13316     
13317     var encodeDate = function(o){
13318         return '"' + o.getFullYear() + "-" +
13319                 pad(o.getMonth() + 1) + "-" +
13320                 pad(o.getDate()) + "T" +
13321                 pad(o.getHours()) + ":" +
13322                 pad(o.getMinutes()) + ":" +
13323                 pad(o.getSeconds()) + '"';
13324     };
13325     
13326     /**
13327      * Encodes an Object, Array or other value
13328      * @param {Mixed} o The variable to encode
13329      * @return {String} The JSON string
13330      */
13331     this.encode = function(o)
13332     {
13333         // should this be extended to fully wrap stringify..
13334         
13335         if(typeof o == "undefined" || o === null){
13336             return "null";
13337         }else if(o instanceof Array){
13338             return encodeArray(o);
13339         }else if(o instanceof Date){
13340             return encodeDate(o);
13341         }else if(typeof o == "string"){
13342             return encodeString(o);
13343         }else if(typeof o == "number"){
13344             return isFinite(o) ? String(o) : "null";
13345         }else if(typeof o == "boolean"){
13346             return String(o);
13347         }else {
13348             var a = ["{"], b, i, v;
13349             for (i in o) {
13350                 if(!useHasOwn || o.hasOwnProperty(i)) {
13351                     v = o[i];
13352                     switch (typeof v) {
13353                     case "undefined":
13354                     case "function":
13355                     case "unknown":
13356                         break;
13357                     default:
13358                         if(b){
13359                             a.push(',');
13360                         }
13361                         a.push(this.encode(i), ":",
13362                                 v === null ? "null" : this.encode(v));
13363                         b = true;
13364                     }
13365                 }
13366             }
13367             a.push("}");
13368             return a.join("");
13369         }
13370     };
13371     
13372     /**
13373      * Decodes (parses) a JSON string to an object. If the JSON is invalid, this function throws a SyntaxError.
13374      * @param {String} json The JSON string
13375      * @return {Object} The resulting object
13376      */
13377     this.decode = function(json){
13378         
13379         return  /** eval:var:json */ eval("(" + json + ')');
13380     };
13381 })();
13382 /** 
13383  * Shorthand for {@link Roo.util.JSON#encode}
13384  * @member Roo encode 
13385  * @method */
13386 Roo.encode = typeof(JSON) != 'undefined' && JSON.stringify ? JSON.stringify : Roo.util.JSON.encode;
13387 /** 
13388  * Shorthand for {@link Roo.util.JSON#decode}
13389  * @member Roo decode 
13390  * @method */
13391 Roo.decode = typeof(JSON) != 'undefined' && JSON.parse ? JSON.parse : Roo.util.JSON.decode;
13392 /*
13393  * Based on:
13394  * Ext JS Library 1.1.1
13395  * Copyright(c) 2006-2007, Ext JS, LLC.
13396  *
13397  * Originally Released Under LGPL - original licence link has changed is not relivant.
13398  *
13399  * Fork - LGPL
13400  * <script type="text/javascript">
13401  */
13402  
13403 /**
13404  * @class Roo.util.Format
13405  * Reusable data formatting functions
13406  * @singleton
13407  */
13408 Roo.util.Format = function(){
13409     var trimRe = /^\s+|\s+$/g;
13410     return {
13411         /**
13412          * Truncate a string and add an ellipsis ('...') to the end if it exceeds the specified length
13413          * @param {String} value The string to truncate
13414          * @param {Number} length The maximum length to allow before truncating
13415          * @return {String} The converted text
13416          */
13417         ellipsis : function(value, len){
13418             if(value && value.length > len){
13419                 return value.substr(0, len-3)+"...";
13420             }
13421             return value;
13422         },
13423
13424         /**
13425          * Checks a reference and converts it to empty string if it is undefined
13426          * @param {Mixed} value Reference to check
13427          * @return {Mixed} Empty string if converted, otherwise the original value
13428          */
13429         undef : function(value){
13430             return typeof value != "undefined" ? value : "";
13431         },
13432
13433         /**
13434          * Convert certain characters (&, <, >, and ') to their HTML character equivalents for literal display in web pages.
13435          * @param {String} value The string to encode
13436          * @return {String} The encoded text
13437          */
13438         htmlEncode : function(value){
13439             return !value ? value : String(value).replace(/&/g, "&amp;").replace(/>/g, "&gt;").replace(/</g, "&lt;").replace(/"/g, "&quot;");
13440         },
13441
13442         /**
13443          * Convert certain characters (&, <, >, and ') from their HTML character equivalents.
13444          * @param {String} value The string to decode
13445          * @return {String} The decoded text
13446          */
13447         htmlDecode : function(value){
13448             return !value ? value : String(value).replace(/&amp;/g, "&").replace(/&gt;/g, ">").replace(/&lt;/g, "<").replace(/&quot;/g, '"');
13449         },
13450
13451         /**
13452          * Trims any whitespace from either side of a string
13453          * @param {String} value The text to trim
13454          * @return {String} The trimmed text
13455          */
13456         trim : function(value){
13457             return String(value).replace(trimRe, "");
13458         },
13459
13460         /**
13461          * Returns a substring from within an original string
13462          * @param {String} value The original text
13463          * @param {Number} start The start index of the substring
13464          * @param {Number} length The length of the substring
13465          * @return {String} The substring
13466          */
13467         substr : function(value, start, length){
13468             return String(value).substr(start, length);
13469         },
13470
13471         /**
13472          * Converts a string to all lower case letters
13473          * @param {String} value The text to convert
13474          * @return {String} The converted text
13475          */
13476         lowercase : function(value){
13477             return String(value).toLowerCase();
13478         },
13479
13480         /**
13481          * Converts a string to all upper case letters
13482          * @param {String} value The text to convert
13483          * @return {String} The converted text
13484          */
13485         uppercase : function(value){
13486             return String(value).toUpperCase();
13487         },
13488
13489         /**
13490          * Converts the first character only of a string to upper case
13491          * @param {String} value The text to convert
13492          * @return {String} The converted text
13493          */
13494         capitalize : function(value){
13495             return !value ? value : value.charAt(0).toUpperCase() + value.substr(1).toLowerCase();
13496         },
13497
13498         // private
13499         call : function(value, fn){
13500             if(arguments.length > 2){
13501                 var args = Array.prototype.slice.call(arguments, 2);
13502                 args.unshift(value);
13503                  
13504                 return /** eval:var:value */  eval(fn).apply(window, args);
13505             }else{
13506                 /** eval:var:value */
13507                 return /** eval:var:value */ eval(fn).call(window, value);
13508             }
13509         },
13510
13511        
13512         /**
13513          * safer version of Math.toFixed..??/
13514          * @param {Number/String} value The numeric value to format
13515          * @param {Number/String} value Decimal places 
13516          * @return {String} The formatted currency string
13517          */
13518         toFixed : function(v, n)
13519         {
13520             // why not use to fixed - precision is buggered???
13521             if (!n) {
13522                 return Math.round(v-0);
13523             }
13524             var fact = Math.pow(10,n+1);
13525             v = (Math.round((v-0)*fact))/fact;
13526             var z = (''+fact).substring(2);
13527             if (v == Math.floor(v)) {
13528                 return Math.floor(v) + '.' + z;
13529             }
13530             
13531             // now just padd decimals..
13532             var ps = String(v).split('.');
13533             var fd = (ps[1] + z);
13534             var r = fd.substring(0,n); 
13535             var rm = fd.substring(n); 
13536             if (rm < 5) {
13537                 return ps[0] + '.' + r;
13538             }
13539             r*=1; // turn it into a number;
13540             r++;
13541             if (String(r).length != n) {
13542                 ps[0]*=1;
13543                 ps[0]++;
13544                 r = String(r).substring(1); // chop the end off.
13545             }
13546             
13547             return ps[0] + '.' + r;
13548              
13549         },
13550         
13551         /**
13552          * Format a number as US currency
13553          * @param {Number/String} value The numeric value to format
13554          * @return {String} The formatted currency string
13555          */
13556         usMoney : function(v){
13557             return '$' + Roo.util.Format.number(v);
13558         },
13559         
13560         /**
13561          * Format a number
13562          * eventually this should probably emulate php's number_format
13563          * @param {Number/String} value The numeric value to format
13564          * @param {Number} decimals number of decimal places
13565          * @return {String} The formatted currency string
13566          */
13567         number : function(v,decimals)
13568         {
13569             // multiply and round.
13570             decimals = typeof(decimals) == 'undefined' ? 2 : decimals;
13571             var mul = Math.pow(10, decimals);
13572             var zero = String(mul).substring(1);
13573             v = (Math.round((v-0)*mul))/mul;
13574             
13575             // if it's '0' number.. then
13576             
13577             //v = (v == Math.floor(v)) ? v + "." + zero : ((v*10 == Math.floor(v*10)) ? v + "0" : v);
13578             v = String(v);
13579             var ps = v.split('.');
13580             var whole = ps[0];
13581             
13582             
13583             var r = /(\d+)(\d{3})/;
13584             // add comma's
13585             while (r.test(whole)) {
13586                 whole = whole.replace(r, '$1' + ',' + '$2');
13587             }
13588             
13589             
13590             var sub = ps[1] ?
13591                     // has decimals..
13592                     (decimals ?  ('.'+ ps[1] + zero.substring(ps[1].length)) : '') :
13593                     // does not have decimals
13594                     (decimals ? ('.' + zero) : '');
13595             
13596             
13597             return whole + sub ;
13598         },
13599         
13600         /**
13601          * Parse a value into a formatted date using the specified format pattern.
13602          * @param {Mixed} value The value to format
13603          * @param {String} format (optional) Any valid date format string (defaults to 'm/d/Y')
13604          * @return {String} The formatted date string
13605          */
13606         date : function(v, format){
13607             if(!v){
13608                 return "";
13609             }
13610             if(!(v instanceof Date)){
13611                 v = new Date(Date.parse(v));
13612             }
13613             return v.dateFormat(format || Roo.util.Format.defaults.date);
13614         },
13615
13616         /**
13617          * Returns a date rendering function that can be reused to apply a date format multiple times efficiently
13618          * @param {String} format Any valid date format string
13619          * @return {Function} The date formatting function
13620          */
13621         dateRenderer : function(format){
13622             return function(v){
13623                 return Roo.util.Format.date(v, format);  
13624             };
13625         },
13626
13627         // private
13628         stripTagsRE : /<\/?[^>]+>/gi,
13629         
13630         /**
13631          * Strips all HTML tags
13632          * @param {Mixed} value The text from which to strip tags
13633          * @return {String} The stripped text
13634          */
13635         stripTags : function(v){
13636             return !v ? v : String(v).replace(this.stripTagsRE, "");
13637         }
13638     };
13639 }();
13640 Roo.util.Format.defaults = {
13641     date : 'd/M/Y'
13642 };/*
13643  * Based on:
13644  * Ext JS Library 1.1.1
13645  * Copyright(c) 2006-2007, Ext JS, LLC.
13646  *
13647  * Originally Released Under LGPL - original licence link has changed is not relivant.
13648  *
13649  * Fork - LGPL
13650  * <script type="text/javascript">
13651  */
13652
13653
13654  
13655
13656 /**
13657  * @class Roo.MasterTemplate
13658  * @extends Roo.Template
13659  * Provides a template that can have child templates. The syntax is:
13660 <pre><code>
13661 var t = new Roo.MasterTemplate(
13662         '&lt;select name="{name}"&gt;',
13663                 '&lt;tpl name="options"&gt;&lt;option value="{value:trim}"&gt;{text:ellipsis(10)}&lt;/option&gt;&lt;/tpl&gt;',
13664         '&lt;/select&gt;'
13665 );
13666 t.add('options', {value: 'foo', text: 'bar'});
13667 // or you can add multiple child elements in one shot
13668 t.addAll('options', [
13669     {value: 'foo', text: 'bar'},
13670     {value: 'foo2', text: 'bar2'},
13671     {value: 'foo3', text: 'bar3'}
13672 ]);
13673 // then append, applying the master template values
13674 t.append('my-form', {name: 'my-select'});
13675 </code></pre>
13676 * A name attribute for the child template is not required if you have only one child
13677 * template or you want to refer to them by index.
13678  */
13679 Roo.MasterTemplate = function(){
13680     Roo.MasterTemplate.superclass.constructor.apply(this, arguments);
13681     this.originalHtml = this.html;
13682     var st = {};
13683     var m, re = this.subTemplateRe;
13684     re.lastIndex = 0;
13685     var subIndex = 0;
13686     while(m = re.exec(this.html)){
13687         var name = m[1], content = m[2];
13688         st[subIndex] = {
13689             name: name,
13690             index: subIndex,
13691             buffer: [],
13692             tpl : new Roo.Template(content)
13693         };
13694         if(name){
13695             st[name] = st[subIndex];
13696         }
13697         st[subIndex].tpl.compile();
13698         st[subIndex].tpl.call = this.call.createDelegate(this);
13699         subIndex++;
13700     }
13701     this.subCount = subIndex;
13702     this.subs = st;
13703 };
13704 Roo.extend(Roo.MasterTemplate, Roo.Template, {
13705     /**
13706     * The regular expression used to match sub templates
13707     * @type RegExp
13708     * @property
13709     */
13710     subTemplateRe : /<tpl(?:\sname="([\w-]+)")?>((?:.|\n)*?)<\/tpl>/gi,
13711
13712     /**
13713      * Applies the passed values to a child template.
13714      * @param {String/Number} name (optional) The name or index of the child template
13715      * @param {Array/Object} values The values to be applied to the template
13716      * @return {MasterTemplate} this
13717      */
13718      add : function(name, values){
13719         if(arguments.length == 1){
13720             values = arguments[0];
13721             name = 0;
13722         }
13723         var s = this.subs[name];
13724         s.buffer[s.buffer.length] = s.tpl.apply(values);
13725         return this;
13726     },
13727
13728     /**
13729      * Applies all the passed values to a child template.
13730      * @param {String/Number} name (optional) The name or index of the child template
13731      * @param {Array} values The values to be applied to the template, this should be an array of objects.
13732      * @param {Boolean} reset (optional) True to reset the template first
13733      * @return {MasterTemplate} this
13734      */
13735     fill : function(name, values, reset){
13736         var a = arguments;
13737         if(a.length == 1 || (a.length == 2 && typeof a[1] == "boolean")){
13738             values = a[0];
13739             name = 0;
13740             reset = a[1];
13741         }
13742         if(reset){
13743             this.reset();
13744         }
13745         for(var i = 0, len = values.length; i < len; i++){
13746             this.add(name, values[i]);
13747         }
13748         return this;
13749     },
13750
13751     /**
13752      * Resets the template for reuse
13753      * @return {MasterTemplate} this
13754      */
13755      reset : function(){
13756         var s = this.subs;
13757         for(var i = 0; i < this.subCount; i++){
13758             s[i].buffer = [];
13759         }
13760         return this;
13761     },
13762
13763     applyTemplate : function(values){
13764         var s = this.subs;
13765         var replaceIndex = -1;
13766         this.html = this.originalHtml.replace(this.subTemplateRe, function(m, name){
13767             return s[++replaceIndex].buffer.join("");
13768         });
13769         return Roo.MasterTemplate.superclass.applyTemplate.call(this, values);
13770     },
13771
13772     apply : function(){
13773         return this.applyTemplate.apply(this, arguments);
13774     },
13775
13776     compile : function(){return this;}
13777 });
13778
13779 /**
13780  * Alias for fill().
13781  * @method
13782  */
13783 Roo.MasterTemplate.prototype.addAll = Roo.MasterTemplate.prototype.fill;
13784  /**
13785  * Creates a template from the passed element's value (display:none textarea, preferred) or innerHTML. e.g.
13786  * var tpl = Roo.MasterTemplate.from('element-id');
13787  * @param {String/HTMLElement} el
13788  * @param {Object} config
13789  * @static
13790  */
13791 Roo.MasterTemplate.from = function(el, config){
13792     el = Roo.getDom(el);
13793     return new Roo.MasterTemplate(el.value || el.innerHTML, config || '');
13794 };/*
13795  * Based on:
13796  * Ext JS Library 1.1.1
13797  * Copyright(c) 2006-2007, Ext JS, LLC.
13798  *
13799  * Originally Released Under LGPL - original licence link has changed is not relivant.
13800  *
13801  * Fork - LGPL
13802  * <script type="text/javascript">
13803  */
13804
13805  
13806 /**
13807  * @class Roo.util.CSS
13808  * Utility class for manipulating CSS rules
13809  * @singleton
13810  */
13811 Roo.util.CSS = function(){
13812         var rules = null;
13813         var doc = document;
13814
13815     var camelRe = /(-[a-z])/gi;
13816     var camelFn = function(m, a){ return a.charAt(1).toUpperCase(); };
13817
13818    return {
13819    /**
13820     * Very simple dynamic creation of stylesheets from a text blob of rules.  The text will wrapped in a style
13821     * tag and appended to the HEAD of the document.
13822     * @param {String|Object} cssText The text containing the css rules
13823     * @param {String} id An id to add to the stylesheet for later removal
13824     * @return {StyleSheet}
13825     */
13826     createStyleSheet : function(cssText, id){
13827         var ss;
13828         var head = doc.getElementsByTagName("head")[0];
13829         var nrules = doc.createElement("style");
13830         nrules.setAttribute("type", "text/css");
13831         if(id){
13832             nrules.setAttribute("id", id);
13833         }
13834         if (typeof(cssText) != 'string') {
13835             // support object maps..
13836             // not sure if this a good idea.. 
13837             // perhaps it should be merged with the general css handling
13838             // and handle js style props.
13839             var cssTextNew = [];
13840             for(var n in cssText) {
13841                 var citems = [];
13842                 for(var k in cssText[n]) {
13843                     citems.push( k + ' : ' +cssText[n][k] + ';' );
13844                 }
13845                 cssTextNew.push( n + ' { ' + citems.join(' ') + '} ');
13846                 
13847             }
13848             cssText = cssTextNew.join("\n");
13849             
13850         }
13851        
13852        
13853        if(Roo.isIE){
13854            head.appendChild(nrules);
13855            ss = nrules.styleSheet;
13856            ss.cssText = cssText;
13857        }else{
13858            try{
13859                 nrules.appendChild(doc.createTextNode(cssText));
13860            }catch(e){
13861                nrules.cssText = cssText; 
13862            }
13863            head.appendChild(nrules);
13864            ss = nrules.styleSheet ? nrules.styleSheet : (nrules.sheet || doc.styleSheets[doc.styleSheets.length-1]);
13865        }
13866        this.cacheStyleSheet(ss);
13867        return ss;
13868    },
13869
13870    /**
13871     * Removes a style or link tag by id
13872     * @param {String} id The id of the tag
13873     */
13874    removeStyleSheet : function(id){
13875        var existing = doc.getElementById(id);
13876        if(existing){
13877            existing.parentNode.removeChild(existing);
13878        }
13879    },
13880
13881    /**
13882     * Dynamically swaps an existing stylesheet reference for a new one
13883     * @param {String} id The id of an existing link tag to remove
13884     * @param {String} url The href of the new stylesheet to include
13885     */
13886    swapStyleSheet : function(id, url){
13887        this.removeStyleSheet(id);
13888        var ss = doc.createElement("link");
13889        ss.setAttribute("rel", "stylesheet");
13890        ss.setAttribute("type", "text/css");
13891        ss.setAttribute("id", id);
13892        ss.setAttribute("href", url);
13893        doc.getElementsByTagName("head")[0].appendChild(ss);
13894    },
13895    
13896    /**
13897     * Refresh the rule cache if you have dynamically added stylesheets
13898     * @return {Object} An object (hash) of rules indexed by selector
13899     */
13900    refreshCache : function(){
13901        return this.getRules(true);
13902    },
13903
13904    // private
13905    cacheStyleSheet : function(stylesheet){
13906        if(!rules){
13907            rules = {};
13908        }
13909        try{// try catch for cross domain access issue
13910            var ssRules = stylesheet.cssRules || stylesheet.rules;
13911            for(var j = ssRules.length-1; j >= 0; --j){
13912                rules[ssRules[j].selectorText] = ssRules[j];
13913            }
13914        }catch(e){}
13915    },
13916    
13917    /**
13918     * Gets all css rules for the document
13919     * @param {Boolean} refreshCache true to refresh the internal cache
13920     * @return {Object} An object (hash) of rules indexed by selector
13921     */
13922    getRules : function(refreshCache){
13923                 if(rules == null || refreshCache){
13924                         rules = {};
13925                         var ds = doc.styleSheets;
13926                         for(var i =0, len = ds.length; i < len; i++){
13927                             try{
13928                         this.cacheStyleSheet(ds[i]);
13929                     }catch(e){} 
13930                 }
13931                 }
13932                 return rules;
13933         },
13934         
13935         /**
13936     * Gets an an individual CSS rule by selector(s)
13937     * @param {String/Array} selector The CSS selector or an array of selectors to try. The first selector that is found is returned.
13938     * @param {Boolean} refreshCache true to refresh the internal cache if you have recently updated any rules or added styles dynamically
13939     * @return {CSSRule} The CSS rule or null if one is not found
13940     */
13941    getRule : function(selector, refreshCache){
13942                 var rs = this.getRules(refreshCache);
13943                 if(!(selector instanceof Array)){
13944                     return rs[selector];
13945                 }
13946                 for(var i = 0; i < selector.length; i++){
13947                         if(rs[selector[i]]){
13948                                 return rs[selector[i]];
13949                         }
13950                 }
13951                 return null;
13952         },
13953         
13954         
13955         /**
13956     * Updates a rule property
13957     * @param {String/Array} selector If it's an array it tries each selector until it finds one. Stops immediately once one is found.
13958     * @param {String} property The css property
13959     * @param {String} value The new value for the property
13960     * @return {Boolean} true If a rule was found and updated
13961     */
13962    updateRule : function(selector, property, value){
13963                 if(!(selector instanceof Array)){
13964                         var rule = this.getRule(selector);
13965                         if(rule){
13966                                 rule.style[property.replace(camelRe, camelFn)] = value;
13967                                 return true;
13968                         }
13969                 }else{
13970                         for(var i = 0; i < selector.length; i++){
13971                                 if(this.updateRule(selector[i], property, value)){
13972                                         return true;
13973                                 }
13974                         }
13975                 }
13976                 return false;
13977         }
13978    };   
13979 }();/*
13980  * Based on:
13981  * Ext JS Library 1.1.1
13982  * Copyright(c) 2006-2007, Ext JS, LLC.
13983  *
13984  * Originally Released Under LGPL - original licence link has changed is not relivant.
13985  *
13986  * Fork - LGPL
13987  * <script type="text/javascript">
13988  */
13989
13990  
13991
13992 /**
13993  * @class Roo.util.ClickRepeater
13994  * @extends Roo.util.Observable
13995  * 
13996  * A wrapper class which can be applied to any element. Fires a "click" event while the
13997  * mouse is pressed. The interval between firings may be specified in the config but
13998  * defaults to 10 milliseconds.
13999  * 
14000  * Optionally, a CSS class may be applied to the element during the time it is pressed.
14001  * 
14002  * @cfg {String/HTMLElement/Element} el The element to act as a button.
14003  * @cfg {Number} delay The initial delay before the repeating event begins firing.
14004  * Similar to an autorepeat key delay.
14005  * @cfg {Number} interval The interval between firings of the "click" event. Default 10 ms.
14006  * @cfg {String} pressClass A CSS class name to be applied to the element while pressed.
14007  * @cfg {Boolean} accelerate True if autorepeating should start slowly and accelerate.
14008  *           "interval" and "delay" are ignored. "immediate" is honored.
14009  * @cfg {Boolean} preventDefault True to prevent the default click event
14010  * @cfg {Boolean} stopDefault True to stop the default click event
14011  * 
14012  * @history
14013  *     2007-02-02 jvs Original code contributed by Nige "Animal" White
14014  *     2007-02-02 jvs Renamed to ClickRepeater
14015  *   2007-02-03 jvs Modifications for FF Mac and Safari 
14016  *
14017  *  @constructor
14018  * @param {String/HTMLElement/Element} el The element to listen on
14019  * @param {Object} config
14020  **/
14021 Roo.util.ClickRepeater = function(el, config)
14022 {
14023     this.el = Roo.get(el);
14024     this.el.unselectable();
14025
14026     Roo.apply(this, config);
14027
14028     this.addEvents({
14029     /**
14030      * @event mousedown
14031      * Fires when the mouse button is depressed.
14032      * @param {Roo.util.ClickRepeater} this
14033      */
14034         "mousedown" : true,
14035     /**
14036      * @event click
14037      * Fires on a specified interval during the time the element is pressed.
14038      * @param {Roo.util.ClickRepeater} this
14039      */
14040         "click" : true,
14041     /**
14042      * @event mouseup
14043      * Fires when the mouse key is released.
14044      * @param {Roo.util.ClickRepeater} this
14045      */
14046         "mouseup" : true
14047     });
14048
14049     this.el.on("mousedown", this.handleMouseDown, this);
14050     if(this.preventDefault || this.stopDefault){
14051         this.el.on("click", function(e){
14052             if(this.preventDefault){
14053                 e.preventDefault();
14054             }
14055             if(this.stopDefault){
14056                 e.stopEvent();
14057             }
14058         }, this);
14059     }
14060
14061     // allow inline handler
14062     if(this.handler){
14063         this.on("click", this.handler,  this.scope || this);
14064     }
14065
14066     Roo.util.ClickRepeater.superclass.constructor.call(this);
14067 };
14068
14069 Roo.extend(Roo.util.ClickRepeater, Roo.util.Observable, {
14070     interval : 20,
14071     delay: 250,
14072     preventDefault : true,
14073     stopDefault : false,
14074     timer : 0,
14075
14076     // private
14077     handleMouseDown : function(){
14078         clearTimeout(this.timer);
14079         this.el.blur();
14080         if(this.pressClass){
14081             this.el.addClass(this.pressClass);
14082         }
14083         this.mousedownTime = new Date();
14084
14085         Roo.get(document).on("mouseup", this.handleMouseUp, this);
14086         this.el.on("mouseout", this.handleMouseOut, this);
14087
14088         this.fireEvent("mousedown", this);
14089         this.fireEvent("click", this);
14090         
14091         this.timer = this.click.defer(this.delay || this.interval, this);
14092     },
14093
14094     // private
14095     click : function(){
14096         this.fireEvent("click", this);
14097         this.timer = this.click.defer(this.getInterval(), this);
14098     },
14099
14100     // private
14101     getInterval: function(){
14102         if(!this.accelerate){
14103             return this.interval;
14104         }
14105         var pressTime = this.mousedownTime.getElapsed();
14106         if(pressTime < 500){
14107             return 400;
14108         }else if(pressTime < 1700){
14109             return 320;
14110         }else if(pressTime < 2600){
14111             return 250;
14112         }else if(pressTime < 3500){
14113             return 180;
14114         }else if(pressTime < 4400){
14115             return 140;
14116         }else if(pressTime < 5300){
14117             return 80;
14118         }else if(pressTime < 6200){
14119             return 50;
14120         }else{
14121             return 10;
14122         }
14123     },
14124
14125     // private
14126     handleMouseOut : function(){
14127         clearTimeout(this.timer);
14128         if(this.pressClass){
14129             this.el.removeClass(this.pressClass);
14130         }
14131         this.el.on("mouseover", this.handleMouseReturn, this);
14132     },
14133
14134     // private
14135     handleMouseReturn : function(){
14136         this.el.un("mouseover", this.handleMouseReturn);
14137         if(this.pressClass){
14138             this.el.addClass(this.pressClass);
14139         }
14140         this.click();
14141     },
14142
14143     // private
14144     handleMouseUp : function(){
14145         clearTimeout(this.timer);
14146         this.el.un("mouseover", this.handleMouseReturn);
14147         this.el.un("mouseout", this.handleMouseOut);
14148         Roo.get(document).un("mouseup", this.handleMouseUp);
14149         this.el.removeClass(this.pressClass);
14150         this.fireEvent("mouseup", this);
14151     }
14152 });/*
14153  * Based on:
14154  * Ext JS Library 1.1.1
14155  * Copyright(c) 2006-2007, Ext JS, LLC.
14156  *
14157  * Originally Released Under LGPL - original licence link has changed is not relivant.
14158  *
14159  * Fork - LGPL
14160  * <script type="text/javascript">
14161  */
14162
14163  
14164 /**
14165  * @class Roo.KeyNav
14166  * <p>Provides a convenient wrapper for normalized keyboard navigation.  KeyNav allows you to bind
14167  * navigation keys to function calls that will get called when the keys are pressed, providing an easy
14168  * way to implement custom navigation schemes for any UI component.</p>
14169  * <p>The following are all of the possible keys that can be implemented: enter, left, right, up, down, tab, esc,
14170  * pageUp, pageDown, del, home, end.  Usage:</p>
14171  <pre><code>
14172 var nav = new Roo.KeyNav("my-element", {
14173     "left" : function(e){
14174         this.moveLeft(e.ctrlKey);
14175     },
14176     "right" : function(e){
14177         this.moveRight(e.ctrlKey);
14178     },
14179     "enter" : function(e){
14180         this.save();
14181     },
14182     scope : this
14183 });
14184 </code></pre>
14185  * @constructor
14186  * @param {String/HTMLElement/Roo.Element} el The element to bind to
14187  * @param {Object} config The config
14188  */
14189 Roo.KeyNav = function(el, config){
14190     this.el = Roo.get(el);
14191     Roo.apply(this, config);
14192     if(!this.disabled){
14193         this.disabled = true;
14194         this.enable();
14195     }
14196 };
14197
14198 Roo.KeyNav.prototype = {
14199     /**
14200      * @cfg {Boolean} disabled
14201      * True to disable this KeyNav instance (defaults to false)
14202      */
14203     disabled : false,
14204     /**
14205      * @cfg {String} defaultEventAction
14206      * The method to call on the {@link Roo.EventObject} after this KeyNav intercepts a key.  Valid values are
14207      * {@link Roo.EventObject#stopEvent}, {@link Roo.EventObject#preventDefault} and
14208      * {@link Roo.EventObject#stopPropagation} (defaults to 'stopEvent')
14209      */
14210     defaultEventAction: "stopEvent",
14211     /**
14212      * @cfg {Boolean} forceKeyDown
14213      * Handle the keydown event instead of keypress (defaults to false).  KeyNav automatically does this for IE since
14214      * IE does not propagate special keys on keypress, but setting this to true will force other browsers to also
14215      * handle keydown instead of keypress.
14216      */
14217     forceKeyDown : false,
14218
14219     // private
14220     prepareEvent : function(e){
14221         var k = e.getKey();
14222         var h = this.keyToHandler[k];
14223         //if(h && this[h]){
14224         //    e.stopPropagation();
14225         //}
14226         if(Roo.isSafari && h && k >= 37 && k <= 40){
14227             e.stopEvent();
14228         }
14229     },
14230
14231     // private
14232     relay : function(e){
14233         var k = e.getKey();
14234         var h = this.keyToHandler[k];
14235         if(h && this[h]){
14236             if(this.doRelay(e, this[h], h) !== true){
14237                 e[this.defaultEventAction]();
14238             }
14239         }
14240     },
14241
14242     // private
14243     doRelay : function(e, h, hname){
14244         return h.call(this.scope || this, e);
14245     },
14246
14247     // possible handlers
14248     enter : false,
14249     left : false,
14250     right : false,
14251     up : false,
14252     down : false,
14253     tab : false,
14254     esc : false,
14255     pageUp : false,
14256     pageDown : false,
14257     del : false,
14258     home : false,
14259     end : false,
14260
14261     // quick lookup hash
14262     keyToHandler : {
14263         37 : "left",
14264         39 : "right",
14265         38 : "up",
14266         40 : "down",
14267         33 : "pageUp",
14268         34 : "pageDown",
14269         46 : "del",
14270         36 : "home",
14271         35 : "end",
14272         13 : "enter",
14273         27 : "esc",
14274         9  : "tab"
14275     },
14276
14277         /**
14278          * Enable this KeyNav
14279          */
14280         enable: function(){
14281                 if(this.disabled){
14282             // ie won't do special keys on keypress, no one else will repeat keys with keydown
14283             // the EventObject will normalize Safari automatically
14284             if(this.forceKeyDown || Roo.isIE || Roo.isAir){
14285                 this.el.on("keydown", this.relay,  this);
14286             }else{
14287                 this.el.on("keydown", this.prepareEvent,  this);
14288                 this.el.on("keypress", this.relay,  this);
14289             }
14290                     this.disabled = false;
14291                 }
14292         },
14293
14294         /**
14295          * Disable this KeyNav
14296          */
14297         disable: function(){
14298                 if(!this.disabled){
14299                     if(this.forceKeyDown || Roo.isIE || Roo.isAir){
14300                 this.el.un("keydown", this.relay);
14301             }else{
14302                 this.el.un("keydown", this.prepareEvent);
14303                 this.el.un("keypress", this.relay);
14304             }
14305                     this.disabled = true;
14306                 }
14307         }
14308 };/*
14309  * Based on:
14310  * Ext JS Library 1.1.1
14311  * Copyright(c) 2006-2007, Ext JS, LLC.
14312  *
14313  * Originally Released Under LGPL - original licence link has changed is not relivant.
14314  *
14315  * Fork - LGPL
14316  * <script type="text/javascript">
14317  */
14318
14319  
14320 /**
14321  * @class Roo.KeyMap
14322  * Handles mapping keys to actions for an element. One key map can be used for multiple actions.
14323  * The constructor accepts the same config object as defined by {@link #addBinding}.
14324  * If you bind a callback function to a KeyMap, anytime the KeyMap handles an expected key
14325  * combination it will call the function with this signature (if the match is a multi-key
14326  * combination the callback will still be called only once): (String key, Roo.EventObject e)
14327  * A KeyMap can also handle a string representation of keys.<br />
14328  * Usage:
14329  <pre><code>
14330 // map one key by key code
14331 var map = new Roo.KeyMap("my-element", {
14332     key: 13, // or Roo.EventObject.ENTER
14333     fn: myHandler,
14334     scope: myObject
14335 });
14336
14337 // map multiple keys to one action by string
14338 var map = new Roo.KeyMap("my-element", {
14339     key: "a\r\n\t",
14340     fn: myHandler,
14341     scope: myObject
14342 });
14343
14344 // map multiple keys to multiple actions by strings and array of codes
14345 var map = new Roo.KeyMap("my-element", [
14346     {
14347         key: [10,13],
14348         fn: function(){ alert("Return was pressed"); }
14349     }, {
14350         key: "abc",
14351         fn: function(){ alert('a, b or c was pressed'); }
14352     }, {
14353         key: "\t",
14354         ctrl:true,
14355         shift:true,
14356         fn: function(){ alert('Control + shift + tab was pressed.'); }
14357     }
14358 ]);
14359 </code></pre>
14360  * <b>Note: A KeyMap starts enabled</b>
14361  * @constructor
14362  * @param {String/HTMLElement/Roo.Element} el The element to bind to
14363  * @param {Object} config The config (see {@link #addBinding})
14364  * @param {String} eventName (optional) The event to bind to (defaults to "keydown")
14365  */
14366 Roo.KeyMap = function(el, config, eventName){
14367     this.el  = Roo.get(el);
14368     this.eventName = eventName || "keydown";
14369     this.bindings = [];
14370     if(config){
14371         this.addBinding(config);
14372     }
14373     this.enable();
14374 };
14375
14376 Roo.KeyMap.prototype = {
14377     /**
14378      * True to stop the event from bubbling and prevent the default browser action if the
14379      * key was handled by the KeyMap (defaults to false)
14380      * @type Boolean
14381      */
14382     stopEvent : false,
14383
14384     /**
14385      * Add a new binding to this KeyMap. The following config object properties are supported:
14386      * <pre>
14387 Property    Type             Description
14388 ----------  ---------------  ----------------------------------------------------------------------
14389 key         String/Array     A single keycode or an array of keycodes to handle
14390 shift       Boolean          True to handle key only when shift is pressed (defaults to false)
14391 ctrl        Boolean          True to handle key only when ctrl is pressed (defaults to false)
14392 alt         Boolean          True to handle key only when alt is pressed (defaults to false)
14393 fn          Function         The function to call when KeyMap finds the expected key combination
14394 scope       Object           The scope of the callback function
14395 </pre>
14396      *
14397      * Usage:
14398      * <pre><code>
14399 // Create a KeyMap
14400 var map = new Roo.KeyMap(document, {
14401     key: Roo.EventObject.ENTER,
14402     fn: handleKey,
14403     scope: this
14404 });
14405
14406 //Add a new binding to the existing KeyMap later
14407 map.addBinding({
14408     key: 'abc',
14409     shift: true,
14410     fn: handleKey,
14411     scope: this
14412 });
14413 </code></pre>
14414      * @param {Object/Array} config A single KeyMap config or an array of configs
14415      */
14416         addBinding : function(config){
14417         if(config instanceof Array){
14418             for(var i = 0, len = config.length; i < len; i++){
14419                 this.addBinding(config[i]);
14420             }
14421             return;
14422         }
14423         var keyCode = config.key,
14424             shift = config.shift, 
14425             ctrl = config.ctrl, 
14426             alt = config.alt,
14427             fn = config.fn,
14428             scope = config.scope;
14429         if(typeof keyCode == "string"){
14430             var ks = [];
14431             var keyString = keyCode.toUpperCase();
14432             for(var j = 0, len = keyString.length; j < len; j++){
14433                 ks.push(keyString.charCodeAt(j));
14434             }
14435             keyCode = ks;
14436         }
14437         var keyArray = keyCode instanceof Array;
14438         var handler = function(e){
14439             if((!shift || e.shiftKey) && (!ctrl || e.ctrlKey) &&  (!alt || e.altKey)){
14440                 var k = e.getKey();
14441                 if(keyArray){
14442                     for(var i = 0, len = keyCode.length; i < len; i++){
14443                         if(keyCode[i] == k){
14444                           if(this.stopEvent){
14445                               e.stopEvent();
14446                           }
14447                           fn.call(scope || window, k, e);
14448                           return;
14449                         }
14450                     }
14451                 }else{
14452                     if(k == keyCode){
14453                         if(this.stopEvent){
14454                            e.stopEvent();
14455                         }
14456                         fn.call(scope || window, k, e);
14457                     }
14458                 }
14459             }
14460         };
14461         this.bindings.push(handler);  
14462         },
14463
14464     /**
14465      * Shorthand for adding a single key listener
14466      * @param {Number/Array/Object} key Either the numeric key code, array of key codes or an object with the
14467      * following options:
14468      * {key: (number or array), shift: (true/false), ctrl: (true/false), alt: (true/false)}
14469      * @param {Function} fn The function to call
14470      * @param {Object} scope (optional) The scope of the function
14471      */
14472     on : function(key, fn, scope){
14473         var keyCode, shift, ctrl, alt;
14474         if(typeof key == "object" && !(key instanceof Array)){
14475             keyCode = key.key;
14476             shift = key.shift;
14477             ctrl = key.ctrl;
14478             alt = key.alt;
14479         }else{
14480             keyCode = key;
14481         }
14482         this.addBinding({
14483             key: keyCode,
14484             shift: shift,
14485             ctrl: ctrl,
14486             alt: alt,
14487             fn: fn,
14488             scope: scope
14489         })
14490     },
14491
14492     // private
14493     handleKeyDown : function(e){
14494             if(this.enabled){ //just in case
14495             var b = this.bindings;
14496             for(var i = 0, len = b.length; i < len; i++){
14497                 b[i].call(this, e);
14498             }
14499             }
14500         },
14501         
14502         /**
14503          * Returns true if this KeyMap is enabled
14504          * @return {Boolean} 
14505          */
14506         isEnabled : function(){
14507             return this.enabled;  
14508         },
14509         
14510         /**
14511          * Enables this KeyMap
14512          */
14513         enable: function(){
14514                 if(!this.enabled){
14515                     this.el.on(this.eventName, this.handleKeyDown, this);
14516                     this.enabled = true;
14517                 }
14518         },
14519
14520         /**
14521          * Disable this KeyMap
14522          */
14523         disable: function(){
14524                 if(this.enabled){
14525                     this.el.removeListener(this.eventName, this.handleKeyDown, this);
14526                     this.enabled = false;
14527                 }
14528         }
14529 };/*
14530  * Based on:
14531  * Ext JS Library 1.1.1
14532  * Copyright(c) 2006-2007, Ext JS, LLC.
14533  *
14534  * Originally Released Under LGPL - original licence link has changed is not relivant.
14535  *
14536  * Fork - LGPL
14537  * <script type="text/javascript">
14538  */
14539
14540  
14541 /**
14542  * @class Roo.util.TextMetrics
14543  * Provides precise pixel measurements for blocks of text so that you can determine exactly how high and
14544  * wide, in pixels, a given block of text will be.
14545  * @singleton
14546  */
14547 Roo.util.TextMetrics = function(){
14548     var shared;
14549     return {
14550         /**
14551          * Measures the size of the specified text
14552          * @param {String/HTMLElement} el The element, dom node or id from which to copy existing CSS styles
14553          * that can affect the size of the rendered text
14554          * @param {String} text The text to measure
14555          * @param {Number} fixedWidth (optional) If the text will be multiline, you have to set a fixed width
14556          * in order to accurately measure the text height
14557          * @return {Object} An object containing the text's size {width: (width), height: (height)}
14558          */
14559         measure : function(el, text, fixedWidth){
14560             if(!shared){
14561                 shared = Roo.util.TextMetrics.Instance(el, fixedWidth);
14562             }
14563             shared.bind(el);
14564             shared.setFixedWidth(fixedWidth || 'auto');
14565             return shared.getSize(text);
14566         },
14567
14568         /**
14569          * Return a unique TextMetrics instance that can be bound directly to an element and reused.  This reduces
14570          * the overhead of multiple calls to initialize the style properties on each measurement.
14571          * @param {String/HTMLElement} el The element, dom node or id that the instance will be bound to
14572          * @param {Number} fixedWidth (optional) If the text will be multiline, you have to set a fixed width
14573          * in order to accurately measure the text height
14574          * @return {Roo.util.TextMetrics.Instance} instance The new instance
14575          */
14576         createInstance : function(el, fixedWidth){
14577             return Roo.util.TextMetrics.Instance(el, fixedWidth);
14578         }
14579     };
14580 }();
14581
14582  
14583
14584 Roo.util.TextMetrics.Instance = function(bindTo, fixedWidth){
14585     var ml = new Roo.Element(document.createElement('div'));
14586     document.body.appendChild(ml.dom);
14587     ml.position('absolute');
14588     ml.setLeftTop(-1000, -1000);
14589     ml.hide();
14590
14591     if(fixedWidth){
14592         ml.setWidth(fixedWidth);
14593     }
14594      
14595     var instance = {
14596         /**
14597          * Returns the size of the specified text based on the internal element's style and width properties
14598          * @memberOf Roo.util.TextMetrics.Instance#
14599          * @param {String} text The text to measure
14600          * @return {Object} An object containing the text's size {width: (width), height: (height)}
14601          */
14602         getSize : function(text){
14603             ml.update(text);
14604             var s = ml.getSize();
14605             ml.update('');
14606             return s;
14607         },
14608
14609         /**
14610          * Binds this TextMetrics instance to an element from which to copy existing CSS styles
14611          * that can affect the size of the rendered text
14612          * @memberOf Roo.util.TextMetrics.Instance#
14613          * @param {String/HTMLElement} el The element, dom node or id
14614          */
14615         bind : function(el){
14616             ml.setStyle(
14617                 Roo.fly(el).getStyles('font-size','font-style', 'font-weight', 'font-family','line-height')
14618             );
14619         },
14620
14621         /**
14622          * Sets a fixed width on the internal measurement element.  If the text will be multiline, you have
14623          * to set a fixed width in order to accurately measure the text height.
14624          * @memberOf Roo.util.TextMetrics.Instance#
14625          * @param {Number} width The width to set on the element
14626          */
14627         setFixedWidth : function(width){
14628             ml.setWidth(width);
14629         },
14630
14631         /**
14632          * Returns the measured width of the specified text
14633          * @memberOf Roo.util.TextMetrics.Instance#
14634          * @param {String} text The text to measure
14635          * @return {Number} width The width in pixels
14636          */
14637         getWidth : function(text){
14638             ml.dom.style.width = 'auto';
14639             return this.getSize(text).width;
14640         },
14641
14642         /**
14643          * Returns the measured height of the specified text.  For multiline text, be sure to call
14644          * {@link #setFixedWidth} if necessary.
14645          * @memberOf Roo.util.TextMetrics.Instance#
14646          * @param {String} text The text to measure
14647          * @return {Number} height The height in pixels
14648          */
14649         getHeight : function(text){
14650             return this.getSize(text).height;
14651         }
14652     };
14653
14654     instance.bind(bindTo);
14655
14656     return instance;
14657 };
14658
14659 // backwards compat
14660 Roo.Element.measureText = Roo.util.TextMetrics.measure;/*
14661  * Based on:
14662  * Ext JS Library 1.1.1
14663  * Copyright(c) 2006-2007, Ext JS, LLC.
14664  *
14665  * Originally Released Under LGPL - original licence link has changed is not relivant.
14666  *
14667  * Fork - LGPL
14668  * <script type="text/javascript">
14669  */
14670
14671 /**
14672  * @class Roo.state.Provider
14673  * Abstract base class for state provider implementations. This class provides methods
14674  * for encoding and decoding <b>typed</b> variables including dates and defines the 
14675  * Provider interface.
14676  */
14677 Roo.state.Provider = function(){
14678     /**
14679      * @event statechange
14680      * Fires when a state change occurs.
14681      * @param {Provider} this This state provider
14682      * @param {String} key The state key which was changed
14683      * @param {String} value The encoded value for the state
14684      */
14685     this.addEvents({
14686         "statechange": true
14687     });
14688     this.state = {};
14689     Roo.state.Provider.superclass.constructor.call(this);
14690 };
14691 Roo.extend(Roo.state.Provider, Roo.util.Observable, {
14692     /**
14693      * Returns the current value for a key
14694      * @param {String} name The key name
14695      * @param {Mixed} defaultValue A default value to return if the key's value is not found
14696      * @return {Mixed} The state data
14697      */
14698     get : function(name, defaultValue){
14699         return typeof this.state[name] == "undefined" ?
14700             defaultValue : this.state[name];
14701     },
14702     
14703     /**
14704      * Clears a value from the state
14705      * @param {String} name The key name
14706      */
14707     clear : function(name){
14708         delete this.state[name];
14709         this.fireEvent("statechange", this, name, null);
14710     },
14711     
14712     /**
14713      * Sets the value for a key
14714      * @param {String} name The key name
14715      * @param {Mixed} value The value to set
14716      */
14717     set : function(name, value){
14718         this.state[name] = value;
14719         this.fireEvent("statechange", this, name, value);
14720     },
14721     
14722     /**
14723      * Decodes a string previously encoded with {@link #encodeValue}.
14724      * @param {String} value The value to decode
14725      * @return {Mixed} The decoded value
14726      */
14727     decodeValue : function(cookie){
14728         var re = /^(a|n|d|b|s|o)\:(.*)$/;
14729         var matches = re.exec(unescape(cookie));
14730         if(!matches || !matches[1]) return; // non state cookie
14731         var type = matches[1];
14732         var v = matches[2];
14733         switch(type){
14734             case "n":
14735                 return parseFloat(v);
14736             case "d":
14737                 return new Date(Date.parse(v));
14738             case "b":
14739                 return (v == "1");
14740             case "a":
14741                 var all = [];
14742                 var values = v.split("^");
14743                 for(var i = 0, len = values.length; i < len; i++){
14744                     all.push(this.decodeValue(values[i]));
14745                 }
14746                 return all;
14747            case "o":
14748                 var all = {};
14749                 var values = v.split("^");
14750                 for(var i = 0, len = values.length; i < len; i++){
14751                     var kv = values[i].split("=");
14752                     all[kv[0]] = this.decodeValue(kv[1]);
14753                 }
14754                 return all;
14755            default:
14756                 return v;
14757         }
14758     },
14759     
14760     /**
14761      * Encodes a value including type information.  Decode with {@link #decodeValue}.
14762      * @param {Mixed} value The value to encode
14763      * @return {String} The encoded value
14764      */
14765     encodeValue : function(v){
14766         var enc;
14767         if(typeof v == "number"){
14768             enc = "n:" + v;
14769         }else if(typeof v == "boolean"){
14770             enc = "b:" + (v ? "1" : "0");
14771         }else if(v instanceof Date){
14772             enc = "d:" + v.toGMTString();
14773         }else if(v instanceof Array){
14774             var flat = "";
14775             for(var i = 0, len = v.length; i < len; i++){
14776                 flat += this.encodeValue(v[i]);
14777                 if(i != len-1) flat += "^";
14778             }
14779             enc = "a:" + flat;
14780         }else if(typeof v == "object"){
14781             var flat = "";
14782             for(var key in v){
14783                 if(typeof v[key] != "function"){
14784                     flat += key + "=" + this.encodeValue(v[key]) + "^";
14785                 }
14786             }
14787             enc = "o:" + flat.substring(0, flat.length-1);
14788         }else{
14789             enc = "s:" + v;
14790         }
14791         return escape(enc);        
14792     }
14793 });
14794
14795 /*
14796  * Based on:
14797  * Ext JS Library 1.1.1
14798  * Copyright(c) 2006-2007, Ext JS, LLC.
14799  *
14800  * Originally Released Under LGPL - original licence link has changed is not relivant.
14801  *
14802  * Fork - LGPL
14803  * <script type="text/javascript">
14804  */
14805 /**
14806  * @class Roo.state.Manager
14807  * This is the global state manager. By default all components that are "state aware" check this class
14808  * for state information if you don't pass them a custom state provider. In order for this class
14809  * to be useful, it must be initialized with a provider when your application initializes.
14810  <pre><code>
14811 // in your initialization function
14812 init : function(){
14813    Roo.state.Manager.setProvider(new Roo.state.CookieProvider());
14814    ...
14815    // supposed you have a {@link Roo.BorderLayout}
14816    var layout = new Roo.BorderLayout(...);
14817    layout.restoreState();
14818    // or a {Roo.BasicDialog}
14819    var dialog = new Roo.BasicDialog(...);
14820    dialog.restoreState();
14821  </code></pre>
14822  * @singleton
14823  */
14824 Roo.state.Manager = function(){
14825     var provider = new Roo.state.Provider();
14826     
14827     return {
14828         /**
14829          * Configures the default state provider for your application
14830          * @param {Provider} stateProvider The state provider to set
14831          */
14832         setProvider : function(stateProvider){
14833             provider = stateProvider;
14834         },
14835         
14836         /**
14837          * Returns the current value for a key
14838          * @param {String} name The key name
14839          * @param {Mixed} defaultValue The default value to return if the key lookup does not match
14840          * @return {Mixed} The state data
14841          */
14842         get : function(key, defaultValue){
14843             return provider.get(key, defaultValue);
14844         },
14845         
14846         /**
14847          * Sets the value for a key
14848          * @param {String} name The key name
14849          * @param {Mixed} value The state data
14850          */
14851          set : function(key, value){
14852             provider.set(key, value);
14853         },
14854         
14855         /**
14856          * Clears a value from the state
14857          * @param {String} name The key name
14858          */
14859         clear : function(key){
14860             provider.clear(key);
14861         },
14862         
14863         /**
14864          * Gets the currently configured state provider
14865          * @return {Provider} The state provider
14866          */
14867         getProvider : function(){
14868             return provider;
14869         }
14870     };
14871 }();
14872 /*
14873  * Based on:
14874  * Ext JS Library 1.1.1
14875  * Copyright(c) 2006-2007, Ext JS, LLC.
14876  *
14877  * Originally Released Under LGPL - original licence link has changed is not relivant.
14878  *
14879  * Fork - LGPL
14880  * <script type="text/javascript">
14881  */
14882 /**
14883  * @class Roo.state.CookieProvider
14884  * @extends Roo.state.Provider
14885  * The default Provider implementation which saves state via cookies.
14886  * <br />Usage:
14887  <pre><code>
14888    var cp = new Roo.state.CookieProvider({
14889        path: "/cgi-bin/",
14890        expires: new Date(new Date().getTime()+(1000*60*60*24*30)); //30 days
14891        domain: "roojs.com"
14892    })
14893    Roo.state.Manager.setProvider(cp);
14894  </code></pre>
14895  * @cfg {String} path The path for which the cookie is active (defaults to root '/' which makes it active for all pages in the site)
14896  * @cfg {Date} expires The cookie expiration date (defaults to 7 days from now)
14897  * @cfg {String} domain The domain to save the cookie for.  Note that you cannot specify a different domain than
14898  * your page is on, but you can specify a sub-domain, or simply the domain itself like 'roojs.com' to include
14899  * all sub-domains if you need to access cookies across different sub-domains (defaults to null which uses the same
14900  * domain the page is running on including the 'www' like 'www.roojs.com')
14901  * @cfg {Boolean} secure True if the site is using SSL (defaults to false)
14902  * @constructor
14903  * Create a new CookieProvider
14904  * @param {Object} config The configuration object
14905  */
14906 Roo.state.CookieProvider = function(config){
14907     Roo.state.CookieProvider.superclass.constructor.call(this);
14908     this.path = "/";
14909     this.expires = new Date(new Date().getTime()+(1000*60*60*24*7)); //7 days
14910     this.domain = null;
14911     this.secure = false;
14912     Roo.apply(this, config);
14913     this.state = this.readCookies();
14914 };
14915
14916 Roo.extend(Roo.state.CookieProvider, Roo.state.Provider, {
14917     // private
14918     set : function(name, value){
14919         if(typeof value == "undefined" || value === null){
14920             this.clear(name);
14921             return;
14922         }
14923         this.setCookie(name, value);
14924         Roo.state.CookieProvider.superclass.set.call(this, name, value);
14925     },
14926
14927     // private
14928     clear : function(name){
14929         this.clearCookie(name);
14930         Roo.state.CookieProvider.superclass.clear.call(this, name);
14931     },
14932
14933     // private
14934     readCookies : function(){
14935         var cookies = {};
14936         var c = document.cookie + ";";
14937         var re = /\s?(.*?)=(.*?);/g;
14938         var matches;
14939         while((matches = re.exec(c)) != null){
14940             var name = matches[1];
14941             var value = matches[2];
14942             if(name && name.substring(0,3) == "ys-"){
14943                 cookies[name.substr(3)] = this.decodeValue(value);
14944             }
14945         }
14946         return cookies;
14947     },
14948
14949     // private
14950     setCookie : function(name, value){
14951         document.cookie = "ys-"+ name + "=" + this.encodeValue(value) +
14952            ((this.expires == null) ? "" : ("; expires=" + this.expires.toGMTString())) +
14953            ((this.path == null) ? "" : ("; path=" + this.path)) +
14954            ((this.domain == null) ? "" : ("; domain=" + this.domain)) +
14955            ((this.secure == true) ? "; secure" : "");
14956     },
14957
14958     // private
14959     clearCookie : function(name){
14960         document.cookie = "ys-" + name + "=null; expires=Thu, 01-Jan-70 00:00:01 GMT" +
14961            ((this.path == null) ? "" : ("; path=" + this.path)) +
14962            ((this.domain == null) ? "" : ("; domain=" + this.domain)) +
14963            ((this.secure == true) ? "; secure" : "");
14964     }
14965 });/*
14966  * Based on:
14967  * Ext JS Library 1.1.1
14968  * Copyright(c) 2006-2007, Ext JS, LLC.
14969  *
14970  * Originally Released Under LGPL - original licence link has changed is not relivant.
14971  *
14972  * Fork - LGPL
14973  * <script type="text/javascript">
14974  */
14975  
14976
14977 /**
14978  * @class Roo.ComponentMgr
14979  * Provides a common registry of all components on a page so that they can be easily accessed by component id (see {@link Roo.getCmp}).
14980  * @singleton
14981  */
14982 Roo.ComponentMgr = function(){
14983     var all = new Roo.util.MixedCollection();
14984
14985     return {
14986         /**
14987          * Registers a component.
14988          * @param {Roo.Component} c The component
14989          */
14990         register : function(c){
14991             all.add(c);
14992         },
14993
14994         /**
14995          * Unregisters a component.
14996          * @param {Roo.Component} c The component
14997          */
14998         unregister : function(c){
14999             all.remove(c);
15000         },
15001
15002         /**
15003          * Returns a component by id
15004          * @param {String} id The component id
15005          */
15006         get : function(id){
15007             return all.get(id);
15008         },
15009
15010         /**
15011          * Registers a function that will be called when a specified component is added to ComponentMgr
15012          * @param {String} id The component id
15013          * @param {Funtction} fn The callback function
15014          * @param {Object} scope The scope of the callback
15015          */
15016         onAvailable : function(id, fn, scope){
15017             all.on("add", function(index, o){
15018                 if(o.id == id){
15019                     fn.call(scope || o, o);
15020                     all.un("add", fn, scope);
15021                 }
15022             });
15023         }
15024     };
15025 }();/*
15026  * Based on:
15027  * Ext JS Library 1.1.1
15028  * Copyright(c) 2006-2007, Ext JS, LLC.
15029  *
15030  * Originally Released Under LGPL - original licence link has changed is not relivant.
15031  *
15032  * Fork - LGPL
15033  * <script type="text/javascript">
15034  */
15035  
15036 /**
15037  * @class Roo.Component
15038  * @extends Roo.util.Observable
15039  * Base class for all major Roo components.  All subclasses of Component can automatically participate in the standard
15040  * Roo component lifecycle of creation, rendering and destruction.  They also have automatic support for basic hide/show
15041  * and enable/disable behavior.  Component allows any subclass to be lazy-rendered into any {@link Roo.Container} and
15042  * to be automatically registered with the {@link Roo.ComponentMgr} so that it can be referenced at any time via {@link Roo.getCmp}.
15043  * All visual components (widgets) that require rendering into a layout should subclass Component.
15044  * @constructor
15045  * @param {Roo.Element/String/Object} config The configuration options.  If an element is passed, it is set as the internal
15046  * 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
15047  * and is used as the component id.  Otherwise, it is assumed to be a standard config object and is applied to the component.
15048  */
15049 Roo.Component = function(config){
15050     config = config || {};
15051     if(config.tagName || config.dom || typeof config == "string"){ // element object
15052         config = {el: config, id: config.id || config};
15053     }
15054     this.initialConfig = config;
15055
15056     Roo.apply(this, config);
15057     this.addEvents({
15058         /**
15059          * @event disable
15060          * Fires after the component is disabled.
15061              * @param {Roo.Component} this
15062              */
15063         disable : true,
15064         /**
15065          * @event enable
15066          * Fires after the component is enabled.
15067              * @param {Roo.Component} this
15068              */
15069         enable : true,
15070         /**
15071          * @event beforeshow
15072          * Fires before the component is shown.  Return false to stop the show.
15073              * @param {Roo.Component} this
15074              */
15075         beforeshow : true,
15076         /**
15077          * @event show
15078          * Fires after the component is shown.
15079              * @param {Roo.Component} this
15080              */
15081         show : true,
15082         /**
15083          * @event beforehide
15084          * Fires before the component is hidden. Return false to stop the hide.
15085              * @param {Roo.Component} this
15086              */
15087         beforehide : true,
15088         /**
15089          * @event hide
15090          * Fires after the component is hidden.
15091              * @param {Roo.Component} this
15092              */
15093         hide : true,
15094         /**
15095          * @event beforerender
15096          * Fires before the component is rendered. Return false to stop the render.
15097              * @param {Roo.Component} this
15098              */
15099         beforerender : true,
15100         /**
15101          * @event render
15102          * Fires after the component is rendered.
15103              * @param {Roo.Component} this
15104              */
15105         render : true,
15106         /**
15107          * @event beforedestroy
15108          * Fires before the component is destroyed. Return false to stop the destroy.
15109              * @param {Roo.Component} this
15110              */
15111         beforedestroy : true,
15112         /**
15113          * @event destroy
15114          * Fires after the component is destroyed.
15115              * @param {Roo.Component} this
15116              */
15117         destroy : true
15118     });
15119     if(!this.id){
15120         this.id = "ext-comp-" + (++Roo.Component.AUTO_ID);
15121     }
15122     Roo.ComponentMgr.register(this);
15123     Roo.Component.superclass.constructor.call(this);
15124     this.initComponent();
15125     if(this.renderTo){ // not supported by all components yet. use at your own risk!
15126         this.render(this.renderTo);
15127         delete this.renderTo;
15128     }
15129 };
15130
15131 /** @private */
15132 Roo.Component.AUTO_ID = 1000;
15133
15134 Roo.extend(Roo.Component, Roo.util.Observable, {
15135     /**
15136      * @scope Roo.Component.prototype
15137      * @type {Boolean}
15138      * true if this component is hidden. Read-only.
15139      */
15140     hidden : false,
15141     /**
15142      * @type {Boolean}
15143      * true if this component is disabled. Read-only.
15144      */
15145     disabled : false,
15146     /**
15147      * @type {Boolean}
15148      * true if this component has been rendered. Read-only.
15149      */
15150     rendered : false,
15151     
15152     /** @cfg {String} disableClass
15153      * CSS class added to the component when it is disabled (defaults to "x-item-disabled").
15154      */
15155     disabledClass : "x-item-disabled",
15156         /** @cfg {Boolean} allowDomMove
15157          * Whether the component can move the Dom node when rendering (defaults to true).
15158          */
15159     allowDomMove : true,
15160     /** @cfg {String} hideMode
15161      * How this component should hidden. Supported values are
15162      * "visibility" (css visibility), "offsets" (negative offset position) and
15163      * "display" (css display) - defaults to "display".
15164      */
15165     hideMode: 'display',
15166
15167     /** @private */
15168     ctype : "Roo.Component",
15169
15170     /**
15171      * @cfg {String} actionMode 
15172      * which property holds the element that used for  hide() / show() / disable() / enable()
15173      * default is 'el' 
15174      */
15175     actionMode : "el",
15176
15177     /** @private */
15178     getActionEl : function(){
15179         return this[this.actionMode];
15180     },
15181
15182     initComponent : Roo.emptyFn,
15183     /**
15184      * If this is a lazy rendering component, render it to its container element.
15185      * @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.
15186      */
15187     render : function(container, position){
15188         if(!this.rendered && this.fireEvent("beforerender", this) !== false){
15189             if(!container && this.el){
15190                 this.el = Roo.get(this.el);
15191                 container = this.el.dom.parentNode;
15192                 this.allowDomMove = false;
15193             }
15194             this.container = Roo.get(container);
15195             this.rendered = true;
15196             if(position !== undefined){
15197                 if(typeof position == 'number'){
15198                     position = this.container.dom.childNodes[position];
15199                 }else{
15200                     position = Roo.getDom(position);
15201                 }
15202             }
15203             this.onRender(this.container, position || null);
15204             if(this.cls){
15205                 this.el.addClass(this.cls);
15206                 delete this.cls;
15207             }
15208             if(this.style){
15209                 this.el.applyStyles(this.style);
15210                 delete this.style;
15211             }
15212             this.fireEvent("render", this);
15213             this.afterRender(this.container);
15214             if(this.hidden){
15215                 this.hide();
15216             }
15217             if(this.disabled){
15218                 this.disable();
15219             }
15220         }
15221         return this;
15222     },
15223
15224     /** @private */
15225     // default function is not really useful
15226     onRender : function(ct, position){
15227         if(this.el){
15228             this.el = Roo.get(this.el);
15229             if(this.allowDomMove !== false){
15230                 ct.dom.insertBefore(this.el.dom, position);
15231             }
15232         }
15233     },
15234
15235     /** @private */
15236     getAutoCreate : function(){
15237         var cfg = typeof this.autoCreate == "object" ?
15238                       this.autoCreate : Roo.apply({}, this.defaultAutoCreate);
15239         if(this.id && !cfg.id){
15240             cfg.id = this.id;
15241         }
15242         return cfg;
15243     },
15244
15245     /** @private */
15246     afterRender : Roo.emptyFn,
15247
15248     /**
15249      * Destroys this component by purging any event listeners, removing the component's element from the DOM,
15250      * removing the component from its {@link Roo.Container} (if applicable) and unregistering it from {@link Roo.ComponentMgr}.
15251      */
15252     destroy : function(){
15253         if(this.fireEvent("beforedestroy", this) !== false){
15254             this.purgeListeners();
15255             this.beforeDestroy();
15256             if(this.rendered){
15257                 this.el.removeAllListeners();
15258                 this.el.remove();
15259                 if(this.actionMode == "container"){
15260                     this.container.remove();
15261                 }
15262             }
15263             this.onDestroy();
15264             Roo.ComponentMgr.unregister(this);
15265             this.fireEvent("destroy", this);
15266         }
15267     },
15268
15269         /** @private */
15270     beforeDestroy : function(){
15271
15272     },
15273
15274         /** @private */
15275         onDestroy : function(){
15276
15277     },
15278
15279     /**
15280      * Returns the underlying {@link Roo.Element}.
15281      * @return {Roo.Element} The element
15282      */
15283     getEl : function(){
15284         return this.el;
15285     },
15286
15287     /**
15288      * Returns the id of this component.
15289      * @return {String}
15290      */
15291     getId : function(){
15292         return this.id;
15293     },
15294
15295     /**
15296      * Try to focus this component.
15297      * @param {Boolean} selectText True to also select the text in this component (if applicable)
15298      * @return {Roo.Component} this
15299      */
15300     focus : function(selectText){
15301         if(this.rendered){
15302             this.el.focus();
15303             if(selectText === true){
15304                 this.el.dom.select();
15305             }
15306         }
15307         return this;
15308     },
15309
15310     /** @private */
15311     blur : function(){
15312         if(this.rendered){
15313             this.el.blur();
15314         }
15315         return this;
15316     },
15317
15318     /**
15319      * Disable this component.
15320      * @return {Roo.Component} this
15321      */
15322     disable : function(){
15323         if(this.rendered){
15324             this.onDisable();
15325         }
15326         this.disabled = true;
15327         this.fireEvent("disable", this);
15328         return this;
15329     },
15330
15331         // private
15332     onDisable : function(){
15333         this.getActionEl().addClass(this.disabledClass);
15334         this.el.dom.disabled = true;
15335     },
15336
15337     /**
15338      * Enable this component.
15339      * @return {Roo.Component} this
15340      */
15341     enable : function(){
15342         if(this.rendered){
15343             this.onEnable();
15344         }
15345         this.disabled = false;
15346         this.fireEvent("enable", this);
15347         return this;
15348     },
15349
15350         // private
15351     onEnable : function(){
15352         this.getActionEl().removeClass(this.disabledClass);
15353         this.el.dom.disabled = false;
15354     },
15355
15356     /**
15357      * Convenience function for setting disabled/enabled by boolean.
15358      * @param {Boolean} disabled
15359      */
15360     setDisabled : function(disabled){
15361         this[disabled ? "disable" : "enable"]();
15362     },
15363
15364     /**
15365      * Show this component.
15366      * @return {Roo.Component} this
15367      */
15368     show: function(){
15369         if(this.fireEvent("beforeshow", this) !== false){
15370             this.hidden = false;
15371             if(this.rendered){
15372                 this.onShow();
15373             }
15374             this.fireEvent("show", this);
15375         }
15376         return this;
15377     },
15378
15379     // private
15380     onShow : function(){
15381         var ae = this.getActionEl();
15382         if(this.hideMode == 'visibility'){
15383             ae.dom.style.visibility = "visible";
15384         }else if(this.hideMode == 'offsets'){
15385             ae.removeClass('x-hidden');
15386         }else{
15387             ae.dom.style.display = "";
15388         }
15389     },
15390
15391     /**
15392      * Hide this component.
15393      * @return {Roo.Component} this
15394      */
15395     hide: function(){
15396         if(this.fireEvent("beforehide", this) !== false){
15397             this.hidden = true;
15398             if(this.rendered){
15399                 this.onHide();
15400             }
15401             this.fireEvent("hide", this);
15402         }
15403         return this;
15404     },
15405
15406     // private
15407     onHide : function(){
15408         var ae = this.getActionEl();
15409         if(this.hideMode == 'visibility'){
15410             ae.dom.style.visibility = "hidden";
15411         }else if(this.hideMode == 'offsets'){
15412             ae.addClass('x-hidden');
15413         }else{
15414             ae.dom.style.display = "none";
15415         }
15416     },
15417
15418     /**
15419      * Convenience function to hide or show this component by boolean.
15420      * @param {Boolean} visible True to show, false to hide
15421      * @return {Roo.Component} this
15422      */
15423     setVisible: function(visible){
15424         if(visible) {
15425             this.show();
15426         }else{
15427             this.hide();
15428         }
15429         return this;
15430     },
15431
15432     /**
15433      * Returns true if this component is visible.
15434      */
15435     isVisible : function(){
15436         return this.getActionEl().isVisible();
15437     },
15438
15439     cloneConfig : function(overrides){
15440         overrides = overrides || {};
15441         var id = overrides.id || Roo.id();
15442         var cfg = Roo.applyIf(overrides, this.initialConfig);
15443         cfg.id = id; // prevent dup id
15444         return new this.constructor(cfg);
15445     }
15446 });/*
15447  * Based on:
15448  * Ext JS Library 1.1.1
15449  * Copyright(c) 2006-2007, Ext JS, LLC.
15450  *
15451  * Originally Released Under LGPL - original licence link has changed is not relivant.
15452  *
15453  * Fork - LGPL
15454  * <script type="text/javascript">
15455  */
15456
15457 /**
15458  * @class Roo.BoxComponent
15459  * @extends Roo.Component
15460  * Base class for any visual {@link Roo.Component} that uses a box container.  BoxComponent provides automatic box
15461  * model adjustments for sizing and positioning and will work correctly withnin the Component rendering model.  All
15462  * container classes should subclass BoxComponent so that they will work consistently when nested within other Ext
15463  * layout containers.
15464  * @constructor
15465  * @param {Roo.Element/String/Object} config The configuration options.
15466  */
15467 Roo.BoxComponent = function(config){
15468     Roo.Component.call(this, config);
15469     this.addEvents({
15470         /**
15471          * @event resize
15472          * Fires after the component is resized.
15473              * @param {Roo.Component} this
15474              * @param {Number} adjWidth The box-adjusted width that was set
15475              * @param {Number} adjHeight The box-adjusted height that was set
15476              * @param {Number} rawWidth The width that was originally specified
15477              * @param {Number} rawHeight The height that was originally specified
15478              */
15479         resize : true,
15480         /**
15481          * @event move
15482          * Fires after the component is moved.
15483              * @param {Roo.Component} this
15484              * @param {Number} x The new x position
15485              * @param {Number} y The new y position
15486              */
15487         move : true
15488     });
15489 };
15490
15491 Roo.extend(Roo.BoxComponent, Roo.Component, {
15492     // private, set in afterRender to signify that the component has been rendered
15493     boxReady : false,
15494     // private, used to defer height settings to subclasses
15495     deferHeight: false,
15496     /** @cfg {Number} width
15497      * width (optional) size of component
15498      */
15499      /** @cfg {Number} height
15500      * height (optional) size of component
15501      */
15502      
15503     /**
15504      * Sets the width and height of the component.  This method fires the resize event.  This method can accept
15505      * either width and height as separate numeric arguments, or you can pass a size object like {width:10, height:20}.
15506      * @param {Number/Object} width The new width to set, or a size object in the format {width, height}
15507      * @param {Number} height The new height to set (not required if a size object is passed as the first arg)
15508      * @return {Roo.BoxComponent} this
15509      */
15510     setSize : function(w, h){
15511         // support for standard size objects
15512         if(typeof w == 'object'){
15513             h = w.height;
15514             w = w.width;
15515         }
15516         // not rendered
15517         if(!this.boxReady){
15518             this.width = w;
15519             this.height = h;
15520             return this;
15521         }
15522
15523         // prevent recalcs when not needed
15524         if(this.lastSize && this.lastSize.width == w && this.lastSize.height == h){
15525             return this;
15526         }
15527         this.lastSize = {width: w, height: h};
15528
15529         var adj = this.adjustSize(w, h);
15530         var aw = adj.width, ah = adj.height;
15531         if(aw !== undefined || ah !== undefined){ // this code is nasty but performs better with floaters
15532             var rz = this.getResizeEl();
15533             if(!this.deferHeight && aw !== undefined && ah !== undefined){
15534                 rz.setSize(aw, ah);
15535             }else if(!this.deferHeight && ah !== undefined){
15536                 rz.setHeight(ah);
15537             }else if(aw !== undefined){
15538                 rz.setWidth(aw);
15539             }
15540             this.onResize(aw, ah, w, h);
15541             this.fireEvent('resize', this, aw, ah, w, h);
15542         }
15543         return this;
15544     },
15545
15546     /**
15547      * Gets the current size of the component's underlying element.
15548      * @return {Object} An object containing the element's size {width: (element width), height: (element height)}
15549      */
15550     getSize : function(){
15551         return this.el.getSize();
15552     },
15553
15554     /**
15555      * Gets the current XY position of the component's underlying element.
15556      * @param {Boolean} local (optional) If true the element's left and top are returned instead of page XY (defaults to false)
15557      * @return {Array} The XY position of the element (e.g., [100, 200])
15558      */
15559     getPosition : function(local){
15560         if(local === true){
15561             return [this.el.getLeft(true), this.el.getTop(true)];
15562         }
15563         return this.xy || this.el.getXY();
15564     },
15565
15566     /**
15567      * Gets the current box measurements of the component's underlying element.
15568      * @param {Boolean} local (optional) If true the element's left and top are returned instead of page XY (defaults to false)
15569      * @returns {Object} box An object in the format {x, y, width, height}
15570      */
15571     getBox : function(local){
15572         var s = this.el.getSize();
15573         if(local){
15574             s.x = this.el.getLeft(true);
15575             s.y = this.el.getTop(true);
15576         }else{
15577             var xy = this.xy || this.el.getXY();
15578             s.x = xy[0];
15579             s.y = xy[1];
15580         }
15581         return s;
15582     },
15583
15584     /**
15585      * Sets the current box measurements of the component's underlying element.
15586      * @param {Object} box An object in the format {x, y, width, height}
15587      * @returns {Roo.BoxComponent} this
15588      */
15589     updateBox : function(box){
15590         this.setSize(box.width, box.height);
15591         this.setPagePosition(box.x, box.y);
15592         return this;
15593     },
15594
15595     // protected
15596     getResizeEl : function(){
15597         return this.resizeEl || this.el;
15598     },
15599
15600     // protected
15601     getPositionEl : function(){
15602         return this.positionEl || this.el;
15603     },
15604
15605     /**
15606      * Sets the left and top of the component.  To set the page XY position instead, use {@link #setPagePosition}.
15607      * This method fires the move event.
15608      * @param {Number} left The new left
15609      * @param {Number} top The new top
15610      * @returns {Roo.BoxComponent} this
15611      */
15612     setPosition : function(x, y){
15613         this.x = x;
15614         this.y = y;
15615         if(!this.boxReady){
15616             return this;
15617         }
15618         var adj = this.adjustPosition(x, y);
15619         var ax = adj.x, ay = adj.y;
15620
15621         var el = this.getPositionEl();
15622         if(ax !== undefined || ay !== undefined){
15623             if(ax !== undefined && ay !== undefined){
15624                 el.setLeftTop(ax, ay);
15625             }else if(ax !== undefined){
15626                 el.setLeft(ax);
15627             }else if(ay !== undefined){
15628                 el.setTop(ay);
15629             }
15630             this.onPosition(ax, ay);
15631             this.fireEvent('move', this, ax, ay);
15632         }
15633         return this;
15634     },
15635
15636     /**
15637      * Sets the page XY position of the component.  To set the left and top instead, use {@link #setPosition}.
15638      * This method fires the move event.
15639      * @param {Number} x The new x position
15640      * @param {Number} y The new y position
15641      * @returns {Roo.BoxComponent} this
15642      */
15643     setPagePosition : function(x, y){
15644         this.pageX = x;
15645         this.pageY = y;
15646         if(!this.boxReady){
15647             return;
15648         }
15649         if(x === undefined || y === undefined){ // cannot translate undefined points
15650             return;
15651         }
15652         var p = this.el.translatePoints(x, y);
15653         this.setPosition(p.left, p.top);
15654         return this;
15655     },
15656
15657     // private
15658     onRender : function(ct, position){
15659         Roo.BoxComponent.superclass.onRender.call(this, ct, position);
15660         if(this.resizeEl){
15661             this.resizeEl = Roo.get(this.resizeEl);
15662         }
15663         if(this.positionEl){
15664             this.positionEl = Roo.get(this.positionEl);
15665         }
15666     },
15667
15668     // private
15669     afterRender : function(){
15670         Roo.BoxComponent.superclass.afterRender.call(this);
15671         this.boxReady = true;
15672         this.setSize(this.width, this.height);
15673         if(this.x || this.y){
15674             this.setPosition(this.x, this.y);
15675         }
15676         if(this.pageX || this.pageY){
15677             this.setPagePosition(this.pageX, this.pageY);
15678         }
15679     },
15680
15681     /**
15682      * Force the component's size to recalculate based on the underlying element's current height and width.
15683      * @returns {Roo.BoxComponent} this
15684      */
15685     syncSize : function(){
15686         delete this.lastSize;
15687         this.setSize(this.el.getWidth(), this.el.getHeight());
15688         return this;
15689     },
15690
15691     /**
15692      * Called after the component is resized, this method is empty by default but can be implemented by any
15693      * subclass that needs to perform custom logic after a resize occurs.
15694      * @param {Number} adjWidth The box-adjusted width that was set
15695      * @param {Number} adjHeight The box-adjusted height that was set
15696      * @param {Number} rawWidth The width that was originally specified
15697      * @param {Number} rawHeight The height that was originally specified
15698      */
15699     onResize : function(adjWidth, adjHeight, rawWidth, rawHeight){
15700
15701     },
15702
15703     /**
15704      * Called after the component is moved, this method is empty by default but can be implemented by any
15705      * subclass that needs to perform custom logic after a move occurs.
15706      * @param {Number} x The new x position
15707      * @param {Number} y The new y position
15708      */
15709     onPosition : function(x, y){
15710
15711     },
15712
15713     // private
15714     adjustSize : function(w, h){
15715         if(this.autoWidth){
15716             w = 'auto';
15717         }
15718         if(this.autoHeight){
15719             h = 'auto';
15720         }
15721         return {width : w, height: h};
15722     },
15723
15724     // private
15725     adjustPosition : function(x, y){
15726         return {x : x, y: y};
15727     }
15728 });/*
15729  * Original code for Roojs - LGPL
15730  * <script type="text/javascript">
15731  */
15732  
15733 /**
15734  * @class Roo.XComponent
15735  * A delayed Element creator...
15736  * Or a way to group chunks of interface together.
15737  * 
15738  * Mypart.xyx = new Roo.XComponent({
15739
15740     parent : 'Mypart.xyz', // empty == document.element.!!
15741     order : '001',
15742     name : 'xxxx'
15743     region : 'xxxx'
15744     disabled : function() {} 
15745      
15746     tree : function() { // return an tree of xtype declared components
15747         var MODULE = this;
15748         return 
15749         {
15750             xtype : 'NestedLayoutPanel',
15751             // technicall
15752         }
15753      ]
15754  *})
15755  *
15756  *
15757  * It can be used to build a big heiracy, with parent etc.
15758  * or you can just use this to render a single compoent to a dom element
15759  * MYPART.render(Roo.Element | String(id) | dom_element )
15760  * 
15761  * @extends Roo.util.Observable
15762  * @constructor
15763  * @param cfg {Object} configuration of component
15764  * 
15765  */
15766 Roo.XComponent = function(cfg) {
15767     Roo.apply(this, cfg);
15768     this.addEvents({ 
15769         /**
15770              * @event built
15771              * Fires when this the componnt is built
15772              * @param {Roo.XComponent} c the component
15773              */
15774         'built' : true
15775         
15776     });
15777     this.region = this.region || 'center'; // default..
15778     Roo.XComponent.register(this);
15779     this.modules = false;
15780     this.el = false; // where the layout goes..
15781     
15782     
15783 }
15784 Roo.extend(Roo.XComponent, Roo.util.Observable, {
15785     /**
15786      * @property el
15787      * The created element (with Roo.factory())
15788      * @type {Roo.Layout}
15789      */
15790     el  : false,
15791     
15792     /**
15793      * @property el
15794      * for BC  - use el in new code
15795      * @type {Roo.Layout}
15796      */
15797     panel : false,
15798     
15799     /**
15800      * @property layout
15801      * for BC  - use el in new code
15802      * @type {Roo.Layout}
15803      */
15804     layout : false,
15805     
15806      /**
15807      * @cfg {Function|boolean} disabled
15808      * If this module is disabled by some rule, return true from the funtion
15809      */
15810     disabled : false,
15811     
15812     /**
15813      * @cfg {String} parent 
15814      * Name of parent element which it get xtype added to..
15815      */
15816     parent: false,
15817     
15818     /**
15819      * @cfg {String} order
15820      * Used to set the order in which elements are created (usefull for multiple tabs)
15821      */
15822     
15823     order : false,
15824     /**
15825      * @cfg {String} name
15826      * String to display while loading.
15827      */
15828     name : false,
15829     /**
15830      * @cfg {String} region
15831      * Region to render component to (defaults to center)
15832      */
15833     region : 'center',
15834     
15835     /**
15836      * @cfg {Array} items
15837      * A single item array - the first element is the root of the tree..
15838      * It's done this way to stay compatible with the Xtype system...
15839      */
15840     items : false,
15841     
15842     /**
15843      * @property _tree
15844      * The method that retuns the tree of parts that make up this compoennt 
15845      * @type {function}
15846      */
15847     _tree  : false,
15848     
15849      /**
15850      * render
15851      * render element to dom or tree
15852      * @param {Roo.Element|String|DomElement} optional render to if parent is not set.
15853      */
15854     
15855     render : function(el)
15856     {
15857         
15858         el = el || false;
15859         var hp = this.parent ? 1 : 0;
15860         
15861         if (!el && typeof(this.parent) == 'string' && this.parent.substring(0,1) == '#') {
15862             // if parent is a '#.....' string, then let's use that..
15863             var ename = this.parent.substr(1)
15864             this.parent = (this.parent == '#bootstrap') ? { el : true}  : false; // flags it as a top module...
15865             el = Roo.get(ename);
15866             if (!el && !this.parent) {
15867                 Roo.log("Warning - element can not be found :#" + ename );
15868                 return;
15869             }
15870         }
15871         var tree = this._tree ? this._tree() : this.tree();
15872
15873         
15874         if (!this.parent && typeof(Roo.bootstrap) != 'undefined' && tree.xns == Roo.bootstrap) {
15875             //el = Roo.get(document.body);
15876             this.parent = { el : true };
15877         }
15878             
15879             
15880         
15881         if (!this.parent) {
15882             
15883             Roo.log("no parent - creating one");
15884             
15885             el = el ? Roo.get(el) : false;      
15886             
15887             // it's a top level one..
15888             this.parent =  {
15889                 el : new Roo.BorderLayout(el || document.body, {
15890                 
15891                      center: {
15892                          titlebar: false,
15893                          autoScroll:false,
15894                          closeOnTab: true,
15895                          tabPosition: 'top',
15896                           //resizeTabs: true,
15897                          alwaysShowTabs: el && hp? false :  true,
15898                          hideTabs: el || !hp ? true :  false,
15899                          minTabWidth: 140
15900                      }
15901                  })
15902             }
15903         }
15904         
15905                 if (!this.parent.el) {
15906                         // probably an old style ctor, which has been disabled.
15907                         return;
15908                         
15909                 }
15910                 // The 'tree' method is  '_tree now' 
15911             
15912         tree.region = tree.region || this.region;
15913         
15914         if (this.parent.el === true) {
15915             // bootstrap... - body..
15916             this.parent.el = Roo.factory(tree);
15917         }
15918         
15919         this.el = this.parent.el.addxtype(tree);
15920         this.fireEvent('built', this);
15921         
15922         this.panel = this.el;
15923         this.layout = this.panel.layout;
15924                 this.parentLayout = this.parent.layout  || false;  
15925          
15926     }
15927     
15928 });
15929
15930 Roo.apply(Roo.XComponent, {
15931     /**
15932      * @property  hideProgress
15933      * true to disable the building progress bar.. usefull on single page renders.
15934      * @type Boolean
15935      */
15936     hideProgress : false,
15937     /**
15938      * @property  buildCompleted
15939      * True when the builder has completed building the interface.
15940      * @type Boolean
15941      */
15942     buildCompleted : false,
15943      
15944     /**
15945      * @property  topModule
15946      * the upper most module - uses document.element as it's constructor.
15947      * @type Object
15948      */
15949      
15950     topModule  : false,
15951       
15952     /**
15953      * @property  modules
15954      * array of modules to be created by registration system.
15955      * @type {Array} of Roo.XComponent
15956      */
15957     
15958     modules : [],
15959     /**
15960      * @property  elmodules
15961      * array of modules to be created by which use #ID 
15962      * @type {Array} of Roo.XComponent
15963      */
15964      
15965     elmodules : [],
15966
15967      /**
15968      * @property  build_from_html
15969      * Build elements from html - used by bootstrap HTML stuff 
15970      *    - this is cleared after build is completed
15971      * @type {boolean} true  (default false)
15972      */
15973      
15974     build_from_html : false,
15975
15976     /**
15977      * Register components to be built later.
15978      *
15979      * This solves the following issues
15980      * - Building is not done on page load, but after an authentication process has occured.
15981      * - Interface elements are registered on page load
15982      * - Parent Interface elements may not be loaded before child, so this handles that..
15983      * 
15984      *
15985      * example:
15986      * 
15987      * MyApp.register({
15988           order : '000001',
15989           module : 'Pman.Tab.projectMgr',
15990           region : 'center',
15991           parent : 'Pman.layout',
15992           disabled : false,  // or use a function..
15993         })
15994      
15995      * * @param {Object} details about module
15996      */
15997     register : function(obj) {
15998                 
15999         Roo.XComponent.event.fireEvent('register', obj);
16000         switch(typeof(obj.disabled) ) {
16001                 
16002             case 'undefined':
16003                 break;
16004             
16005             case 'function':
16006                 if ( obj.disabled() ) {
16007                         return;
16008                 }
16009                 break;
16010             
16011             default:
16012                 if (obj.disabled) {
16013                         return;
16014                 }
16015                 break;
16016         }
16017                 
16018         this.modules.push(obj);
16019          
16020     },
16021     /**
16022      * convert a string to an object..
16023      * eg. 'AAA.BBB' -> finds AAA.BBB
16024
16025      */
16026     
16027     toObject : function(str)
16028     {
16029         if (!str || typeof(str) == 'object') {
16030             return str;
16031         }
16032         if (str.substring(0,1) == '#') {
16033             return str;
16034         }
16035
16036         var ar = str.split('.');
16037         var rt, o;
16038         rt = ar.shift();
16039             /** eval:var:o */
16040         try {
16041             eval('if (typeof ' + rt + ' == "undefined"){ o = false;} o = ' + rt + ';');
16042         } catch (e) {
16043             throw "Module not found : " + str;
16044         }
16045         
16046         if (o === false) {
16047             throw "Module not found : " + str;
16048         }
16049         Roo.each(ar, function(e) {
16050             if (typeof(o[e]) == 'undefined') {
16051                 throw "Module not found : " + str;
16052             }
16053             o = o[e];
16054         });
16055         
16056         return o;
16057         
16058     },
16059     
16060     
16061     /**
16062      * move modules into their correct place in the tree..
16063      * 
16064      */
16065     preBuild : function ()
16066     {
16067         var _t = this;
16068         Roo.each(this.modules , function (obj)
16069         {
16070             Roo.XComponent.event.fireEvent('beforebuild', obj);
16071             
16072             var opar = obj.parent;
16073             try { 
16074                 obj.parent = this.toObject(opar);
16075             } catch(e) {
16076                 Roo.log("parent:toObject failed: " + e.toString());
16077                 return;
16078             }
16079             
16080             if (!obj.parent) {
16081                 Roo.debug && Roo.log("GOT top level module");
16082                 Roo.debug && Roo.log(obj);
16083                 obj.modules = new Roo.util.MixedCollection(false, 
16084                     function(o) { return o.order + '' }
16085                 );
16086                 this.topModule = obj;
16087                 return;
16088             }
16089                         // parent is a string (usually a dom element name..)
16090             if (typeof(obj.parent) == 'string') {
16091                 this.elmodules.push(obj);
16092                 return;
16093             }
16094             if (obj.parent.constructor != Roo.XComponent) {
16095                 Roo.log("Warning : Object Parent is not instance of XComponent:" + obj.name)
16096             }
16097             if (!obj.parent.modules) {
16098                 obj.parent.modules = new Roo.util.MixedCollection(false, 
16099                     function(o) { return o.order + '' }
16100                 );
16101             }
16102             if (obj.parent.disabled) {
16103                 obj.disabled = true;
16104             }
16105             obj.parent.modules.add(obj);
16106         }, this);
16107     },
16108     
16109      /**
16110      * make a list of modules to build.
16111      * @return {Array} list of modules. 
16112      */ 
16113     
16114     buildOrder : function()
16115     {
16116         var _this = this;
16117         var cmp = function(a,b) {   
16118             return String(a).toUpperCase() > String(b).toUpperCase() ? 1 : -1;
16119         };
16120         if ((!this.topModule || !this.topModule.modules) && !this.elmodules.length) {
16121             throw "No top level modules to build";
16122         }
16123         
16124         // make a flat list in order of modules to build.
16125         var mods = this.topModule ? [ this.topModule ] : [];
16126                 
16127         
16128         // elmodules (is a list of DOM based modules )
16129         Roo.each(this.elmodules, function(e) {
16130             mods.push(e);
16131             if (!this.topModule &&
16132                 typeof(e.parent) == 'string' &&
16133                 e.parent.substring(0,1) == '#' &&
16134                 Roo.get(e.parent.substr(1))
16135                ) {
16136                 
16137                 _this.topModule = e;
16138             }
16139             
16140         });
16141
16142         
16143         // add modules to their parents..
16144         var addMod = function(m) {
16145             Roo.debug && Roo.log("build Order: add: " + m.name);
16146                 
16147             mods.push(m);
16148             if (m.modules && !m.disabled) {
16149                 Roo.debug && Roo.log("build Order: " + m.modules.length + " child modules");
16150                 m.modules.keySort('ASC',  cmp );
16151                 Roo.debug && Roo.log("build Order: " + m.modules.length + " child modules (after sort)");
16152     
16153                 m.modules.each(addMod);
16154             } else {
16155                 Roo.debug && Roo.log("build Order: no child modules");
16156             }
16157             // not sure if this is used any more..
16158             if (m.finalize) {
16159                 m.finalize.name = m.name + " (clean up) ";
16160                 mods.push(m.finalize);
16161             }
16162             
16163         }
16164         if (this.topModule && this.topModule.modules) { 
16165             this.topModule.modules.keySort('ASC',  cmp );
16166             this.topModule.modules.each(addMod);
16167         } 
16168         return mods;
16169     },
16170     
16171      /**
16172      * Build the registered modules.
16173      * @param {Object} parent element.
16174      * @param {Function} optional method to call after module has been added.
16175      * 
16176      */ 
16177    
16178     build : function(opts) 
16179     {
16180         
16181         if (typeof(opts) != 'undefined') {
16182             Roo.apply(this,opts);
16183         }
16184         
16185         this.preBuild();
16186         var mods = this.buildOrder();
16187       
16188         //this.allmods = mods;
16189         //Roo.debug && Roo.log(mods);
16190         //return;
16191         if (!mods.length) { // should not happen
16192             throw "NO modules!!!";
16193         }
16194         
16195         
16196         var msg = "Building Interface...";
16197         // flash it up as modal - so we store the mask!?
16198         if (!this.hideProgress && Roo.MessageBox) {
16199             Roo.MessageBox.show({ title: 'loading' });
16200             Roo.MessageBox.show({
16201                title: "Please wait...",
16202                msg: msg,
16203                width:450,
16204                progress:true,
16205                closable:false,
16206                modal: false
16207               
16208             });
16209         }
16210         var total = mods.length;
16211         
16212         var _this = this;
16213         var progressRun = function() {
16214             if (!mods.length) {
16215                 Roo.debug && Roo.log('hide?');
16216                 if (!this.hideProgress && Roo.MessageBox) {
16217                     Roo.MessageBox.hide();
16218                 }
16219                 Roo.XComponent.build_from_html = false; // reset, so dialogs will be build from javascript
16220                 
16221                 Roo.XComponent.event.fireEvent('buildcomplete', _this.topModule);
16222                 
16223                 // THE END...
16224                 return false;   
16225             }
16226             
16227             var m = mods.shift();
16228             
16229             
16230             Roo.debug && Roo.log(m);
16231             // not sure if this is supported any more.. - modules that are are just function
16232             if (typeof(m) == 'function') { 
16233                 m.call(this);
16234                 return progressRun.defer(10, _this);
16235             } 
16236             
16237             
16238             msg = "Building Interface " + (total  - mods.length) + 
16239                     " of " + total + 
16240                     (m.name ? (' - ' + m.name) : '');
16241                         Roo.debug && Roo.log(msg);
16242             if (!this.hideProgress &&  Roo.MessageBox) { 
16243                 Roo.MessageBox.updateProgress(  (total  - mods.length)/total, msg  );
16244             }
16245             
16246          
16247             // is the module disabled?
16248             var disabled = (typeof(m.disabled) == 'function') ?
16249                 m.disabled.call(m.module.disabled) : m.disabled;    
16250             
16251             
16252             if (disabled) {
16253                 return progressRun(); // we do not update the display!
16254             }
16255             
16256             // now build 
16257             
16258                         
16259                         
16260             m.render();
16261             // it's 10 on top level, and 1 on others??? why...
16262             return progressRun.defer(10, _this);
16263              
16264         }
16265         progressRun.defer(1, _this);
16266      
16267         
16268         
16269     },
16270         
16271         
16272         /**
16273          * Event Object.
16274          *
16275          *
16276          */
16277         event: false, 
16278     /**
16279          * wrapper for event.on - aliased later..  
16280          * Typically use to register a event handler for register:
16281          *
16282          * eg. Roo.XComponent.on('register', function(comp) { comp.disable = true } );
16283          *
16284          */
16285     on : false
16286    
16287     
16288     
16289 });
16290
16291 Roo.XComponent.event = new Roo.util.Observable({
16292                 events : { 
16293                         /**
16294                          * @event register
16295                          * Fires when an Component is registered,
16296                          * set the disable property on the Component to stop registration.
16297                          * @param {Roo.XComponent} c the component being registerd.
16298                          * 
16299                          */
16300                         'register' : true,
16301             /**
16302                          * @event beforebuild
16303                          * Fires before each Component is built
16304                          * can be used to apply permissions.
16305                          * @param {Roo.XComponent} c the component being registerd.
16306                          * 
16307                          */
16308                         'beforebuild' : true,
16309                         /**
16310                          * @event buildcomplete
16311                          * Fires on the top level element when all elements have been built
16312                          * @param {Roo.XComponent} the top level component.
16313                          */
16314                         'buildcomplete' : true
16315                         
16316                 }
16317 });
16318
16319 Roo.XComponent.on = Roo.XComponent.event.on.createDelegate(Roo.XComponent.event); 
16320  /*
16321  * Based on:
16322  * Ext JS Library 1.1.1
16323  * Copyright(c) 2006-2007, Ext JS, LLC.
16324  *
16325  * Originally Released Under LGPL - original licence link has changed is not relivant.
16326  *
16327  * Fork - LGPL
16328  * <script type="text/javascript">
16329  */
16330
16331
16332
16333 /*
16334  * These classes are derivatives of the similarly named classes in the YUI Library.
16335  * The original license:
16336  * Copyright (c) 2006, Yahoo! Inc. All rights reserved.
16337  * Code licensed under the BSD License:
16338  * http://developer.yahoo.net/yui/license.txt
16339  */
16340
16341 (function() {
16342
16343 var Event=Roo.EventManager;
16344 var Dom=Roo.lib.Dom;
16345
16346 /**
16347  * @class Roo.dd.DragDrop
16348  * @extends Roo.util.Observable
16349  * Defines the interface and base operation of items that that can be
16350  * dragged or can be drop targets.  It was designed to be extended, overriding
16351  * the event handlers for startDrag, onDrag, onDragOver and onDragOut.
16352  * Up to three html elements can be associated with a DragDrop instance:
16353  * <ul>
16354  * <li>linked element: the element that is passed into the constructor.
16355  * This is the element which defines the boundaries for interaction with
16356  * other DragDrop objects.</li>
16357  * <li>handle element(s): The drag operation only occurs if the element that
16358  * was clicked matches a handle element.  By default this is the linked
16359  * element, but there are times that you will want only a portion of the
16360  * linked element to initiate the drag operation, and the setHandleElId()
16361  * method provides a way to define this.</li>
16362  * <li>drag element: this represents the element that would be moved along
16363  * with the cursor during a drag operation.  By default, this is the linked
16364  * element itself as in {@link Roo.dd.DD}.  setDragElId() lets you define
16365  * a separate element that would be moved, as in {@link Roo.dd.DDProxy}.
16366  * </li>
16367  * </ul>
16368  * This class should not be instantiated until the onload event to ensure that
16369  * the associated elements are available.
16370  * The following would define a DragDrop obj that would interact with any
16371  * other DragDrop obj in the "group1" group:
16372  * <pre>
16373  *  dd = new Roo.dd.DragDrop("div1", "group1");
16374  * </pre>
16375  * Since none of the event handlers have been implemented, nothing would
16376  * actually happen if you were to run the code above.  Normally you would
16377  * override this class or one of the default implementations, but you can
16378  * also override the methods you want on an instance of the class...
16379  * <pre>
16380  *  dd.onDragDrop = function(e, id) {
16381  *  &nbsp;&nbsp;alert("dd was dropped on " + id);
16382  *  }
16383  * </pre>
16384  * @constructor
16385  * @param {String} id of the element that is linked to this instance
16386  * @param {String} sGroup the group of related DragDrop objects
16387  * @param {object} config an object containing configurable attributes
16388  *                Valid properties for DragDrop:
16389  *                    padding, isTarget, maintainOffset, primaryButtonOnly
16390  */
16391 Roo.dd.DragDrop = function(id, sGroup, config) {
16392     if (id) {
16393         this.init(id, sGroup, config);
16394     }
16395     
16396 };
16397
16398 Roo.extend(Roo.dd.DragDrop, Roo.util.Observable , {
16399
16400     /**
16401      * The id of the element associated with this object.  This is what we
16402      * refer to as the "linked element" because the size and position of
16403      * this element is used to determine when the drag and drop objects have
16404      * interacted.
16405      * @property id
16406      * @type String
16407      */
16408     id: null,
16409
16410     /**
16411      * Configuration attributes passed into the constructor
16412      * @property config
16413      * @type object
16414      */
16415     config: null,
16416
16417     /**
16418      * The id of the element that will be dragged.  By default this is same
16419      * as the linked element , but could be changed to another element. Ex:
16420      * Roo.dd.DDProxy
16421      * @property dragElId
16422      * @type String
16423      * @private
16424      */
16425     dragElId: null,
16426
16427     /**
16428      * the id of the element that initiates the drag operation.  By default
16429      * this is the linked element, but could be changed to be a child of this
16430      * element.  This lets us do things like only starting the drag when the
16431      * header element within the linked html element is clicked.
16432      * @property handleElId
16433      * @type String
16434      * @private
16435      */
16436     handleElId: null,
16437
16438     /**
16439      * An associative array of HTML tags that will be ignored if clicked.
16440      * @property invalidHandleTypes
16441      * @type {string: string}
16442      */
16443     invalidHandleTypes: null,
16444
16445     /**
16446      * An associative array of ids for elements that will be ignored if clicked
16447      * @property invalidHandleIds
16448      * @type {string: string}
16449      */
16450     invalidHandleIds: null,
16451
16452     /**
16453      * An indexted array of css class names for elements that will be ignored
16454      * if clicked.
16455      * @property invalidHandleClasses
16456      * @type string[]
16457      */
16458     invalidHandleClasses: null,
16459
16460     /**
16461      * The linked element's absolute X position at the time the drag was
16462      * started
16463      * @property startPageX
16464      * @type int
16465      * @private
16466      */
16467     startPageX: 0,
16468
16469     /**
16470      * The linked element's absolute X position at the time the drag was
16471      * started
16472      * @property startPageY
16473      * @type int
16474      * @private
16475      */
16476     startPageY: 0,
16477
16478     /**
16479      * The group defines a logical collection of DragDrop objects that are
16480      * related.  Instances only get events when interacting with other
16481      * DragDrop object in the same group.  This lets us define multiple
16482      * groups using a single DragDrop subclass if we want.
16483      * @property groups
16484      * @type {string: string}
16485      */
16486     groups: null,
16487
16488     /**
16489      * Individual drag/drop instances can be locked.  This will prevent
16490      * onmousedown start drag.
16491      * @property locked
16492      * @type boolean
16493      * @private
16494      */
16495     locked: false,
16496
16497     /**
16498      * Lock this instance
16499      * @method lock
16500      */
16501     lock: function() { this.locked = true; },
16502
16503     /**
16504      * Unlock this instace
16505      * @method unlock
16506      */
16507     unlock: function() { this.locked = false; },
16508
16509     /**
16510      * By default, all insances can be a drop target.  This can be disabled by
16511      * setting isTarget to false.
16512      * @method isTarget
16513      * @type boolean
16514      */
16515     isTarget: true,
16516
16517     /**
16518      * The padding configured for this drag and drop object for calculating
16519      * the drop zone intersection with this object.
16520      * @method padding
16521      * @type int[]
16522      */
16523     padding: null,
16524
16525     /**
16526      * Cached reference to the linked element
16527      * @property _domRef
16528      * @private
16529      */
16530     _domRef: null,
16531
16532     /**
16533      * Internal typeof flag
16534      * @property __ygDragDrop
16535      * @private
16536      */
16537     __ygDragDrop: true,
16538
16539     /**
16540      * Set to true when horizontal contraints are applied
16541      * @property constrainX
16542      * @type boolean
16543      * @private
16544      */
16545     constrainX: false,
16546
16547     /**
16548      * Set to true when vertical contraints are applied
16549      * @property constrainY
16550      * @type boolean
16551      * @private
16552      */
16553     constrainY: false,
16554
16555     /**
16556      * The left constraint
16557      * @property minX
16558      * @type int
16559      * @private
16560      */
16561     minX: 0,
16562
16563     /**
16564      * The right constraint
16565      * @property maxX
16566      * @type int
16567      * @private
16568      */
16569     maxX: 0,
16570
16571     /**
16572      * The up constraint
16573      * @property minY
16574      * @type int
16575      * @type int
16576      * @private
16577      */
16578     minY: 0,
16579
16580     /**
16581      * The down constraint
16582      * @property maxY
16583      * @type int
16584      * @private
16585      */
16586     maxY: 0,
16587
16588     /**
16589      * Maintain offsets when we resetconstraints.  Set to true when you want
16590      * the position of the element relative to its parent to stay the same
16591      * when the page changes
16592      *
16593      * @property maintainOffset
16594      * @type boolean
16595      */
16596     maintainOffset: false,
16597
16598     /**
16599      * Array of pixel locations the element will snap to if we specified a
16600      * horizontal graduation/interval.  This array is generated automatically
16601      * when you define a tick interval.
16602      * @property xTicks
16603      * @type int[]
16604      */
16605     xTicks: null,
16606
16607     /**
16608      * Array of pixel locations the element will snap to if we specified a
16609      * vertical graduation/interval.  This array is generated automatically
16610      * when you define a tick interval.
16611      * @property yTicks
16612      * @type int[]
16613      */
16614     yTicks: null,
16615
16616     /**
16617      * By default the drag and drop instance will only respond to the primary
16618      * button click (left button for a right-handed mouse).  Set to true to
16619      * allow drag and drop to start with any mouse click that is propogated
16620      * by the browser
16621      * @property primaryButtonOnly
16622      * @type boolean
16623      */
16624     primaryButtonOnly: true,
16625
16626     /**
16627      * The availabe property is false until the linked dom element is accessible.
16628      * @property available
16629      * @type boolean
16630      */
16631     available: false,
16632
16633     /**
16634      * By default, drags can only be initiated if the mousedown occurs in the
16635      * region the linked element is.  This is done in part to work around a
16636      * bug in some browsers that mis-report the mousedown if the previous
16637      * mouseup happened outside of the window.  This property is set to true
16638      * if outer handles are defined.
16639      *
16640      * @property hasOuterHandles
16641      * @type boolean
16642      * @default false
16643      */
16644     hasOuterHandles: false,
16645
16646     /**
16647      * Code that executes immediately before the startDrag event
16648      * @method b4StartDrag
16649      * @private
16650      */
16651     b4StartDrag: function(x, y) { },
16652
16653     /**
16654      * Abstract method called after a drag/drop object is clicked
16655      * and the drag or mousedown time thresholds have beeen met.
16656      * @method startDrag
16657      * @param {int} X click location
16658      * @param {int} Y click location
16659      */
16660     startDrag: function(x, y) { /* override this */ },
16661
16662     /**
16663      * Code that executes immediately before the onDrag event
16664      * @method b4Drag
16665      * @private
16666      */
16667     b4Drag: function(e) { },
16668
16669     /**
16670      * Abstract method called during the onMouseMove event while dragging an
16671      * object.
16672      * @method onDrag
16673      * @param {Event} e the mousemove event
16674      */
16675     onDrag: function(e) { /* override this */ },
16676
16677     /**
16678      * Abstract method called when this element fist begins hovering over
16679      * another DragDrop obj
16680      * @method onDragEnter
16681      * @param {Event} e the mousemove event
16682      * @param {String|DragDrop[]} id In POINT mode, the element
16683      * id this is hovering over.  In INTERSECT mode, an array of one or more
16684      * dragdrop items being hovered over.
16685      */
16686     onDragEnter: function(e, id) { /* override this */ },
16687
16688     /**
16689      * Code that executes immediately before the onDragOver event
16690      * @method b4DragOver
16691      * @private
16692      */
16693     b4DragOver: function(e) { },
16694
16695     /**
16696      * Abstract method called when this element is hovering over another
16697      * DragDrop obj
16698      * @method onDragOver
16699      * @param {Event} e the mousemove event
16700      * @param {String|DragDrop[]} id In POINT mode, the element
16701      * id this is hovering over.  In INTERSECT mode, an array of dd items
16702      * being hovered over.
16703      */
16704     onDragOver: function(e, id) { /* override this */ },
16705
16706     /**
16707      * Code that executes immediately before the onDragOut event
16708      * @method b4DragOut
16709      * @private
16710      */
16711     b4DragOut: function(e) { },
16712
16713     /**
16714      * Abstract method called when we are no longer hovering over an element
16715      * @method onDragOut
16716      * @param {Event} e the mousemove event
16717      * @param {String|DragDrop[]} id In POINT mode, the element
16718      * id this was hovering over.  In INTERSECT mode, an array of dd items
16719      * that the mouse is no longer over.
16720      */
16721     onDragOut: function(e, id) { /* override this */ },
16722
16723     /**
16724      * Code that executes immediately before the onDragDrop event
16725      * @method b4DragDrop
16726      * @private
16727      */
16728     b4DragDrop: function(e) { },
16729
16730     /**
16731      * Abstract method called when this item is dropped on another DragDrop
16732      * obj
16733      * @method onDragDrop
16734      * @param {Event} e the mouseup event
16735      * @param {String|DragDrop[]} id In POINT mode, the element
16736      * id this was dropped on.  In INTERSECT mode, an array of dd items this
16737      * was dropped on.
16738      */
16739     onDragDrop: function(e, id) { /* override this */ },
16740
16741     /**
16742      * Abstract method called when this item is dropped on an area with no
16743      * drop target
16744      * @method onInvalidDrop
16745      * @param {Event} e the mouseup event
16746      */
16747     onInvalidDrop: function(e) { /* override this */ },
16748
16749     /**
16750      * Code that executes immediately before the endDrag event
16751      * @method b4EndDrag
16752      * @private
16753      */
16754     b4EndDrag: function(e) { },
16755
16756     /**
16757      * Fired when we are done dragging the object
16758      * @method endDrag
16759      * @param {Event} e the mouseup event
16760      */
16761     endDrag: function(e) { /* override this */ },
16762
16763     /**
16764      * Code executed immediately before the onMouseDown event
16765      * @method b4MouseDown
16766      * @param {Event} e the mousedown event
16767      * @private
16768      */
16769     b4MouseDown: function(e) {  },
16770
16771     /**
16772      * Event handler that fires when a drag/drop obj gets a mousedown
16773      * @method onMouseDown
16774      * @param {Event} e the mousedown event
16775      */
16776     onMouseDown: function(e) { /* override this */ },
16777
16778     /**
16779      * Event handler that fires when a drag/drop obj gets a mouseup
16780      * @method onMouseUp
16781      * @param {Event} e the mouseup event
16782      */
16783     onMouseUp: function(e) { /* override this */ },
16784
16785     /**
16786      * Override the onAvailable method to do what is needed after the initial
16787      * position was determined.
16788      * @method onAvailable
16789      */
16790     onAvailable: function () {
16791     },
16792
16793     /*
16794      * Provides default constraint padding to "constrainTo" elements (defaults to {left: 0, right:0, top:0, bottom:0}).
16795      * @type Object
16796      */
16797     defaultPadding : {left:0, right:0, top:0, bottom:0},
16798
16799     /*
16800      * Initializes the drag drop object's constraints to restrict movement to a certain element.
16801  *
16802  * Usage:
16803  <pre><code>
16804  var dd = new Roo.dd.DDProxy("dragDiv1", "proxytest",
16805                 { dragElId: "existingProxyDiv" });
16806  dd.startDrag = function(){
16807      this.constrainTo("parent-id");
16808  };
16809  </code></pre>
16810  * Or you can initalize it using the {@link Roo.Element} object:
16811  <pre><code>
16812  Roo.get("dragDiv1").initDDProxy("proxytest", {dragElId: "existingProxyDiv"}, {
16813      startDrag : function(){
16814          this.constrainTo("parent-id");
16815      }
16816  });
16817  </code></pre>
16818      * @param {String/HTMLElement/Element} constrainTo The element to constrain to.
16819      * @param {Object/Number} pad (optional) Pad provides a way to specify "padding" of the constraints,
16820      * and can be either a number for symmetrical padding (4 would be equal to {left:4, right:4, top:4, bottom:4}) or
16821      * an object containing the sides to pad. For example: {right:10, bottom:10}
16822      * @param {Boolean} inContent (optional) Constrain the draggable in the content box of the element (inside padding and borders)
16823      */
16824     constrainTo : function(constrainTo, pad, inContent){
16825         if(typeof pad == "number"){
16826             pad = {left: pad, right:pad, top:pad, bottom:pad};
16827         }
16828         pad = pad || this.defaultPadding;
16829         var b = Roo.get(this.getEl()).getBox();
16830         var ce = Roo.get(constrainTo);
16831         var s = ce.getScroll();
16832         var c, cd = ce.dom;
16833         if(cd == document.body){
16834             c = { x: s.left, y: s.top, width: Roo.lib.Dom.getViewWidth(), height: Roo.lib.Dom.getViewHeight()};
16835         }else{
16836             xy = ce.getXY();
16837             c = {x : xy[0]+s.left, y: xy[1]+s.top, width: cd.clientWidth, height: cd.clientHeight};
16838         }
16839
16840
16841         var topSpace = b.y - c.y;
16842         var leftSpace = b.x - c.x;
16843
16844         this.resetConstraints();
16845         this.setXConstraint(leftSpace - (pad.left||0), // left
16846                 c.width - leftSpace - b.width - (pad.right||0) //right
16847         );
16848         this.setYConstraint(topSpace - (pad.top||0), //top
16849                 c.height - topSpace - b.height - (pad.bottom||0) //bottom
16850         );
16851     },
16852
16853     /**
16854      * Returns a reference to the linked element
16855      * @method getEl
16856      * @return {HTMLElement} the html element
16857      */
16858     getEl: function() {
16859         if (!this._domRef) {
16860             this._domRef = Roo.getDom(this.id);
16861         }
16862
16863         return this._domRef;
16864     },
16865
16866     /**
16867      * Returns a reference to the actual element to drag.  By default this is
16868      * the same as the html element, but it can be assigned to another
16869      * element. An example of this can be found in Roo.dd.DDProxy
16870      * @method getDragEl
16871      * @return {HTMLElement} the html element
16872      */
16873     getDragEl: function() {
16874         return Roo.getDom(this.dragElId);
16875     },
16876
16877     /**
16878      * Sets up the DragDrop object.  Must be called in the constructor of any
16879      * Roo.dd.DragDrop subclass
16880      * @method init
16881      * @param id the id of the linked element
16882      * @param {String} sGroup the group of related items
16883      * @param {object} config configuration attributes
16884      */
16885     init: function(id, sGroup, config) {
16886         this.initTarget(id, sGroup, config);
16887         if (!Roo.isTouch) {
16888             Event.on(this.id, "mousedown", this.handleMouseDown, this);
16889         }
16890         Event.on(this.id, "touchstart", this.handleMouseDown, this);
16891         // Event.on(this.id, "selectstart", Event.preventDefault);
16892     },
16893
16894     /**
16895      * Initializes Targeting functionality only... the object does not
16896      * get a mousedown handler.
16897      * @method initTarget
16898      * @param id the id of the linked element
16899      * @param {String} sGroup the group of related items
16900      * @param {object} config configuration attributes
16901      */
16902     initTarget: function(id, sGroup, config) {
16903
16904         // configuration attributes
16905         this.config = config || {};
16906
16907         // create a local reference to the drag and drop manager
16908         this.DDM = Roo.dd.DDM;
16909         // initialize the groups array
16910         this.groups = {};
16911
16912         // assume that we have an element reference instead of an id if the
16913         // parameter is not a string
16914         if (typeof id !== "string") {
16915             id = Roo.id(id);
16916         }
16917
16918         // set the id
16919         this.id = id;
16920
16921         // add to an interaction group
16922         this.addToGroup((sGroup) ? sGroup : "default");
16923
16924         // We don't want to register this as the handle with the manager
16925         // so we just set the id rather than calling the setter.
16926         this.handleElId = id;
16927
16928         // the linked element is the element that gets dragged by default
16929         this.setDragElId(id);
16930
16931         // by default, clicked anchors will not start drag operations.
16932         this.invalidHandleTypes = { A: "A" };
16933         this.invalidHandleIds = {};
16934         this.invalidHandleClasses = [];
16935
16936         this.applyConfig();
16937
16938         this.handleOnAvailable();
16939     },
16940
16941     /**
16942      * Applies the configuration parameters that were passed into the constructor.
16943      * This is supposed to happen at each level through the inheritance chain.  So
16944      * a DDProxy implentation will execute apply config on DDProxy, DD, and
16945      * DragDrop in order to get all of the parameters that are available in
16946      * each object.
16947      * @method applyConfig
16948      */
16949     applyConfig: function() {
16950
16951         // configurable properties:
16952         //    padding, isTarget, maintainOffset, primaryButtonOnly
16953         this.padding           = this.config.padding || [0, 0, 0, 0];
16954         this.isTarget          = (this.config.isTarget !== false);
16955         this.maintainOffset    = (this.config.maintainOffset);
16956         this.primaryButtonOnly = (this.config.primaryButtonOnly !== false);
16957
16958     },
16959
16960     /**
16961      * Executed when the linked element is available
16962      * @method handleOnAvailable
16963      * @private
16964      */
16965     handleOnAvailable: function() {
16966         this.available = true;
16967         this.resetConstraints();
16968         this.onAvailable();
16969     },
16970
16971      /**
16972      * Configures the padding for the target zone in px.  Effectively expands
16973      * (or reduces) the virtual object size for targeting calculations.
16974      * Supports css-style shorthand; if only one parameter is passed, all sides
16975      * will have that padding, and if only two are passed, the top and bottom
16976      * will have the first param, the left and right the second.
16977      * @method setPadding
16978      * @param {int} iTop    Top pad
16979      * @param {int} iRight  Right pad
16980      * @param {int} iBot    Bot pad
16981      * @param {int} iLeft   Left pad
16982      */
16983     setPadding: function(iTop, iRight, iBot, iLeft) {
16984         // this.padding = [iLeft, iRight, iTop, iBot];
16985         if (!iRight && 0 !== iRight) {
16986             this.padding = [iTop, iTop, iTop, iTop];
16987         } else if (!iBot && 0 !== iBot) {
16988             this.padding = [iTop, iRight, iTop, iRight];
16989         } else {
16990             this.padding = [iTop, iRight, iBot, iLeft];
16991         }
16992     },
16993
16994     /**
16995      * Stores the initial placement of the linked element.
16996      * @method setInitialPosition
16997      * @param {int} diffX   the X offset, default 0
16998      * @param {int} diffY   the Y offset, default 0
16999      */
17000     setInitPosition: function(diffX, diffY) {
17001         var el = this.getEl();
17002
17003         if (!this.DDM.verifyEl(el)) {
17004             return;
17005         }
17006
17007         var dx = diffX || 0;
17008         var dy = diffY || 0;
17009
17010         var p = Dom.getXY( el );
17011
17012         this.initPageX = p[0] - dx;
17013         this.initPageY = p[1] - dy;
17014
17015         this.lastPageX = p[0];
17016         this.lastPageY = p[1];
17017
17018
17019         this.setStartPosition(p);
17020     },
17021
17022     /**
17023      * Sets the start position of the element.  This is set when the obj
17024      * is initialized, the reset when a drag is started.
17025      * @method setStartPosition
17026      * @param pos current position (from previous lookup)
17027      * @private
17028      */
17029     setStartPosition: function(pos) {
17030         var p = pos || Dom.getXY( this.getEl() );
17031         this.deltaSetXY = null;
17032
17033         this.startPageX = p[0];
17034         this.startPageY = p[1];
17035     },
17036
17037     /**
17038      * Add this instance to a group of related drag/drop objects.  All
17039      * instances belong to at least one group, and can belong to as many
17040      * groups as needed.
17041      * @method addToGroup
17042      * @param sGroup {string} the name of the group
17043      */
17044     addToGroup: function(sGroup) {
17045         this.groups[sGroup] = true;
17046         this.DDM.regDragDrop(this, sGroup);
17047     },
17048
17049     /**
17050      * Remove's this instance from the supplied interaction group
17051      * @method removeFromGroup
17052      * @param {string}  sGroup  The group to drop
17053      */
17054     removeFromGroup: function(sGroup) {
17055         if (this.groups[sGroup]) {
17056             delete this.groups[sGroup];
17057         }
17058
17059         this.DDM.removeDDFromGroup(this, sGroup);
17060     },
17061
17062     /**
17063      * Allows you to specify that an element other than the linked element
17064      * will be moved with the cursor during a drag
17065      * @method setDragElId
17066      * @param id {string} the id of the element that will be used to initiate the drag
17067      */
17068     setDragElId: function(id) {
17069         this.dragElId = id;
17070     },
17071
17072     /**
17073      * Allows you to specify a child of the linked element that should be
17074      * used to initiate the drag operation.  An example of this would be if
17075      * you have a content div with text and links.  Clicking anywhere in the
17076      * content area would normally start the drag operation.  Use this method
17077      * to specify that an element inside of the content div is the element
17078      * that starts the drag operation.
17079      * @method setHandleElId
17080      * @param id {string} the id of the element that will be used to
17081      * initiate the drag.
17082      */
17083     setHandleElId: function(id) {
17084         if (typeof id !== "string") {
17085             id = Roo.id(id);
17086         }
17087         this.handleElId = id;
17088         this.DDM.regHandle(this.id, id);
17089     },
17090
17091     /**
17092      * Allows you to set an element outside of the linked element as a drag
17093      * handle
17094      * @method setOuterHandleElId
17095      * @param id the id of the element that will be used to initiate the drag
17096      */
17097     setOuterHandleElId: function(id) {
17098         if (typeof id !== "string") {
17099             id = Roo.id(id);
17100         }
17101         Event.on(id, "mousedown",
17102                 this.handleMouseDown, this);
17103         this.setHandleElId(id);
17104
17105         this.hasOuterHandles = true;
17106     },
17107
17108     /**
17109      * Remove all drag and drop hooks for this element
17110      * @method unreg
17111      */
17112     unreg: function() {
17113         Event.un(this.id, "mousedown",
17114                 this.handleMouseDown);
17115         Event.un(this.id, "touchstart",
17116                 this.handleMouseDown);
17117         this._domRef = null;
17118         this.DDM._remove(this);
17119     },
17120
17121     destroy : function(){
17122         this.unreg();
17123     },
17124
17125     /**
17126      * Returns true if this instance is locked, or the drag drop mgr is locked
17127      * (meaning that all drag/drop is disabled on the page.)
17128      * @method isLocked
17129      * @return {boolean} true if this obj or all drag/drop is locked, else
17130      * false
17131      */
17132     isLocked: function() {
17133         return (this.DDM.isLocked() || this.locked);
17134     },
17135
17136     /**
17137      * Fired when this object is clicked
17138      * @method handleMouseDown
17139      * @param {Event} e
17140      * @param {Roo.dd.DragDrop} oDD the clicked dd object (this dd obj)
17141      * @private
17142      */
17143     handleMouseDown: function(e, oDD){
17144      
17145         if (!Roo.isTouch && this.primaryButtonOnly && e.button != 0) {
17146             //Roo.log('not touch/ button !=0');
17147             return;
17148         }
17149         if (e.browserEvent.touches && e.browserEvent.touches.length != 1) {
17150             return; // double touch..
17151         }
17152         
17153
17154         if (this.isLocked()) {
17155             //Roo.log('locked');
17156             return;
17157         }
17158
17159         this.DDM.refreshCache(this.groups);
17160 //        Roo.log([Roo.lib.Event.getPageX(e), Roo.lib.Event.getPageY(e)]);
17161         var pt = new Roo.lib.Point(Roo.lib.Event.getPageX(e), Roo.lib.Event.getPageY(e));
17162         if (!this.hasOuterHandles && !this.DDM.isOverTarget(pt, this) )  {
17163             //Roo.log('no outer handes or not over target');
17164                 // do nothing.
17165         } else {
17166 //            Roo.log('check validator');
17167             if (this.clickValidator(e)) {
17168 //                Roo.log('validate success');
17169                 // set the initial element position
17170                 this.setStartPosition();
17171
17172
17173                 this.b4MouseDown(e);
17174                 this.onMouseDown(e);
17175
17176                 this.DDM.handleMouseDown(e, this);
17177
17178                 this.DDM.stopEvent(e);
17179             } else {
17180
17181
17182             }
17183         }
17184     },
17185
17186     clickValidator: function(e) {
17187         var target = e.getTarget();
17188         return ( this.isValidHandleChild(target) &&
17189                     (this.id == this.handleElId ||
17190                         this.DDM.handleWasClicked(target, this.id)) );
17191     },
17192
17193     /**
17194      * Allows you to specify a tag name that should not start a drag operation
17195      * when clicked.  This is designed to facilitate embedding links within a
17196      * drag handle that do something other than start the drag.
17197      * @method addInvalidHandleType
17198      * @param {string} tagName the type of element to exclude
17199      */
17200     addInvalidHandleType: function(tagName) {
17201         var type = tagName.toUpperCase();
17202         this.invalidHandleTypes[type] = type;
17203     },
17204
17205     /**
17206      * Lets you to specify an element id for a child of a drag handle
17207      * that should not initiate a drag
17208      * @method addInvalidHandleId
17209      * @param {string} id the element id of the element you wish to ignore
17210      */
17211     addInvalidHandleId: function(id) {
17212         if (typeof id !== "string") {
17213             id = Roo.id(id);
17214         }
17215         this.invalidHandleIds[id] = id;
17216     },
17217
17218     /**
17219      * Lets you specify a css class of elements that will not initiate a drag
17220      * @method addInvalidHandleClass
17221      * @param {string} cssClass the class of the elements you wish to ignore
17222      */
17223     addInvalidHandleClass: function(cssClass) {
17224         this.invalidHandleClasses.push(cssClass);
17225     },
17226
17227     /**
17228      * Unsets an excluded tag name set by addInvalidHandleType
17229      * @method removeInvalidHandleType
17230      * @param {string} tagName the type of element to unexclude
17231      */
17232     removeInvalidHandleType: function(tagName) {
17233         var type = tagName.toUpperCase();
17234         // this.invalidHandleTypes[type] = null;
17235         delete this.invalidHandleTypes[type];
17236     },
17237
17238     /**
17239      * Unsets an invalid handle id
17240      * @method removeInvalidHandleId
17241      * @param {string} id the id of the element to re-enable
17242      */
17243     removeInvalidHandleId: function(id) {
17244         if (typeof id !== "string") {
17245             id = Roo.id(id);
17246         }
17247         delete this.invalidHandleIds[id];
17248     },
17249
17250     /**
17251      * Unsets an invalid css class
17252      * @method removeInvalidHandleClass
17253      * @param {string} cssClass the class of the element(s) you wish to
17254      * re-enable
17255      */
17256     removeInvalidHandleClass: function(cssClass) {
17257         for (var i=0, len=this.invalidHandleClasses.length; i<len; ++i) {
17258             if (this.invalidHandleClasses[i] == cssClass) {
17259                 delete this.invalidHandleClasses[i];
17260             }
17261         }
17262     },
17263
17264     /**
17265      * Checks the tag exclusion list to see if this click should be ignored
17266      * @method isValidHandleChild
17267      * @param {HTMLElement} node the HTMLElement to evaluate
17268      * @return {boolean} true if this is a valid tag type, false if not
17269      */
17270     isValidHandleChild: function(node) {
17271
17272         var valid = true;
17273         // var n = (node.nodeName == "#text") ? node.parentNode : node;
17274         var nodeName;
17275         try {
17276             nodeName = node.nodeName.toUpperCase();
17277         } catch(e) {
17278             nodeName = node.nodeName;
17279         }
17280         valid = valid && !this.invalidHandleTypes[nodeName];
17281         valid = valid && !this.invalidHandleIds[node.id];
17282
17283         for (var i=0, len=this.invalidHandleClasses.length; valid && i<len; ++i) {
17284             valid = !Dom.hasClass(node, this.invalidHandleClasses[i]);
17285         }
17286
17287
17288         return valid;
17289
17290     },
17291
17292     /**
17293      * Create the array of horizontal tick marks if an interval was specified
17294      * in setXConstraint().
17295      * @method setXTicks
17296      * @private
17297      */
17298     setXTicks: function(iStartX, iTickSize) {
17299         this.xTicks = [];
17300         this.xTickSize = iTickSize;
17301
17302         var tickMap = {};
17303
17304         for (var i = this.initPageX; i >= this.minX; i = i - iTickSize) {
17305             if (!tickMap[i]) {
17306                 this.xTicks[this.xTicks.length] = i;
17307                 tickMap[i] = true;
17308             }
17309         }
17310
17311         for (i = this.initPageX; i <= this.maxX; i = i + iTickSize) {
17312             if (!tickMap[i]) {
17313                 this.xTicks[this.xTicks.length] = i;
17314                 tickMap[i] = true;
17315             }
17316         }
17317
17318         this.xTicks.sort(this.DDM.numericSort) ;
17319     },
17320
17321     /**
17322      * Create the array of vertical tick marks if an interval was specified in
17323      * setYConstraint().
17324      * @method setYTicks
17325      * @private
17326      */
17327     setYTicks: function(iStartY, iTickSize) {
17328         this.yTicks = [];
17329         this.yTickSize = iTickSize;
17330
17331         var tickMap = {};
17332
17333         for (var i = this.initPageY; i >= this.minY; i = i - iTickSize) {
17334             if (!tickMap[i]) {
17335                 this.yTicks[this.yTicks.length] = i;
17336                 tickMap[i] = true;
17337             }
17338         }
17339
17340         for (i = this.initPageY; i <= this.maxY; i = i + iTickSize) {
17341             if (!tickMap[i]) {
17342                 this.yTicks[this.yTicks.length] = i;
17343                 tickMap[i] = true;
17344             }
17345         }
17346
17347         this.yTicks.sort(this.DDM.numericSort) ;
17348     },
17349
17350     /**
17351      * By default, the element can be dragged any place on the screen.  Use
17352      * this method to limit the horizontal travel of the element.  Pass in
17353      * 0,0 for the parameters if you want to lock the drag to the y axis.
17354      * @method setXConstraint
17355      * @param {int} iLeft the number of pixels the element can move to the left
17356      * @param {int} iRight the number of pixels the element can move to the
17357      * right
17358      * @param {int} iTickSize optional parameter for specifying that the
17359      * element
17360      * should move iTickSize pixels at a time.
17361      */
17362     setXConstraint: function(iLeft, iRight, iTickSize) {
17363         this.leftConstraint = iLeft;
17364         this.rightConstraint = iRight;
17365
17366         this.minX = this.initPageX - iLeft;
17367         this.maxX = this.initPageX + iRight;
17368         if (iTickSize) { this.setXTicks(this.initPageX, iTickSize); }
17369
17370         this.constrainX = true;
17371     },
17372
17373     /**
17374      * Clears any constraints applied to this instance.  Also clears ticks
17375      * since they can't exist independent of a constraint at this time.
17376      * @method clearConstraints
17377      */
17378     clearConstraints: function() {
17379         this.constrainX = false;
17380         this.constrainY = false;
17381         this.clearTicks();
17382     },
17383
17384     /**
17385      * Clears any tick interval defined for this instance
17386      * @method clearTicks
17387      */
17388     clearTicks: function() {
17389         this.xTicks = null;
17390         this.yTicks = null;
17391         this.xTickSize = 0;
17392         this.yTickSize = 0;
17393     },
17394
17395     /**
17396      * By default, the element can be dragged any place on the screen.  Set
17397      * this to limit the vertical travel of the element.  Pass in 0,0 for the
17398      * parameters if you want to lock the drag to the x axis.
17399      * @method setYConstraint
17400      * @param {int} iUp the number of pixels the element can move up
17401      * @param {int} iDown the number of pixels the element can move down
17402      * @param {int} iTickSize optional parameter for specifying that the
17403      * element should move iTickSize pixels at a time.
17404      */
17405     setYConstraint: function(iUp, iDown, iTickSize) {
17406         this.topConstraint = iUp;
17407         this.bottomConstraint = iDown;
17408
17409         this.minY = this.initPageY - iUp;
17410         this.maxY = this.initPageY + iDown;
17411         if (iTickSize) { this.setYTicks(this.initPageY, iTickSize); }
17412
17413         this.constrainY = true;
17414
17415     },
17416
17417     /**
17418      * resetConstraints must be called if you manually reposition a dd element.
17419      * @method resetConstraints
17420      * @param {boolean} maintainOffset
17421      */
17422     resetConstraints: function() {
17423
17424
17425         // Maintain offsets if necessary
17426         if (this.initPageX || this.initPageX === 0) {
17427             // figure out how much this thing has moved
17428             var dx = (this.maintainOffset) ? this.lastPageX - this.initPageX : 0;
17429             var dy = (this.maintainOffset) ? this.lastPageY - this.initPageY : 0;
17430
17431             this.setInitPosition(dx, dy);
17432
17433         // This is the first time we have detected the element's position
17434         } else {
17435             this.setInitPosition();
17436         }
17437
17438         if (this.constrainX) {
17439             this.setXConstraint( this.leftConstraint,
17440                                  this.rightConstraint,
17441                                  this.xTickSize        );
17442         }
17443
17444         if (this.constrainY) {
17445             this.setYConstraint( this.topConstraint,
17446                                  this.bottomConstraint,
17447                                  this.yTickSize         );
17448         }
17449     },
17450
17451     /**
17452      * Normally the drag element is moved pixel by pixel, but we can specify
17453      * that it move a number of pixels at a time.  This method resolves the
17454      * location when we have it set up like this.
17455      * @method getTick
17456      * @param {int} val where we want to place the object
17457      * @param {int[]} tickArray sorted array of valid points
17458      * @return {int} the closest tick
17459      * @private
17460      */
17461     getTick: function(val, tickArray) {
17462
17463         if (!tickArray) {
17464             // If tick interval is not defined, it is effectively 1 pixel,
17465             // so we return the value passed to us.
17466             return val;
17467         } else if (tickArray[0] >= val) {
17468             // The value is lower than the first tick, so we return the first
17469             // tick.
17470             return tickArray[0];
17471         } else {
17472             for (var i=0, len=tickArray.length; i<len; ++i) {
17473                 var next = i + 1;
17474                 if (tickArray[next] && tickArray[next] >= val) {
17475                     var diff1 = val - tickArray[i];
17476                     var diff2 = tickArray[next] - val;
17477                     return (diff2 > diff1) ? tickArray[i] : tickArray[next];
17478                 }
17479             }
17480
17481             // The value is larger than the last tick, so we return the last
17482             // tick.
17483             return tickArray[tickArray.length - 1];
17484         }
17485     },
17486
17487     /**
17488      * toString method
17489      * @method toString
17490      * @return {string} string representation of the dd obj
17491      */
17492     toString: function() {
17493         return ("DragDrop " + this.id);
17494     }
17495
17496 });
17497
17498 })();
17499 /*
17500  * Based on:
17501  * Ext JS Library 1.1.1
17502  * Copyright(c) 2006-2007, Ext JS, LLC.
17503  *
17504  * Originally Released Under LGPL - original licence link has changed is not relivant.
17505  *
17506  * Fork - LGPL
17507  * <script type="text/javascript">
17508  */
17509
17510
17511 /**
17512  * The drag and drop utility provides a framework for building drag and drop
17513  * applications.  In addition to enabling drag and drop for specific elements,
17514  * the drag and drop elements are tracked by the manager class, and the
17515  * interactions between the various elements are tracked during the drag and
17516  * the implementing code is notified about these important moments.
17517  */
17518
17519 // Only load the library once.  Rewriting the manager class would orphan
17520 // existing drag and drop instances.
17521 if (!Roo.dd.DragDropMgr) {
17522
17523 /**
17524  * @class Roo.dd.DragDropMgr
17525  * DragDropMgr is a singleton that tracks the element interaction for
17526  * all DragDrop items in the window.  Generally, you will not call
17527  * this class directly, but it does have helper methods that could
17528  * be useful in your DragDrop implementations.
17529  * @singleton
17530  */
17531 Roo.dd.DragDropMgr = function() {
17532
17533     var Event = Roo.EventManager;
17534
17535     return {
17536
17537         /**
17538          * Two dimensional Array of registered DragDrop objects.  The first
17539          * dimension is the DragDrop item group, the second the DragDrop
17540          * object.
17541          * @property ids
17542          * @type {string: string}
17543          * @private
17544          * @static
17545          */
17546         ids: {},
17547
17548         /**
17549          * Array of element ids defined as drag handles.  Used to determine
17550          * if the element that generated the mousedown event is actually the
17551          * handle and not the html element itself.
17552          * @property handleIds
17553          * @type {string: string}
17554          * @private
17555          * @static
17556          */
17557         handleIds: {},
17558
17559         /**
17560          * the DragDrop object that is currently being dragged
17561          * @property dragCurrent
17562          * @type DragDrop
17563          * @private
17564          * @static
17565          **/
17566         dragCurrent: null,
17567
17568         /**
17569          * the DragDrop object(s) that are being hovered over
17570          * @property dragOvers
17571          * @type Array
17572          * @private
17573          * @static
17574          */
17575         dragOvers: {},
17576
17577         /**
17578          * the X distance between the cursor and the object being dragged
17579          * @property deltaX
17580          * @type int
17581          * @private
17582          * @static
17583          */
17584         deltaX: 0,
17585
17586         /**
17587          * the Y distance between the cursor and the object being dragged
17588          * @property deltaY
17589          * @type int
17590          * @private
17591          * @static
17592          */
17593         deltaY: 0,
17594
17595         /**
17596          * Flag to determine if we should prevent the default behavior of the
17597          * events we define. By default this is true, but this can be set to
17598          * false if you need the default behavior (not recommended)
17599          * @property preventDefault
17600          * @type boolean
17601          * @static
17602          */
17603         preventDefault: true,
17604
17605         /**
17606          * Flag to determine if we should stop the propagation of the events
17607          * we generate. This is true by default but you may want to set it to
17608          * false if the html element contains other features that require the
17609          * mouse click.
17610          * @property stopPropagation
17611          * @type boolean
17612          * @static
17613          */
17614         stopPropagation: true,
17615
17616         /**
17617          * Internal flag that is set to true when drag and drop has been
17618          * intialized
17619          * @property initialized
17620          * @private
17621          * @static
17622          */
17623         initalized: false,
17624
17625         /**
17626          * All drag and drop can be disabled.
17627          * @property locked
17628          * @private
17629          * @static
17630          */
17631         locked: false,
17632
17633         /**
17634          * Called the first time an element is registered.
17635          * @method init
17636          * @private
17637          * @static
17638          */
17639         init: function() {
17640             this.initialized = true;
17641         },
17642
17643         /**
17644          * In point mode, drag and drop interaction is defined by the
17645          * location of the cursor during the drag/drop
17646          * @property POINT
17647          * @type int
17648          * @static
17649          */
17650         POINT: 0,
17651
17652         /**
17653          * In intersect mode, drag and drop interactio nis defined by the
17654          * overlap of two or more drag and drop objects.
17655          * @property INTERSECT
17656          * @type int
17657          * @static
17658          */
17659         INTERSECT: 1,
17660
17661         /**
17662          * The current drag and drop mode.  Default: POINT
17663          * @property mode
17664          * @type int
17665          * @static
17666          */
17667         mode: 0,
17668
17669         /**
17670          * Runs method on all drag and drop objects
17671          * @method _execOnAll
17672          * @private
17673          * @static
17674          */
17675         _execOnAll: function(sMethod, args) {
17676             for (var i in this.ids) {
17677                 for (var j in this.ids[i]) {
17678                     var oDD = this.ids[i][j];
17679                     if (! this.isTypeOfDD(oDD)) {
17680                         continue;
17681                     }
17682                     oDD[sMethod].apply(oDD, args);
17683                 }
17684             }
17685         },
17686
17687         /**
17688          * Drag and drop initialization.  Sets up the global event handlers
17689          * @method _onLoad
17690          * @private
17691          * @static
17692          */
17693         _onLoad: function() {
17694
17695             this.init();
17696
17697             if (!Roo.isTouch) {
17698                 Event.on(document, "mouseup",   this.handleMouseUp, this, true);
17699                 Event.on(document, "mousemove", this.handleMouseMove, this, true);
17700             }
17701             Event.on(document, "touchend",   this.handleMouseUp, this, true);
17702             Event.on(document, "touchmove", this.handleMouseMove, this, true);
17703             
17704             Event.on(window,   "unload",    this._onUnload, this, true);
17705             Event.on(window,   "resize",    this._onResize, this, true);
17706             // Event.on(window,   "mouseout",    this._test);
17707
17708         },
17709
17710         /**
17711          * Reset constraints on all drag and drop objs
17712          * @method _onResize
17713          * @private
17714          * @static
17715          */
17716         _onResize: function(e) {
17717             this._execOnAll("resetConstraints", []);
17718         },
17719
17720         /**
17721          * Lock all drag and drop functionality
17722          * @method lock
17723          * @static
17724          */
17725         lock: function() { this.locked = true; },
17726
17727         /**
17728          * Unlock all drag and drop functionality
17729          * @method unlock
17730          * @static
17731          */
17732         unlock: function() { this.locked = false; },
17733
17734         /**
17735          * Is drag and drop locked?
17736          * @method isLocked
17737          * @return {boolean} True if drag and drop is locked, false otherwise.
17738          * @static
17739          */
17740         isLocked: function() { return this.locked; },
17741
17742         /**
17743          * Location cache that is set for all drag drop objects when a drag is
17744          * initiated, cleared when the drag is finished.
17745          * @property locationCache
17746          * @private
17747          * @static
17748          */
17749         locationCache: {},
17750
17751         /**
17752          * Set useCache to false if you want to force object the lookup of each
17753          * drag and drop linked element constantly during a drag.
17754          * @property useCache
17755          * @type boolean
17756          * @static
17757          */
17758         useCache: true,
17759
17760         /**
17761          * The number of pixels that the mouse needs to move after the
17762          * mousedown before the drag is initiated.  Default=3;
17763          * @property clickPixelThresh
17764          * @type int
17765          * @static
17766          */
17767         clickPixelThresh: 3,
17768
17769         /**
17770          * The number of milliseconds after the mousedown event to initiate the
17771          * drag if we don't get a mouseup event. Default=1000
17772          * @property clickTimeThresh
17773          * @type int
17774          * @static
17775          */
17776         clickTimeThresh: 350,
17777
17778         /**
17779          * Flag that indicates that either the drag pixel threshold or the
17780          * mousdown time threshold has been met
17781          * @property dragThreshMet
17782          * @type boolean
17783          * @private
17784          * @static
17785          */
17786         dragThreshMet: false,
17787
17788         /**
17789          * Timeout used for the click time threshold
17790          * @property clickTimeout
17791          * @type Object
17792          * @private
17793          * @static
17794          */
17795         clickTimeout: null,
17796
17797         /**
17798          * The X position of the mousedown event stored for later use when a
17799          * drag threshold is met.
17800          * @property startX
17801          * @type int
17802          * @private
17803          * @static
17804          */
17805         startX: 0,
17806
17807         /**
17808          * The Y position of the mousedown event stored for later use when a
17809          * drag threshold is met.
17810          * @property startY
17811          * @type int
17812          * @private
17813          * @static
17814          */
17815         startY: 0,
17816
17817         /**
17818          * Each DragDrop instance must be registered with the DragDropMgr.
17819          * This is executed in DragDrop.init()
17820          * @method regDragDrop
17821          * @param {DragDrop} oDD the DragDrop object to register
17822          * @param {String} sGroup the name of the group this element belongs to
17823          * @static
17824          */
17825         regDragDrop: function(oDD, sGroup) {
17826             if (!this.initialized) { this.init(); }
17827
17828             if (!this.ids[sGroup]) {
17829                 this.ids[sGroup] = {};
17830             }
17831             this.ids[sGroup][oDD.id] = oDD;
17832         },
17833
17834         /**
17835          * Removes the supplied dd instance from the supplied group. Executed
17836          * by DragDrop.removeFromGroup, so don't call this function directly.
17837          * @method removeDDFromGroup
17838          * @private
17839          * @static
17840          */
17841         removeDDFromGroup: function(oDD, sGroup) {
17842             if (!this.ids[sGroup]) {
17843                 this.ids[sGroup] = {};
17844             }
17845
17846             var obj = this.ids[sGroup];
17847             if (obj && obj[oDD.id]) {
17848                 delete obj[oDD.id];
17849             }
17850         },
17851
17852         /**
17853          * Unregisters a drag and drop item.  This is executed in
17854          * DragDrop.unreg, use that method instead of calling this directly.
17855          * @method _remove
17856          * @private
17857          * @static
17858          */
17859         _remove: function(oDD) {
17860             for (var g in oDD.groups) {
17861                 if (g && this.ids[g][oDD.id]) {
17862                     delete this.ids[g][oDD.id];
17863                 }
17864             }
17865             delete this.handleIds[oDD.id];
17866         },
17867
17868         /**
17869          * Each DragDrop handle element must be registered.  This is done
17870          * automatically when executing DragDrop.setHandleElId()
17871          * @method regHandle
17872          * @param {String} sDDId the DragDrop id this element is a handle for
17873          * @param {String} sHandleId the id of the element that is the drag
17874          * handle
17875          * @static
17876          */
17877         regHandle: function(sDDId, sHandleId) {
17878             if (!this.handleIds[sDDId]) {
17879                 this.handleIds[sDDId] = {};
17880             }
17881             this.handleIds[sDDId][sHandleId] = sHandleId;
17882         },
17883
17884         /**
17885          * Utility function to determine if a given element has been
17886          * registered as a drag drop item.
17887          * @method isDragDrop
17888          * @param {String} id the element id to check
17889          * @return {boolean} true if this element is a DragDrop item,
17890          * false otherwise
17891          * @static
17892          */
17893         isDragDrop: function(id) {
17894             return ( this.getDDById(id) ) ? true : false;
17895         },
17896
17897         /**
17898          * Returns the drag and drop instances that are in all groups the
17899          * passed in instance belongs to.
17900          * @method getRelated
17901          * @param {DragDrop} p_oDD the obj to get related data for
17902          * @param {boolean} bTargetsOnly if true, only return targetable objs
17903          * @return {DragDrop[]} the related instances
17904          * @static
17905          */
17906         getRelated: function(p_oDD, bTargetsOnly) {
17907             var oDDs = [];
17908             for (var i in p_oDD.groups) {
17909                 for (j in this.ids[i]) {
17910                     var dd = this.ids[i][j];
17911                     if (! this.isTypeOfDD(dd)) {
17912                         continue;
17913                     }
17914                     if (!bTargetsOnly || dd.isTarget) {
17915                         oDDs[oDDs.length] = dd;
17916                     }
17917                 }
17918             }
17919
17920             return oDDs;
17921         },
17922
17923         /**
17924          * Returns true if the specified dd target is a legal target for
17925          * the specifice drag obj
17926          * @method isLegalTarget
17927          * @param {DragDrop} the drag obj
17928          * @param {DragDrop} the target
17929          * @return {boolean} true if the target is a legal target for the
17930          * dd obj
17931          * @static
17932          */
17933         isLegalTarget: function (oDD, oTargetDD) {
17934             var targets = this.getRelated(oDD, true);
17935             for (var i=0, len=targets.length;i<len;++i) {
17936                 if (targets[i].id == oTargetDD.id) {
17937                     return true;
17938                 }
17939             }
17940
17941             return false;
17942         },
17943
17944         /**
17945          * My goal is to be able to transparently determine if an object is
17946          * typeof DragDrop, and the exact subclass of DragDrop.  typeof
17947          * returns "object", oDD.constructor.toString() always returns
17948          * "DragDrop" and not the name of the subclass.  So for now it just
17949          * evaluates a well-known variable in DragDrop.
17950          * @method isTypeOfDD
17951          * @param {Object} the object to evaluate
17952          * @return {boolean} true if typeof oDD = DragDrop
17953          * @static
17954          */
17955         isTypeOfDD: function (oDD) {
17956             return (oDD && oDD.__ygDragDrop);
17957         },
17958
17959         /**
17960          * Utility function to determine if a given element has been
17961          * registered as a drag drop handle for the given Drag Drop object.
17962          * @method isHandle
17963          * @param {String} id the element id to check
17964          * @return {boolean} true if this element is a DragDrop handle, false
17965          * otherwise
17966          * @static
17967          */
17968         isHandle: function(sDDId, sHandleId) {
17969             return ( this.handleIds[sDDId] &&
17970                             this.handleIds[sDDId][sHandleId] );
17971         },
17972
17973         /**
17974          * Returns the DragDrop instance for a given id
17975          * @method getDDById
17976          * @param {String} id the id of the DragDrop object
17977          * @return {DragDrop} the drag drop object, null if it is not found
17978          * @static
17979          */
17980         getDDById: function(id) {
17981             for (var i in this.ids) {
17982                 if (this.ids[i][id]) {
17983                     return this.ids[i][id];
17984                 }
17985             }
17986             return null;
17987         },
17988
17989         /**
17990          * Fired after a registered DragDrop object gets the mousedown event.
17991          * Sets up the events required to track the object being dragged
17992          * @method handleMouseDown
17993          * @param {Event} e the event
17994          * @param oDD the DragDrop object being dragged
17995          * @private
17996          * @static
17997          */
17998         handleMouseDown: function(e, oDD) {
17999             if(Roo.QuickTips){
18000                 Roo.QuickTips.disable();
18001             }
18002             this.currentTarget = e.getTarget();
18003
18004             this.dragCurrent = oDD;
18005
18006             var el = oDD.getEl();
18007
18008             // track start position
18009             this.startX = e.getPageX();
18010             this.startY = e.getPageY();
18011
18012             this.deltaX = this.startX - el.offsetLeft;
18013             this.deltaY = this.startY - el.offsetTop;
18014
18015             this.dragThreshMet = false;
18016
18017             this.clickTimeout = setTimeout(
18018                     function() {
18019                         var DDM = Roo.dd.DDM;
18020                         DDM.startDrag(DDM.startX, DDM.startY);
18021                     },
18022                     this.clickTimeThresh );
18023         },
18024
18025         /**
18026          * Fired when either the drag pixel threshol or the mousedown hold
18027          * time threshold has been met.
18028          * @method startDrag
18029          * @param x {int} the X position of the original mousedown
18030          * @param y {int} the Y position of the original mousedown
18031          * @static
18032          */
18033         startDrag: function(x, y) {
18034             clearTimeout(this.clickTimeout);
18035             if (this.dragCurrent) {
18036                 this.dragCurrent.b4StartDrag(x, y);
18037                 this.dragCurrent.startDrag(x, y);
18038             }
18039             this.dragThreshMet = true;
18040         },
18041
18042         /**
18043          * Internal function to handle the mouseup event.  Will be invoked
18044          * from the context of the document.
18045          * @method handleMouseUp
18046          * @param {Event} e the event
18047          * @private
18048          * @static
18049          */
18050         handleMouseUp: function(e) {
18051
18052             if(Roo.QuickTips){
18053                 Roo.QuickTips.enable();
18054             }
18055             if (! this.dragCurrent) {
18056                 return;
18057             }
18058
18059             clearTimeout(this.clickTimeout);
18060
18061             if (this.dragThreshMet) {
18062                 this.fireEvents(e, true);
18063             } else {
18064             }
18065
18066             this.stopDrag(e);
18067
18068             this.stopEvent(e);
18069         },
18070
18071         /**
18072          * Utility to stop event propagation and event default, if these
18073          * features are turned on.
18074          * @method stopEvent
18075          * @param {Event} e the event as returned by this.getEvent()
18076          * @static
18077          */
18078         stopEvent: function(e){
18079             if(this.stopPropagation) {
18080                 e.stopPropagation();
18081             }
18082
18083             if (this.preventDefault) {
18084                 e.preventDefault();
18085             }
18086         },
18087
18088         /**
18089          * Internal function to clean up event handlers after the drag
18090          * operation is complete
18091          * @method stopDrag
18092          * @param {Event} e the event
18093          * @private
18094          * @static
18095          */
18096         stopDrag: function(e) {
18097             // Fire the drag end event for the item that was dragged
18098             if (this.dragCurrent) {
18099                 if (this.dragThreshMet) {
18100                     this.dragCurrent.b4EndDrag(e);
18101                     this.dragCurrent.endDrag(e);
18102                 }
18103
18104                 this.dragCurrent.onMouseUp(e);
18105             }
18106
18107             this.dragCurrent = null;
18108             this.dragOvers = {};
18109         },
18110
18111         /**
18112          * Internal function to handle the mousemove event.  Will be invoked
18113          * from the context of the html element.
18114          *
18115          * @TODO figure out what we can do about mouse events lost when the
18116          * user drags objects beyond the window boundary.  Currently we can
18117          * detect this in internet explorer by verifying that the mouse is
18118          * down during the mousemove event.  Firefox doesn't give us the
18119          * button state on the mousemove event.
18120          * @method handleMouseMove
18121          * @param {Event} e the event
18122          * @private
18123          * @static
18124          */
18125         handleMouseMove: function(e) {
18126             if (! this.dragCurrent) {
18127                 return true;
18128             }
18129
18130             // var button = e.which || e.button;
18131
18132             // check for IE mouseup outside of page boundary
18133             if (Roo.isIE && (e.button !== 0 && e.button !== 1 && e.button !== 2)) {
18134                 this.stopEvent(e);
18135                 return this.handleMouseUp(e);
18136             }
18137
18138             if (!this.dragThreshMet) {
18139                 var diffX = Math.abs(this.startX - e.getPageX());
18140                 var diffY = Math.abs(this.startY - e.getPageY());
18141                 if (diffX > this.clickPixelThresh ||
18142                             diffY > this.clickPixelThresh) {
18143                     this.startDrag(this.startX, this.startY);
18144                 }
18145             }
18146
18147             if (this.dragThreshMet) {
18148                 this.dragCurrent.b4Drag(e);
18149                 this.dragCurrent.onDrag(e);
18150                 if(!this.dragCurrent.moveOnly){
18151                     this.fireEvents(e, false);
18152                 }
18153             }
18154
18155             this.stopEvent(e);
18156
18157             return true;
18158         },
18159
18160         /**
18161          * Iterates over all of the DragDrop elements to find ones we are
18162          * hovering over or dropping on
18163          * @method fireEvents
18164          * @param {Event} e the event
18165          * @param {boolean} isDrop is this a drop op or a mouseover op?
18166          * @private
18167          * @static
18168          */
18169         fireEvents: function(e, isDrop) {
18170             var dc = this.dragCurrent;
18171
18172             // If the user did the mouse up outside of the window, we could
18173             // get here even though we have ended the drag.
18174             if (!dc || dc.isLocked()) {
18175                 return;
18176             }
18177
18178             var pt = e.getPoint();
18179
18180             // cache the previous dragOver array
18181             var oldOvers = [];
18182
18183             var outEvts   = [];
18184             var overEvts  = [];
18185             var dropEvts  = [];
18186             var enterEvts = [];
18187
18188             // Check to see if the object(s) we were hovering over is no longer
18189             // being hovered over so we can fire the onDragOut event
18190             for (var i in this.dragOvers) {
18191
18192                 var ddo = this.dragOvers[i];
18193
18194                 if (! this.isTypeOfDD(ddo)) {
18195                     continue;
18196                 }
18197
18198                 if (! this.isOverTarget(pt, ddo, this.mode)) {
18199                     outEvts.push( ddo );
18200                 }
18201
18202                 oldOvers[i] = true;
18203                 delete this.dragOvers[i];
18204             }
18205
18206             for (var sGroup in dc.groups) {
18207
18208                 if ("string" != typeof sGroup) {
18209                     continue;
18210                 }
18211
18212                 for (i in this.ids[sGroup]) {
18213                     var oDD = this.ids[sGroup][i];
18214                     if (! this.isTypeOfDD(oDD)) {
18215                         continue;
18216                     }
18217
18218                     if (oDD.isTarget && !oDD.isLocked() && oDD != dc) {
18219                         if (this.isOverTarget(pt, oDD, this.mode)) {
18220                             // look for drop interactions
18221                             if (isDrop) {
18222                                 dropEvts.push( oDD );
18223                             // look for drag enter and drag over interactions
18224                             } else {
18225
18226                                 // initial drag over: dragEnter fires
18227                                 if (!oldOvers[oDD.id]) {
18228                                     enterEvts.push( oDD );
18229                                 // subsequent drag overs: dragOver fires
18230                                 } else {
18231                                     overEvts.push( oDD );
18232                                 }
18233
18234                                 this.dragOvers[oDD.id] = oDD;
18235                             }
18236                         }
18237                     }
18238                 }
18239             }
18240
18241             if (this.mode) {
18242                 if (outEvts.length) {
18243                     dc.b4DragOut(e, outEvts);
18244                     dc.onDragOut(e, outEvts);
18245                 }
18246
18247                 if (enterEvts.length) {
18248                     dc.onDragEnter(e, enterEvts);
18249                 }
18250
18251                 if (overEvts.length) {
18252                     dc.b4DragOver(e, overEvts);
18253                     dc.onDragOver(e, overEvts);
18254                 }
18255
18256                 if (dropEvts.length) {
18257                     dc.b4DragDrop(e, dropEvts);
18258                     dc.onDragDrop(e, dropEvts);
18259                 }
18260
18261             } else {
18262                 // fire dragout events
18263                 var len = 0;
18264                 for (i=0, len=outEvts.length; i<len; ++i) {
18265                     dc.b4DragOut(e, outEvts[i].id);
18266                     dc.onDragOut(e, outEvts[i].id);
18267                 }
18268
18269                 // fire enter events
18270                 for (i=0,len=enterEvts.length; i<len; ++i) {
18271                     // dc.b4DragEnter(e, oDD.id);
18272                     dc.onDragEnter(e, enterEvts[i].id);
18273                 }
18274
18275                 // fire over events
18276                 for (i=0,len=overEvts.length; i<len; ++i) {
18277                     dc.b4DragOver(e, overEvts[i].id);
18278                     dc.onDragOver(e, overEvts[i].id);
18279                 }
18280
18281                 // fire drop events
18282                 for (i=0, len=dropEvts.length; i<len; ++i) {
18283                     dc.b4DragDrop(e, dropEvts[i].id);
18284                     dc.onDragDrop(e, dropEvts[i].id);
18285                 }
18286
18287             }
18288
18289             // notify about a drop that did not find a target
18290             if (isDrop && !dropEvts.length) {
18291                 dc.onInvalidDrop(e);
18292             }
18293
18294         },
18295
18296         /**
18297          * Helper function for getting the best match from the list of drag
18298          * and drop objects returned by the drag and drop events when we are
18299          * in INTERSECT mode.  It returns either the first object that the
18300          * cursor is over, or the object that has the greatest overlap with
18301          * the dragged element.
18302          * @method getBestMatch
18303          * @param  {DragDrop[]} dds The array of drag and drop objects
18304          * targeted
18305          * @return {DragDrop}       The best single match
18306          * @static
18307          */
18308         getBestMatch: function(dds) {
18309             var winner = null;
18310             // Return null if the input is not what we expect
18311             //if (!dds || !dds.length || dds.length == 0) {
18312                // winner = null;
18313             // If there is only one item, it wins
18314             //} else if (dds.length == 1) {
18315
18316             var len = dds.length;
18317
18318             if (len == 1) {
18319                 winner = dds[0];
18320             } else {
18321                 // Loop through the targeted items
18322                 for (var i=0; i<len; ++i) {
18323                     var dd = dds[i];
18324                     // If the cursor is over the object, it wins.  If the
18325                     // cursor is over multiple matches, the first one we come
18326                     // to wins.
18327                     if (dd.cursorIsOver) {
18328                         winner = dd;
18329                         break;
18330                     // Otherwise the object with the most overlap wins
18331                     } else {
18332                         if (!winner ||
18333                             winner.overlap.getArea() < dd.overlap.getArea()) {
18334                             winner = dd;
18335                         }
18336                     }
18337                 }
18338             }
18339
18340             return winner;
18341         },
18342
18343         /**
18344          * Refreshes the cache of the top-left and bottom-right points of the
18345          * drag and drop objects in the specified group(s).  This is in the
18346          * format that is stored in the drag and drop instance, so typical
18347          * usage is:
18348          * <code>
18349          * Roo.dd.DragDropMgr.refreshCache(ddinstance.groups);
18350          * </code>
18351          * Alternatively:
18352          * <code>
18353          * Roo.dd.DragDropMgr.refreshCache({group1:true, group2:true});
18354          * </code>
18355          * @TODO this really should be an indexed array.  Alternatively this
18356          * method could accept both.
18357          * @method refreshCache
18358          * @param {Object} groups an associative array of groups to refresh
18359          * @static
18360          */
18361         refreshCache: function(groups) {
18362             for (var sGroup in groups) {
18363                 if ("string" != typeof sGroup) {
18364                     continue;
18365                 }
18366                 for (var i in this.ids[sGroup]) {
18367                     var oDD = this.ids[sGroup][i];
18368
18369                     if (this.isTypeOfDD(oDD)) {
18370                     // if (this.isTypeOfDD(oDD) && oDD.isTarget) {
18371                         var loc = this.getLocation(oDD);
18372                         if (loc) {
18373                             this.locationCache[oDD.id] = loc;
18374                         } else {
18375                             delete this.locationCache[oDD.id];
18376                             // this will unregister the drag and drop object if
18377                             // the element is not in a usable state
18378                             // oDD.unreg();
18379                         }
18380                     }
18381                 }
18382             }
18383         },
18384
18385         /**
18386          * This checks to make sure an element exists and is in the DOM.  The
18387          * main purpose is to handle cases where innerHTML is used to remove
18388          * drag and drop objects from the DOM.  IE provides an 'unspecified
18389          * error' when trying to access the offsetParent of such an element
18390          * @method verifyEl
18391          * @param {HTMLElement} el the element to check
18392          * @return {boolean} true if the element looks usable
18393          * @static
18394          */
18395         verifyEl: function(el) {
18396             if (el) {
18397                 var parent;
18398                 if(Roo.isIE){
18399                     try{
18400                         parent = el.offsetParent;
18401                     }catch(e){}
18402                 }else{
18403                     parent = el.offsetParent;
18404                 }
18405                 if (parent) {
18406                     return true;
18407                 }
18408             }
18409
18410             return false;
18411         },
18412
18413         /**
18414          * Returns a Region object containing the drag and drop element's position
18415          * and size, including the padding configured for it
18416          * @method getLocation
18417          * @param {DragDrop} oDD the drag and drop object to get the
18418          *                       location for
18419          * @return {Roo.lib.Region} a Region object representing the total area
18420          *                             the element occupies, including any padding
18421          *                             the instance is configured for.
18422          * @static
18423          */
18424         getLocation: function(oDD) {
18425             if (! this.isTypeOfDD(oDD)) {
18426                 return null;
18427             }
18428
18429             var el = oDD.getEl(), pos, x1, x2, y1, y2, t, r, b, l;
18430
18431             try {
18432                 pos= Roo.lib.Dom.getXY(el);
18433             } catch (e) { }
18434
18435             if (!pos) {
18436                 return null;
18437             }
18438
18439             x1 = pos[0];
18440             x2 = x1 + el.offsetWidth;
18441             y1 = pos[1];
18442             y2 = y1 + el.offsetHeight;
18443
18444             t = y1 - oDD.padding[0];
18445             r = x2 + oDD.padding[1];
18446             b = y2 + oDD.padding[2];
18447             l = x1 - oDD.padding[3];
18448
18449             return new Roo.lib.Region( t, r, b, l );
18450         },
18451
18452         /**
18453          * Checks the cursor location to see if it over the target
18454          * @method isOverTarget
18455          * @param {Roo.lib.Point} pt The point to evaluate
18456          * @param {DragDrop} oTarget the DragDrop object we are inspecting
18457          * @return {boolean} true if the mouse is over the target
18458          * @private
18459          * @static
18460          */
18461         isOverTarget: function(pt, oTarget, intersect) {
18462             // use cache if available
18463             var loc = this.locationCache[oTarget.id];
18464             if (!loc || !this.useCache) {
18465                 loc = this.getLocation(oTarget);
18466                 this.locationCache[oTarget.id] = loc;
18467
18468             }
18469
18470             if (!loc) {
18471                 return false;
18472             }
18473
18474             oTarget.cursorIsOver = loc.contains( pt );
18475
18476             // DragDrop is using this as a sanity check for the initial mousedown
18477             // in this case we are done.  In POINT mode, if the drag obj has no
18478             // contraints, we are also done. Otherwise we need to evaluate the
18479             // location of the target as related to the actual location of the
18480             // dragged element.
18481             var dc = this.dragCurrent;
18482             if (!dc || !dc.getTargetCoord ||
18483                     (!intersect && !dc.constrainX && !dc.constrainY)) {
18484                 return oTarget.cursorIsOver;
18485             }
18486
18487             oTarget.overlap = null;
18488
18489             // Get the current location of the drag element, this is the
18490             // location of the mouse event less the delta that represents
18491             // where the original mousedown happened on the element.  We
18492             // need to consider constraints and ticks as well.
18493             var pos = dc.getTargetCoord(pt.x, pt.y);
18494
18495             var el = dc.getDragEl();
18496             var curRegion = new Roo.lib.Region( pos.y,
18497                                                    pos.x + el.offsetWidth,
18498                                                    pos.y + el.offsetHeight,
18499                                                    pos.x );
18500
18501             var overlap = curRegion.intersect(loc);
18502
18503             if (overlap) {
18504                 oTarget.overlap = overlap;
18505                 return (intersect) ? true : oTarget.cursorIsOver;
18506             } else {
18507                 return false;
18508             }
18509         },
18510
18511         /**
18512          * unload event handler
18513          * @method _onUnload
18514          * @private
18515          * @static
18516          */
18517         _onUnload: function(e, me) {
18518             Roo.dd.DragDropMgr.unregAll();
18519         },
18520
18521         /**
18522          * Cleans up the drag and drop events and objects.
18523          * @method unregAll
18524          * @private
18525          * @static
18526          */
18527         unregAll: function() {
18528
18529             if (this.dragCurrent) {
18530                 this.stopDrag();
18531                 this.dragCurrent = null;
18532             }
18533
18534             this._execOnAll("unreg", []);
18535
18536             for (i in this.elementCache) {
18537                 delete this.elementCache[i];
18538             }
18539
18540             this.elementCache = {};
18541             this.ids = {};
18542         },
18543
18544         /**
18545          * A cache of DOM elements
18546          * @property elementCache
18547          * @private
18548          * @static
18549          */
18550         elementCache: {},
18551
18552         /**
18553          * Get the wrapper for the DOM element specified
18554          * @method getElWrapper
18555          * @param {String} id the id of the element to get
18556          * @return {Roo.dd.DDM.ElementWrapper} the wrapped element
18557          * @private
18558          * @deprecated This wrapper isn't that useful
18559          * @static
18560          */
18561         getElWrapper: function(id) {
18562             var oWrapper = this.elementCache[id];
18563             if (!oWrapper || !oWrapper.el) {
18564                 oWrapper = this.elementCache[id] =
18565                     new this.ElementWrapper(Roo.getDom(id));
18566             }
18567             return oWrapper;
18568         },
18569
18570         /**
18571          * Returns the actual DOM element
18572          * @method getElement
18573          * @param {String} id the id of the elment to get
18574          * @return {Object} The element
18575          * @deprecated use Roo.getDom instead
18576          * @static
18577          */
18578         getElement: function(id) {
18579             return Roo.getDom(id);
18580         },
18581
18582         /**
18583          * Returns the style property for the DOM element (i.e.,
18584          * document.getElById(id).style)
18585          * @method getCss
18586          * @param {String} id the id of the elment to get
18587          * @return {Object} The style property of the element
18588          * @deprecated use Roo.getDom instead
18589          * @static
18590          */
18591         getCss: function(id) {
18592             var el = Roo.getDom(id);
18593             return (el) ? el.style : null;
18594         },
18595
18596         /**
18597          * Inner class for cached elements
18598          * @class DragDropMgr.ElementWrapper
18599          * @for DragDropMgr
18600          * @private
18601          * @deprecated
18602          */
18603         ElementWrapper: function(el) {
18604                 /**
18605                  * The element
18606                  * @property el
18607                  */
18608                 this.el = el || null;
18609                 /**
18610                  * The element id
18611                  * @property id
18612                  */
18613                 this.id = this.el && el.id;
18614                 /**
18615                  * A reference to the style property
18616                  * @property css
18617                  */
18618                 this.css = this.el && el.style;
18619             },
18620
18621         /**
18622          * Returns the X position of an html element
18623          * @method getPosX
18624          * @param el the element for which to get the position
18625          * @return {int} the X coordinate
18626          * @for DragDropMgr
18627          * @deprecated use Roo.lib.Dom.getX instead
18628          * @static
18629          */
18630         getPosX: function(el) {
18631             return Roo.lib.Dom.getX(el);
18632         },
18633
18634         /**
18635          * Returns the Y position of an html element
18636          * @method getPosY
18637          * @param el the element for which to get the position
18638          * @return {int} the Y coordinate
18639          * @deprecated use Roo.lib.Dom.getY instead
18640          * @static
18641          */
18642         getPosY: function(el) {
18643             return Roo.lib.Dom.getY(el);
18644         },
18645
18646         /**
18647          * Swap two nodes.  In IE, we use the native method, for others we
18648          * emulate the IE behavior
18649          * @method swapNode
18650          * @param n1 the first node to swap
18651          * @param n2 the other node to swap
18652          * @static
18653          */
18654         swapNode: function(n1, n2) {
18655             if (n1.swapNode) {
18656                 n1.swapNode(n2);
18657             } else {
18658                 var p = n2.parentNode;
18659                 var s = n2.nextSibling;
18660
18661                 if (s == n1) {
18662                     p.insertBefore(n1, n2);
18663                 } else if (n2 == n1.nextSibling) {
18664                     p.insertBefore(n2, n1);
18665                 } else {
18666                     n1.parentNode.replaceChild(n2, n1);
18667                     p.insertBefore(n1, s);
18668                 }
18669             }
18670         },
18671
18672         /**
18673          * Returns the current scroll position
18674          * @method getScroll
18675          * @private
18676          * @static
18677          */
18678         getScroll: function () {
18679             var t, l, dde=document.documentElement, db=document.body;
18680             if (dde && (dde.scrollTop || dde.scrollLeft)) {
18681                 t = dde.scrollTop;
18682                 l = dde.scrollLeft;
18683             } else if (db) {
18684                 t = db.scrollTop;
18685                 l = db.scrollLeft;
18686             } else {
18687
18688             }
18689             return { top: t, left: l };
18690         },
18691
18692         /**
18693          * Returns the specified element style property
18694          * @method getStyle
18695          * @param {HTMLElement} el          the element
18696          * @param {string}      styleProp   the style property
18697          * @return {string} The value of the style property
18698          * @deprecated use Roo.lib.Dom.getStyle
18699          * @static
18700          */
18701         getStyle: function(el, styleProp) {
18702             return Roo.fly(el).getStyle(styleProp);
18703         },
18704
18705         /**
18706          * Gets the scrollTop
18707          * @method getScrollTop
18708          * @return {int} the document's scrollTop
18709          * @static
18710          */
18711         getScrollTop: function () { return this.getScroll().top; },
18712
18713         /**
18714          * Gets the scrollLeft
18715          * @method getScrollLeft
18716          * @return {int} the document's scrollTop
18717          * @static
18718          */
18719         getScrollLeft: function () { return this.getScroll().left; },
18720
18721         /**
18722          * Sets the x/y position of an element to the location of the
18723          * target element.
18724          * @method moveToEl
18725          * @param {HTMLElement} moveEl      The element to move
18726          * @param {HTMLElement} targetEl    The position reference element
18727          * @static
18728          */
18729         moveToEl: function (moveEl, targetEl) {
18730             var aCoord = Roo.lib.Dom.getXY(targetEl);
18731             Roo.lib.Dom.setXY(moveEl, aCoord);
18732         },
18733
18734         /**
18735          * Numeric array sort function
18736          * @method numericSort
18737          * @static
18738          */
18739         numericSort: function(a, b) { return (a - b); },
18740
18741         /**
18742          * Internal counter
18743          * @property _timeoutCount
18744          * @private
18745          * @static
18746          */
18747         _timeoutCount: 0,
18748
18749         /**
18750          * Trying to make the load order less important.  Without this we get
18751          * an error if this file is loaded before the Event Utility.
18752          * @method _addListeners
18753          * @private
18754          * @static
18755          */
18756         _addListeners: function() {
18757             var DDM = Roo.dd.DDM;
18758             if ( Roo.lib.Event && document ) {
18759                 DDM._onLoad();
18760             } else {
18761                 if (DDM._timeoutCount > 2000) {
18762                 } else {
18763                     setTimeout(DDM._addListeners, 10);
18764                     if (document && document.body) {
18765                         DDM._timeoutCount += 1;
18766                     }
18767                 }
18768             }
18769         },
18770
18771         /**
18772          * Recursively searches the immediate parent and all child nodes for
18773          * the handle element in order to determine wheter or not it was
18774          * clicked.
18775          * @method handleWasClicked
18776          * @param node the html element to inspect
18777          * @static
18778          */
18779         handleWasClicked: function(node, id) {
18780             if (this.isHandle(id, node.id)) {
18781                 return true;
18782             } else {
18783                 // check to see if this is a text node child of the one we want
18784                 var p = node.parentNode;
18785
18786                 while (p) {
18787                     if (this.isHandle(id, p.id)) {
18788                         return true;
18789                     } else {
18790                         p = p.parentNode;
18791                     }
18792                 }
18793             }
18794
18795             return false;
18796         }
18797
18798     };
18799
18800 }();
18801
18802 // shorter alias, save a few bytes
18803 Roo.dd.DDM = Roo.dd.DragDropMgr;
18804 Roo.dd.DDM._addListeners();
18805
18806 }/*
18807  * Based on:
18808  * Ext JS Library 1.1.1
18809  * Copyright(c) 2006-2007, Ext JS, LLC.
18810  *
18811  * Originally Released Under LGPL - original licence link has changed is not relivant.
18812  *
18813  * Fork - LGPL
18814  * <script type="text/javascript">
18815  */
18816
18817 /**
18818  * @class Roo.dd.DD
18819  * A DragDrop implementation where the linked element follows the
18820  * mouse cursor during a drag.
18821  * @extends Roo.dd.DragDrop
18822  * @constructor
18823  * @param {String} id the id of the linked element
18824  * @param {String} sGroup the group of related DragDrop items
18825  * @param {object} config an object containing configurable attributes
18826  *                Valid properties for DD:
18827  *                    scroll
18828  */
18829 Roo.dd.DD = function(id, sGroup, config) {
18830     if (id) {
18831         this.init(id, sGroup, config);
18832     }
18833 };
18834
18835 Roo.extend(Roo.dd.DD, Roo.dd.DragDrop, {
18836
18837     /**
18838      * When set to true, the utility automatically tries to scroll the browser
18839      * window wehn a drag and drop element is dragged near the viewport boundary.
18840      * Defaults to true.
18841      * @property scroll
18842      * @type boolean
18843      */
18844     scroll: true,
18845
18846     /**
18847      * Sets the pointer offset to the distance between the linked element's top
18848      * left corner and the location the element was clicked
18849      * @method autoOffset
18850      * @param {int} iPageX the X coordinate of the click
18851      * @param {int} iPageY the Y coordinate of the click
18852      */
18853     autoOffset: function(iPageX, iPageY) {
18854         var x = iPageX - this.startPageX;
18855         var y = iPageY - this.startPageY;
18856         this.setDelta(x, y);
18857     },
18858
18859     /**
18860      * Sets the pointer offset.  You can call this directly to force the
18861      * offset to be in a particular location (e.g., pass in 0,0 to set it
18862      * to the center of the object)
18863      * @method setDelta
18864      * @param {int} iDeltaX the distance from the left
18865      * @param {int} iDeltaY the distance from the top
18866      */
18867     setDelta: function(iDeltaX, iDeltaY) {
18868         this.deltaX = iDeltaX;
18869         this.deltaY = iDeltaY;
18870     },
18871
18872     /**
18873      * Sets the drag element to the location of the mousedown or click event,
18874      * maintaining the cursor location relative to the location on the element
18875      * that was clicked.  Override this if you want to place the element in a
18876      * location other than where the cursor is.
18877      * @method setDragElPos
18878      * @param {int} iPageX the X coordinate of the mousedown or drag event
18879      * @param {int} iPageY the Y coordinate of the mousedown or drag event
18880      */
18881     setDragElPos: function(iPageX, iPageY) {
18882         // the first time we do this, we are going to check to make sure
18883         // the element has css positioning
18884
18885         var el = this.getDragEl();
18886         this.alignElWithMouse(el, iPageX, iPageY);
18887     },
18888
18889     /**
18890      * Sets the element to the location of the mousedown or click event,
18891      * maintaining the cursor location relative to the location on the element
18892      * that was clicked.  Override this if you want to place the element in a
18893      * location other than where the cursor is.
18894      * @method alignElWithMouse
18895      * @param {HTMLElement} el the element to move
18896      * @param {int} iPageX the X coordinate of the mousedown or drag event
18897      * @param {int} iPageY the Y coordinate of the mousedown or drag event
18898      */
18899     alignElWithMouse: function(el, iPageX, iPageY) {
18900         var oCoord = this.getTargetCoord(iPageX, iPageY);
18901         var fly = el.dom ? el : Roo.fly(el);
18902         if (!this.deltaSetXY) {
18903             var aCoord = [oCoord.x, oCoord.y];
18904             fly.setXY(aCoord);
18905             var newLeft = fly.getLeft(true);
18906             var newTop  = fly.getTop(true);
18907             this.deltaSetXY = [ newLeft - oCoord.x, newTop - oCoord.y ];
18908         } else {
18909             fly.setLeftTop(oCoord.x + this.deltaSetXY[0], oCoord.y + this.deltaSetXY[1]);
18910         }
18911
18912         this.cachePosition(oCoord.x, oCoord.y);
18913         this.autoScroll(oCoord.x, oCoord.y, el.offsetHeight, el.offsetWidth);
18914         return oCoord;
18915     },
18916
18917     /**
18918      * Saves the most recent position so that we can reset the constraints and
18919      * tick marks on-demand.  We need to know this so that we can calculate the
18920      * number of pixels the element is offset from its original position.
18921      * @method cachePosition
18922      * @param iPageX the current x position (optional, this just makes it so we
18923      * don't have to look it up again)
18924      * @param iPageY the current y position (optional, this just makes it so we
18925      * don't have to look it up again)
18926      */
18927     cachePosition: function(iPageX, iPageY) {
18928         if (iPageX) {
18929             this.lastPageX = iPageX;
18930             this.lastPageY = iPageY;
18931         } else {
18932             var aCoord = Roo.lib.Dom.getXY(this.getEl());
18933             this.lastPageX = aCoord[0];
18934             this.lastPageY = aCoord[1];
18935         }
18936     },
18937
18938     /**
18939      * Auto-scroll the window if the dragged object has been moved beyond the
18940      * visible window boundary.
18941      * @method autoScroll
18942      * @param {int} x the drag element's x position
18943      * @param {int} y the drag element's y position
18944      * @param {int} h the height of the drag element
18945      * @param {int} w the width of the drag element
18946      * @private
18947      */
18948     autoScroll: function(x, y, h, w) {
18949
18950         if (this.scroll) {
18951             // The client height
18952             var clientH = Roo.lib.Dom.getViewWidth();
18953
18954             // The client width
18955             var clientW = Roo.lib.Dom.getViewHeight();
18956
18957             // The amt scrolled down
18958             var st = this.DDM.getScrollTop();
18959
18960             // The amt scrolled right
18961             var sl = this.DDM.getScrollLeft();
18962
18963             // Location of the bottom of the element
18964             var bot = h + y;
18965
18966             // Location of the right of the element
18967             var right = w + x;
18968
18969             // The distance from the cursor to the bottom of the visible area,
18970             // adjusted so that we don't scroll if the cursor is beyond the
18971             // element drag constraints
18972             var toBot = (clientH + st - y - this.deltaY);
18973
18974             // The distance from the cursor to the right of the visible area
18975             var toRight = (clientW + sl - x - this.deltaX);
18976
18977
18978             // How close to the edge the cursor must be before we scroll
18979             // var thresh = (document.all) ? 100 : 40;
18980             var thresh = 40;
18981
18982             // How many pixels to scroll per autoscroll op.  This helps to reduce
18983             // clunky scrolling. IE is more sensitive about this ... it needs this
18984             // value to be higher.
18985             var scrAmt = (document.all) ? 80 : 30;
18986
18987             // Scroll down if we are near the bottom of the visible page and the
18988             // obj extends below the crease
18989             if ( bot > clientH && toBot < thresh ) {
18990                 window.scrollTo(sl, st + scrAmt);
18991             }
18992
18993             // Scroll up if the window is scrolled down and the top of the object
18994             // goes above the top border
18995             if ( y < st && st > 0 && y - st < thresh ) {
18996                 window.scrollTo(sl, st - scrAmt);
18997             }
18998
18999             // Scroll right if the obj is beyond the right border and the cursor is
19000             // near the border.
19001             if ( right > clientW && toRight < thresh ) {
19002                 window.scrollTo(sl + scrAmt, st);
19003             }
19004
19005             // Scroll left if the window has been scrolled to the right and the obj
19006             // extends past the left border
19007             if ( x < sl && sl > 0 && x - sl < thresh ) {
19008                 window.scrollTo(sl - scrAmt, st);
19009             }
19010         }
19011     },
19012
19013     /**
19014      * Finds the location the element should be placed if we want to move
19015      * it to where the mouse location less the click offset would place us.
19016      * @method getTargetCoord
19017      * @param {int} iPageX the X coordinate of the click
19018      * @param {int} iPageY the Y coordinate of the click
19019      * @return an object that contains the coordinates (Object.x and Object.y)
19020      * @private
19021      */
19022     getTargetCoord: function(iPageX, iPageY) {
19023
19024
19025         var x = iPageX - this.deltaX;
19026         var y = iPageY - this.deltaY;
19027
19028         if (this.constrainX) {
19029             if (x < this.minX) { x = this.minX; }
19030             if (x > this.maxX) { x = this.maxX; }
19031         }
19032
19033         if (this.constrainY) {
19034             if (y < this.minY) { y = this.minY; }
19035             if (y > this.maxY) { y = this.maxY; }
19036         }
19037
19038         x = this.getTick(x, this.xTicks);
19039         y = this.getTick(y, this.yTicks);
19040
19041
19042         return {x:x, y:y};
19043     },
19044
19045     /*
19046      * Sets up config options specific to this class. Overrides
19047      * Roo.dd.DragDrop, but all versions of this method through the
19048      * inheritance chain are called
19049      */
19050     applyConfig: function() {
19051         Roo.dd.DD.superclass.applyConfig.call(this);
19052         this.scroll = (this.config.scroll !== false);
19053     },
19054
19055     /*
19056      * Event that fires prior to the onMouseDown event.  Overrides
19057      * Roo.dd.DragDrop.
19058      */
19059     b4MouseDown: function(e) {
19060         // this.resetConstraints();
19061         this.autoOffset(e.getPageX(),
19062                             e.getPageY());
19063     },
19064
19065     /*
19066      * Event that fires prior to the onDrag event.  Overrides
19067      * Roo.dd.DragDrop.
19068      */
19069     b4Drag: function(e) {
19070         this.setDragElPos(e.getPageX(),
19071                             e.getPageY());
19072     },
19073
19074     toString: function() {
19075         return ("DD " + this.id);
19076     }
19077
19078     //////////////////////////////////////////////////////////////////////////
19079     // Debugging ygDragDrop events that can be overridden
19080     //////////////////////////////////////////////////////////////////////////
19081     /*
19082     startDrag: function(x, y) {
19083     },
19084
19085     onDrag: function(e) {
19086     },
19087
19088     onDragEnter: function(e, id) {
19089     },
19090
19091     onDragOver: function(e, id) {
19092     },
19093
19094     onDragOut: function(e, id) {
19095     },
19096
19097     onDragDrop: function(e, id) {
19098     },
19099
19100     endDrag: function(e) {
19101     }
19102
19103     */
19104
19105 });/*
19106  * Based on:
19107  * Ext JS Library 1.1.1
19108  * Copyright(c) 2006-2007, Ext JS, LLC.
19109  *
19110  * Originally Released Under LGPL - original licence link has changed is not relivant.
19111  *
19112  * Fork - LGPL
19113  * <script type="text/javascript">
19114  */
19115
19116 /**
19117  * @class Roo.dd.DDProxy
19118  * A DragDrop implementation that inserts an empty, bordered div into
19119  * the document that follows the cursor during drag operations.  At the time of
19120  * the click, the frame div is resized to the dimensions of the linked html
19121  * element, and moved to the exact location of the linked element.
19122  *
19123  * References to the "frame" element refer to the single proxy element that
19124  * was created to be dragged in place of all DDProxy elements on the
19125  * page.
19126  *
19127  * @extends Roo.dd.DD
19128  * @constructor
19129  * @param {String} id the id of the linked html element
19130  * @param {String} sGroup the group of related DragDrop objects
19131  * @param {object} config an object containing configurable attributes
19132  *                Valid properties for DDProxy in addition to those in DragDrop:
19133  *                   resizeFrame, centerFrame, dragElId
19134  */
19135 Roo.dd.DDProxy = function(id, sGroup, config) {
19136     if (id) {
19137         this.init(id, sGroup, config);
19138         this.initFrame();
19139     }
19140 };
19141
19142 /**
19143  * The default drag frame div id
19144  * @property Roo.dd.DDProxy.dragElId
19145  * @type String
19146  * @static
19147  */
19148 Roo.dd.DDProxy.dragElId = "ygddfdiv";
19149
19150 Roo.extend(Roo.dd.DDProxy, Roo.dd.DD, {
19151
19152     /**
19153      * By default we resize the drag frame to be the same size as the element
19154      * we want to drag (this is to get the frame effect).  We can turn it off
19155      * if we want a different behavior.
19156      * @property resizeFrame
19157      * @type boolean
19158      */
19159     resizeFrame: true,
19160
19161     /**
19162      * By default the frame is positioned exactly where the drag element is, so
19163      * we use the cursor offset provided by Roo.dd.DD.  Another option that works only if
19164      * you do not have constraints on the obj is to have the drag frame centered
19165      * around the cursor.  Set centerFrame to true for this effect.
19166      * @property centerFrame
19167      * @type boolean
19168      */
19169     centerFrame: false,
19170
19171     /**
19172      * Creates the proxy element if it does not yet exist
19173      * @method createFrame
19174      */
19175     createFrame: function() {
19176         var self = this;
19177         var body = document.body;
19178
19179         if (!body || !body.firstChild) {
19180             setTimeout( function() { self.createFrame(); }, 50 );
19181             return;
19182         }
19183
19184         var div = this.getDragEl();
19185
19186         if (!div) {
19187             div    = document.createElement("div");
19188             div.id = this.dragElId;
19189             var s  = div.style;
19190
19191             s.position   = "absolute";
19192             s.visibility = "hidden";
19193             s.cursor     = "move";
19194             s.border     = "2px solid #aaa";
19195             s.zIndex     = 999;
19196
19197             // appendChild can blow up IE if invoked prior to the window load event
19198             // while rendering a table.  It is possible there are other scenarios
19199             // that would cause this to happen as well.
19200             body.insertBefore(div, body.firstChild);
19201         }
19202     },
19203
19204     /**
19205      * Initialization for the drag frame element.  Must be called in the
19206      * constructor of all subclasses
19207      * @method initFrame
19208      */
19209     initFrame: function() {
19210         this.createFrame();
19211     },
19212
19213     applyConfig: function() {
19214         Roo.dd.DDProxy.superclass.applyConfig.call(this);
19215
19216         this.resizeFrame = (this.config.resizeFrame !== false);
19217         this.centerFrame = (this.config.centerFrame);
19218         this.setDragElId(this.config.dragElId || Roo.dd.DDProxy.dragElId);
19219     },
19220
19221     /**
19222      * Resizes the drag frame to the dimensions of the clicked object, positions
19223      * it over the object, and finally displays it
19224      * @method showFrame
19225      * @param {int} iPageX X click position
19226      * @param {int} iPageY Y click position
19227      * @private
19228      */
19229     showFrame: function(iPageX, iPageY) {
19230         var el = this.getEl();
19231         var dragEl = this.getDragEl();
19232         var s = dragEl.style;
19233
19234         this._resizeProxy();
19235
19236         if (this.centerFrame) {
19237             this.setDelta( Math.round(parseInt(s.width,  10)/2),
19238                            Math.round(parseInt(s.height, 10)/2) );
19239         }
19240
19241         this.setDragElPos(iPageX, iPageY);
19242
19243         Roo.fly(dragEl).show();
19244     },
19245
19246     /**
19247      * The proxy is automatically resized to the dimensions of the linked
19248      * element when a drag is initiated, unless resizeFrame is set to false
19249      * @method _resizeProxy
19250      * @private
19251      */
19252     _resizeProxy: function() {
19253         if (this.resizeFrame) {
19254             var el = this.getEl();
19255             Roo.fly(this.getDragEl()).setSize(el.offsetWidth, el.offsetHeight);
19256         }
19257     },
19258
19259     // overrides Roo.dd.DragDrop
19260     b4MouseDown: function(e) {
19261         var x = e.getPageX();
19262         var y = e.getPageY();
19263         this.autoOffset(x, y);
19264         this.setDragElPos(x, y);
19265     },
19266
19267     // overrides Roo.dd.DragDrop
19268     b4StartDrag: function(x, y) {
19269         // show the drag frame
19270         this.showFrame(x, y);
19271     },
19272
19273     // overrides Roo.dd.DragDrop
19274     b4EndDrag: function(e) {
19275         Roo.fly(this.getDragEl()).hide();
19276     },
19277
19278     // overrides Roo.dd.DragDrop
19279     // By default we try to move the element to the last location of the frame.
19280     // This is so that the default behavior mirrors that of Roo.dd.DD.
19281     endDrag: function(e) {
19282
19283         var lel = this.getEl();
19284         var del = this.getDragEl();
19285
19286         // Show the drag frame briefly so we can get its position
19287         del.style.visibility = "";
19288
19289         this.beforeMove();
19290         // Hide the linked element before the move to get around a Safari
19291         // rendering bug.
19292         lel.style.visibility = "hidden";
19293         Roo.dd.DDM.moveToEl(lel, del);
19294         del.style.visibility = "hidden";
19295         lel.style.visibility = "";
19296
19297         this.afterDrag();
19298     },
19299
19300     beforeMove : function(){
19301
19302     },
19303
19304     afterDrag : function(){
19305
19306     },
19307
19308     toString: function() {
19309         return ("DDProxy " + this.id);
19310     }
19311
19312 });
19313 /*
19314  * Based on:
19315  * Ext JS Library 1.1.1
19316  * Copyright(c) 2006-2007, Ext JS, LLC.
19317  *
19318  * Originally Released Under LGPL - original licence link has changed is not relivant.
19319  *
19320  * Fork - LGPL
19321  * <script type="text/javascript">
19322  */
19323
19324  /**
19325  * @class Roo.dd.DDTarget
19326  * A DragDrop implementation that does not move, but can be a drop
19327  * target.  You would get the same result by simply omitting implementation
19328  * for the event callbacks, but this way we reduce the processing cost of the
19329  * event listener and the callbacks.
19330  * @extends Roo.dd.DragDrop
19331  * @constructor
19332  * @param {String} id the id of the element that is a drop target
19333  * @param {String} sGroup the group of related DragDrop objects
19334  * @param {object} config an object containing configurable attributes
19335  *                 Valid properties for DDTarget in addition to those in
19336  *                 DragDrop:
19337  *                    none
19338  */
19339 Roo.dd.DDTarget = function(id, sGroup, config) {
19340     if (id) {
19341         this.initTarget(id, sGroup, config);
19342     }
19343     if (config.listeners || config.events) { 
19344        Roo.dd.DragDrop.superclass.constructor.call(this,  { 
19345             listeners : config.listeners || {}, 
19346             events : config.events || {} 
19347         });    
19348     }
19349 };
19350
19351 // Roo.dd.DDTarget.prototype = new Roo.dd.DragDrop();
19352 Roo.extend(Roo.dd.DDTarget, Roo.dd.DragDrop, {
19353     toString: function() {
19354         return ("DDTarget " + this.id);
19355     }
19356 });
19357 /*
19358  * Based on:
19359  * Ext JS Library 1.1.1
19360  * Copyright(c) 2006-2007, Ext JS, LLC.
19361  *
19362  * Originally Released Under LGPL - original licence link has changed is not relivant.
19363  *
19364  * Fork - LGPL
19365  * <script type="text/javascript">
19366  */
19367  
19368
19369 /**
19370  * @class Roo.dd.ScrollManager
19371  * Provides automatic scrolling of overflow regions in the page during drag operations.<br><br>
19372  * <b>Note: This class uses "Point Mode" and is untested in "Intersect Mode".</b>
19373  * @singleton
19374  */
19375 Roo.dd.ScrollManager = function(){
19376     var ddm = Roo.dd.DragDropMgr;
19377     var els = {};
19378     var dragEl = null;
19379     var proc = {};
19380     
19381     
19382     
19383     var onStop = function(e){
19384         dragEl = null;
19385         clearProc();
19386     };
19387     
19388     var triggerRefresh = function(){
19389         if(ddm.dragCurrent){
19390              ddm.refreshCache(ddm.dragCurrent.groups);
19391         }
19392     };
19393     
19394     var doScroll = function(){
19395         if(ddm.dragCurrent){
19396             var dds = Roo.dd.ScrollManager;
19397             if(!dds.animate){
19398                 if(proc.el.scroll(proc.dir, dds.increment)){
19399                     triggerRefresh();
19400                 }
19401             }else{
19402                 proc.el.scroll(proc.dir, dds.increment, true, dds.animDuration, triggerRefresh);
19403             }
19404         }
19405     };
19406     
19407     var clearProc = function(){
19408         if(proc.id){
19409             clearInterval(proc.id);
19410         }
19411         proc.id = 0;
19412         proc.el = null;
19413         proc.dir = "";
19414     };
19415     
19416     var startProc = function(el, dir){
19417          Roo.log('scroll startproc');
19418         clearProc();
19419         proc.el = el;
19420         proc.dir = dir;
19421         proc.id = setInterval(doScroll, Roo.dd.ScrollManager.frequency);
19422     };
19423     
19424     var onFire = function(e, isDrop){
19425        
19426         if(isDrop || !ddm.dragCurrent){ return; }
19427         var dds = Roo.dd.ScrollManager;
19428         if(!dragEl || dragEl != ddm.dragCurrent){
19429             dragEl = ddm.dragCurrent;
19430             // refresh regions on drag start
19431             dds.refreshCache();
19432         }
19433         
19434         var xy = Roo.lib.Event.getXY(e);
19435         var pt = new Roo.lib.Point(xy[0], xy[1]);
19436         for(var id in els){
19437             var el = els[id], r = el._region;
19438             if(r && r.contains(pt) && el.isScrollable()){
19439                 if(r.bottom - pt.y <= dds.thresh){
19440                     if(proc.el != el){
19441                         startProc(el, "down");
19442                     }
19443                     return;
19444                 }else if(r.right - pt.x <= dds.thresh){
19445                     if(proc.el != el){
19446                         startProc(el, "left");
19447                     }
19448                     return;
19449                 }else if(pt.y - r.top <= dds.thresh){
19450                     if(proc.el != el){
19451                         startProc(el, "up");
19452                     }
19453                     return;
19454                 }else if(pt.x - r.left <= dds.thresh){
19455                     if(proc.el != el){
19456                         startProc(el, "right");
19457                     }
19458                     return;
19459                 }
19460             }
19461         }
19462         clearProc();
19463     };
19464     
19465     ddm.fireEvents = ddm.fireEvents.createSequence(onFire, ddm);
19466     ddm.stopDrag = ddm.stopDrag.createSequence(onStop, ddm);
19467     
19468     return {
19469         /**
19470          * Registers new overflow element(s) to auto scroll
19471          * @param {String/HTMLElement/Element/Array} el The id of or the element to be scrolled or an array of either
19472          */
19473         register : function(el){
19474             if(el instanceof Array){
19475                 for(var i = 0, len = el.length; i < len; i++) {
19476                         this.register(el[i]);
19477                 }
19478             }else{
19479                 el = Roo.get(el);
19480                 els[el.id] = el;
19481             }
19482             Roo.dd.ScrollManager.els = els;
19483         },
19484         
19485         /**
19486          * Unregisters overflow element(s) so they are no longer scrolled
19487          * @param {String/HTMLElement/Element/Array} el The id of or the element to be removed or an array of either
19488          */
19489         unregister : function(el){
19490             if(el instanceof Array){
19491                 for(var i = 0, len = el.length; i < len; i++) {
19492                         this.unregister(el[i]);
19493                 }
19494             }else{
19495                 el = Roo.get(el);
19496                 delete els[el.id];
19497             }
19498         },
19499         
19500         /**
19501          * The number of pixels from the edge of a container the pointer needs to be to 
19502          * trigger scrolling (defaults to 25)
19503          * @type Number
19504          */
19505         thresh : 25,
19506         
19507         /**
19508          * The number of pixels to scroll in each scroll increment (defaults to 50)
19509          * @type Number
19510          */
19511         increment : 100,
19512         
19513         /**
19514          * The frequency of scrolls in milliseconds (defaults to 500)
19515          * @type Number
19516          */
19517         frequency : 500,
19518         
19519         /**
19520          * True to animate the scroll (defaults to true)
19521          * @type Boolean
19522          */
19523         animate: true,
19524         
19525         /**
19526          * The animation duration in seconds - 
19527          * MUST BE less than Roo.dd.ScrollManager.frequency! (defaults to .4)
19528          * @type Number
19529          */
19530         animDuration: .4,
19531         
19532         /**
19533          * Manually trigger a cache refresh.
19534          */
19535         refreshCache : function(){
19536             for(var id in els){
19537                 if(typeof els[id] == 'object'){ // for people extending the object prototype
19538                     els[id]._region = els[id].getRegion();
19539                 }
19540             }
19541         }
19542     };
19543 }();/*
19544  * Based on:
19545  * Ext JS Library 1.1.1
19546  * Copyright(c) 2006-2007, Ext JS, LLC.
19547  *
19548  * Originally Released Under LGPL - original licence link has changed is not relivant.
19549  *
19550  * Fork - LGPL
19551  * <script type="text/javascript">
19552  */
19553  
19554
19555 /**
19556  * @class Roo.dd.Registry
19557  * Provides easy access to all drag drop components that are registered on a page.  Items can be retrieved either
19558  * directly by DOM node id, or by passing in the drag drop event that occurred and looking up the event target.
19559  * @singleton
19560  */
19561 Roo.dd.Registry = function(){
19562     var elements = {}; 
19563     var handles = {}; 
19564     var autoIdSeed = 0;
19565
19566     var getId = function(el, autogen){
19567         if(typeof el == "string"){
19568             return el;
19569         }
19570         var id = el.id;
19571         if(!id && autogen !== false){
19572             id = "roodd-" + (++autoIdSeed);
19573             el.id = id;
19574         }
19575         return id;
19576     };
19577     
19578     return {
19579     /**
19580      * Register a drag drop element
19581      * @param {String|HTMLElement} element The id or DOM node to register
19582      * @param {Object} data (optional) A custom data object that will be passed between the elements that are involved
19583      * in drag drop operations.  You can populate this object with any arbitrary properties that your own code
19584      * knows how to interpret, plus there are some specific properties known to the Registry that should be
19585      * populated in the data object (if applicable):
19586      * <pre>
19587 Value      Description<br />
19588 ---------  ------------------------------------------<br />
19589 handles    Array of DOM nodes that trigger dragging<br />
19590            for the element being registered<br />
19591 isHandle   True if the element passed in triggers<br />
19592            dragging itself, else false
19593 </pre>
19594      */
19595         register : function(el, data){
19596             data = data || {};
19597             if(typeof el == "string"){
19598                 el = document.getElementById(el);
19599             }
19600             data.ddel = el;
19601             elements[getId(el)] = data;
19602             if(data.isHandle !== false){
19603                 handles[data.ddel.id] = data;
19604             }
19605             if(data.handles){
19606                 var hs = data.handles;
19607                 for(var i = 0, len = hs.length; i < len; i++){
19608                         handles[getId(hs[i])] = data;
19609                 }
19610             }
19611         },
19612
19613     /**
19614      * Unregister a drag drop element
19615      * @param {String|HTMLElement}  element The id or DOM node to unregister
19616      */
19617         unregister : function(el){
19618             var id = getId(el, false);
19619             var data = elements[id];
19620             if(data){
19621                 delete elements[id];
19622                 if(data.handles){
19623                     var hs = data.handles;
19624                     for(var i = 0, len = hs.length; i < len; i++){
19625                         delete handles[getId(hs[i], false)];
19626                     }
19627                 }
19628             }
19629         },
19630
19631     /**
19632      * Returns the handle registered for a DOM Node by id
19633      * @param {String|HTMLElement} id The DOM node or id to look up
19634      * @return {Object} handle The custom handle data
19635      */
19636         getHandle : function(id){
19637             if(typeof id != "string"){ // must be element?
19638                 id = id.id;
19639             }
19640             return handles[id];
19641         },
19642
19643     /**
19644      * Returns the handle that is registered for the DOM node that is the target of the event
19645      * @param {Event} e The event
19646      * @return {Object} handle The custom handle data
19647      */
19648         getHandleFromEvent : function(e){
19649             var t = Roo.lib.Event.getTarget(e);
19650             return t ? handles[t.id] : null;
19651         },
19652
19653     /**
19654      * Returns a custom data object that is registered for a DOM node by id
19655      * @param {String|HTMLElement} id The DOM node or id to look up
19656      * @return {Object} data The custom data
19657      */
19658         getTarget : function(id){
19659             if(typeof id != "string"){ // must be element?
19660                 id = id.id;
19661             }
19662             return elements[id];
19663         },
19664
19665     /**
19666      * Returns a custom data object that is registered for the DOM node that is the target of the event
19667      * @param {Event} e The event
19668      * @return {Object} data The custom data
19669      */
19670         getTargetFromEvent : function(e){
19671             var t = Roo.lib.Event.getTarget(e);
19672             return t ? elements[t.id] || handles[t.id] : null;
19673         }
19674     };
19675 }();/*
19676  * Based on:
19677  * Ext JS Library 1.1.1
19678  * Copyright(c) 2006-2007, Ext JS, LLC.
19679  *
19680  * Originally Released Under LGPL - original licence link has changed is not relivant.
19681  *
19682  * Fork - LGPL
19683  * <script type="text/javascript">
19684  */
19685  
19686
19687 /**
19688  * @class Roo.dd.StatusProxy
19689  * A specialized drag proxy that supports a drop status icon, {@link Roo.Layer} styles and auto-repair.  This is the
19690  * default drag proxy used by all Roo.dd components.
19691  * @constructor
19692  * @param {Object} config
19693  */
19694 Roo.dd.StatusProxy = function(config){
19695     Roo.apply(this, config);
19696     this.id = this.id || Roo.id();
19697     this.el = new Roo.Layer({
19698         dh: {
19699             id: this.id, tag: "div", cls: "x-dd-drag-proxy "+this.dropNotAllowed, children: [
19700                 {tag: "div", cls: "x-dd-drop-icon"},
19701                 {tag: "div", cls: "x-dd-drag-ghost"}
19702             ]
19703         }, 
19704         shadow: !config || config.shadow !== false
19705     });
19706     this.ghost = Roo.get(this.el.dom.childNodes[1]);
19707     this.dropStatus = this.dropNotAllowed;
19708 };
19709
19710 Roo.dd.StatusProxy.prototype = {
19711     /**
19712      * @cfg {String} dropAllowed
19713      * The CSS class to apply to the status element when drop is allowed (defaults to "x-dd-drop-ok").
19714      */
19715     dropAllowed : "x-dd-drop-ok",
19716     /**
19717      * @cfg {String} dropNotAllowed
19718      * The CSS class to apply to the status element when drop is not allowed (defaults to "x-dd-drop-nodrop").
19719      */
19720     dropNotAllowed : "x-dd-drop-nodrop",
19721
19722     /**
19723      * Updates the proxy's visual element to indicate the status of whether or not drop is allowed
19724      * over the current target element.
19725      * @param {String} cssClass The css class for the new drop status indicator image
19726      */
19727     setStatus : function(cssClass){
19728         cssClass = cssClass || this.dropNotAllowed;
19729         if(this.dropStatus != cssClass){
19730             this.el.replaceClass(this.dropStatus, cssClass);
19731             this.dropStatus = cssClass;
19732         }
19733     },
19734
19735     /**
19736      * Resets the status indicator to the default dropNotAllowed value
19737      * @param {Boolean} clearGhost True to also remove all content from the ghost, false to preserve it
19738      */
19739     reset : function(clearGhost){
19740         this.el.dom.className = "x-dd-drag-proxy " + this.dropNotAllowed;
19741         this.dropStatus = this.dropNotAllowed;
19742         if(clearGhost){
19743             this.ghost.update("");
19744         }
19745     },
19746
19747     /**
19748      * Updates the contents of the ghost element
19749      * @param {String} html The html that will replace the current innerHTML of the ghost element
19750      */
19751     update : function(html){
19752         if(typeof html == "string"){
19753             this.ghost.update(html);
19754         }else{
19755             this.ghost.update("");
19756             html.style.margin = "0";
19757             this.ghost.dom.appendChild(html);
19758         }
19759         // ensure float = none set?? cant remember why though.
19760         var el = this.ghost.dom.firstChild;
19761                 if(el){
19762                         Roo.fly(el).setStyle('float', 'none');
19763                 }
19764     },
19765     
19766     /**
19767      * Returns the underlying proxy {@link Roo.Layer}
19768      * @return {Roo.Layer} el
19769     */
19770     getEl : function(){
19771         return this.el;
19772     },
19773
19774     /**
19775      * Returns the ghost element
19776      * @return {Roo.Element} el
19777      */
19778     getGhost : function(){
19779         return this.ghost;
19780     },
19781
19782     /**
19783      * Hides the proxy
19784      * @param {Boolean} clear True to reset the status and clear the ghost contents, false to preserve them
19785      */
19786     hide : function(clear){
19787         this.el.hide();
19788         if(clear){
19789             this.reset(true);
19790         }
19791     },
19792
19793     /**
19794      * Stops the repair animation if it's currently running
19795      */
19796     stop : function(){
19797         if(this.anim && this.anim.isAnimated && this.anim.isAnimated()){
19798             this.anim.stop();
19799         }
19800     },
19801
19802     /**
19803      * Displays this proxy
19804      */
19805     show : function(){
19806         this.el.show();
19807     },
19808
19809     /**
19810      * Force the Layer to sync its shadow and shim positions to the element
19811      */
19812     sync : function(){
19813         this.el.sync();
19814     },
19815
19816     /**
19817      * Causes the proxy to return to its position of origin via an animation.  Should be called after an
19818      * invalid drop operation by the item being dragged.
19819      * @param {Array} xy The XY position of the element ([x, y])
19820      * @param {Function} callback The function to call after the repair is complete
19821      * @param {Object} scope The scope in which to execute the callback
19822      */
19823     repair : function(xy, callback, scope){
19824         this.callback = callback;
19825         this.scope = scope;
19826         if(xy && this.animRepair !== false){
19827             this.el.addClass("x-dd-drag-repair");
19828             this.el.hideUnders(true);
19829             this.anim = this.el.shift({
19830                 duration: this.repairDuration || .5,
19831                 easing: 'easeOut',
19832                 xy: xy,
19833                 stopFx: true,
19834                 callback: this.afterRepair,
19835                 scope: this
19836             });
19837         }else{
19838             this.afterRepair();
19839         }
19840     },
19841
19842     // private
19843     afterRepair : function(){
19844         this.hide(true);
19845         if(typeof this.callback == "function"){
19846             this.callback.call(this.scope || this);
19847         }
19848         this.callback = null;
19849         this.scope = null;
19850     }
19851 };/*
19852  * Based on:
19853  * Ext JS Library 1.1.1
19854  * Copyright(c) 2006-2007, Ext JS, LLC.
19855  *
19856  * Originally Released Under LGPL - original licence link has changed is not relivant.
19857  *
19858  * Fork - LGPL
19859  * <script type="text/javascript">
19860  */
19861
19862 /**
19863  * @class Roo.dd.DragSource
19864  * @extends Roo.dd.DDProxy
19865  * A simple class that provides the basic implementation needed to make any element draggable.
19866  * @constructor
19867  * @param {String/HTMLElement/Element} el The container element
19868  * @param {Object} config
19869  */
19870 Roo.dd.DragSource = function(el, config){
19871     this.el = Roo.get(el);
19872     this.dragData = {};
19873     
19874     Roo.apply(this, config);
19875     
19876     if(!this.proxy){
19877         this.proxy = new Roo.dd.StatusProxy();
19878     }
19879
19880     Roo.dd.DragSource.superclass.constructor.call(this, this.el.dom, this.ddGroup || this.group,
19881           {dragElId : this.proxy.id, resizeFrame: false, isTarget: false, scroll: this.scroll === true});
19882     
19883     this.dragging = false;
19884 };
19885
19886 Roo.extend(Roo.dd.DragSource, Roo.dd.DDProxy, {
19887     /**
19888      * @cfg {String} dropAllowed
19889      * The CSS class returned to the drag source when drop is allowed (defaults to "x-dd-drop-ok").
19890      */
19891     dropAllowed : "x-dd-drop-ok",
19892     /**
19893      * @cfg {String} dropNotAllowed
19894      * The CSS class returned to the drag source when drop is not allowed (defaults to "x-dd-drop-nodrop").
19895      */
19896     dropNotAllowed : "x-dd-drop-nodrop",
19897
19898     /**
19899      * Returns the data object associated with this drag source
19900      * @return {Object} data An object containing arbitrary data
19901      */
19902     getDragData : function(e){
19903         return this.dragData;
19904     },
19905
19906     // private
19907     onDragEnter : function(e, id){
19908         var target = Roo.dd.DragDropMgr.getDDById(id);
19909         this.cachedTarget = target;
19910         if(this.beforeDragEnter(target, e, id) !== false){
19911             if(target.isNotifyTarget){
19912                 var status = target.notifyEnter(this, e, this.dragData);
19913                 this.proxy.setStatus(status);
19914             }else{
19915                 this.proxy.setStatus(this.dropAllowed);
19916             }
19917             
19918             if(this.afterDragEnter){
19919                 /**
19920                  * An empty function by default, but provided so that you can perform a custom action
19921                  * when the dragged item enters the drop target by providing an implementation.
19922                  * @param {Roo.dd.DragDrop} target The drop target
19923                  * @param {Event} e The event object
19924                  * @param {String} id The id of the dragged element
19925                  * @method afterDragEnter
19926                  */
19927                 this.afterDragEnter(target, e, id);
19928             }
19929         }
19930     },
19931
19932     /**
19933      * An empty function by default, but provided so that you can perform a custom action
19934      * before the dragged item enters the drop target and optionally cancel the onDragEnter.
19935      * @param {Roo.dd.DragDrop} target The drop target
19936      * @param {Event} e The event object
19937      * @param {String} id The id of the dragged element
19938      * @return {Boolean} isValid True if the drag event is valid, else false to cancel
19939      */
19940     beforeDragEnter : function(target, e, id){
19941         return true;
19942     },
19943
19944     // private
19945     alignElWithMouse: function() {
19946         Roo.dd.DragSource.superclass.alignElWithMouse.apply(this, arguments);
19947         this.proxy.sync();
19948     },
19949
19950     // private
19951     onDragOver : function(e, id){
19952         var target = this.cachedTarget || Roo.dd.DragDropMgr.getDDById(id);
19953         if(this.beforeDragOver(target, e, id) !== false){
19954             if(target.isNotifyTarget){
19955                 var status = target.notifyOver(this, e, this.dragData);
19956                 this.proxy.setStatus(status);
19957             }
19958
19959             if(this.afterDragOver){
19960                 /**
19961                  * An empty function by default, but provided so that you can perform a custom action
19962                  * while the dragged item is over the drop target by providing an implementation.
19963                  * @param {Roo.dd.DragDrop} target The drop target
19964                  * @param {Event} e The event object
19965                  * @param {String} id The id of the dragged element
19966                  * @method afterDragOver
19967                  */
19968                 this.afterDragOver(target, e, id);
19969             }
19970         }
19971     },
19972
19973     /**
19974      * An empty function by default, but provided so that you can perform a custom action
19975      * while the dragged item is over the drop target and optionally cancel the onDragOver.
19976      * @param {Roo.dd.DragDrop} target The drop target
19977      * @param {Event} e The event object
19978      * @param {String} id The id of the dragged element
19979      * @return {Boolean} isValid True if the drag event is valid, else false to cancel
19980      */
19981     beforeDragOver : function(target, e, id){
19982         return true;
19983     },
19984
19985     // private
19986     onDragOut : function(e, id){
19987         var target = this.cachedTarget || Roo.dd.DragDropMgr.getDDById(id);
19988         if(this.beforeDragOut(target, e, id) !== false){
19989             if(target.isNotifyTarget){
19990                 target.notifyOut(this, e, this.dragData);
19991             }
19992             this.proxy.reset();
19993             if(this.afterDragOut){
19994                 /**
19995                  * An empty function by default, but provided so that you can perform a custom action
19996                  * after the dragged item is dragged out of the target without dropping.
19997                  * @param {Roo.dd.DragDrop} target The drop target
19998                  * @param {Event} e The event object
19999                  * @param {String} id The id of the dragged element
20000                  * @method afterDragOut
20001                  */
20002                 this.afterDragOut(target, e, id);
20003             }
20004         }
20005         this.cachedTarget = null;
20006     },
20007
20008     /**
20009      * An empty function by default, but provided so that you can perform a custom action before the dragged
20010      * item is dragged out of the target without dropping, and optionally cancel the onDragOut.
20011      * @param {Roo.dd.DragDrop} target The drop target
20012      * @param {Event} e The event object
20013      * @param {String} id The id of the dragged element
20014      * @return {Boolean} isValid True if the drag event is valid, else false to cancel
20015      */
20016     beforeDragOut : function(target, e, id){
20017         return true;
20018     },
20019     
20020     // private
20021     onDragDrop : function(e, id){
20022         var target = this.cachedTarget || Roo.dd.DragDropMgr.getDDById(id);
20023         if(this.beforeDragDrop(target, e, id) !== false){
20024             if(target.isNotifyTarget){
20025                 if(target.notifyDrop(this, e, this.dragData)){ // valid drop?
20026                     this.onValidDrop(target, e, id);
20027                 }else{
20028                     this.onInvalidDrop(target, e, id);
20029                 }
20030             }else{
20031                 this.onValidDrop(target, e, id);
20032             }
20033             
20034             if(this.afterDragDrop){
20035                 /**
20036                  * An empty function by default, but provided so that you can perform a custom action
20037                  * after a valid drag drop has occurred by providing an implementation.
20038                  * @param {Roo.dd.DragDrop} target The drop target
20039                  * @param {Event} e The event object
20040                  * @param {String} id The id of the dropped element
20041                  * @method afterDragDrop
20042                  */
20043                 this.afterDragDrop(target, e, id);
20044             }
20045         }
20046         delete this.cachedTarget;
20047     },
20048
20049     /**
20050      * An empty function by default, but provided so that you can perform a custom action before the dragged
20051      * item is dropped onto the target and optionally cancel the onDragDrop.
20052      * @param {Roo.dd.DragDrop} target The drop target
20053      * @param {Event} e The event object
20054      * @param {String} id The id of the dragged element
20055      * @return {Boolean} isValid True if the drag drop event is valid, else false to cancel
20056      */
20057     beforeDragDrop : function(target, e, id){
20058         return true;
20059     },
20060
20061     // private
20062     onValidDrop : function(target, e, id){
20063         this.hideProxy();
20064         if(this.afterValidDrop){
20065             /**
20066              * An empty function by default, but provided so that you can perform a custom action
20067              * after a valid drop has occurred by providing an implementation.
20068              * @param {Object} target The target DD 
20069              * @param {Event} e The event object
20070              * @param {String} id The id of the dropped element
20071              * @method afterInvalidDrop
20072              */
20073             this.afterValidDrop(target, e, id);
20074         }
20075     },
20076
20077     // private
20078     getRepairXY : function(e, data){
20079         return this.el.getXY();  
20080     },
20081
20082     // private
20083     onInvalidDrop : function(target, e, id){
20084         this.beforeInvalidDrop(target, e, id);
20085         if(this.cachedTarget){
20086             if(this.cachedTarget.isNotifyTarget){
20087                 this.cachedTarget.notifyOut(this, e, this.dragData);
20088             }
20089             this.cacheTarget = null;
20090         }
20091         this.proxy.repair(this.getRepairXY(e, this.dragData), this.afterRepair, this);
20092
20093         if(this.afterInvalidDrop){
20094             /**
20095              * An empty function by default, but provided so that you can perform a custom action
20096              * after an invalid drop has occurred by providing an implementation.
20097              * @param {Event} e The event object
20098              * @param {String} id The id of the dropped element
20099              * @method afterInvalidDrop
20100              */
20101             this.afterInvalidDrop(e, id);
20102         }
20103     },
20104
20105     // private
20106     afterRepair : function(){
20107         if(Roo.enableFx){
20108             this.el.highlight(this.hlColor || "c3daf9");
20109         }
20110         this.dragging = false;
20111     },
20112
20113     /**
20114      * An empty function by default, but provided so that you can perform a custom action after an invalid
20115      * drop has occurred.
20116      * @param {Roo.dd.DragDrop} target The drop target
20117      * @param {Event} e The event object
20118      * @param {String} id The id of the dragged element
20119      * @return {Boolean} isValid True if the invalid drop should proceed, else false to cancel
20120      */
20121     beforeInvalidDrop : function(target, e, id){
20122         return true;
20123     },
20124
20125     // private
20126     handleMouseDown : function(e){
20127         if(this.dragging) {
20128             return;
20129         }
20130         var data = this.getDragData(e);
20131         if(data && this.onBeforeDrag(data, e) !== false){
20132             this.dragData = data;
20133             this.proxy.stop();
20134             Roo.dd.DragSource.superclass.handleMouseDown.apply(this, arguments);
20135         } 
20136     },
20137
20138     /**
20139      * An empty function by default, but provided so that you can perform a custom action before the initial
20140      * drag event begins and optionally cancel it.
20141      * @param {Object} data An object containing arbitrary data to be shared with drop targets
20142      * @param {Event} e The event object
20143      * @return {Boolean} isValid True if the drag event is valid, else false to cancel
20144      */
20145     onBeforeDrag : function(data, e){
20146         return true;
20147     },
20148
20149     /**
20150      * An empty function by default, but provided so that you can perform a custom action once the initial
20151      * drag event has begun.  The drag cannot be canceled from this function.
20152      * @param {Number} x The x position of the click on the dragged object
20153      * @param {Number} y The y position of the click on the dragged object
20154      */
20155     onStartDrag : Roo.emptyFn,
20156
20157     // private - YUI override
20158     startDrag : function(x, y){
20159         this.proxy.reset();
20160         this.dragging = true;
20161         this.proxy.update("");
20162         this.onInitDrag(x, y);
20163         this.proxy.show();
20164     },
20165
20166     // private
20167     onInitDrag : function(x, y){
20168         var clone = this.el.dom.cloneNode(true);
20169         clone.id = Roo.id(); // prevent duplicate ids
20170         this.proxy.update(clone);
20171         this.onStartDrag(x, y);
20172         return true;
20173     },
20174
20175     /**
20176      * Returns the drag source's underlying {@link Roo.dd.StatusProxy}
20177      * @return {Roo.dd.StatusProxy} proxy The StatusProxy
20178      */
20179     getProxy : function(){
20180         return this.proxy;  
20181     },
20182
20183     /**
20184      * Hides the drag source's {@link Roo.dd.StatusProxy}
20185      */
20186     hideProxy : function(){
20187         this.proxy.hide();  
20188         this.proxy.reset(true);
20189         this.dragging = false;
20190     },
20191
20192     // private
20193     triggerCacheRefresh : function(){
20194         Roo.dd.DDM.refreshCache(this.groups);
20195     },
20196
20197     // private - override to prevent hiding
20198     b4EndDrag: function(e) {
20199     },
20200
20201     // private - override to prevent moving
20202     endDrag : function(e){
20203         this.onEndDrag(this.dragData, e);
20204     },
20205
20206     // private
20207     onEndDrag : function(data, e){
20208     },
20209     
20210     // private - pin to cursor
20211     autoOffset : function(x, y) {
20212         this.setDelta(-12, -20);
20213     }    
20214 });/*
20215  * Based on:
20216  * Ext JS Library 1.1.1
20217  * Copyright(c) 2006-2007, Ext JS, LLC.
20218  *
20219  * Originally Released Under LGPL - original licence link has changed is not relivant.
20220  *
20221  * Fork - LGPL
20222  * <script type="text/javascript">
20223  */
20224
20225
20226 /**
20227  * @class Roo.dd.DropTarget
20228  * @extends Roo.dd.DDTarget
20229  * A simple class that provides the basic implementation needed to make any element a drop target that can have
20230  * draggable items dropped onto it.  The drop has no effect until an implementation of notifyDrop is provided.
20231  * @constructor
20232  * @param {String/HTMLElement/Element} el The container element
20233  * @param {Object} config
20234  */
20235 Roo.dd.DropTarget = function(el, config){
20236     this.el = Roo.get(el);
20237     
20238     var listeners = false; ;
20239     if (config && config.listeners) {
20240         listeners= config.listeners;
20241         delete config.listeners;
20242     }
20243     Roo.apply(this, config);
20244     
20245     if(this.containerScroll){
20246         Roo.dd.ScrollManager.register(this.el);
20247     }
20248     this.addEvents( {
20249          /**
20250          * @scope Roo.dd.DropTarget
20251          */
20252          
20253          /**
20254          * @event enter
20255          * The function a {@link Roo.dd.DragSource} calls once to notify this drop target that the source is now over the
20256          * target.  This default implementation adds the CSS class specified by overClass (if any) to the drop element
20257          * and returns the dropAllowed config value.  This method should be overridden if drop validation is required.
20258          * 
20259          * IMPORTANT : it should set this.overClass and this.dropAllowed
20260          * 
20261          * @param {Roo.dd.DragSource} source The drag source that was dragged over this drop target
20262          * @param {Event} e The event
20263          * @param {Object} data An object containing arbitrary data supplied by the drag source
20264          */
20265         "enter" : true,
20266         
20267          /**
20268          * @event over
20269          * The function a {@link Roo.dd.DragSource} calls continuously while it is being dragged over the target.
20270          * This method will be called on every mouse movement while the drag source is over the drop target.
20271          * This default implementation simply returns the dropAllowed config value.
20272          * 
20273          * IMPORTANT : it should set this.dropAllowed
20274          * 
20275          * @param {Roo.dd.DragSource} source The drag source that was dragged over this drop target
20276          * @param {Event} e The event
20277          * @param {Object} data An object containing arbitrary data supplied by the drag source
20278          
20279          */
20280         "over" : true,
20281         /**
20282          * @event out
20283          * The function a {@link Roo.dd.DragSource} calls once to notify this drop target that the source has been dragged
20284          * out of the target without dropping.  This default implementation simply removes the CSS class specified by
20285          * overClass (if any) from the drop element.
20286          * 
20287          * @param {Roo.dd.DragSource} source The drag source that was dragged over this drop target
20288          * @param {Event} e The event
20289          * @param {Object} data An object containing arbitrary data supplied by the drag source
20290          */
20291          "out" : true,
20292          
20293         /**
20294          * @event drop
20295          * The function a {@link Roo.dd.DragSource} calls once to notify this drop target that the dragged item has
20296          * been dropped on it.  This method has no default implementation and returns false, so you must provide an
20297          * implementation that does something to process the drop event and returns true so that the drag source's
20298          * repair action does not run.
20299          * 
20300          * IMPORTANT : it should set this.success
20301          * 
20302          * @param {Roo.dd.DragSource} source The drag source that was dragged over this drop target
20303          * @param {Event} e The event
20304          * @param {Object} data An object containing arbitrary data supplied by the drag source
20305         */
20306          "drop" : true
20307     });
20308             
20309      
20310     Roo.dd.DropTarget.superclass.constructor.call(  this, 
20311         this.el.dom, 
20312         this.ddGroup || this.group,
20313         {
20314             isTarget: true,
20315             listeners : listeners || {} 
20316            
20317         
20318         }
20319     );
20320
20321 };
20322
20323 Roo.extend(Roo.dd.DropTarget, Roo.dd.DDTarget, {
20324     /**
20325      * @cfg {String} overClass
20326      * The CSS class applied to the drop target element while the drag source is over it (defaults to "").
20327      */
20328      /**
20329      * @cfg {String} ddGroup
20330      * The drag drop group to handle drop events for
20331      */
20332      
20333     /**
20334      * @cfg {String} dropAllowed
20335      * The CSS class returned to the drag source when drop is allowed (defaults to "x-dd-drop-ok").
20336      */
20337     dropAllowed : "x-dd-drop-ok",
20338     /**
20339      * @cfg {String} dropNotAllowed
20340      * The CSS class returned to the drag source when drop is not allowed (defaults to "x-dd-drop-nodrop").
20341      */
20342     dropNotAllowed : "x-dd-drop-nodrop",
20343     /**
20344      * @cfg {boolean} success
20345      * set this after drop listener.. 
20346      */
20347     success : false,
20348     /**
20349      * @cfg {boolean|String} valid true/false or string (ok-add/ok-sub/ok/nodrop)
20350      * if the drop point is valid for over/enter..
20351      */
20352     valid : false,
20353     // private
20354     isTarget : true,
20355
20356     // private
20357     isNotifyTarget : true,
20358     
20359     /**
20360      * @hide
20361      */
20362     notifyEnter : function(dd, e, data)
20363     {
20364         this.valid = true;
20365         this.fireEvent('enter', dd, e, data);
20366         if(this.overClass){
20367             this.el.addClass(this.overClass);
20368         }
20369         return typeof(this.valid) == 'string' ? 'x-dd-drop-' + this.valid : (
20370             this.valid ? this.dropAllowed : this.dropNotAllowed
20371         );
20372     },
20373
20374     /**
20375      * @hide
20376      */
20377     notifyOver : function(dd, e, data)
20378     {
20379         this.valid = true;
20380         this.fireEvent('over', dd, e, data);
20381         return typeof(this.valid) == 'string' ? 'x-dd-drop-' + this.valid : (
20382             this.valid ? this.dropAllowed : this.dropNotAllowed
20383         );
20384     },
20385
20386     /**
20387      * @hide
20388      */
20389     notifyOut : function(dd, e, data)
20390     {
20391         this.fireEvent('out', dd, e, data);
20392         if(this.overClass){
20393             this.el.removeClass(this.overClass);
20394         }
20395     },
20396
20397     /**
20398      * @hide
20399      */
20400     notifyDrop : function(dd, e, data)
20401     {
20402         this.success = false;
20403         this.fireEvent('drop', dd, e, data);
20404         return this.success;
20405     }
20406 });/*
20407  * Based on:
20408  * Ext JS Library 1.1.1
20409  * Copyright(c) 2006-2007, Ext JS, LLC.
20410  *
20411  * Originally Released Under LGPL - original licence link has changed is not relivant.
20412  *
20413  * Fork - LGPL
20414  * <script type="text/javascript">
20415  */
20416
20417
20418 /**
20419  * @class Roo.dd.DragZone
20420  * @extends Roo.dd.DragSource
20421  * This class provides a container DD instance that proxies for multiple child node sources.<br />
20422  * By default, this class requires that draggable child nodes are registered with {@link Roo.dd.Registry}.
20423  * @constructor
20424  * @param {String/HTMLElement/Element} el The container element
20425  * @param {Object} config
20426  */
20427 Roo.dd.DragZone = function(el, config){
20428     Roo.dd.DragZone.superclass.constructor.call(this, el, config);
20429     if(this.containerScroll){
20430         Roo.dd.ScrollManager.register(this.el);
20431     }
20432 };
20433
20434 Roo.extend(Roo.dd.DragZone, Roo.dd.DragSource, {
20435     /**
20436      * @cfg {Boolean} containerScroll True to register this container with the Scrollmanager
20437      * for auto scrolling during drag operations.
20438      */
20439     /**
20440      * @cfg {String} hlColor The color to use when visually highlighting the drag source in the afterRepair
20441      * method after a failed drop (defaults to "c3daf9" - light blue)
20442      */
20443
20444     /**
20445      * Called when a mousedown occurs in this container. Looks in {@link Roo.dd.Registry}
20446      * for a valid target to drag based on the mouse down. Override this method
20447      * to provide your own lookup logic (e.g. finding a child by class name). Make sure your returned
20448      * object has a "ddel" attribute (with an HTML Element) for other functions to work.
20449      * @param {EventObject} e The mouse down event
20450      * @return {Object} The dragData
20451      */
20452     getDragData : function(e){
20453         return Roo.dd.Registry.getHandleFromEvent(e);
20454     },
20455     
20456     /**
20457      * Called once drag threshold has been reached to initialize the proxy element. By default, it clones the
20458      * this.dragData.ddel
20459      * @param {Number} x The x position of the click on the dragged object
20460      * @param {Number} y The y position of the click on the dragged object
20461      * @return {Boolean} true to continue the drag, false to cancel
20462      */
20463     onInitDrag : function(x, y){
20464         this.proxy.update(this.dragData.ddel.cloneNode(true));
20465         this.onStartDrag(x, y);
20466         return true;
20467     },
20468     
20469     /**
20470      * Called after a repair of an invalid drop. By default, highlights this.dragData.ddel 
20471      */
20472     afterRepair : function(){
20473         if(Roo.enableFx){
20474             Roo.Element.fly(this.dragData.ddel).highlight(this.hlColor || "c3daf9");
20475         }
20476         this.dragging = false;
20477     },
20478
20479     /**
20480      * Called before a repair of an invalid drop to get the XY to animate to. By default returns
20481      * the XY of this.dragData.ddel
20482      * @param {EventObject} e The mouse up event
20483      * @return {Array} The xy location (e.g. [100, 200])
20484      */
20485     getRepairXY : function(e){
20486         return Roo.Element.fly(this.dragData.ddel).getXY();  
20487     }
20488 });/*
20489  * Based on:
20490  * Ext JS Library 1.1.1
20491  * Copyright(c) 2006-2007, Ext JS, LLC.
20492  *
20493  * Originally Released Under LGPL - original licence link has changed is not relivant.
20494  *
20495  * Fork - LGPL
20496  * <script type="text/javascript">
20497  */
20498 /**
20499  * @class Roo.dd.DropZone
20500  * @extends Roo.dd.DropTarget
20501  * This class provides a container DD instance that proxies for multiple child node targets.<br />
20502  * By default, this class requires that child nodes accepting drop are registered with {@link Roo.dd.Registry}.
20503  * @constructor
20504  * @param {String/HTMLElement/Element} el The container element
20505  * @param {Object} config
20506  */
20507 Roo.dd.DropZone = function(el, config){
20508     Roo.dd.DropZone.superclass.constructor.call(this, el, config);
20509 };
20510
20511 Roo.extend(Roo.dd.DropZone, Roo.dd.DropTarget, {
20512     /**
20513      * Returns a custom data object associated with the DOM node that is the target of the event.  By default
20514      * this looks up the event target in the {@link Roo.dd.Registry}, although you can override this method to
20515      * provide your own custom lookup.
20516      * @param {Event} e The event
20517      * @return {Object} data The custom data
20518      */
20519     getTargetFromEvent : function(e){
20520         return Roo.dd.Registry.getTargetFromEvent(e);
20521     },
20522
20523     /**
20524      * Called internally when the DropZone determines that a {@link Roo.dd.DragSource} has entered a drop node
20525      * that it has registered.  This method has no default implementation and should be overridden to provide
20526      * node-specific processing if necessary.
20527      * @param {Object} nodeData The custom data associated with the drop node (this is the same value returned from 
20528      * {@link #getTargetFromEvent} for this node)
20529      * @param {Roo.dd.DragSource} source The drag source that was dragged over this drop zone
20530      * @param {Event} e The event
20531      * @param {Object} data An object containing arbitrary data supplied by the drag source
20532      */
20533     onNodeEnter : function(n, dd, e, data){
20534         
20535     },
20536
20537     /**
20538      * Called internally while the DropZone determines that a {@link Roo.dd.DragSource} is over a drop node
20539      * that it has registered.  The default implementation returns this.dropNotAllowed, so it should be
20540      * overridden to provide the proper feedback.
20541      * @param {Object} nodeData The custom data associated with the drop node (this is the same value returned from
20542      * {@link #getTargetFromEvent} for this node)
20543      * @param {Roo.dd.DragSource} source The drag source that was dragged over this drop zone
20544      * @param {Event} e The event
20545      * @param {Object} data An object containing arbitrary data supplied by the drag source
20546      * @return {String} status The CSS class that communicates the drop status back to the source so that the
20547      * underlying {@link Roo.dd.StatusProxy} can be updated
20548      */
20549     onNodeOver : function(n, dd, e, data){
20550         return this.dropAllowed;
20551     },
20552
20553     /**
20554      * Called internally when the DropZone determines that a {@link Roo.dd.DragSource} has been dragged out of
20555      * the drop node without dropping.  This method has no default implementation and should be overridden to provide
20556      * node-specific processing if necessary.
20557      * @param {Object} nodeData The custom data associated with the drop node (this is the same value returned from
20558      * {@link #getTargetFromEvent} for this node)
20559      * @param {Roo.dd.DragSource} source The drag source that was dragged over this drop zone
20560      * @param {Event} e The event
20561      * @param {Object} data An object containing arbitrary data supplied by the drag source
20562      */
20563     onNodeOut : function(n, dd, e, data){
20564         
20565     },
20566
20567     /**
20568      * Called internally when the DropZone determines that a {@link Roo.dd.DragSource} has been dropped onto
20569      * the drop node.  The default implementation returns false, so it should be overridden to provide the
20570      * appropriate processing of the drop event and return true so that the drag source's repair action does not run.
20571      * @param {Object} nodeData The custom data associated with the drop node (this is the same value returned from
20572      * {@link #getTargetFromEvent} for this node)
20573      * @param {Roo.dd.DragSource} source The drag source that was dragged over this drop zone
20574      * @param {Event} e The event
20575      * @param {Object} data An object containing arbitrary data supplied by the drag source
20576      * @return {Boolean} True if the drop was valid, else false
20577      */
20578     onNodeDrop : function(n, dd, e, data){
20579         return false;
20580     },
20581
20582     /**
20583      * Called internally while the DropZone determines that a {@link Roo.dd.DragSource} is being dragged over it,
20584      * but not over any of its registered drop nodes.  The default implementation returns this.dropNotAllowed, so
20585      * it should be overridden to provide the proper feedback if necessary.
20586      * @param {Roo.dd.DragSource} source The drag source that was dragged over this drop zone
20587      * @param {Event} e The event
20588      * @param {Object} data An object containing arbitrary data supplied by the drag source
20589      * @return {String} status The CSS class that communicates the drop status back to the source so that the
20590      * underlying {@link Roo.dd.StatusProxy} can be updated
20591      */
20592     onContainerOver : function(dd, e, data){
20593         return this.dropNotAllowed;
20594     },
20595
20596     /**
20597      * Called internally when the DropZone determines that a {@link Roo.dd.DragSource} has been dropped on it,
20598      * but not on any of its registered drop nodes.  The default implementation returns false, so it should be
20599      * overridden to provide the appropriate processing of the drop event if you need the drop zone itself to
20600      * be able to accept drops.  It should return true when valid so that the drag source's repair action does not run.
20601      * @param {Roo.dd.DragSource} source The drag source that was dragged over this drop zone
20602      * @param {Event} e The event
20603      * @param {Object} data An object containing arbitrary data supplied by the drag source
20604      * @return {Boolean} True if the drop was valid, else false
20605      */
20606     onContainerDrop : function(dd, e, data){
20607         return false;
20608     },
20609
20610     /**
20611      * The function a {@link Roo.dd.DragSource} calls once to notify this drop zone that the source is now over
20612      * the zone.  The default implementation returns this.dropNotAllowed and expects that only registered drop
20613      * nodes can process drag drop operations, so if you need the drop zone itself to be able to process drops
20614      * you should override this method and provide a custom implementation.
20615      * @param {Roo.dd.DragSource} source The drag source that was dragged over this drop zone
20616      * @param {Event} e The event
20617      * @param {Object} data An object containing arbitrary data supplied by the drag source
20618      * @return {String} status The CSS class that communicates the drop status back to the source so that the
20619      * underlying {@link Roo.dd.StatusProxy} can be updated
20620      */
20621     notifyEnter : function(dd, e, data){
20622         return this.dropNotAllowed;
20623     },
20624
20625     /**
20626      * The function a {@link Roo.dd.DragSource} calls continuously while it is being dragged over the drop zone.
20627      * This method will be called on every mouse movement while the drag source is over the drop zone.
20628      * It will call {@link #onNodeOver} while the drag source is over a registered node, and will also automatically
20629      * delegate to the appropriate node-specific methods as necessary when the drag source enters and exits
20630      * registered nodes ({@link #onNodeEnter}, {@link #onNodeOut}). If the drag source is not currently over a
20631      * registered node, it will call {@link #onContainerOver}.
20632      * @param {Roo.dd.DragSource} source The drag source that was dragged over this drop zone
20633      * @param {Event} e The event
20634      * @param {Object} data An object containing arbitrary data supplied by the drag source
20635      * @return {String} status The CSS class that communicates the drop status back to the source so that the
20636      * underlying {@link Roo.dd.StatusProxy} can be updated
20637      */
20638     notifyOver : function(dd, e, data){
20639         var n = this.getTargetFromEvent(e);
20640         if(!n){ // not over valid drop target
20641             if(this.lastOverNode){
20642                 this.onNodeOut(this.lastOverNode, dd, e, data);
20643                 this.lastOverNode = null;
20644             }
20645             return this.onContainerOver(dd, e, data);
20646         }
20647         if(this.lastOverNode != n){
20648             if(this.lastOverNode){
20649                 this.onNodeOut(this.lastOverNode, dd, e, data);
20650             }
20651             this.onNodeEnter(n, dd, e, data);
20652             this.lastOverNode = n;
20653         }
20654         return this.onNodeOver(n, dd, e, data);
20655     },
20656
20657     /**
20658      * The function a {@link Roo.dd.DragSource} calls once to notify this drop zone that the source has been dragged
20659      * out of the zone without dropping.  If the drag source is currently over a registered node, the notification
20660      * will be delegated to {@link #onNodeOut} for node-specific handling, otherwise it will be ignored.
20661      * @param {Roo.dd.DragSource} source The drag source that was dragged over this drop target
20662      * @param {Event} e The event
20663      * @param {Object} data An object containing arbitrary data supplied by the drag zone
20664      */
20665     notifyOut : function(dd, e, data){
20666         if(this.lastOverNode){
20667             this.onNodeOut(this.lastOverNode, dd, e, data);
20668             this.lastOverNode = null;
20669         }
20670     },
20671
20672     /**
20673      * The function a {@link Roo.dd.DragSource} calls once to notify this drop zone that the dragged item has
20674      * been dropped on it.  The drag zone will look up the target node based on the event passed in, and if there
20675      * is a node registered for that event, it will delegate to {@link #onNodeDrop} for node-specific handling,
20676      * otherwise it will call {@link #onContainerDrop}.
20677      * @param {Roo.dd.DragSource} source The drag source that was dragged over this drop zone
20678      * @param {Event} e The event
20679      * @param {Object} data An object containing arbitrary data supplied by the drag source
20680      * @return {Boolean} True if the drop was valid, else false
20681      */
20682     notifyDrop : function(dd, e, data){
20683         if(this.lastOverNode){
20684             this.onNodeOut(this.lastOverNode, dd, e, data);
20685             this.lastOverNode = null;
20686         }
20687         var n = this.getTargetFromEvent(e);
20688         return n ?
20689             this.onNodeDrop(n, dd, e, data) :
20690             this.onContainerDrop(dd, e, data);
20691     },
20692
20693     // private
20694     triggerCacheRefresh : function(){
20695         Roo.dd.DDM.refreshCache(this.groups);
20696     }  
20697 });/*
20698  * Based on:
20699  * Ext JS Library 1.1.1
20700  * Copyright(c) 2006-2007, Ext JS, LLC.
20701  *
20702  * Originally Released Under LGPL - original licence link has changed is not relivant.
20703  *
20704  * Fork - LGPL
20705  * <script type="text/javascript">
20706  */
20707
20708
20709 /**
20710  * @class Roo.data.SortTypes
20711  * @singleton
20712  * Defines the default sorting (casting?) comparison functions used when sorting data.
20713  */
20714 Roo.data.SortTypes = {
20715     /**
20716      * Default sort that does nothing
20717      * @param {Mixed} s The value being converted
20718      * @return {Mixed} The comparison value
20719      */
20720     none : function(s){
20721         return s;
20722     },
20723     
20724     /**
20725      * The regular expression used to strip tags
20726      * @type {RegExp}
20727      * @property
20728      */
20729     stripTagsRE : /<\/?[^>]+>/gi,
20730     
20731     /**
20732      * Strips all HTML tags to sort on text only
20733      * @param {Mixed} s The value being converted
20734      * @return {String} The comparison value
20735      */
20736     asText : function(s){
20737         return String(s).replace(this.stripTagsRE, "");
20738     },
20739     
20740     /**
20741      * Strips all HTML tags to sort on text only - Case insensitive
20742      * @param {Mixed} s The value being converted
20743      * @return {String} The comparison value
20744      */
20745     asUCText : function(s){
20746         return String(s).toUpperCase().replace(this.stripTagsRE, "");
20747     },
20748     
20749     /**
20750      * Case insensitive string
20751      * @param {Mixed} s The value being converted
20752      * @return {String} The comparison value
20753      */
20754     asUCString : function(s) {
20755         return String(s).toUpperCase();
20756     },
20757     
20758     /**
20759      * Date sorting
20760      * @param {Mixed} s The value being converted
20761      * @return {Number} The comparison value
20762      */
20763     asDate : function(s) {
20764         if(!s){
20765             return 0;
20766         }
20767         if(s instanceof Date){
20768             return s.getTime();
20769         }
20770         return Date.parse(String(s));
20771     },
20772     
20773     /**
20774      * Float sorting
20775      * @param {Mixed} s The value being converted
20776      * @return {Float} The comparison value
20777      */
20778     asFloat : function(s) {
20779         var val = parseFloat(String(s).replace(/,/g, ""));
20780         if(isNaN(val)) val = 0;
20781         return val;
20782     },
20783     
20784     /**
20785      * Integer sorting
20786      * @param {Mixed} s The value being converted
20787      * @return {Number} The comparison value
20788      */
20789     asInt : function(s) {
20790         var val = parseInt(String(s).replace(/,/g, ""));
20791         if(isNaN(val)) val = 0;
20792         return val;
20793     }
20794 };/*
20795  * Based on:
20796  * Ext JS Library 1.1.1
20797  * Copyright(c) 2006-2007, Ext JS, LLC.
20798  *
20799  * Originally Released Under LGPL - original licence link has changed is not relivant.
20800  *
20801  * Fork - LGPL
20802  * <script type="text/javascript">
20803  */
20804
20805 /**
20806 * @class Roo.data.Record
20807  * Instances of this class encapsulate both record <em>definition</em> information, and record
20808  * <em>value</em> information for use in {@link Roo.data.Store} objects, or any code which needs
20809  * to access Records cached in an {@link Roo.data.Store} object.<br>
20810  * <p>
20811  * Constructors for this class are generated by passing an Array of field definition objects to {@link #create}.
20812  * Instances are usually only created by {@link Roo.data.Reader} implementations when processing unformatted data
20813  * objects.<br>
20814  * <p>
20815  * Record objects generated by this constructor inherit all the methods of Roo.data.Record listed below.
20816  * @constructor
20817  * This constructor should not be used to create Record objects. Instead, use the constructor generated by
20818  * {@link #create}. The parameters are the same.
20819  * @param {Array} data An associative Array of data values keyed by the field name.
20820  * @param {Object} id (Optional) The id of the record. This id should be unique, and is used by the
20821  * {@link Roo.data.Store} object which owns the Record to index its collection of Records. If
20822  * not specified an integer id is generated.
20823  */
20824 Roo.data.Record = function(data, id){
20825     this.id = (id || id === 0) ? id : ++Roo.data.Record.AUTO_ID;
20826     this.data = data;
20827 };
20828
20829 /**
20830  * Generate a constructor for a specific record layout.
20831  * @param {Array} o An Array of field definition objects which specify field names, and optionally,
20832  * data types, and a mapping for an {@link Roo.data.Reader} to extract the field's value from a data object.
20833  * Each field definition object may contain the following properties: <ul>
20834  * <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,
20835  * for example the <em>dataIndex</em> property in column definition objects passed to {@link Roo.grid.ColumnModel}</p></li>
20836  * <li><b>mapping</b> : String<p style="margin-left:1em">(Optional) A path specification for use by the {@link Roo.data.Reader} implementation
20837  * that is creating the Record to access the data value from the data object. If an {@link Roo.data.JsonReader}
20838  * is being used, then this is a string containing the javascript expression to reference the data relative to 
20839  * the record item's root. If an {@link Roo.data.XmlReader} is being used, this is an {@link Roo.DomQuery} path
20840  * to the data item relative to the record element. If the mapping expression is the same as the field name,
20841  * this may be omitted.</p></li>
20842  * <li><b>type</b> : String<p style="margin-left:1em">(Optional) The data type for conversion to displayable value. Possible values are
20843  * <ul><li>auto (Default, implies no conversion)</li>
20844  * <li>string</li>
20845  * <li>int</li>
20846  * <li>float</li>
20847  * <li>boolean</li>
20848  * <li>date</li></ul></p></li>
20849  * <li><b>sortType</b> : Mixed<p style="margin-left:1em">(Optional) A member of {@link Roo.data.SortTypes}.</p></li>
20850  * <li><b>sortDir</b> : String<p style="margin-left:1em">(Optional) Initial direction to sort. "ASC" or "DESC"</p></li>
20851  * <li><b>convert</b> : Function<p style="margin-left:1em">(Optional) A function which converts the value provided
20852  * by the Reader into an object that will be stored in the Record. It is passed the
20853  * following parameters:<ul>
20854  * <li><b>v</b> : Mixed<p style="margin-left:1em">The data value as read by the Reader.</p></li>
20855  * </ul></p></li>
20856  * <li><b>dateFormat</b> : String<p style="margin-left:1em">(Optional) A format String for the Date.parseDate function.</p></li>
20857  * </ul>
20858  * <br>usage:<br><pre><code>
20859 var TopicRecord = Roo.data.Record.create(
20860     {name: 'title', mapping: 'topic_title'},
20861     {name: 'author', mapping: 'username'},
20862     {name: 'totalPosts', mapping: 'topic_replies', type: 'int'},
20863     {name: 'lastPost', mapping: 'post_time', type: 'date'},
20864     {name: 'lastPoster', mapping: 'user2'},
20865     {name: 'excerpt', mapping: 'post_text'}
20866 );
20867
20868 var myNewRecord = new TopicRecord({
20869     title: 'Do my job please',
20870     author: 'noobie',
20871     totalPosts: 1,
20872     lastPost: new Date(),
20873     lastPoster: 'Animal',
20874     excerpt: 'No way dude!'
20875 });
20876 myStore.add(myNewRecord);
20877 </code></pre>
20878  * @method create
20879  * @static
20880  */
20881 Roo.data.Record.create = function(o){
20882     var f = function(){
20883         f.superclass.constructor.apply(this, arguments);
20884     };
20885     Roo.extend(f, Roo.data.Record);
20886     var p = f.prototype;
20887     p.fields = new Roo.util.MixedCollection(false, function(field){
20888         return field.name;
20889     });
20890     for(var i = 0, len = o.length; i < len; i++){
20891         p.fields.add(new Roo.data.Field(o[i]));
20892     }
20893     f.getField = function(name){
20894         return p.fields.get(name);  
20895     };
20896     return f;
20897 };
20898
20899 Roo.data.Record.AUTO_ID = 1000;
20900 Roo.data.Record.EDIT = 'edit';
20901 Roo.data.Record.REJECT = 'reject';
20902 Roo.data.Record.COMMIT = 'commit';
20903
20904 Roo.data.Record.prototype = {
20905     /**
20906      * Readonly flag - true if this record has been modified.
20907      * @type Boolean
20908      */
20909     dirty : false,
20910     editing : false,
20911     error: null,
20912     modified: null,
20913
20914     // private
20915     join : function(store){
20916         this.store = store;
20917     },
20918
20919     /**
20920      * Set the named field to the specified value.
20921      * @param {String} name The name of the field to set.
20922      * @param {Object} value The value to set the field to.
20923      */
20924     set : function(name, value){
20925         if(this.data[name] == value){
20926             return;
20927         }
20928         this.dirty = true;
20929         if(!this.modified){
20930             this.modified = {};
20931         }
20932         if(typeof this.modified[name] == 'undefined'){
20933             this.modified[name] = this.data[name];
20934         }
20935         this.data[name] = value;
20936         if(!this.editing && this.store){
20937             this.store.afterEdit(this);
20938         }       
20939     },
20940
20941     /**
20942      * Get the value of the named field.
20943      * @param {String} name The name of the field to get the value of.
20944      * @return {Object} The value of the field.
20945      */
20946     get : function(name){
20947         return this.data[name]; 
20948     },
20949
20950     // private
20951     beginEdit : function(){
20952         this.editing = true;
20953         this.modified = {}; 
20954     },
20955
20956     // private
20957     cancelEdit : function(){
20958         this.editing = false;
20959         delete this.modified;
20960     },
20961
20962     // private
20963     endEdit : function(){
20964         this.editing = false;
20965         if(this.dirty && this.store){
20966             this.store.afterEdit(this);
20967         }
20968     },
20969
20970     /**
20971      * Usually called by the {@link Roo.data.Store} which owns the Record.
20972      * Rejects all changes made to the Record since either creation, or the last commit operation.
20973      * Modified fields are reverted to their original values.
20974      * <p>
20975      * Developers should subscribe to the {@link Roo.data.Store#update} event to have their code notified
20976      * of reject operations.
20977      */
20978     reject : function(){
20979         var m = this.modified;
20980         for(var n in m){
20981             if(typeof m[n] != "function"){
20982                 this.data[n] = m[n];
20983             }
20984         }
20985         this.dirty = false;
20986         delete this.modified;
20987         this.editing = false;
20988         if(this.store){
20989             this.store.afterReject(this);
20990         }
20991     },
20992
20993     /**
20994      * Usually called by the {@link Roo.data.Store} which owns the Record.
20995      * Commits all changes made to the Record since either creation, or the last commit operation.
20996      * <p>
20997      * Developers should subscribe to the {@link Roo.data.Store#update} event to have their code notified
20998      * of commit operations.
20999      */
21000     commit : function(){
21001         this.dirty = false;
21002         delete this.modified;
21003         this.editing = false;
21004         if(this.store){
21005             this.store.afterCommit(this);
21006         }
21007     },
21008
21009     // private
21010     hasError : function(){
21011         return this.error != null;
21012     },
21013
21014     // private
21015     clearError : function(){
21016         this.error = null;
21017     },
21018
21019     /**
21020      * Creates a copy of this record.
21021      * @param {String} id (optional) A new record id if you don't want to use this record's id
21022      * @return {Record}
21023      */
21024     copy : function(newId) {
21025         return new this.constructor(Roo.apply({}, this.data), newId || this.id);
21026     }
21027 };/*
21028  * Based on:
21029  * Ext JS Library 1.1.1
21030  * Copyright(c) 2006-2007, Ext JS, LLC.
21031  *
21032  * Originally Released Under LGPL - original licence link has changed is not relivant.
21033  *
21034  * Fork - LGPL
21035  * <script type="text/javascript">
21036  */
21037
21038
21039
21040 /**
21041  * @class Roo.data.Store
21042  * @extends Roo.util.Observable
21043  * The Store class encapsulates a client side cache of {@link Roo.data.Record} objects which provide input data
21044  * for widgets such as the Roo.grid.Grid, or the Roo.form.ComboBox.<br>
21045  * <p>
21046  * 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
21047  * has no knowledge of the format of the data returned by the Proxy.<br>
21048  * <p>
21049  * A Store object uses its configured implementation of {@link Roo.data.DataReader} to create {@link Roo.data.Record}
21050  * instances from the data object. These records are cached and made available through accessor functions.
21051  * @constructor
21052  * Creates a new Store.
21053  * @param {Object} config A config object containing the objects needed for the Store to access data,
21054  * and read the data into Records.
21055  */
21056 Roo.data.Store = function(config){
21057     this.data = new Roo.util.MixedCollection(false);
21058     this.data.getKey = function(o){
21059         return o.id;
21060     };
21061     this.baseParams = {};
21062     // private
21063     this.paramNames = {
21064         "start" : "start",
21065         "limit" : "limit",
21066         "sort" : "sort",
21067         "dir" : "dir",
21068         "multisort" : "_multisort"
21069     };
21070
21071     if(config && config.data){
21072         this.inlineData = config.data;
21073         delete config.data;
21074     }
21075
21076     Roo.apply(this, config);
21077     
21078     if(this.reader){ // reader passed
21079         this.reader = Roo.factory(this.reader, Roo.data);
21080         this.reader.xmodule = this.xmodule || false;
21081         if(!this.recordType){
21082             this.recordType = this.reader.recordType;
21083         }
21084         if(this.reader.onMetaChange){
21085             this.reader.onMetaChange = this.onMetaChange.createDelegate(this);
21086         }
21087     }
21088
21089     if(this.recordType){
21090         this.fields = this.recordType.prototype.fields;
21091     }
21092     this.modified = [];
21093
21094     this.addEvents({
21095         /**
21096          * @event datachanged
21097          * Fires when the data cache has changed, and a widget which is using this Store
21098          * as a Record cache should refresh its view.
21099          * @param {Store} this
21100          */
21101         datachanged : true,
21102         /**
21103          * @event metachange
21104          * Fires when this store's reader provides new metadata (fields). This is currently only support for JsonReaders.
21105          * @param {Store} this
21106          * @param {Object} meta The JSON metadata
21107          */
21108         metachange : true,
21109         /**
21110          * @event add
21111          * Fires when Records have been added to the Store
21112          * @param {Store} this
21113          * @param {Roo.data.Record[]} records The array of Records added
21114          * @param {Number} index The index at which the record(s) were added
21115          */
21116         add : true,
21117         /**
21118          * @event remove
21119          * Fires when a Record has been removed from the Store
21120          * @param {Store} this
21121          * @param {Roo.data.Record} record The Record that was removed
21122          * @param {Number} index The index at which the record was removed
21123          */
21124         remove : true,
21125         /**
21126          * @event update
21127          * Fires when a Record has been updated
21128          * @param {Store} this
21129          * @param {Roo.data.Record} record The Record that was updated
21130          * @param {String} operation The update operation being performed.  Value may be one of:
21131          * <pre><code>
21132  Roo.data.Record.EDIT
21133  Roo.data.Record.REJECT
21134  Roo.data.Record.COMMIT
21135          * </code></pre>
21136          */
21137         update : true,
21138         /**
21139          * @event clear
21140          * Fires when the data cache has been cleared.
21141          * @param {Store} this
21142          */
21143         clear : true,
21144         /**
21145          * @event beforeload
21146          * Fires before a request is made for a new data object.  If the beforeload handler returns false
21147          * the load action will be canceled.
21148          * @param {Store} this
21149          * @param {Object} options The loading options that were specified (see {@link #load} for details)
21150          */
21151         beforeload : true,
21152         /**
21153          * @event beforeloadadd
21154          * Fires after a new set of Records has been loaded.
21155          * @param {Store} this
21156          * @param {Roo.data.Record[]} records The Records that were loaded
21157          * @param {Object} options The loading options that were specified (see {@link #load} for details)
21158          */
21159         beforeloadadd : true,
21160         /**
21161          * @event load
21162          * Fires after a new set of Records has been loaded, before they are added to the store.
21163          * @param {Store} this
21164          * @param {Roo.data.Record[]} records The Records that were loaded
21165          * @param {Object} options The loading options that were specified (see {@link #load} for details)
21166          * @params {Object} return from reader
21167          */
21168         load : true,
21169         /**
21170          * @event loadexception
21171          * Fires if an exception occurs in the Proxy during loading.
21172          * Called with the signature of the Proxy's "loadexception" event.
21173          * If you return Json { data: [] , success: false, .... } then this will be thrown with the following args
21174          * 
21175          * @param {Proxy} 
21176          * @param {Object} return from JsonData.reader() - success, totalRecords, records
21177          * @param {Object} load options 
21178          * @param {Object} jsonData from your request (normally this contains the Exception)
21179          */
21180         loadexception : true
21181     });
21182     
21183     if(this.proxy){
21184         this.proxy = Roo.factory(this.proxy, Roo.data);
21185         this.proxy.xmodule = this.xmodule || false;
21186         this.relayEvents(this.proxy,  ["loadexception"]);
21187     }
21188     this.sortToggle = {};
21189     this.sortOrder = []; // array of order of sorting - updated by grid if multisort is enabled.
21190
21191     Roo.data.Store.superclass.constructor.call(this);
21192
21193     if(this.inlineData){
21194         this.loadData(this.inlineData);
21195         delete this.inlineData;
21196     }
21197 };
21198
21199 Roo.extend(Roo.data.Store, Roo.util.Observable, {
21200      /**
21201     * @cfg {boolean} isLocal   flag if data is locally available (and can be always looked up
21202     * without a remote query - used by combo/forms at present.
21203     */
21204     
21205     /**
21206     * @cfg {Roo.data.DataProxy} proxy The Proxy object which provides access to a data object.
21207     */
21208     /**
21209     * @cfg {Array} data Inline data to be loaded when the store is initialized.
21210     */
21211     /**
21212     * @cfg {Roo.data.Reader} reader The Reader object which processes the data object and returns
21213     * an Array of Roo.data.record objects which are cached keyed by their <em>id</em> property.
21214     */
21215     /**
21216     * @cfg {Object} baseParams An object containing properties which are to be sent as parameters
21217     * on any HTTP request
21218     */
21219     /**
21220     * @cfg {Object} sortInfo A config object in the format: {field: "fieldName", direction: "ASC|DESC"}
21221     */
21222     /**
21223     * @cfg {Boolean} multiSort enable multi column sorting (sort is based on the order of columns, remote only at present)
21224     */
21225     multiSort: false,
21226     /**
21227     * @cfg {boolean} remoteSort True if sorting is to be handled by requesting the Proxy to provide a refreshed
21228     * version of the data object in sorted order, as opposed to sorting the Record cache in place (defaults to false).
21229     */
21230     remoteSort : false,
21231
21232     /**
21233     * @cfg {boolean} pruneModifiedRecords True to clear all modified record information each time the store is
21234      * loaded or when a record is removed. (defaults to false).
21235     */
21236     pruneModifiedRecords : false,
21237
21238     // private
21239     lastOptions : null,
21240
21241     /**
21242      * Add Records to the Store and fires the add event.
21243      * @param {Roo.data.Record[]} records An Array of Roo.data.Record objects to add to the cache.
21244      */
21245     add : function(records){
21246         records = [].concat(records);
21247         for(var i = 0, len = records.length; i < len; i++){
21248             records[i].join(this);
21249         }
21250         var index = this.data.length;
21251         this.data.addAll(records);
21252         this.fireEvent("add", this, records, index);
21253     },
21254
21255     /**
21256      * Remove a Record from the Store and fires the remove event.
21257      * @param {Ext.data.Record} record The Roo.data.Record object to remove from the cache.
21258      */
21259     remove : function(record){
21260         var index = this.data.indexOf(record);
21261         this.data.removeAt(index);
21262         if(this.pruneModifiedRecords){
21263             this.modified.remove(record);
21264         }
21265         this.fireEvent("remove", this, record, index);
21266     },
21267
21268     /**
21269      * Remove all Records from the Store and fires the clear event.
21270      */
21271     removeAll : function(){
21272         this.data.clear();
21273         if(this.pruneModifiedRecords){
21274             this.modified = [];
21275         }
21276         this.fireEvent("clear", this);
21277     },
21278
21279     /**
21280      * Inserts Records to the Store at the given index and fires the add event.
21281      * @param {Number} index The start index at which to insert the passed Records.
21282      * @param {Roo.data.Record[]} records An Array of Roo.data.Record objects to add to the cache.
21283      */
21284     insert : function(index, records){
21285         records = [].concat(records);
21286         for(var i = 0, len = records.length; i < len; i++){
21287             this.data.insert(index, records[i]);
21288             records[i].join(this);
21289         }
21290         this.fireEvent("add", this, records, index);
21291     },
21292
21293     /**
21294      * Get the index within the cache of the passed Record.
21295      * @param {Roo.data.Record} record The Roo.data.Record object to to find.
21296      * @return {Number} The index of the passed Record. Returns -1 if not found.
21297      */
21298     indexOf : function(record){
21299         return this.data.indexOf(record);
21300     },
21301
21302     /**
21303      * Get the index within the cache of the Record with the passed id.
21304      * @param {String} id The id of the Record to find.
21305      * @return {Number} The index of the Record. Returns -1 if not found.
21306      */
21307     indexOfId : function(id){
21308         return this.data.indexOfKey(id);
21309     },
21310
21311     /**
21312      * Get the Record with the specified id.
21313      * @param {String} id The id of the Record to find.
21314      * @return {Roo.data.Record} The Record with the passed id. Returns undefined if not found.
21315      */
21316     getById : function(id){
21317         return this.data.key(id);
21318     },
21319
21320     /**
21321      * Get the Record at the specified index.
21322      * @param {Number} index The index of the Record to find.
21323      * @return {Roo.data.Record} The Record at the passed index. Returns undefined if not found.
21324      */
21325     getAt : function(index){
21326         return this.data.itemAt(index);
21327     },
21328
21329     /**
21330      * Returns a range of Records between specified indices.
21331      * @param {Number} startIndex (optional) The starting index (defaults to 0)
21332      * @param {Number} endIndex (optional) The ending index (defaults to the last Record in the Store)
21333      * @return {Roo.data.Record[]} An array of Records
21334      */
21335     getRange : function(start, end){
21336         return this.data.getRange(start, end);
21337     },
21338
21339     // private
21340     storeOptions : function(o){
21341         o = Roo.apply({}, o);
21342         delete o.callback;
21343         delete o.scope;
21344         this.lastOptions = o;
21345     },
21346
21347     /**
21348      * Loads the Record cache from the configured Proxy using the configured Reader.
21349      * <p>
21350      * If using remote paging, then the first load call must specify the <em>start</em>
21351      * and <em>limit</em> properties in the options.params property to establish the initial
21352      * position within the dataset, and the number of Records to cache on each read from the Proxy.
21353      * <p>
21354      * <strong>It is important to note that for remote data sources, loading is asynchronous,
21355      * and this call will return before the new data has been loaded. Perform any post-processing
21356      * in a callback function, or in a "load" event handler.</strong>
21357      * <p>
21358      * @param {Object} options An object containing properties which control loading options:<ul>
21359      * <li>params {Object} An object containing properties to pass as HTTP parameters to a remote data source.</li>
21360      * <li>callback {Function} A function to be called after the Records have been loaded. The callback is
21361      * passed the following arguments:<ul>
21362      * <li>r : Roo.data.Record[]</li>
21363      * <li>options: Options object from the load call</li>
21364      * <li>success: Boolean success indicator</li></ul></li>
21365      * <li>scope {Object} Scope with which to call the callback (defaults to the Store object)</li>
21366      * <li>add {Boolean} indicator to append loaded records rather than replace the current cache.</li>
21367      * </ul>
21368      */
21369     load : function(options){
21370         options = options || {};
21371         if(this.fireEvent("beforeload", this, options) !== false){
21372             this.storeOptions(options);
21373             var p = Roo.apply(options.params || {}, this.baseParams);
21374             // if meta was not loaded from remote source.. try requesting it.
21375             if (!this.reader.metaFromRemote) {
21376                 p._requestMeta = 1;
21377             }
21378             if(this.sortInfo && this.remoteSort){
21379                 var pn = this.paramNames;
21380                 p[pn["sort"]] = this.sortInfo.field;
21381                 p[pn["dir"]] = this.sortInfo.direction;
21382             }
21383             if (this.multiSort) {
21384                 var pn = this.paramNames;
21385                 p[pn["multisort"]] = Roo.encode( { sort : this.sortToggle, order: this.sortOrder });
21386             }
21387             
21388             this.proxy.load(p, this.reader, this.loadRecords, this, options);
21389         }
21390     },
21391
21392     /**
21393      * Reloads the Record cache from the configured Proxy using the configured Reader and
21394      * the options from the last load operation performed.
21395      * @param {Object} options (optional) An object containing properties which may override the options
21396      * used in the last load operation. See {@link #load} for details (defaults to null, in which case
21397      * the most recently used options are reused).
21398      */
21399     reload : function(options){
21400         this.load(Roo.applyIf(options||{}, this.lastOptions));
21401     },
21402
21403     // private
21404     // Called as a callback by the Reader during a load operation.
21405     loadRecords : function(o, options, success){
21406         if(!o || success === false){
21407             if(success !== false){
21408                 this.fireEvent("load", this, [], options, o);
21409             }
21410             if(options.callback){
21411                 options.callback.call(options.scope || this, [], options, false);
21412             }
21413             return;
21414         }
21415         // if data returned failure - throw an exception.
21416         if (o.success === false) {
21417             // show a message if no listener is registered.
21418             if (!this.hasListener('loadexception') && typeof(o.raw.errorMsg) != 'undefined') {
21419                     Roo.MessageBox.alert("Error loading",o.raw.errorMsg);
21420             }
21421             // loadmask wil be hooked into this..
21422             this.fireEvent("loadexception", this, o, options, o.raw.errorMsg);
21423             return;
21424         }
21425         var r = o.records, t = o.totalRecords || r.length;
21426         
21427         this.fireEvent("beforeloadadd", this, r, options, o);
21428         
21429         if(!options || options.add !== true){
21430             if(this.pruneModifiedRecords){
21431                 this.modified = [];
21432             }
21433             for(var i = 0, len = r.length; i < len; i++){
21434                 r[i].join(this);
21435             }
21436             if(this.snapshot){
21437                 this.data = this.snapshot;
21438                 delete this.snapshot;
21439             }
21440             this.data.clear();
21441             this.data.addAll(r);
21442             this.totalLength = t;
21443             this.applySort();
21444             this.fireEvent("datachanged", this);
21445         }else{
21446             this.totalLength = Math.max(t, this.data.length+r.length);
21447             this.add(r);
21448         }
21449         this.fireEvent("load", this, r, options, o);
21450         if(options.callback){
21451             options.callback.call(options.scope || this, r, options, true);
21452         }
21453     },
21454
21455
21456     /**
21457      * Loads data from a passed data block. A Reader which understands the format of the data
21458      * must have been configured in the constructor.
21459      * @param {Object} data The data block from which to read the Records.  The format of the data expected
21460      * is dependent on the type of Reader that is configured and should correspond to that Reader's readRecords parameter.
21461      * @param {Boolean} append (Optional) True to append the new Records rather than replace the existing cache.
21462      */
21463     loadData : function(o, append){
21464         var r = this.reader.readRecords(o);
21465         this.loadRecords(r, {add: append}, true);
21466     },
21467
21468     /**
21469      * Gets the number of cached records.
21470      * <p>
21471      * <em>If using paging, this may not be the total size of the dataset. If the data object
21472      * used by the Reader contains the dataset size, then the getTotalCount() function returns
21473      * the data set size</em>
21474      */
21475     getCount : function(){
21476         return this.data.length || 0;
21477     },
21478
21479     /**
21480      * Gets the total number of records in the dataset as returned by the server.
21481      * <p>
21482      * <em>If using paging, for this to be accurate, the data object used by the Reader must contain
21483      * the dataset size</em>
21484      */
21485     getTotalCount : function(){
21486         return this.totalLength || 0;
21487     },
21488
21489     /**
21490      * Returns the sort state of the Store as an object with two properties:
21491      * <pre><code>
21492  field {String} The name of the field by which the Records are sorted
21493  direction {String} The sort order, "ASC" or "DESC"
21494      * </code></pre>
21495      */
21496     getSortState : function(){
21497         return this.sortInfo;
21498     },
21499
21500     // private
21501     applySort : function(){
21502         if(this.sortInfo && !this.remoteSort){
21503             var s = this.sortInfo, f = s.field;
21504             var st = this.fields.get(f).sortType;
21505             var fn = function(r1, r2){
21506                 var v1 = st(r1.data[f]), v2 = st(r2.data[f]);
21507                 return v1 > v2 ? 1 : (v1 < v2 ? -1 : 0);
21508             };
21509             this.data.sort(s.direction, fn);
21510             if(this.snapshot && this.snapshot != this.data){
21511                 this.snapshot.sort(s.direction, fn);
21512             }
21513         }
21514     },
21515
21516     /**
21517      * Sets the default sort column and order to be used by the next load operation.
21518      * @param {String} fieldName The name of the field to sort by.
21519      * @param {String} dir (optional) The sort order, "ASC" or "DESC" (defaults to "ASC")
21520      */
21521     setDefaultSort : function(field, dir){
21522         this.sortInfo = {field: field, direction: dir ? dir.toUpperCase() : "ASC"};
21523     },
21524
21525     /**
21526      * Sort the Records.
21527      * If remote sorting is used, the sort is performed on the server, and the cache is
21528      * reloaded. If local sorting is used, the cache is sorted internally.
21529      * @param {String} fieldName The name of the field to sort by.
21530      * @param {String} dir (optional) The sort order, "ASC" or "DESC" (defaults to "ASC")
21531      */
21532     sort : function(fieldName, dir){
21533         var f = this.fields.get(fieldName);
21534         if(!dir){
21535             this.sortToggle[f.name] = this.sortToggle[f.name] || f.sortDir;
21536             
21537             if(this.multiSort || (this.sortInfo && this.sortInfo.field == f.name) ){ // toggle sort dir
21538                 dir = (this.sortToggle[f.name] || "ASC").toggle("ASC", "DESC");
21539             }else{
21540                 dir = f.sortDir;
21541             }
21542         }
21543         this.sortToggle[f.name] = dir;
21544         this.sortInfo = {field: f.name, direction: dir};
21545         if(!this.remoteSort){
21546             this.applySort();
21547             this.fireEvent("datachanged", this);
21548         }else{
21549             this.load(this.lastOptions);
21550         }
21551     },
21552
21553     /**
21554      * Calls the specified function for each of the Records in the cache.
21555      * @param {Function} fn The function to call. The Record is passed as the first parameter.
21556      * Returning <em>false</em> aborts and exits the iteration.
21557      * @param {Object} scope (optional) The scope in which to call the function (defaults to the Record).
21558      */
21559     each : function(fn, scope){
21560         this.data.each(fn, scope);
21561     },
21562
21563     /**
21564      * Gets all records modified since the last commit.  Modified records are persisted across load operations
21565      * (e.g., during paging).
21566      * @return {Roo.data.Record[]} An array of Records containing outstanding modifications.
21567      */
21568     getModifiedRecords : function(){
21569         return this.modified;
21570     },
21571
21572     // private
21573     createFilterFn : function(property, value, anyMatch){
21574         if(!value.exec){ // not a regex
21575             value = String(value);
21576             if(value.length == 0){
21577                 return false;
21578             }
21579             value = new RegExp((anyMatch === true ? '' : '^') + Roo.escapeRe(value), "i");
21580         }
21581         return function(r){
21582             return value.test(r.data[property]);
21583         };
21584     },
21585
21586     /**
21587      * Sums the value of <i>property</i> for each record between start and end and returns the result.
21588      * @param {String} property A field on your records
21589      * @param {Number} start The record index to start at (defaults to 0)
21590      * @param {Number} end The last record index to include (defaults to length - 1)
21591      * @return {Number} The sum
21592      */
21593     sum : function(property, start, end){
21594         var rs = this.data.items, v = 0;
21595         start = start || 0;
21596         end = (end || end === 0) ? end : rs.length-1;
21597
21598         for(var i = start; i <= end; i++){
21599             v += (rs[i].data[property] || 0);
21600         }
21601         return v;
21602     },
21603
21604     /**
21605      * Filter the records by a specified property.
21606      * @param {String} field A field on your records
21607      * @param {String/RegExp} value Either a string that the field
21608      * should start with or a RegExp to test against the field
21609      * @param {Boolean} anyMatch True to match any part not just the beginning
21610      */
21611     filter : function(property, value, anyMatch){
21612         var fn = this.createFilterFn(property, value, anyMatch);
21613         return fn ? this.filterBy(fn) : this.clearFilter();
21614     },
21615
21616     /**
21617      * Filter by a function. The specified function will be called with each
21618      * record in this data source. If the function returns true the record is included,
21619      * otherwise it is filtered.
21620      * @param {Function} fn The function to be called, it will receive 2 args (record, id)
21621      * @param {Object} scope (optional) The scope of the function (defaults to this)
21622      */
21623     filterBy : function(fn, scope){
21624         this.snapshot = this.snapshot || this.data;
21625         this.data = this.queryBy(fn, scope||this);
21626         this.fireEvent("datachanged", this);
21627     },
21628
21629     /**
21630      * Query the records by a specified property.
21631      * @param {String} field A field on your records
21632      * @param {String/RegExp} value Either a string that the field
21633      * should start with or a RegExp to test against the field
21634      * @param {Boolean} anyMatch True to match any part not just the beginning
21635      * @return {MixedCollection} Returns an Roo.util.MixedCollection of the matched records
21636      */
21637     query : function(property, value, anyMatch){
21638         var fn = this.createFilterFn(property, value, anyMatch);
21639         return fn ? this.queryBy(fn) : this.data.clone();
21640     },
21641
21642     /**
21643      * Query by a function. The specified function will be called with each
21644      * record in this data source. If the function returns true the record is included
21645      * in the results.
21646      * @param {Function} fn The function to be called, it will receive 2 args (record, id)
21647      * @param {Object} scope (optional) The scope of the function (defaults to this)
21648       @return {MixedCollection} Returns an Roo.util.MixedCollection of the matched records
21649      **/
21650     queryBy : function(fn, scope){
21651         var data = this.snapshot || this.data;
21652         return data.filterBy(fn, scope||this);
21653     },
21654
21655     /**
21656      * Collects unique values for a particular dataIndex from this store.
21657      * @param {String} dataIndex The property to collect
21658      * @param {Boolean} allowNull (optional) Pass true to allow null, undefined or empty string values
21659      * @param {Boolean} bypassFilter (optional) Pass true to collect from all records, even ones which are filtered
21660      * @return {Array} An array of the unique values
21661      **/
21662     collect : function(dataIndex, allowNull, bypassFilter){
21663         var d = (bypassFilter === true && this.snapshot) ?
21664                 this.snapshot.items : this.data.items;
21665         var v, sv, r = [], l = {};
21666         for(var i = 0, len = d.length; i < len; i++){
21667             v = d[i].data[dataIndex];
21668             sv = String(v);
21669             if((allowNull || !Roo.isEmpty(v)) && !l[sv]){
21670                 l[sv] = true;
21671                 r[r.length] = v;
21672             }
21673         }
21674         return r;
21675     },
21676
21677     /**
21678      * Revert to a view of the Record cache with no filtering applied.
21679      * @param {Boolean} suppressEvent If true the filter is cleared silently without notifying listeners
21680      */
21681     clearFilter : function(suppressEvent){
21682         if(this.snapshot && this.snapshot != this.data){
21683             this.data = this.snapshot;
21684             delete this.snapshot;
21685             if(suppressEvent !== true){
21686                 this.fireEvent("datachanged", this);
21687             }
21688         }
21689     },
21690
21691     // private
21692     afterEdit : function(record){
21693         if(this.modified.indexOf(record) == -1){
21694             this.modified.push(record);
21695         }
21696         this.fireEvent("update", this, record, Roo.data.Record.EDIT);
21697     },
21698     
21699     // private
21700     afterReject : function(record){
21701         this.modified.remove(record);
21702         this.fireEvent("update", this, record, Roo.data.Record.REJECT);
21703     },
21704
21705     // private
21706     afterCommit : function(record){
21707         this.modified.remove(record);
21708         this.fireEvent("update", this, record, Roo.data.Record.COMMIT);
21709     },
21710
21711     /**
21712      * Commit all Records with outstanding changes. To handle updates for changes, subscribe to the
21713      * Store's "update" event, and perform updating when the third parameter is Roo.data.Record.COMMIT.
21714      */
21715     commitChanges : function(){
21716         var m = this.modified.slice(0);
21717         this.modified = [];
21718         for(var i = 0, len = m.length; i < len; i++){
21719             m[i].commit();
21720         }
21721     },
21722
21723     /**
21724      * Cancel outstanding changes on all changed records.
21725      */
21726     rejectChanges : function(){
21727         var m = this.modified.slice(0);
21728         this.modified = [];
21729         for(var i = 0, len = m.length; i < len; i++){
21730             m[i].reject();
21731         }
21732     },
21733
21734     onMetaChange : function(meta, rtype, o){
21735         this.recordType = rtype;
21736         this.fields = rtype.prototype.fields;
21737         delete this.snapshot;
21738         this.sortInfo = meta.sortInfo || this.sortInfo;
21739         this.modified = [];
21740         this.fireEvent('metachange', this, this.reader.meta);
21741     }
21742 });/*
21743  * Based on:
21744  * Ext JS Library 1.1.1
21745  * Copyright(c) 2006-2007, Ext JS, LLC.
21746  *
21747  * Originally Released Under LGPL - original licence link has changed is not relivant.
21748  *
21749  * Fork - LGPL
21750  * <script type="text/javascript">
21751  */
21752
21753 /**
21754  * @class Roo.data.SimpleStore
21755  * @extends Roo.data.Store
21756  * Small helper class to make creating Stores from Array data easier.
21757  * @cfg {Number} id The array index of the record id. Leave blank to auto generate ids.
21758  * @cfg {Array} fields An array of field definition objects, or field name strings.
21759  * @cfg {Array} data The multi-dimensional array of data
21760  * @constructor
21761  * @param {Object} config
21762  */
21763 Roo.data.SimpleStore = function(config){
21764     Roo.data.SimpleStore.superclass.constructor.call(this, {
21765         isLocal : true,
21766         reader: new Roo.data.ArrayReader({
21767                 id: config.id
21768             },
21769             Roo.data.Record.create(config.fields)
21770         ),
21771         proxy : new Roo.data.MemoryProxy(config.data)
21772     });
21773     this.load();
21774 };
21775 Roo.extend(Roo.data.SimpleStore, Roo.data.Store);/*
21776  * Based on:
21777  * Ext JS Library 1.1.1
21778  * Copyright(c) 2006-2007, Ext JS, LLC.
21779  *
21780  * Originally Released Under LGPL - original licence link has changed is not relivant.
21781  *
21782  * Fork - LGPL
21783  * <script type="text/javascript">
21784  */
21785
21786 /**
21787 /**
21788  * @extends Roo.data.Store
21789  * @class Roo.data.JsonStore
21790  * Small helper class to make creating Stores for JSON data easier. <br/>
21791 <pre><code>
21792 var store = new Roo.data.JsonStore({
21793     url: 'get-images.php',
21794     root: 'images',
21795     fields: ['name', 'url', {name:'size', type: 'float'}, {name:'lastmod', type:'date'}]
21796 });
21797 </code></pre>
21798  * <b>Note: Although they are not listed, this class inherits all of the config options of Store,
21799  * JsonReader and HttpProxy (unless inline data is provided).</b>
21800  * @cfg {Array} fields An array of field definition objects, or field name strings.
21801  * @constructor
21802  * @param {Object} config
21803  */
21804 Roo.data.JsonStore = function(c){
21805     Roo.data.JsonStore.superclass.constructor.call(this, Roo.apply(c, {
21806         proxy: !c.data ? new Roo.data.HttpProxy({url: c.url}) : undefined,
21807         reader: new Roo.data.JsonReader(c, c.fields)
21808     }));
21809 };
21810 Roo.extend(Roo.data.JsonStore, Roo.data.Store);/*
21811  * Based on:
21812  * Ext JS Library 1.1.1
21813  * Copyright(c) 2006-2007, Ext JS, LLC.
21814  *
21815  * Originally Released Under LGPL - original licence link has changed is not relivant.
21816  *
21817  * Fork - LGPL
21818  * <script type="text/javascript">
21819  */
21820
21821  
21822 Roo.data.Field = function(config){
21823     if(typeof config == "string"){
21824         config = {name: config};
21825     }
21826     Roo.apply(this, config);
21827     
21828     if(!this.type){
21829         this.type = "auto";
21830     }
21831     
21832     var st = Roo.data.SortTypes;
21833     // named sortTypes are supported, here we look them up
21834     if(typeof this.sortType == "string"){
21835         this.sortType = st[this.sortType];
21836     }
21837     
21838     // set default sortType for strings and dates
21839     if(!this.sortType){
21840         switch(this.type){
21841             case "string":
21842                 this.sortType = st.asUCString;
21843                 break;
21844             case "date":
21845                 this.sortType = st.asDate;
21846                 break;
21847             default:
21848                 this.sortType = st.none;
21849         }
21850     }
21851
21852     // define once
21853     var stripRe = /[\$,%]/g;
21854
21855     // prebuilt conversion function for this field, instead of
21856     // switching every time we're reading a value
21857     if(!this.convert){
21858         var cv, dateFormat = this.dateFormat;
21859         switch(this.type){
21860             case "":
21861             case "auto":
21862             case undefined:
21863                 cv = function(v){ return v; };
21864                 break;
21865             case "string":
21866                 cv = function(v){ return (v === undefined || v === null) ? '' : String(v); };
21867                 break;
21868             case "int":
21869                 cv = function(v){
21870                     return v !== undefined && v !== null && v !== '' ?
21871                            parseInt(String(v).replace(stripRe, ""), 10) : '';
21872                     };
21873                 break;
21874             case "float":
21875                 cv = function(v){
21876                     return v !== undefined && v !== null && v !== '' ?
21877                            parseFloat(String(v).replace(stripRe, ""), 10) : ''; 
21878                     };
21879                 break;
21880             case "bool":
21881             case "boolean":
21882                 cv = function(v){ return v === true || v === "true" || v == 1; };
21883                 break;
21884             case "date":
21885                 cv = function(v){
21886                     if(!v){
21887                         return '';
21888                     }
21889                     if(v instanceof Date){
21890                         return v;
21891                     }
21892                     if(dateFormat){
21893                         if(dateFormat == "timestamp"){
21894                             return new Date(v*1000);
21895                         }
21896                         return Date.parseDate(v, dateFormat);
21897                     }
21898                     var parsed = Date.parse(v);
21899                     return parsed ? new Date(parsed) : null;
21900                 };
21901              break;
21902             
21903         }
21904         this.convert = cv;
21905     }
21906 };
21907
21908 Roo.data.Field.prototype = {
21909     dateFormat: null,
21910     defaultValue: "",
21911     mapping: null,
21912     sortType : null,
21913     sortDir : "ASC"
21914 };/*
21915  * Based on:
21916  * Ext JS Library 1.1.1
21917  * Copyright(c) 2006-2007, Ext JS, LLC.
21918  *
21919  * Originally Released Under LGPL - original licence link has changed is not relivant.
21920  *
21921  * Fork - LGPL
21922  * <script type="text/javascript">
21923  */
21924  
21925 // Base class for reading structured data from a data source.  This class is intended to be
21926 // extended (see ArrayReader, JsonReader and XmlReader) and should not be created directly.
21927
21928 /**
21929  * @class Roo.data.DataReader
21930  * Base class for reading structured data from a data source.  This class is intended to be
21931  * extended (see {Roo.data.ArrayReader}, {Roo.data.JsonReader} and {Roo.data.XmlReader}) and should not be created directly.
21932  */
21933
21934 Roo.data.DataReader = function(meta, recordType){
21935     
21936     this.meta = meta;
21937     
21938     this.recordType = recordType instanceof Array ? 
21939         Roo.data.Record.create(recordType) : recordType;
21940 };
21941
21942 Roo.data.DataReader.prototype = {
21943      /**
21944      * Create an empty record
21945      * @param {Object} data (optional) - overlay some values
21946      * @return {Roo.data.Record} record created.
21947      */
21948     newRow :  function(d) {
21949         var da =  {};
21950         this.recordType.prototype.fields.each(function(c) {
21951             switch( c.type) {
21952                 case 'int' : da[c.name] = 0; break;
21953                 case 'date' : da[c.name] = new Date(); break;
21954                 case 'float' : da[c.name] = 0.0; break;
21955                 case 'boolean' : da[c.name] = false; break;
21956                 default : da[c.name] = ""; break;
21957             }
21958             
21959         });
21960         return new this.recordType(Roo.apply(da, d));
21961     }
21962     
21963 };/*
21964  * Based on:
21965  * Ext JS Library 1.1.1
21966  * Copyright(c) 2006-2007, Ext JS, LLC.
21967  *
21968  * Originally Released Under LGPL - original licence link has changed is not relivant.
21969  *
21970  * Fork - LGPL
21971  * <script type="text/javascript">
21972  */
21973
21974 /**
21975  * @class Roo.data.DataProxy
21976  * @extends Roo.data.Observable
21977  * This class is an abstract base class for implementations which provide retrieval of
21978  * unformatted data objects.<br>
21979  * <p>
21980  * DataProxy implementations are usually used in conjunction with an implementation of Roo.data.DataReader
21981  * (of the appropriate type which knows how to parse the data object) to provide a block of
21982  * {@link Roo.data.Records} to an {@link Roo.data.Store}.<br>
21983  * <p>
21984  * Custom implementations must implement the load method as described in
21985  * {@link Roo.data.HttpProxy#load}.
21986  */
21987 Roo.data.DataProxy = function(){
21988     this.addEvents({
21989         /**
21990          * @event beforeload
21991          * Fires before a network request is made to retrieve a data object.
21992          * @param {Object} This DataProxy object.
21993          * @param {Object} params The params parameter to the load function.
21994          */
21995         beforeload : true,
21996         /**
21997          * @event load
21998          * Fires before the load method's callback is called.
21999          * @param {Object} This DataProxy object.
22000          * @param {Object} o The data object.
22001          * @param {Object} arg The callback argument object passed to the load function.
22002          */
22003         load : true,
22004         /**
22005          * @event loadexception
22006          * Fires if an Exception occurs during data retrieval.
22007          * @param {Object} This DataProxy object.
22008          * @param {Object} o The data object.
22009          * @param {Object} arg The callback argument object passed to the load function.
22010          * @param {Object} e The Exception.
22011          */
22012         loadexception : true
22013     });
22014     Roo.data.DataProxy.superclass.constructor.call(this);
22015 };
22016
22017 Roo.extend(Roo.data.DataProxy, Roo.util.Observable);
22018
22019     /**
22020      * @cfg {void} listeners (Not available) Constructor blocks listeners from being set
22021      */
22022 /*
22023  * Based on:
22024  * Ext JS Library 1.1.1
22025  * Copyright(c) 2006-2007, Ext JS, LLC.
22026  *
22027  * Originally Released Under LGPL - original licence link has changed is not relivant.
22028  *
22029  * Fork - LGPL
22030  * <script type="text/javascript">
22031  */
22032 /**
22033  * @class Roo.data.MemoryProxy
22034  * An implementation of Roo.data.DataProxy that simply passes the data specified in its constructor
22035  * to the Reader when its load method is called.
22036  * @constructor
22037  * @param {Object} data The data object which the Reader uses to construct a block of Roo.data.Records.
22038  */
22039 Roo.data.MemoryProxy = function(data){
22040     if (data.data) {
22041         data = data.data;
22042     }
22043     Roo.data.MemoryProxy.superclass.constructor.call(this);
22044     this.data = data;
22045 };
22046
22047 Roo.extend(Roo.data.MemoryProxy, Roo.data.DataProxy, {
22048     /**
22049      * Load data from the requested source (in this case an in-memory
22050      * data object passed to the constructor), read the data object into
22051      * a block of Roo.data.Records using the passed Roo.data.DataReader implementation, and
22052      * process that block using the passed callback.
22053      * @param {Object} params This parameter is not used by the MemoryProxy class.
22054      * @param {Roo.data.DataReader} reader The Reader object which converts the data
22055      * object into a block of Roo.data.Records.
22056      * @param {Function} callback The function into which to pass the block of Roo.data.records.
22057      * The function must be passed <ul>
22058      * <li>The Record block object</li>
22059      * <li>The "arg" argument from the load function</li>
22060      * <li>A boolean success indicator</li>
22061      * </ul>
22062      * @param {Object} scope The scope in which to call the callback
22063      * @param {Object} arg An optional argument which is passed to the callback as its second parameter.
22064      */
22065     load : function(params, reader, callback, scope, arg){
22066         params = params || {};
22067         var result;
22068         try {
22069             result = reader.readRecords(this.data);
22070         }catch(e){
22071             this.fireEvent("loadexception", this, arg, null, e);
22072             callback.call(scope, null, arg, false);
22073             return;
22074         }
22075         callback.call(scope, result, arg, true);
22076     },
22077     
22078     // private
22079     update : function(params, records){
22080         
22081     }
22082 });/*
22083  * Based on:
22084  * Ext JS Library 1.1.1
22085  * Copyright(c) 2006-2007, Ext JS, LLC.
22086  *
22087  * Originally Released Under LGPL - original licence link has changed is not relivant.
22088  *
22089  * Fork - LGPL
22090  * <script type="text/javascript">
22091  */
22092 /**
22093  * @class Roo.data.HttpProxy
22094  * @extends Roo.data.DataProxy
22095  * An implementation of {@link Roo.data.DataProxy} that reads a data object from an {@link Roo.data.Connection} object
22096  * configured to reference a certain URL.<br><br>
22097  * <p>
22098  * <em>Note that this class cannot be used to retrieve data from a domain other than the domain
22099  * from which the running page was served.<br><br>
22100  * <p>
22101  * For cross-domain access to remote data, use an {@link Roo.data.ScriptTagProxy}.</em><br><br>
22102  * <p>
22103  * Be aware that to enable the browser to parse an XML document, the server must set
22104  * the Content-Type header in the HTTP response to "text/xml".
22105  * @constructor
22106  * @param {Object} conn Connection config options to add to each request (e.g. {url: 'foo.php'} or
22107  * an {@link Roo.data.Connection} object.  If a Connection config is passed, the singleton {@link Roo.Ajax} object
22108  * will be used to make the request.
22109  */
22110 Roo.data.HttpProxy = function(conn){
22111     Roo.data.HttpProxy.superclass.constructor.call(this);
22112     // is conn a conn config or a real conn?
22113     this.conn = conn;
22114     this.useAjax = !conn || !conn.events;
22115   
22116 };
22117
22118 Roo.extend(Roo.data.HttpProxy, Roo.data.DataProxy, {
22119     // thse are take from connection...
22120     
22121     /**
22122      * @cfg {String} url (Optional) The default URL to be used for requests to the server. (defaults to undefined)
22123      */
22124     /**
22125      * @cfg {Object} extraParams (Optional) An object containing properties which are used as
22126      * extra parameters to each request made by this object. (defaults to undefined)
22127      */
22128     /**
22129      * @cfg {Object} defaultHeaders (Optional) An object containing request headers which are added
22130      *  to each request made by this object. (defaults to undefined)
22131      */
22132     /**
22133      * @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)
22134      */
22135     /**
22136      * @cfg {Number} timeout (Optional) The timeout in milliseconds to be used for requests. (defaults to 30000)
22137      */
22138      /**
22139      * @cfg {Boolean} autoAbort (Optional) Whether this request should abort any pending requests. (defaults to false)
22140      * @type Boolean
22141      */
22142   
22143
22144     /**
22145      * @cfg {Boolean} disableCaching (Optional) True to add a unique cache-buster param to GET requests. (defaults to true)
22146      * @type Boolean
22147      */
22148     /**
22149      * Return the {@link Roo.data.Connection} object being used by this Proxy.
22150      * @return {Connection} The Connection object. This object may be used to subscribe to events on
22151      * a finer-grained basis than the DataProxy events.
22152      */
22153     getConnection : function(){
22154         return this.useAjax ? Roo.Ajax : this.conn;
22155     },
22156
22157     /**
22158      * Load data from the configured {@link Roo.data.Connection}, read the data object into
22159      * a block of Roo.data.Records using the passed {@link Roo.data.DataReader} implementation, and
22160      * process that block using the passed callback.
22161      * @param {Object} params An object containing properties which are to be used as HTTP parameters
22162      * for the request to the remote server.
22163      * @param {Roo.data.DataReader} reader The Reader object which converts the data
22164      * object into a block of Roo.data.Records.
22165      * @param {Function} callback The function into which to pass the block of Roo.data.Records.
22166      * The function must be passed <ul>
22167      * <li>The Record block object</li>
22168      * <li>The "arg" argument from the load function</li>
22169      * <li>A boolean success indicator</li>
22170      * </ul>
22171      * @param {Object} scope The scope in which to call the callback
22172      * @param {Object} arg An optional argument which is passed to the callback as its second parameter.
22173      */
22174     load : function(params, reader, callback, scope, arg){
22175         if(this.fireEvent("beforeload", this, params) !== false){
22176             var  o = {
22177                 params : params || {},
22178                 request: {
22179                     callback : callback,
22180                     scope : scope,
22181                     arg : arg
22182                 },
22183                 reader: reader,
22184                 callback : this.loadResponse,
22185                 scope: this
22186             };
22187             if(this.useAjax){
22188                 Roo.applyIf(o, this.conn);
22189                 if(this.activeRequest){
22190                     Roo.Ajax.abort(this.activeRequest);
22191                 }
22192                 this.activeRequest = Roo.Ajax.request(o);
22193             }else{
22194                 this.conn.request(o);
22195             }
22196         }else{
22197             callback.call(scope||this, null, arg, false);
22198         }
22199     },
22200
22201     // private
22202     loadResponse : function(o, success, response){
22203         delete this.activeRequest;
22204         if(!success){
22205             this.fireEvent("loadexception", this, o, response);
22206             o.request.callback.call(o.request.scope, null, o.request.arg, false);
22207             return;
22208         }
22209         var result;
22210         try {
22211             result = o.reader.read(response);
22212         }catch(e){
22213             this.fireEvent("loadexception", this, o, response, e);
22214             o.request.callback.call(o.request.scope, null, o.request.arg, false);
22215             return;
22216         }
22217         
22218         this.fireEvent("load", this, o, o.request.arg);
22219         o.request.callback.call(o.request.scope, result, o.request.arg, true);
22220     },
22221
22222     // private
22223     update : function(dataSet){
22224
22225     },
22226
22227     // private
22228     updateResponse : function(dataSet){
22229
22230     }
22231 });/*
22232  * Based on:
22233  * Ext JS Library 1.1.1
22234  * Copyright(c) 2006-2007, Ext JS, LLC.
22235  *
22236  * Originally Released Under LGPL - original licence link has changed is not relivant.
22237  *
22238  * Fork - LGPL
22239  * <script type="text/javascript">
22240  */
22241
22242 /**
22243  * @class Roo.data.ScriptTagProxy
22244  * An implementation of Roo.data.DataProxy that reads a data object from a URL which may be in a domain
22245  * other than the originating domain of the running page.<br><br>
22246  * <p>
22247  * <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
22248  * of the running page, you must use this class, rather than DataProxy.</em><br><br>
22249  * <p>
22250  * The content passed back from a server resource requested by a ScriptTagProxy is executable JavaScript
22251  * source code that is used as the source inside a &lt;script> tag.<br><br>
22252  * <p>
22253  * In order for the browser to process the returned data, the server must wrap the data object
22254  * with a call to a callback function, the name of which is passed as a parameter by the ScriptTagProxy.
22255  * Below is a Java example for a servlet which returns data for either a ScriptTagProxy, or an HttpProxy
22256  * depending on whether the callback name was passed:
22257  * <p>
22258  * <pre><code>
22259 boolean scriptTag = false;
22260 String cb = request.getParameter("callback");
22261 if (cb != null) {
22262     scriptTag = true;
22263     response.setContentType("text/javascript");
22264 } else {
22265     response.setContentType("application/x-json");
22266 }
22267 Writer out = response.getWriter();
22268 if (scriptTag) {
22269     out.write(cb + "(");
22270 }
22271 out.print(dataBlock.toJsonString());
22272 if (scriptTag) {
22273     out.write(");");
22274 }
22275 </pre></code>
22276  *
22277  * @constructor
22278  * @param {Object} config A configuration object.
22279  */
22280 Roo.data.ScriptTagProxy = function(config){
22281     Roo.data.ScriptTagProxy.superclass.constructor.call(this);
22282     Roo.apply(this, config);
22283     this.head = document.getElementsByTagName("head")[0];
22284 };
22285
22286 Roo.data.ScriptTagProxy.TRANS_ID = 1000;
22287
22288 Roo.extend(Roo.data.ScriptTagProxy, Roo.data.DataProxy, {
22289     /**
22290      * @cfg {String} url The URL from which to request the data object.
22291      */
22292     /**
22293      * @cfg {Number} timeout (Optional) The number of milliseconds to wait for a response. Defaults to 30 seconds.
22294      */
22295     timeout : 30000,
22296     /**
22297      * @cfg {String} callbackParam (Optional) The name of the parameter to pass to the server which tells
22298      * the server the name of the callback function set up by the load call to process the returned data object.
22299      * Defaults to "callback".<p>The server-side processing must read this parameter value, and generate
22300      * javascript output which calls this named function passing the data object as its only parameter.
22301      */
22302     callbackParam : "callback",
22303     /**
22304      *  @cfg {Boolean} nocache (Optional) Defaults to true. Disable cacheing by adding a unique parameter
22305      * name to the request.
22306      */
22307     nocache : true,
22308
22309     /**
22310      * Load data from the configured URL, read the data object into
22311      * a block of Roo.data.Records using the passed Roo.data.DataReader implementation, and
22312      * process that block using the passed callback.
22313      * @param {Object} params An object containing properties which are to be used as HTTP parameters
22314      * for the request to the remote server.
22315      * @param {Roo.data.DataReader} reader The Reader object which converts the data
22316      * object into a block of Roo.data.Records.
22317      * @param {Function} callback The function into which to pass the block of Roo.data.Records.
22318      * The function must be passed <ul>
22319      * <li>The Record block object</li>
22320      * <li>The "arg" argument from the load function</li>
22321      * <li>A boolean success indicator</li>
22322      * </ul>
22323      * @param {Object} scope The scope in which to call the callback
22324      * @param {Object} arg An optional argument which is passed to the callback as its second parameter.
22325      */
22326     load : function(params, reader, callback, scope, arg){
22327         if(this.fireEvent("beforeload", this, params) !== false){
22328
22329             var p = Roo.urlEncode(Roo.apply(params, this.extraParams));
22330
22331             var url = this.url;
22332             url += (url.indexOf("?") != -1 ? "&" : "?") + p;
22333             if(this.nocache){
22334                 url += "&_dc=" + (new Date().getTime());
22335             }
22336             var transId = ++Roo.data.ScriptTagProxy.TRANS_ID;
22337             var trans = {
22338                 id : transId,
22339                 cb : "stcCallback"+transId,
22340                 scriptId : "stcScript"+transId,
22341                 params : params,
22342                 arg : arg,
22343                 url : url,
22344                 callback : callback,
22345                 scope : scope,
22346                 reader : reader
22347             };
22348             var conn = this;
22349
22350             window[trans.cb] = function(o){
22351                 conn.handleResponse(o, trans);
22352             };
22353
22354             url += String.format("&{0}={1}", this.callbackParam, trans.cb);
22355
22356             if(this.autoAbort !== false){
22357                 this.abort();
22358             }
22359
22360             trans.timeoutId = this.handleFailure.defer(this.timeout, this, [trans]);
22361
22362             var script = document.createElement("script");
22363             script.setAttribute("src", url);
22364             script.setAttribute("type", "text/javascript");
22365             script.setAttribute("id", trans.scriptId);
22366             this.head.appendChild(script);
22367
22368             this.trans = trans;
22369         }else{
22370             callback.call(scope||this, null, arg, false);
22371         }
22372     },
22373
22374     // private
22375     isLoading : function(){
22376         return this.trans ? true : false;
22377     },
22378
22379     /**
22380      * Abort the current server request.
22381      */
22382     abort : function(){
22383         if(this.isLoading()){
22384             this.destroyTrans(this.trans);
22385         }
22386     },
22387
22388     // private
22389     destroyTrans : function(trans, isLoaded){
22390         this.head.removeChild(document.getElementById(trans.scriptId));
22391         clearTimeout(trans.timeoutId);
22392         if(isLoaded){
22393             window[trans.cb] = undefined;
22394             try{
22395                 delete window[trans.cb];
22396             }catch(e){}
22397         }else{
22398             // if hasn't been loaded, wait for load to remove it to prevent script error
22399             window[trans.cb] = function(){
22400                 window[trans.cb] = undefined;
22401                 try{
22402                     delete window[trans.cb];
22403                 }catch(e){}
22404             };
22405         }
22406     },
22407
22408     // private
22409     handleResponse : function(o, trans){
22410         this.trans = false;
22411         this.destroyTrans(trans, true);
22412         var result;
22413         try {
22414             result = trans.reader.readRecords(o);
22415         }catch(e){
22416             this.fireEvent("loadexception", this, o, trans.arg, e);
22417             trans.callback.call(trans.scope||window, null, trans.arg, false);
22418             return;
22419         }
22420         this.fireEvent("load", this, o, trans.arg);
22421         trans.callback.call(trans.scope||window, result, trans.arg, true);
22422     },
22423
22424     // private
22425     handleFailure : function(trans){
22426         this.trans = false;
22427         this.destroyTrans(trans, false);
22428         this.fireEvent("loadexception", this, null, trans.arg);
22429         trans.callback.call(trans.scope||window, null, trans.arg, false);
22430     }
22431 });/*
22432  * Based on:
22433  * Ext JS Library 1.1.1
22434  * Copyright(c) 2006-2007, Ext JS, LLC.
22435  *
22436  * Originally Released Under LGPL - original licence link has changed is not relivant.
22437  *
22438  * Fork - LGPL
22439  * <script type="text/javascript">
22440  */
22441
22442 /**
22443  * @class Roo.data.JsonReader
22444  * @extends Roo.data.DataReader
22445  * Data reader class to create an Array of Roo.data.Record objects from a JSON response
22446  * based on mappings in a provided Roo.data.Record constructor.
22447  * 
22448  * The default behaviour of a store is to send ?_requestMeta=1, unless the class has recieved 'metaData' property
22449  * in the reply previously. 
22450  * 
22451  * <p>
22452  * Example code:
22453  * <pre><code>
22454 var RecordDef = Roo.data.Record.create([
22455     {name: 'name', mapping: 'name'},     // "mapping" property not needed if it's the same as "name"
22456     {name: 'occupation'}                 // This field will use "occupation" as the mapping.
22457 ]);
22458 var myReader = new Roo.data.JsonReader({
22459     totalProperty: "results",    // The property which contains the total dataset size (optional)
22460     root: "rows",                // The property which contains an Array of row objects
22461     id: "id"                     // The property within each row object that provides an ID for the record (optional)
22462 }, RecordDef);
22463 </code></pre>
22464  * <p>
22465  * This would consume a JSON file like this:
22466  * <pre><code>
22467 { 'results': 2, 'rows': [
22468     { 'id': 1, 'name': 'Bill', occupation: 'Gardener' },
22469     { 'id': 2, 'name': 'Ben', occupation: 'Horticulturalist' } ]
22470 }
22471 </code></pre>
22472  * @cfg {String} totalProperty Name of the property from which to retrieve the total number of records
22473  * in the dataset. This is only needed if the whole dataset is not passed in one go, but is being
22474  * paged from the remote server.
22475  * @cfg {String} successProperty Name of the property from which to retrieve the success attribute used by forms.
22476  * @cfg {String} root name of the property which contains the Array of row objects.
22477  * @cfg {String} id Name of the property within a row object that contains a record identifier value.
22478  * @constructor
22479  * Create a new JsonReader
22480  * @param {Object} meta Metadata configuration options
22481  * @param {Object} recordType Either an Array of field definition objects,
22482  * or an {@link Roo.data.Record} object created using {@link Roo.data.Record#create}.
22483  */
22484 Roo.data.JsonReader = function(meta, recordType){
22485     
22486     meta = meta || {};
22487     // set some defaults:
22488     Roo.applyIf(meta, {
22489         totalProperty: 'total',
22490         successProperty : 'success',
22491         root : 'data',
22492         id : 'id'
22493     });
22494     
22495     Roo.data.JsonReader.superclass.constructor.call(this, meta, recordType||meta.fields);
22496 };
22497 Roo.extend(Roo.data.JsonReader, Roo.data.DataReader, {
22498     
22499     /**
22500      * @prop {Boolean} metaFromRemote  - if the meta data was loaded from the remote source.
22501      * Used by Store query builder to append _requestMeta to params.
22502      * 
22503      */
22504     metaFromRemote : false,
22505     /**
22506      * This method is only used by a DataProxy which has retrieved data from a remote server.
22507      * @param {Object} response The XHR object which contains the JSON data in its responseText.
22508      * @return {Object} data A data block which is used by an Roo.data.Store object as
22509      * a cache of Roo.data.Records.
22510      */
22511     read : function(response){
22512         var json = response.responseText;
22513        
22514         var o = /* eval:var:o */ eval("("+json+")");
22515         if(!o) {
22516             throw {message: "JsonReader.read: Json object not found"};
22517         }
22518         
22519         if(o.metaData){
22520             
22521             delete this.ef;
22522             this.metaFromRemote = true;
22523             this.meta = o.metaData;
22524             this.recordType = Roo.data.Record.create(o.metaData.fields);
22525             this.onMetaChange(this.meta, this.recordType, o);
22526         }
22527         return this.readRecords(o);
22528     },
22529
22530     // private function a store will implement
22531     onMetaChange : function(meta, recordType, o){
22532
22533     },
22534
22535     /**
22536          * @ignore
22537          */
22538     simpleAccess: function(obj, subsc) {
22539         return obj[subsc];
22540     },
22541
22542         /**
22543          * @ignore
22544          */
22545     getJsonAccessor: function(){
22546         var re = /[\[\.]/;
22547         return function(expr) {
22548             try {
22549                 return(re.test(expr))
22550                     ? new Function("obj", "return obj." + expr)
22551                     : function(obj){
22552                         return obj[expr];
22553                     };
22554             } catch(e){}
22555             return Roo.emptyFn;
22556         };
22557     }(),
22558
22559     /**
22560      * Create a data block containing Roo.data.Records from an XML document.
22561      * @param {Object} o An object which contains an Array of row objects in the property specified
22562      * in the config as 'root, and optionally a property, specified in the config as 'totalProperty'
22563      * which contains the total size of the dataset.
22564      * @return {Object} data A data block which is used by an Roo.data.Store object as
22565      * a cache of Roo.data.Records.
22566      */
22567     readRecords : function(o){
22568         /**
22569          * After any data loads, the raw JSON data is available for further custom processing.
22570          * @type Object
22571          */
22572         this.o = o;
22573         var s = this.meta, Record = this.recordType,
22574             f = Record.prototype.fields, fi = f.items, fl = f.length;
22575
22576 //      Generate extraction functions for the totalProperty, the root, the id, and for each field
22577         if (!this.ef) {
22578             if(s.totalProperty) {
22579                     this.getTotal = this.getJsonAccessor(s.totalProperty);
22580                 }
22581                 if(s.successProperty) {
22582                     this.getSuccess = this.getJsonAccessor(s.successProperty);
22583                 }
22584                 this.getRoot = s.root ? this.getJsonAccessor(s.root) : function(p){return p;};
22585                 if (s.id) {
22586                         var g = this.getJsonAccessor(s.id);
22587                         this.getId = function(rec) {
22588                                 var r = g(rec);
22589                                 return (r === undefined || r === "") ? null : r;
22590                         };
22591                 } else {
22592                         this.getId = function(){return null;};
22593                 }
22594             this.ef = [];
22595             for(var jj = 0; jj < fl; jj++){
22596                 f = fi[jj];
22597                 var map = (f.mapping !== undefined && f.mapping !== null) ? f.mapping : f.name;
22598                 this.ef[jj] = this.getJsonAccessor(map);
22599             }
22600         }
22601
22602         var root = this.getRoot(o), c = root.length, totalRecords = c, success = true;
22603         if(s.totalProperty){
22604             var vt = parseInt(this.getTotal(o), 10);
22605             if(!isNaN(vt)){
22606                 totalRecords = vt;
22607             }
22608         }
22609         if(s.successProperty){
22610             var vs = this.getSuccess(o);
22611             if(vs === false || vs === 'false'){
22612                 success = false;
22613             }
22614         }
22615         var records = [];
22616             for(var i = 0; i < c; i++){
22617                     var n = root[i];
22618                 var values = {};
22619                 var id = this.getId(n);
22620                 for(var j = 0; j < fl; j++){
22621                     f = fi[j];
22622                 var v = this.ef[j](n);
22623                 if (!f.convert) {
22624                     Roo.log('missing convert for ' + f.name);
22625                     Roo.log(f);
22626                     continue;
22627                 }
22628                 values[f.name] = f.convert((v !== undefined) ? v : f.defaultValue);
22629                 }
22630                 var record = new Record(values, id);
22631                 record.json = n;
22632                 records[i] = record;
22633             }
22634             return {
22635             raw : o,
22636                 success : success,
22637                 records : records,
22638                 totalRecords : totalRecords
22639             };
22640     }
22641 });/*
22642  * Based on:
22643  * Ext JS Library 1.1.1
22644  * Copyright(c) 2006-2007, Ext JS, LLC.
22645  *
22646  * Originally Released Under LGPL - original licence link has changed is not relivant.
22647  *
22648  * Fork - LGPL
22649  * <script type="text/javascript">
22650  */
22651
22652 /**
22653  * @class Roo.data.XmlReader
22654  * @extends Roo.data.DataReader
22655  * Data reader class to create an Array of {@link Roo.data.Record} objects from an XML document
22656  * based on mappings in a provided Roo.data.Record constructor.<br><br>
22657  * <p>
22658  * <em>Note that in order for the browser to parse a returned XML document, the Content-Type
22659  * header in the HTTP response must be set to "text/xml".</em>
22660  * <p>
22661  * Example code:
22662  * <pre><code>
22663 var RecordDef = Roo.data.Record.create([
22664    {name: 'name', mapping: 'name'},     // "mapping" property not needed if it's the same as "name"
22665    {name: 'occupation'}                 // This field will use "occupation" as the mapping.
22666 ]);
22667 var myReader = new Roo.data.XmlReader({
22668    totalRecords: "results", // The element which contains the total dataset size (optional)
22669    record: "row",           // The repeated element which contains row information
22670    id: "id"                 // The element within the row that provides an ID for the record (optional)
22671 }, RecordDef);
22672 </code></pre>
22673  * <p>
22674  * This would consume an XML file like this:
22675  * <pre><code>
22676 &lt;?xml?>
22677 &lt;dataset>
22678  &lt;results>2&lt;/results>
22679  &lt;row>
22680    &lt;id>1&lt;/id>
22681    &lt;name>Bill&lt;/name>
22682    &lt;occupation>Gardener&lt;/occupation>
22683  &lt;/row>
22684  &lt;row>
22685    &lt;id>2&lt;/id>
22686    &lt;name>Ben&lt;/name>
22687    &lt;occupation>Horticulturalist&lt;/occupation>
22688  &lt;/row>
22689 &lt;/dataset>
22690 </code></pre>
22691  * @cfg {String} totalRecords The DomQuery path from which to retrieve the total number of records
22692  * in the dataset. This is only needed if the whole dataset is not passed in one go, but is being
22693  * paged from the remote server.
22694  * @cfg {String} record The DomQuery path to the repeated element which contains record information.
22695  * @cfg {String} success The DomQuery path to the success attribute used by forms.
22696  * @cfg {String} id The DomQuery path relative from the record element to the element that contains
22697  * a record identifier value.
22698  * @constructor
22699  * Create a new XmlReader
22700  * @param {Object} meta Metadata configuration options
22701  * @param {Mixed} recordType The definition of the data record type to produce.  This can be either a valid
22702  * Record subclass created with {@link Roo.data.Record#create}, or an array of objects with which to call
22703  * Roo.data.Record.create.  See the {@link Roo.data.Record} class for more details.
22704  */
22705 Roo.data.XmlReader = function(meta, recordType){
22706     meta = meta || {};
22707     Roo.data.XmlReader.superclass.constructor.call(this, meta, recordType||meta.fields);
22708 };
22709 Roo.extend(Roo.data.XmlReader, Roo.data.DataReader, {
22710     /**
22711      * This method is only used by a DataProxy which has retrieved data from a remote server.
22712          * @param {Object} response The XHR object which contains the parsed XML document.  The response is expected
22713          * to contain a method called 'responseXML' that returns an XML document object.
22714      * @return {Object} records A data block which is used by an {@link Roo.data.Store} as
22715      * a cache of Roo.data.Records.
22716      */
22717     read : function(response){
22718         var doc = response.responseXML;
22719         if(!doc) {
22720             throw {message: "XmlReader.read: XML Document not available"};
22721         }
22722         return this.readRecords(doc);
22723     },
22724
22725     /**
22726      * Create a data block containing Roo.data.Records from an XML document.
22727          * @param {Object} doc A parsed XML document.
22728      * @return {Object} records A data block which is used by an {@link Roo.data.Store} as
22729      * a cache of Roo.data.Records.
22730      */
22731     readRecords : function(doc){
22732         /**
22733          * After any data loads/reads, the raw XML Document is available for further custom processing.
22734          * @type XMLDocument
22735          */
22736         this.xmlData = doc;
22737         var root = doc.documentElement || doc;
22738         var q = Roo.DomQuery;
22739         var recordType = this.recordType, fields = recordType.prototype.fields;
22740         var sid = this.meta.id;
22741         var totalRecords = 0, success = true;
22742         if(this.meta.totalRecords){
22743             totalRecords = q.selectNumber(this.meta.totalRecords, root, 0);
22744         }
22745         
22746         if(this.meta.success){
22747             var sv = q.selectValue(this.meta.success, root, true);
22748             success = sv !== false && sv !== 'false';
22749         }
22750         var records = [];
22751         var ns = q.select(this.meta.record, root);
22752         for(var i = 0, len = ns.length; i < len; i++) {
22753                 var n = ns[i];
22754                 var values = {};
22755                 var id = sid ? q.selectValue(sid, n) : undefined;
22756                 for(var j = 0, jlen = fields.length; j < jlen; j++){
22757                     var f = fields.items[j];
22758                 var v = q.selectValue(f.mapping || f.name, n, f.defaultValue);
22759                     v = f.convert(v);
22760                     values[f.name] = v;
22761                 }
22762                 var record = new recordType(values, id);
22763                 record.node = n;
22764                 records[records.length] = record;
22765             }
22766
22767             return {
22768                 success : success,
22769                 records : records,
22770                 totalRecords : totalRecords || records.length
22771             };
22772     }
22773 });/*
22774  * Based on:
22775  * Ext JS Library 1.1.1
22776  * Copyright(c) 2006-2007, Ext JS, LLC.
22777  *
22778  * Originally Released Under LGPL - original licence link has changed is not relivant.
22779  *
22780  * Fork - LGPL
22781  * <script type="text/javascript">
22782  */
22783
22784 /**
22785  * @class Roo.data.ArrayReader
22786  * @extends Roo.data.DataReader
22787  * Data reader class to create an Array of Roo.data.Record objects from an Array.
22788  * Each element of that Array represents a row of data fields. The
22789  * fields are pulled into a Record object using as a subscript, the <em>mapping</em> property
22790  * of the field definition if it exists, or the field's ordinal position in the definition.<br>
22791  * <p>
22792  * Example code:.
22793  * <pre><code>
22794 var RecordDef = Roo.data.Record.create([
22795     {name: 'name', mapping: 1},         // "mapping" only needed if an "id" field is present which
22796     {name: 'occupation', mapping: 2}    // precludes using the ordinal position as the index.
22797 ]);
22798 var myReader = new Roo.data.ArrayReader({
22799     id: 0                     // The subscript within row Array that provides an ID for the Record (optional)
22800 }, RecordDef);
22801 </code></pre>
22802  * <p>
22803  * This would consume an Array like this:
22804  * <pre><code>
22805 [ [1, 'Bill', 'Gardener'], [2, 'Ben', 'Horticulturalist'] ]
22806   </code></pre>
22807  * @cfg {String} id (optional) The subscript within row Array that provides an ID for the Record
22808  * @constructor
22809  * Create a new JsonReader
22810  * @param {Object} meta Metadata configuration options.
22811  * @param {Object} recordType Either an Array of field definition objects
22812  * as specified to {@link Roo.data.Record#create},
22813  * or an {@link Roo.data.Record} object
22814  * created using {@link Roo.data.Record#create}.
22815  */
22816 Roo.data.ArrayReader = function(meta, recordType){
22817     Roo.data.ArrayReader.superclass.constructor.call(this, meta, recordType);
22818 };
22819
22820 Roo.extend(Roo.data.ArrayReader, Roo.data.JsonReader, {
22821     /**
22822      * Create a data block containing Roo.data.Records from an XML document.
22823      * @param {Object} o An Array of row objects which represents the dataset.
22824      * @return {Object} data A data block which is used by an Roo.data.Store object as
22825      * a cache of Roo.data.Records.
22826      */
22827     readRecords : function(o){
22828         var sid = this.meta ? this.meta.id : null;
22829         var recordType = this.recordType, fields = recordType.prototype.fields;
22830         var records = [];
22831         var root = o;
22832             for(var i = 0; i < root.length; i++){
22833                     var n = root[i];
22834                 var values = {};
22835                 var id = ((sid || sid === 0) && n[sid] !== undefined && n[sid] !== "" ? n[sid] : null);
22836                 for(var j = 0, jlen = fields.length; j < jlen; j++){
22837                 var f = fields.items[j];
22838                 var k = f.mapping !== undefined && f.mapping !== null ? f.mapping : j;
22839                 var v = n[k] !== undefined ? n[k] : f.defaultValue;
22840                 v = f.convert(v);
22841                 values[f.name] = v;
22842             }
22843                 var record = new recordType(values, id);
22844                 record.json = n;
22845                 records[records.length] = record;
22846             }
22847             return {
22848                 records : records,
22849                 totalRecords : records.length
22850             };
22851     }
22852 });/*
22853  * Based on:
22854  * Ext JS Library 1.1.1
22855  * Copyright(c) 2006-2007, Ext JS, LLC.
22856  *
22857  * Originally Released Under LGPL - original licence link has changed is not relivant.
22858  *
22859  * Fork - LGPL
22860  * <script type="text/javascript">
22861  */
22862
22863
22864 /**
22865  * @class Roo.data.Tree
22866  * @extends Roo.util.Observable
22867  * Represents a tree data structure and bubbles all the events for its nodes. The nodes
22868  * in the tree have most standard DOM functionality.
22869  * @constructor
22870  * @param {Node} root (optional) The root node
22871  */
22872 Roo.data.Tree = function(root){
22873    this.nodeHash = {};
22874    /**
22875     * The root node for this tree
22876     * @type Node
22877     */
22878    this.root = null;
22879    if(root){
22880        this.setRootNode(root);
22881    }
22882    this.addEvents({
22883        /**
22884         * @event append
22885         * Fires when a new child node is appended to a node in this tree.
22886         * @param {Tree} tree The owner tree
22887         * @param {Node} parent The parent node
22888         * @param {Node} node The newly appended node
22889         * @param {Number} index The index of the newly appended node
22890         */
22891        "append" : true,
22892        /**
22893         * @event remove
22894         * Fires when a child node is removed from a node in this tree.
22895         * @param {Tree} tree The owner tree
22896         * @param {Node} parent The parent node
22897         * @param {Node} node The child node removed
22898         */
22899        "remove" : true,
22900        /**
22901         * @event move
22902         * Fires when a node is moved to a new location in the tree
22903         * @param {Tree} tree The owner tree
22904         * @param {Node} node The node moved
22905         * @param {Node} oldParent The old parent of this node
22906         * @param {Node} newParent The new parent of this node
22907         * @param {Number} index The index it was moved to
22908         */
22909        "move" : true,
22910        /**
22911         * @event insert
22912         * Fires when a new child node is inserted in a node in this tree.
22913         * @param {Tree} tree The owner tree
22914         * @param {Node} parent The parent node
22915         * @param {Node} node The child node inserted
22916         * @param {Node} refNode The child node the node was inserted before
22917         */
22918        "insert" : true,
22919        /**
22920         * @event beforeappend
22921         * Fires before a new child is appended to a node in this tree, return false to cancel the append.
22922         * @param {Tree} tree The owner tree
22923         * @param {Node} parent The parent node
22924         * @param {Node} node The child node to be appended
22925         */
22926        "beforeappend" : true,
22927        /**
22928         * @event beforeremove
22929         * Fires before a child is removed from a node in this tree, return false to cancel the remove.
22930         * @param {Tree} tree The owner tree
22931         * @param {Node} parent The parent node
22932         * @param {Node} node The child node to be removed
22933         */
22934        "beforeremove" : true,
22935        /**
22936         * @event beforemove
22937         * Fires before a node is moved to a new location in the tree. Return false to cancel the move.
22938         * @param {Tree} tree The owner tree
22939         * @param {Node} node The node being moved
22940         * @param {Node} oldParent The parent of the node
22941         * @param {Node} newParent The new parent the node is moving to
22942         * @param {Number} index The index it is being moved to
22943         */
22944        "beforemove" : true,
22945        /**
22946         * @event beforeinsert
22947         * Fires before a new child is inserted in a node in this tree, return false to cancel the insert.
22948         * @param {Tree} tree The owner tree
22949         * @param {Node} parent The parent node
22950         * @param {Node} node The child node to be inserted
22951         * @param {Node} refNode The child node the node is being inserted before
22952         */
22953        "beforeinsert" : true
22954    });
22955
22956     Roo.data.Tree.superclass.constructor.call(this);
22957 };
22958
22959 Roo.extend(Roo.data.Tree, Roo.util.Observable, {
22960     pathSeparator: "/",
22961
22962     proxyNodeEvent : function(){
22963         return this.fireEvent.apply(this, arguments);
22964     },
22965
22966     /**
22967      * Returns the root node for this tree.
22968      * @return {Node}
22969      */
22970     getRootNode : function(){
22971         return this.root;
22972     },
22973
22974     /**
22975      * Sets the root node for this tree.
22976      * @param {Node} node
22977      * @return {Node}
22978      */
22979     setRootNode : function(node){
22980         this.root = node;
22981         node.ownerTree = this;
22982         node.isRoot = true;
22983         this.registerNode(node);
22984         return node;
22985     },
22986
22987     /**
22988      * Gets a node in this tree by its id.
22989      * @param {String} id
22990      * @return {Node}
22991      */
22992     getNodeById : function(id){
22993         return this.nodeHash[id];
22994     },
22995
22996     registerNode : function(node){
22997         this.nodeHash[node.id] = node;
22998     },
22999
23000     unregisterNode : function(node){
23001         delete this.nodeHash[node.id];
23002     },
23003
23004     toString : function(){
23005         return "[Tree"+(this.id?" "+this.id:"")+"]";
23006     }
23007 });
23008
23009 /**
23010  * @class Roo.data.Node
23011  * @extends Roo.util.Observable
23012  * @cfg {Boolean} leaf true if this node is a leaf and does not have children
23013  * @cfg {String} id The id for this node. If one is not specified, one is generated.
23014  * @constructor
23015  * @param {Object} attributes The attributes/config for the node
23016  */
23017 Roo.data.Node = function(attributes){
23018     /**
23019      * The attributes supplied for the node. You can use this property to access any custom attributes you supplied.
23020      * @type {Object}
23021      */
23022     this.attributes = attributes || {};
23023     this.leaf = this.attributes.leaf;
23024     /**
23025      * The node id. @type String
23026      */
23027     this.id = this.attributes.id;
23028     if(!this.id){
23029         this.id = Roo.id(null, "ynode-");
23030         this.attributes.id = this.id;
23031     }
23032      
23033     
23034     /**
23035      * All child nodes of this node. @type Array
23036      */
23037     this.childNodes = [];
23038     if(!this.childNodes.indexOf){ // indexOf is a must
23039         this.childNodes.indexOf = function(o){
23040             for(var i = 0, len = this.length; i < len; i++){
23041                 if(this[i] == o) {
23042                     return i;
23043                 }
23044             }
23045             return -1;
23046         };
23047     }
23048     /**
23049      * The parent node for this node. @type Node
23050      */
23051     this.parentNode = null;
23052     /**
23053      * The first direct child node of this node, or null if this node has no child nodes. @type Node
23054      */
23055     this.firstChild = null;
23056     /**
23057      * The last direct child node of this node, or null if this node has no child nodes. @type Node
23058      */
23059     this.lastChild = null;
23060     /**
23061      * The node immediately preceding this node in the tree, or null if there is no sibling node. @type Node
23062      */
23063     this.previousSibling = null;
23064     /**
23065      * The node immediately following this node in the tree, or null if there is no sibling node. @type Node
23066      */
23067     this.nextSibling = null;
23068
23069     this.addEvents({
23070        /**
23071         * @event append
23072         * Fires when a new child node is appended
23073         * @param {Tree} tree The owner tree
23074         * @param {Node} this This node
23075         * @param {Node} node The newly appended node
23076         * @param {Number} index The index of the newly appended node
23077         */
23078        "append" : true,
23079        /**
23080         * @event remove
23081         * Fires when a child node is removed
23082         * @param {Tree} tree The owner tree
23083         * @param {Node} this This node
23084         * @param {Node} node The removed node
23085         */
23086        "remove" : true,
23087        /**
23088         * @event move
23089         * Fires when this node is moved to a new location in the tree
23090         * @param {Tree} tree The owner tree
23091         * @param {Node} this This node
23092         * @param {Node} oldParent The old parent of this node
23093         * @param {Node} newParent The new parent of this node
23094         * @param {Number} index The index it was moved to
23095         */
23096        "move" : true,
23097        /**
23098         * @event insert
23099         * Fires when a new child node is inserted.
23100         * @param {Tree} tree The owner tree
23101         * @param {Node} this This node
23102         * @param {Node} node The child node inserted
23103         * @param {Node} refNode The child node the node was inserted before
23104         */
23105        "insert" : true,
23106        /**
23107         * @event beforeappend
23108         * Fires before a new child is appended, return false to cancel the append.
23109         * @param {Tree} tree The owner tree
23110         * @param {Node} this This node
23111         * @param {Node} node The child node to be appended
23112         */
23113        "beforeappend" : true,
23114        /**
23115         * @event beforeremove
23116         * Fires before a child is removed, return false to cancel the remove.
23117         * @param {Tree} tree The owner tree
23118         * @param {Node} this This node
23119         * @param {Node} node The child node to be removed
23120         */
23121        "beforeremove" : true,
23122        /**
23123         * @event beforemove
23124         * Fires before this node is moved to a new location in the tree. Return false to cancel the move.
23125         * @param {Tree} tree The owner tree
23126         * @param {Node} this This node
23127         * @param {Node} oldParent The parent of this node
23128         * @param {Node} newParent The new parent this node is moving to
23129         * @param {Number} index The index it is being moved to
23130         */
23131        "beforemove" : true,
23132        /**
23133         * @event beforeinsert
23134         * Fires before a new child is inserted, return false to cancel the insert.
23135         * @param {Tree} tree The owner tree
23136         * @param {Node} this This node
23137         * @param {Node} node The child node to be inserted
23138         * @param {Node} refNode The child node the node is being inserted before
23139         */
23140        "beforeinsert" : true
23141    });
23142     this.listeners = this.attributes.listeners;
23143     Roo.data.Node.superclass.constructor.call(this);
23144 };
23145
23146 Roo.extend(Roo.data.Node, Roo.util.Observable, {
23147     fireEvent : function(evtName){
23148         // first do standard event for this node
23149         if(Roo.data.Node.superclass.fireEvent.apply(this, arguments) === false){
23150             return false;
23151         }
23152         // then bubble it up to the tree if the event wasn't cancelled
23153         var ot = this.getOwnerTree();
23154         if(ot){
23155             if(ot.proxyNodeEvent.apply(ot, arguments) === false){
23156                 return false;
23157             }
23158         }
23159         return true;
23160     },
23161
23162     /**
23163      * Returns true if this node is a leaf
23164      * @return {Boolean}
23165      */
23166     isLeaf : function(){
23167         return this.leaf === true;
23168     },
23169
23170     // private
23171     setFirstChild : function(node){
23172         this.firstChild = node;
23173     },
23174
23175     //private
23176     setLastChild : function(node){
23177         this.lastChild = node;
23178     },
23179
23180
23181     /**
23182      * Returns true if this node is the last child of its parent
23183      * @return {Boolean}
23184      */
23185     isLast : function(){
23186        return (!this.parentNode ? true : this.parentNode.lastChild == this);
23187     },
23188
23189     /**
23190      * Returns true if this node is the first child of its parent
23191      * @return {Boolean}
23192      */
23193     isFirst : function(){
23194        return (!this.parentNode ? true : this.parentNode.firstChild == this);
23195     },
23196
23197     hasChildNodes : function(){
23198         return !this.isLeaf() && this.childNodes.length > 0;
23199     },
23200
23201     /**
23202      * Insert node(s) as the last child node of this node.
23203      * @param {Node/Array} node The node or Array of nodes to append
23204      * @return {Node} The appended node if single append, or null if an array was passed
23205      */
23206     appendChild : function(node){
23207         var multi = false;
23208         if(node instanceof Array){
23209             multi = node;
23210         }else if(arguments.length > 1){
23211             multi = arguments;
23212         }
23213         // if passed an array or multiple args do them one by one
23214         if(multi){
23215             for(var i = 0, len = multi.length; i < len; i++) {
23216                 this.appendChild(multi[i]);
23217             }
23218         }else{
23219             if(this.fireEvent("beforeappend", this.ownerTree, this, node) === false){
23220                 return false;
23221             }
23222             var index = this.childNodes.length;
23223             var oldParent = node.parentNode;
23224             // it's a move, make sure we move it cleanly
23225             if(oldParent){
23226                 if(node.fireEvent("beforemove", node.getOwnerTree(), node, oldParent, this, index) === false){
23227                     return false;
23228                 }
23229                 oldParent.removeChild(node);
23230             }
23231             index = this.childNodes.length;
23232             if(index == 0){
23233                 this.setFirstChild(node);
23234             }
23235             this.childNodes.push(node);
23236             node.parentNode = this;
23237             var ps = this.childNodes[index-1];
23238             if(ps){
23239                 node.previousSibling = ps;
23240                 ps.nextSibling = node;
23241             }else{
23242                 node.previousSibling = null;
23243             }
23244             node.nextSibling = null;
23245             this.setLastChild(node);
23246             node.setOwnerTree(this.getOwnerTree());
23247             this.fireEvent("append", this.ownerTree, this, node, index);
23248             if(oldParent){
23249                 node.fireEvent("move", this.ownerTree, node, oldParent, this, index);
23250             }
23251             return node;
23252         }
23253     },
23254
23255     /**
23256      * Removes a child node from this node.
23257      * @param {Node} node The node to remove
23258      * @return {Node} The removed node
23259      */
23260     removeChild : function(node){
23261         var index = this.childNodes.indexOf(node);
23262         if(index == -1){
23263             return false;
23264         }
23265         if(this.fireEvent("beforeremove", this.ownerTree, this, node) === false){
23266             return false;
23267         }
23268
23269         // remove it from childNodes collection
23270         this.childNodes.splice(index, 1);
23271
23272         // update siblings
23273         if(node.previousSibling){
23274             node.previousSibling.nextSibling = node.nextSibling;
23275         }
23276         if(node.nextSibling){
23277             node.nextSibling.previousSibling = node.previousSibling;
23278         }
23279
23280         // update child refs
23281         if(this.firstChild == node){
23282             this.setFirstChild(node.nextSibling);
23283         }
23284         if(this.lastChild == node){
23285             this.setLastChild(node.previousSibling);
23286         }
23287
23288         node.setOwnerTree(null);
23289         // clear any references from the node
23290         node.parentNode = null;
23291         node.previousSibling = null;
23292         node.nextSibling = null;
23293         this.fireEvent("remove", this.ownerTree, this, node);
23294         return node;
23295     },
23296
23297     /**
23298      * Inserts the first node before the second node in this nodes childNodes collection.
23299      * @param {Node} node The node to insert
23300      * @param {Node} refNode The node to insert before (if null the node is appended)
23301      * @return {Node} The inserted node
23302      */
23303     insertBefore : function(node, refNode){
23304         if(!refNode){ // like standard Dom, refNode can be null for append
23305             return this.appendChild(node);
23306         }
23307         // nothing to do
23308         if(node == refNode){
23309             return false;
23310         }
23311
23312         if(this.fireEvent("beforeinsert", this.ownerTree, this, node, refNode) === false){
23313             return false;
23314         }
23315         var index = this.childNodes.indexOf(refNode);
23316         var oldParent = node.parentNode;
23317         var refIndex = index;
23318
23319         // when moving internally, indexes will change after remove
23320         if(oldParent == this && this.childNodes.indexOf(node) < index){
23321             refIndex--;
23322         }
23323
23324         // it's a move, make sure we move it cleanly
23325         if(oldParent){
23326             if(node.fireEvent("beforemove", node.getOwnerTree(), node, oldParent, this, index, refNode) === false){
23327                 return false;
23328             }
23329             oldParent.removeChild(node);
23330         }
23331         if(refIndex == 0){
23332             this.setFirstChild(node);
23333         }
23334         this.childNodes.splice(refIndex, 0, node);
23335         node.parentNode = this;
23336         var ps = this.childNodes[refIndex-1];
23337         if(ps){
23338             node.previousSibling = ps;
23339             ps.nextSibling = node;
23340         }else{
23341             node.previousSibling = null;
23342         }
23343         node.nextSibling = refNode;
23344         refNode.previousSibling = node;
23345         node.setOwnerTree(this.getOwnerTree());
23346         this.fireEvent("insert", this.ownerTree, this, node, refNode);
23347         if(oldParent){
23348             node.fireEvent("move", this.ownerTree, node, oldParent, this, refIndex, refNode);
23349         }
23350         return node;
23351     },
23352
23353     /**
23354      * Returns the child node at the specified index.
23355      * @param {Number} index
23356      * @return {Node}
23357      */
23358     item : function(index){
23359         return this.childNodes[index];
23360     },
23361
23362     /**
23363      * Replaces one child node in this node with another.
23364      * @param {Node} newChild The replacement node
23365      * @param {Node} oldChild The node to replace
23366      * @return {Node} The replaced node
23367      */
23368     replaceChild : function(newChild, oldChild){
23369         this.insertBefore(newChild, oldChild);
23370         this.removeChild(oldChild);
23371         return oldChild;
23372     },
23373
23374     /**
23375      * Returns the index of a child node
23376      * @param {Node} node
23377      * @return {Number} The index of the node or -1 if it was not found
23378      */
23379     indexOf : function(child){
23380         return this.childNodes.indexOf(child);
23381     },
23382
23383     /**
23384      * Returns the tree this node is in.
23385      * @return {Tree}
23386      */
23387     getOwnerTree : function(){
23388         // if it doesn't have one, look for one
23389         if(!this.ownerTree){
23390             var p = this;
23391             while(p){
23392                 if(p.ownerTree){
23393                     this.ownerTree = p.ownerTree;
23394                     break;
23395                 }
23396                 p = p.parentNode;
23397             }
23398         }
23399         return this.ownerTree;
23400     },
23401
23402     /**
23403      * Returns depth of this node (the root node has a depth of 0)
23404      * @return {Number}
23405      */
23406     getDepth : function(){
23407         var depth = 0;
23408         var p = this;
23409         while(p.parentNode){
23410             ++depth;
23411             p = p.parentNode;
23412         }
23413         return depth;
23414     },
23415
23416     // private
23417     setOwnerTree : function(tree){
23418         // if it's move, we need to update everyone
23419         if(tree != this.ownerTree){
23420             if(this.ownerTree){
23421                 this.ownerTree.unregisterNode(this);
23422             }
23423             this.ownerTree = tree;
23424             var cs = this.childNodes;
23425             for(var i = 0, len = cs.length; i < len; i++) {
23426                 cs[i].setOwnerTree(tree);
23427             }
23428             if(tree){
23429                 tree.registerNode(this);
23430             }
23431         }
23432     },
23433
23434     /**
23435      * Returns the path for this node. The path can be used to expand or select this node programmatically.
23436      * @param {String} attr (optional) The attr to use for the path (defaults to the node's id)
23437      * @return {String} The path
23438      */
23439     getPath : function(attr){
23440         attr = attr || "id";
23441         var p = this.parentNode;
23442         var b = [this.attributes[attr]];
23443         while(p){
23444             b.unshift(p.attributes[attr]);
23445             p = p.parentNode;
23446         }
23447         var sep = this.getOwnerTree().pathSeparator;
23448         return sep + b.join(sep);
23449     },
23450
23451     /**
23452      * Bubbles up the tree from this node, calling the specified function with each node. The scope (<i>this</i>) of
23453      * function call will be the scope provided or the current node. The arguments to the function
23454      * will be the args provided or the current node. If the function returns false at any point,
23455      * the bubble is stopped.
23456      * @param {Function} fn The function to call
23457      * @param {Object} scope (optional) The scope of the function (defaults to current node)
23458      * @param {Array} args (optional) The args to call the function with (default to passing the current node)
23459      */
23460     bubble : function(fn, scope, args){
23461         var p = this;
23462         while(p){
23463             if(fn.call(scope || p, args || p) === false){
23464                 break;
23465             }
23466             p = p.parentNode;
23467         }
23468     },
23469
23470     /**
23471      * Cascades down the tree from this node, calling the specified function with each node. The scope (<i>this</i>) of
23472      * function call will be the scope provided or the current node. The arguments to the function
23473      * will be the args provided or the current node. If the function returns false at any point,
23474      * the cascade is stopped on that branch.
23475      * @param {Function} fn The function to call
23476      * @param {Object} scope (optional) The scope of the function (defaults to current node)
23477      * @param {Array} args (optional) The args to call the function with (default to passing the current node)
23478      */
23479     cascade : function(fn, scope, args){
23480         if(fn.call(scope || this, args || this) !== false){
23481             var cs = this.childNodes;
23482             for(var i = 0, len = cs.length; i < len; i++) {
23483                 cs[i].cascade(fn, scope, args);
23484             }
23485         }
23486     },
23487
23488     /**
23489      * Interates the child nodes of this node, calling the specified function with each node. The scope (<i>this</i>) of
23490      * function call will be the scope provided or the current node. The arguments to the function
23491      * will be the args provided or the current node. If the function returns false at any point,
23492      * the iteration stops.
23493      * @param {Function} fn The function to call
23494      * @param {Object} scope (optional) The scope of the function (defaults to current node)
23495      * @param {Array} args (optional) The args to call the function with (default to passing the current node)
23496      */
23497     eachChild : function(fn, scope, args){
23498         var cs = this.childNodes;
23499         for(var i = 0, len = cs.length; i < len; i++) {
23500                 if(fn.call(scope || this, args || cs[i]) === false){
23501                     break;
23502                 }
23503         }
23504     },
23505
23506     /**
23507      * Finds the first child that has the attribute with the specified value.
23508      * @param {String} attribute The attribute name
23509      * @param {Mixed} value The value to search for
23510      * @return {Node} The found child or null if none was found
23511      */
23512     findChild : function(attribute, value){
23513         var cs = this.childNodes;
23514         for(var i = 0, len = cs.length; i < len; i++) {
23515                 if(cs[i].attributes[attribute] == value){
23516                     return cs[i];
23517                 }
23518         }
23519         return null;
23520     },
23521
23522     /**
23523      * Finds the first child by a custom function. The child matches if the function passed
23524      * returns true.
23525      * @param {Function} fn
23526      * @param {Object} scope (optional)
23527      * @return {Node} The found child or null if none was found
23528      */
23529     findChildBy : function(fn, scope){
23530         var cs = this.childNodes;
23531         for(var i = 0, len = cs.length; i < len; i++) {
23532                 if(fn.call(scope||cs[i], cs[i]) === true){
23533                     return cs[i];
23534                 }
23535         }
23536         return null;
23537     },
23538
23539     /**
23540      * Sorts this nodes children using the supplied sort function
23541      * @param {Function} fn
23542      * @param {Object} scope (optional)
23543      */
23544     sort : function(fn, scope){
23545         var cs = this.childNodes;
23546         var len = cs.length;
23547         if(len > 0){
23548             var sortFn = scope ? function(){fn.apply(scope, arguments);} : fn;
23549             cs.sort(sortFn);
23550             for(var i = 0; i < len; i++){
23551                 var n = cs[i];
23552                 n.previousSibling = cs[i-1];
23553                 n.nextSibling = cs[i+1];
23554                 if(i == 0){
23555                     this.setFirstChild(n);
23556                 }
23557                 if(i == len-1){
23558                     this.setLastChild(n);
23559                 }
23560             }
23561         }
23562     },
23563
23564     /**
23565      * Returns true if this node is an ancestor (at any point) of the passed node.
23566      * @param {Node} node
23567      * @return {Boolean}
23568      */
23569     contains : function(node){
23570         return node.isAncestor(this);
23571     },
23572
23573     /**
23574      * Returns true if the passed node is an ancestor (at any point) of this node.
23575      * @param {Node} node
23576      * @return {Boolean}
23577      */
23578     isAncestor : function(node){
23579         var p = this.parentNode;
23580         while(p){
23581             if(p == node){
23582                 return true;
23583             }
23584             p = p.parentNode;
23585         }
23586         return false;
23587     },
23588
23589     toString : function(){
23590         return "[Node"+(this.id?" "+this.id:"")+"]";
23591     }
23592 });/*
23593  * Based on:
23594  * Ext JS Library 1.1.1
23595  * Copyright(c) 2006-2007, Ext JS, LLC.
23596  *
23597  * Originally Released Under LGPL - original licence link has changed is not relivant.
23598  *
23599  * Fork - LGPL
23600  * <script type="text/javascript">
23601  */
23602  (function(){ 
23603 /**
23604  * @class Roo.Layer
23605  * @extends Roo.Element
23606  * An extended {@link Roo.Element} object that supports a shadow and shim, constrain to viewport and
23607  * automatic maintaining of shadow/shim positions.
23608  * @cfg {Boolean} shim False to disable the iframe shim in browsers which need one (defaults to true)
23609  * @cfg {String/Boolean} shadow True to create a shadow element with default class "x-layer-shadow", or
23610  * you can pass a string with a CSS class name. False turns off the shadow.
23611  * @cfg {Object} dh DomHelper object config to create element with (defaults to {tag: "div", cls: "x-layer"}).
23612  * @cfg {Boolean} constrain False to disable constrain to viewport (defaults to true)
23613  * @cfg {String} cls CSS class to add to the element
23614  * @cfg {Number} zindex Starting z-index (defaults to 11000)
23615  * @cfg {Number} shadowOffset Number of pixels to offset the shadow (defaults to 3)
23616  * @constructor
23617  * @param {Object} config An object with config options.
23618  * @param {String/HTMLElement} existingEl (optional) Uses an existing DOM element. If the element is not found it creates it.
23619  */
23620
23621 Roo.Layer = function(config, existingEl){
23622     config = config || {};
23623     var dh = Roo.DomHelper;
23624     var cp = config.parentEl, pel = cp ? Roo.getDom(cp) : document.body;
23625     if(existingEl){
23626         this.dom = Roo.getDom(existingEl);
23627     }
23628     if(!this.dom){
23629         var o = config.dh || {tag: "div", cls: "x-layer"};
23630         this.dom = dh.append(pel, o);
23631     }
23632     if(config.cls){
23633         this.addClass(config.cls);
23634     }
23635     this.constrain = config.constrain !== false;
23636     this.visibilityMode = Roo.Element.VISIBILITY;
23637     if(config.id){
23638         this.id = this.dom.id = config.id;
23639     }else{
23640         this.id = Roo.id(this.dom);
23641     }
23642     this.zindex = config.zindex || this.getZIndex();
23643     this.position("absolute", this.zindex);
23644     if(config.shadow){
23645         this.shadowOffset = config.shadowOffset || 4;
23646         this.shadow = new Roo.Shadow({
23647             offset : this.shadowOffset,
23648             mode : config.shadow
23649         });
23650     }else{
23651         this.shadowOffset = 0;
23652     }
23653     this.useShim = config.shim !== false && Roo.useShims;
23654     this.useDisplay = config.useDisplay;
23655     this.hide();
23656 };
23657
23658 var supr = Roo.Element.prototype;
23659
23660 // shims are shared among layer to keep from having 100 iframes
23661 var shims = [];
23662
23663 Roo.extend(Roo.Layer, Roo.Element, {
23664
23665     getZIndex : function(){
23666         return this.zindex || parseInt(this.getStyle("z-index"), 10) || 11000;
23667     },
23668
23669     getShim : function(){
23670         if(!this.useShim){
23671             return null;
23672         }
23673         if(this.shim){
23674             return this.shim;
23675         }
23676         var shim = shims.shift();
23677         if(!shim){
23678             shim = this.createShim();
23679             shim.enableDisplayMode('block');
23680             shim.dom.style.display = 'none';
23681             shim.dom.style.visibility = 'visible';
23682         }
23683         var pn = this.dom.parentNode;
23684         if(shim.dom.parentNode != pn){
23685             pn.insertBefore(shim.dom, this.dom);
23686         }
23687         shim.setStyle('z-index', this.getZIndex()-2);
23688         this.shim = shim;
23689         return shim;
23690     },
23691
23692     hideShim : function(){
23693         if(this.shim){
23694             this.shim.setDisplayed(false);
23695             shims.push(this.shim);
23696             delete this.shim;
23697         }
23698     },
23699
23700     disableShadow : function(){
23701         if(this.shadow){
23702             this.shadowDisabled = true;
23703             this.shadow.hide();
23704             this.lastShadowOffset = this.shadowOffset;
23705             this.shadowOffset = 0;
23706         }
23707     },
23708
23709     enableShadow : function(show){
23710         if(this.shadow){
23711             this.shadowDisabled = false;
23712             this.shadowOffset = this.lastShadowOffset;
23713             delete this.lastShadowOffset;
23714             if(show){
23715                 this.sync(true);
23716             }
23717         }
23718     },
23719
23720     // private
23721     // this code can execute repeatedly in milliseconds (i.e. during a drag) so
23722     // code size was sacrificed for effeciency (e.g. no getBox/setBox, no XY calls)
23723     sync : function(doShow){
23724         var sw = this.shadow;
23725         if(!this.updating && this.isVisible() && (sw || this.useShim)){
23726             var sh = this.getShim();
23727
23728             var w = this.getWidth(),
23729                 h = this.getHeight();
23730
23731             var l = this.getLeft(true),
23732                 t = this.getTop(true);
23733
23734             if(sw && !this.shadowDisabled){
23735                 if(doShow && !sw.isVisible()){
23736                     sw.show(this);
23737                 }else{
23738                     sw.realign(l, t, w, h);
23739                 }
23740                 if(sh){
23741                     if(doShow){
23742                        sh.show();
23743                     }
23744                     // fit the shim behind the shadow, so it is shimmed too
23745                     var a = sw.adjusts, s = sh.dom.style;
23746                     s.left = (Math.min(l, l+a.l))+"px";
23747                     s.top = (Math.min(t, t+a.t))+"px";
23748                     s.width = (w+a.w)+"px";
23749                     s.height = (h+a.h)+"px";
23750                 }
23751             }else if(sh){
23752                 if(doShow){
23753                    sh.show();
23754                 }
23755                 sh.setSize(w, h);
23756                 sh.setLeftTop(l, t);
23757             }
23758             
23759         }
23760     },
23761
23762     // private
23763     destroy : function(){
23764         this.hideShim();
23765         if(this.shadow){
23766             this.shadow.hide();
23767         }
23768         this.removeAllListeners();
23769         var pn = this.dom.parentNode;
23770         if(pn){
23771             pn.removeChild(this.dom);
23772         }
23773         Roo.Element.uncache(this.id);
23774     },
23775
23776     remove : function(){
23777         this.destroy();
23778     },
23779
23780     // private
23781     beginUpdate : function(){
23782         this.updating = true;
23783     },
23784
23785     // private
23786     endUpdate : function(){
23787         this.updating = false;
23788         this.sync(true);
23789     },
23790
23791     // private
23792     hideUnders : function(negOffset){
23793         if(this.shadow){
23794             this.shadow.hide();
23795         }
23796         this.hideShim();
23797     },
23798
23799     // private
23800     constrainXY : function(){
23801         if(this.constrain){
23802             var vw = Roo.lib.Dom.getViewWidth(),
23803                 vh = Roo.lib.Dom.getViewHeight();
23804             var s = Roo.get(document).getScroll();
23805
23806             var xy = this.getXY();
23807             var x = xy[0], y = xy[1];   
23808             var w = this.dom.offsetWidth+this.shadowOffset, h = this.dom.offsetHeight+this.shadowOffset;
23809             // only move it if it needs it
23810             var moved = false;
23811             // first validate right/bottom
23812             if((x + w) > vw+s.left){
23813                 x = vw - w - this.shadowOffset;
23814                 moved = true;
23815             }
23816             if((y + h) > vh+s.top){
23817                 y = vh - h - this.shadowOffset;
23818                 moved = true;
23819             }
23820             // then make sure top/left isn't negative
23821             if(x < s.left){
23822                 x = s.left;
23823                 moved = true;
23824             }
23825             if(y < s.top){
23826                 y = s.top;
23827                 moved = true;
23828             }
23829             if(moved){
23830                 if(this.avoidY){
23831                     var ay = this.avoidY;
23832                     if(y <= ay && (y+h) >= ay){
23833                         y = ay-h-5;   
23834                     }
23835                 }
23836                 xy = [x, y];
23837                 this.storeXY(xy);
23838                 supr.setXY.call(this, xy);
23839                 this.sync();
23840             }
23841         }
23842     },
23843
23844     isVisible : function(){
23845         return this.visible;    
23846     },
23847
23848     // private
23849     showAction : function(){
23850         this.visible = true; // track visibility to prevent getStyle calls
23851         if(this.useDisplay === true){
23852             this.setDisplayed("");
23853         }else if(this.lastXY){
23854             supr.setXY.call(this, this.lastXY);
23855         }else if(this.lastLT){
23856             supr.setLeftTop.call(this, this.lastLT[0], this.lastLT[1]);
23857         }
23858     },
23859
23860     // private
23861     hideAction : function(){
23862         this.visible = false;
23863         if(this.useDisplay === true){
23864             this.setDisplayed(false);
23865         }else{
23866             this.setLeftTop(-10000,-10000);
23867         }
23868     },
23869
23870     // overridden Element method
23871     setVisible : function(v, a, d, c, e){
23872         if(v){
23873             this.showAction();
23874         }
23875         if(a && v){
23876             var cb = function(){
23877                 this.sync(true);
23878                 if(c){
23879                     c();
23880                 }
23881             }.createDelegate(this);
23882             supr.setVisible.call(this, true, true, d, cb, e);
23883         }else{
23884             if(!v){
23885                 this.hideUnders(true);
23886             }
23887             var cb = c;
23888             if(a){
23889                 cb = function(){
23890                     this.hideAction();
23891                     if(c){
23892                         c();
23893                     }
23894                 }.createDelegate(this);
23895             }
23896             supr.setVisible.call(this, v, a, d, cb, e);
23897             if(v){
23898                 this.sync(true);
23899             }else if(!a){
23900                 this.hideAction();
23901             }
23902         }
23903     },
23904
23905     storeXY : function(xy){
23906         delete this.lastLT;
23907         this.lastXY = xy;
23908     },
23909
23910     storeLeftTop : function(left, top){
23911         delete this.lastXY;
23912         this.lastLT = [left, top];
23913     },
23914
23915     // private
23916     beforeFx : function(){
23917         this.beforeAction();
23918         return Roo.Layer.superclass.beforeFx.apply(this, arguments);
23919     },
23920
23921     // private
23922     afterFx : function(){
23923         Roo.Layer.superclass.afterFx.apply(this, arguments);
23924         this.sync(this.isVisible());
23925     },
23926
23927     // private
23928     beforeAction : function(){
23929         if(!this.updating && this.shadow){
23930             this.shadow.hide();
23931         }
23932     },
23933
23934     // overridden Element method
23935     setLeft : function(left){
23936         this.storeLeftTop(left, this.getTop(true));
23937         supr.setLeft.apply(this, arguments);
23938         this.sync();
23939     },
23940
23941     setTop : function(top){
23942         this.storeLeftTop(this.getLeft(true), top);
23943         supr.setTop.apply(this, arguments);
23944         this.sync();
23945     },
23946
23947     setLeftTop : function(left, top){
23948         this.storeLeftTop(left, top);
23949         supr.setLeftTop.apply(this, arguments);
23950         this.sync();
23951     },
23952
23953     setXY : function(xy, a, d, c, e){
23954         this.fixDisplay();
23955         this.beforeAction();
23956         this.storeXY(xy);
23957         var cb = this.createCB(c);
23958         supr.setXY.call(this, xy, a, d, cb, e);
23959         if(!a){
23960             cb();
23961         }
23962     },
23963
23964     // private
23965     createCB : function(c){
23966         var el = this;
23967         return function(){
23968             el.constrainXY();
23969             el.sync(true);
23970             if(c){
23971                 c();
23972             }
23973         };
23974     },
23975
23976     // overridden Element method
23977     setX : function(x, a, d, c, e){
23978         this.setXY([x, this.getY()], a, d, c, e);
23979     },
23980
23981     // overridden Element method
23982     setY : function(y, a, d, c, e){
23983         this.setXY([this.getX(), y], a, d, c, e);
23984     },
23985
23986     // overridden Element method
23987     setSize : function(w, h, a, d, c, e){
23988         this.beforeAction();
23989         var cb = this.createCB(c);
23990         supr.setSize.call(this, w, h, a, d, cb, e);
23991         if(!a){
23992             cb();
23993         }
23994     },
23995
23996     // overridden Element method
23997     setWidth : function(w, a, d, c, e){
23998         this.beforeAction();
23999         var cb = this.createCB(c);
24000         supr.setWidth.call(this, w, a, d, cb, e);
24001         if(!a){
24002             cb();
24003         }
24004     },
24005
24006     // overridden Element method
24007     setHeight : function(h, a, d, c, e){
24008         this.beforeAction();
24009         var cb = this.createCB(c);
24010         supr.setHeight.call(this, h, a, d, cb, e);
24011         if(!a){
24012             cb();
24013         }
24014     },
24015
24016     // overridden Element method
24017     setBounds : function(x, y, w, h, a, d, c, e){
24018         this.beforeAction();
24019         var cb = this.createCB(c);
24020         if(!a){
24021             this.storeXY([x, y]);
24022             supr.setXY.call(this, [x, y]);
24023             supr.setSize.call(this, w, h, a, d, cb, e);
24024             cb();
24025         }else{
24026             supr.setBounds.call(this, x, y, w, h, a, d, cb, e);
24027         }
24028         return this;
24029     },
24030     
24031     /**
24032      * Sets the z-index of this layer and adjusts any shadow and shim z-indexes. The layer z-index is automatically
24033      * incremented by two more than the value passed in so that it always shows above any shadow or shim (the shadow
24034      * element, if any, will be assigned z-index + 1, and the shim element, if any, will be assigned the unmodified z-index).
24035      * @param {Number} zindex The new z-index to set
24036      * @return {this} The Layer
24037      */
24038     setZIndex : function(zindex){
24039         this.zindex = zindex;
24040         this.setStyle("z-index", zindex + 2);
24041         if(this.shadow){
24042             this.shadow.setZIndex(zindex + 1);
24043         }
24044         if(this.shim){
24045             this.shim.setStyle("z-index", zindex);
24046         }
24047     }
24048 });
24049 })();/*
24050  * Based on:
24051  * Ext JS Library 1.1.1
24052  * Copyright(c) 2006-2007, Ext JS, LLC.
24053  *
24054  * Originally Released Under LGPL - original licence link has changed is not relivant.
24055  *
24056  * Fork - LGPL
24057  * <script type="text/javascript">
24058  */
24059
24060
24061 /**
24062  * @class Roo.Shadow
24063  * Simple class that can provide a shadow effect for any element.  Note that the element MUST be absolutely positioned,
24064  * and the shadow does not provide any shimming.  This should be used only in simple cases -- for more advanced
24065  * functionality that can also provide the same shadow effect, see the {@link Roo.Layer} class.
24066  * @constructor
24067  * Create a new Shadow
24068  * @param {Object} config The config object
24069  */
24070 Roo.Shadow = function(config){
24071     Roo.apply(this, config);
24072     if(typeof this.mode != "string"){
24073         this.mode = this.defaultMode;
24074     }
24075     var o = this.offset, a = {h: 0};
24076     var rad = Math.floor(this.offset/2);
24077     switch(this.mode.toLowerCase()){ // all this hideous nonsense calculates the various offsets for shadows
24078         case "drop":
24079             a.w = 0;
24080             a.l = a.t = o;
24081             a.t -= 1;
24082             if(Roo.isIE){
24083                 a.l -= this.offset + rad;
24084                 a.t -= this.offset + rad;
24085                 a.w -= rad;
24086                 a.h -= rad;
24087                 a.t += 1;
24088             }
24089         break;
24090         case "sides":
24091             a.w = (o*2);
24092             a.l = -o;
24093             a.t = o-1;
24094             if(Roo.isIE){
24095                 a.l -= (this.offset - rad);
24096                 a.t -= this.offset + rad;
24097                 a.l += 1;
24098                 a.w -= (this.offset - rad)*2;
24099                 a.w -= rad + 1;
24100                 a.h -= 1;
24101             }
24102         break;
24103         case "frame":
24104             a.w = a.h = (o*2);
24105             a.l = a.t = -o;
24106             a.t += 1;
24107             a.h -= 2;
24108             if(Roo.isIE){
24109                 a.l -= (this.offset - rad);
24110                 a.t -= (this.offset - rad);
24111                 a.l += 1;
24112                 a.w -= (this.offset + rad + 1);
24113                 a.h -= (this.offset + rad);
24114                 a.h += 1;
24115             }
24116         break;
24117     };
24118
24119     this.adjusts = a;
24120 };
24121
24122 Roo.Shadow.prototype = {
24123     /**
24124      * @cfg {String} mode
24125      * The shadow display mode.  Supports the following options:<br />
24126      * sides: Shadow displays on both sides and bottom only<br />
24127      * frame: Shadow displays equally on all four sides<br />
24128      * drop: Traditional bottom-right drop shadow (default)
24129      */
24130     /**
24131      * @cfg {String} offset
24132      * The number of pixels to offset the shadow from the element (defaults to 4)
24133      */
24134     offset: 4,
24135
24136     // private
24137     defaultMode: "drop",
24138
24139     /**
24140      * Displays the shadow under the target element
24141      * @param {String/HTMLElement/Element} targetEl The id or element under which the shadow should display
24142      */
24143     show : function(target){
24144         target = Roo.get(target);
24145         if(!this.el){
24146             this.el = Roo.Shadow.Pool.pull();
24147             if(this.el.dom.nextSibling != target.dom){
24148                 this.el.insertBefore(target);
24149             }
24150         }
24151         this.el.setStyle("z-index", this.zIndex || parseInt(target.getStyle("z-index"), 10)-1);
24152         if(Roo.isIE){
24153             this.el.dom.style.filter="progid:DXImageTransform.Microsoft.alpha(opacity=50) progid:DXImageTransform.Microsoft.Blur(pixelradius="+(this.offset)+")";
24154         }
24155         this.realign(
24156             target.getLeft(true),
24157             target.getTop(true),
24158             target.getWidth(),
24159             target.getHeight()
24160         );
24161         this.el.dom.style.display = "block";
24162     },
24163
24164     /**
24165      * Returns true if the shadow is visible, else false
24166      */
24167     isVisible : function(){
24168         return this.el ? true : false;  
24169     },
24170
24171     /**
24172      * Direct alignment when values are already available. Show must be called at least once before
24173      * calling this method to ensure it is initialized.
24174      * @param {Number} left The target element left position
24175      * @param {Number} top The target element top position
24176      * @param {Number} width The target element width
24177      * @param {Number} height The target element height
24178      */
24179     realign : function(l, t, w, h){
24180         if(!this.el){
24181             return;
24182         }
24183         var a = this.adjusts, d = this.el.dom, s = d.style;
24184         var iea = 0;
24185         s.left = (l+a.l)+"px";
24186         s.top = (t+a.t)+"px";
24187         var sw = (w+a.w), sh = (h+a.h), sws = sw +"px", shs = sh + "px";
24188  
24189         if(s.width != sws || s.height != shs){
24190             s.width = sws;
24191             s.height = shs;
24192             if(!Roo.isIE){
24193                 var cn = d.childNodes;
24194                 var sww = Math.max(0, (sw-12))+"px";
24195                 cn[0].childNodes[1].style.width = sww;
24196                 cn[1].childNodes[1].style.width = sww;
24197                 cn[2].childNodes[1].style.width = sww;
24198                 cn[1].style.height = Math.max(0, (sh-12))+"px";
24199             }
24200         }
24201     },
24202
24203     /**
24204      * Hides this shadow
24205      */
24206     hide : function(){
24207         if(this.el){
24208             this.el.dom.style.display = "none";
24209             Roo.Shadow.Pool.push(this.el);
24210             delete this.el;
24211         }
24212     },
24213
24214     /**
24215      * Adjust the z-index of this shadow
24216      * @param {Number} zindex The new z-index
24217      */
24218     setZIndex : function(z){
24219         this.zIndex = z;
24220         if(this.el){
24221             this.el.setStyle("z-index", z);
24222         }
24223     }
24224 };
24225
24226 // Private utility class that manages the internal Shadow cache
24227 Roo.Shadow.Pool = function(){
24228     var p = [];
24229     var markup = Roo.isIE ?
24230                  '<div class="x-ie-shadow"></div>' :
24231                  '<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>';
24232     return {
24233         pull : function(){
24234             var sh = p.shift();
24235             if(!sh){
24236                 sh = Roo.get(Roo.DomHelper.insertHtml("beforeBegin", document.body.firstChild, markup));
24237                 sh.autoBoxAdjust = false;
24238             }
24239             return sh;
24240         },
24241
24242         push : function(sh){
24243             p.push(sh);
24244         }
24245     };
24246 }();/*
24247  * Based on:
24248  * Ext JS Library 1.1.1
24249  * Copyright(c) 2006-2007, Ext JS, LLC.
24250  *
24251  * Originally Released Under LGPL - original licence link has changed is not relivant.
24252  *
24253  * Fork - LGPL
24254  * <script type="text/javascript">
24255  */
24256
24257
24258 /**
24259  * @class Roo.SplitBar
24260  * @extends Roo.util.Observable
24261  * Creates draggable splitter bar functionality from two elements (element to be dragged and element to be resized).
24262  * <br><br>
24263  * Usage:
24264  * <pre><code>
24265 var split = new Roo.SplitBar("elementToDrag", "elementToSize",
24266                    Roo.SplitBar.HORIZONTAL, Roo.SplitBar.LEFT);
24267 split.setAdapter(new Roo.SplitBar.AbsoluteLayoutAdapter("container"));
24268 split.minSize = 100;
24269 split.maxSize = 600;
24270 split.animate = true;
24271 split.on('moved', splitterMoved);
24272 </code></pre>
24273  * @constructor
24274  * Create a new SplitBar
24275  * @param {String/HTMLElement/Roo.Element} dragElement The element to be dragged and act as the SplitBar. 
24276  * @param {String/HTMLElement/Roo.Element} resizingElement The element to be resized based on where the SplitBar element is dragged 
24277  * @param {Number} orientation (optional) Either Roo.SplitBar.HORIZONTAL or Roo.SplitBar.VERTICAL. (Defaults to HORIZONTAL)
24278  * @param {Number} placement (optional) Either Roo.SplitBar.LEFT or Roo.SplitBar.RIGHT for horizontal or  
24279                         Roo.SplitBar.TOP or Roo.SplitBar.BOTTOM for vertical. (By default, this is determined automatically by the initial
24280                         position of the SplitBar).
24281  */
24282 Roo.SplitBar = function(dragElement, resizingElement, orientation, placement, existingProxy){
24283     
24284     /** @private */
24285     this.el = Roo.get(dragElement, true);
24286     this.el.dom.unselectable = "on";
24287     /** @private */
24288     this.resizingEl = Roo.get(resizingElement, true);
24289
24290     /**
24291      * @private
24292      * The orientation of the split. Either Roo.SplitBar.HORIZONTAL or Roo.SplitBar.VERTICAL. (Defaults to HORIZONTAL)
24293      * Note: If this is changed after creating the SplitBar, the placement property must be manually updated
24294      * @type Number
24295      */
24296     this.orientation = orientation || Roo.SplitBar.HORIZONTAL;
24297     
24298     /**
24299      * The minimum size of the resizing element. (Defaults to 0)
24300      * @type Number
24301      */
24302     this.minSize = 0;
24303     
24304     /**
24305      * The maximum size of the resizing element. (Defaults to 2000)
24306      * @type Number
24307      */
24308     this.maxSize = 2000;
24309     
24310     /**
24311      * Whether to animate the transition to the new size
24312      * @type Boolean
24313      */
24314     this.animate = false;
24315     
24316     /**
24317      * Whether to create a transparent shim that overlays the page when dragging, enables dragging across iframes.
24318      * @type Boolean
24319      */
24320     this.useShim = false;
24321     
24322     /** @private */
24323     this.shim = null;
24324     
24325     if(!existingProxy){
24326         /** @private */
24327         this.proxy = Roo.SplitBar.createProxy(this.orientation);
24328     }else{
24329         this.proxy = Roo.get(existingProxy).dom;
24330     }
24331     /** @private */
24332     this.dd = new Roo.dd.DDProxy(this.el.dom.id, "XSplitBars", {dragElId : this.proxy.id});
24333     
24334     /** @private */
24335     this.dd.b4StartDrag = this.onStartProxyDrag.createDelegate(this);
24336     
24337     /** @private */
24338     this.dd.endDrag = this.onEndProxyDrag.createDelegate(this);
24339     
24340     /** @private */
24341     this.dragSpecs = {};
24342     
24343     /**
24344      * @private The adapter to use to positon and resize elements
24345      */
24346     this.adapter = new Roo.SplitBar.BasicLayoutAdapter();
24347     this.adapter.init(this);
24348     
24349     if(this.orientation == Roo.SplitBar.HORIZONTAL){
24350         /** @private */
24351         this.placement = placement || (this.el.getX() > this.resizingEl.getX() ? Roo.SplitBar.LEFT : Roo.SplitBar.RIGHT);
24352         this.el.addClass("x-splitbar-h");
24353     }else{
24354         /** @private */
24355         this.placement = placement || (this.el.getY() > this.resizingEl.getY() ? Roo.SplitBar.TOP : Roo.SplitBar.BOTTOM);
24356         this.el.addClass("x-splitbar-v");
24357     }
24358     
24359     this.addEvents({
24360         /**
24361          * @event resize
24362          * Fires when the splitter is moved (alias for {@link #event-moved})
24363          * @param {Roo.SplitBar} this
24364          * @param {Number} newSize the new width or height
24365          */
24366         "resize" : true,
24367         /**
24368          * @event moved
24369          * Fires when the splitter is moved
24370          * @param {Roo.SplitBar} this
24371          * @param {Number} newSize the new width or height
24372          */
24373         "moved" : true,
24374         /**
24375          * @event beforeresize
24376          * Fires before the splitter is dragged
24377          * @param {Roo.SplitBar} this
24378          */
24379         "beforeresize" : true,
24380
24381         "beforeapply" : true
24382     });
24383
24384     Roo.util.Observable.call(this);
24385 };
24386
24387 Roo.extend(Roo.SplitBar, Roo.util.Observable, {
24388     onStartProxyDrag : function(x, y){
24389         this.fireEvent("beforeresize", this);
24390         if(!this.overlay){
24391             var o = Roo.DomHelper.insertFirst(document.body,  {cls: "x-drag-overlay", html: "&#160;"}, true);
24392             o.unselectable();
24393             o.enableDisplayMode("block");
24394             // all splitbars share the same overlay
24395             Roo.SplitBar.prototype.overlay = o;
24396         }
24397         this.overlay.setSize(Roo.lib.Dom.getViewWidth(true), Roo.lib.Dom.getViewHeight(true));
24398         this.overlay.show();
24399         Roo.get(this.proxy).setDisplayed("block");
24400         var size = this.adapter.getElementSize(this);
24401         this.activeMinSize = this.getMinimumSize();;
24402         this.activeMaxSize = this.getMaximumSize();;
24403         var c1 = size - this.activeMinSize;
24404         var c2 = Math.max(this.activeMaxSize - size, 0);
24405         if(this.orientation == Roo.SplitBar.HORIZONTAL){
24406             this.dd.resetConstraints();
24407             this.dd.setXConstraint(
24408                 this.placement == Roo.SplitBar.LEFT ? c1 : c2, 
24409                 this.placement == Roo.SplitBar.LEFT ? c2 : c1
24410             );
24411             this.dd.setYConstraint(0, 0);
24412         }else{
24413             this.dd.resetConstraints();
24414             this.dd.setXConstraint(0, 0);
24415             this.dd.setYConstraint(
24416                 this.placement == Roo.SplitBar.TOP ? c1 : c2, 
24417                 this.placement == Roo.SplitBar.TOP ? c2 : c1
24418             );
24419          }
24420         this.dragSpecs.startSize = size;
24421         this.dragSpecs.startPoint = [x, y];
24422         Roo.dd.DDProxy.prototype.b4StartDrag.call(this.dd, x, y);
24423     },
24424     
24425     /** 
24426      * @private Called after the drag operation by the DDProxy
24427      */
24428     onEndProxyDrag : function(e){
24429         Roo.get(this.proxy).setDisplayed(false);
24430         var endPoint = Roo.lib.Event.getXY(e);
24431         if(this.overlay){
24432             this.overlay.hide();
24433         }
24434         var newSize;
24435         if(this.orientation == Roo.SplitBar.HORIZONTAL){
24436             newSize = this.dragSpecs.startSize + 
24437                 (this.placement == Roo.SplitBar.LEFT ?
24438                     endPoint[0] - this.dragSpecs.startPoint[0] :
24439                     this.dragSpecs.startPoint[0] - endPoint[0]
24440                 );
24441         }else{
24442             newSize = this.dragSpecs.startSize + 
24443                 (this.placement == Roo.SplitBar.TOP ?
24444                     endPoint[1] - this.dragSpecs.startPoint[1] :
24445                     this.dragSpecs.startPoint[1] - endPoint[1]
24446                 );
24447         }
24448         newSize = Math.min(Math.max(newSize, this.activeMinSize), this.activeMaxSize);
24449         if(newSize != this.dragSpecs.startSize){
24450             if(this.fireEvent('beforeapply', this, newSize) !== false){
24451                 this.adapter.setElementSize(this, newSize);
24452                 this.fireEvent("moved", this, newSize);
24453                 this.fireEvent("resize", this, newSize);
24454             }
24455         }
24456     },
24457     
24458     /**
24459      * Get the adapter this SplitBar uses
24460      * @return The adapter object
24461      */
24462     getAdapter : function(){
24463         return this.adapter;
24464     },
24465     
24466     /**
24467      * Set the adapter this SplitBar uses
24468      * @param {Object} adapter A SplitBar adapter object
24469      */
24470     setAdapter : function(adapter){
24471         this.adapter = adapter;
24472         this.adapter.init(this);
24473     },
24474     
24475     /**
24476      * Gets the minimum size for the resizing element
24477      * @return {Number} The minimum size
24478      */
24479     getMinimumSize : function(){
24480         return this.minSize;
24481     },
24482     
24483     /**
24484      * Sets the minimum size for the resizing element
24485      * @param {Number} minSize The minimum size
24486      */
24487     setMinimumSize : function(minSize){
24488         this.minSize = minSize;
24489     },
24490     
24491     /**
24492      * Gets the maximum size for the resizing element
24493      * @return {Number} The maximum size
24494      */
24495     getMaximumSize : function(){
24496         return this.maxSize;
24497     },
24498     
24499     /**
24500      * Sets the maximum size for the resizing element
24501      * @param {Number} maxSize The maximum size
24502      */
24503     setMaximumSize : function(maxSize){
24504         this.maxSize = maxSize;
24505     },
24506     
24507     /**
24508      * Sets the initialize size for the resizing element
24509      * @param {Number} size The initial size
24510      */
24511     setCurrentSize : function(size){
24512         var oldAnimate = this.animate;
24513         this.animate = false;
24514         this.adapter.setElementSize(this, size);
24515         this.animate = oldAnimate;
24516     },
24517     
24518     /**
24519      * Destroy this splitbar. 
24520      * @param {Boolean} removeEl True to remove the element
24521      */
24522     destroy : function(removeEl){
24523         if(this.shim){
24524             this.shim.remove();
24525         }
24526         this.dd.unreg();
24527         this.proxy.parentNode.removeChild(this.proxy);
24528         if(removeEl){
24529             this.el.remove();
24530         }
24531     }
24532 });
24533
24534 /**
24535  * @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.
24536  */
24537 Roo.SplitBar.createProxy = function(dir){
24538     var proxy = new Roo.Element(document.createElement("div"));
24539     proxy.unselectable();
24540     var cls = 'x-splitbar-proxy';
24541     proxy.addClass(cls + ' ' + (dir == Roo.SplitBar.HORIZONTAL ? cls +'-h' : cls + '-v'));
24542     document.body.appendChild(proxy.dom);
24543     return proxy.dom;
24544 };
24545
24546 /** 
24547  * @class Roo.SplitBar.BasicLayoutAdapter
24548  * Default Adapter. It assumes the splitter and resizing element are not positioned
24549  * elements and only gets/sets the width of the element. Generally used for table based layouts.
24550  */
24551 Roo.SplitBar.BasicLayoutAdapter = function(){
24552 };
24553
24554 Roo.SplitBar.BasicLayoutAdapter.prototype = {
24555     // do nothing for now
24556     init : function(s){
24557     
24558     },
24559     /**
24560      * Called before drag operations to get the current size of the resizing element. 
24561      * @param {Roo.SplitBar} s The SplitBar using this adapter
24562      */
24563      getElementSize : function(s){
24564         if(s.orientation == Roo.SplitBar.HORIZONTAL){
24565             return s.resizingEl.getWidth();
24566         }else{
24567             return s.resizingEl.getHeight();
24568         }
24569     },
24570     
24571     /**
24572      * Called after drag operations to set the size of the resizing element.
24573      * @param {Roo.SplitBar} s The SplitBar using this adapter
24574      * @param {Number} newSize The new size to set
24575      * @param {Function} onComplete A function to be invoked when resizing is complete
24576      */
24577     setElementSize : function(s, newSize, onComplete){
24578         if(s.orientation == Roo.SplitBar.HORIZONTAL){
24579             if(!s.animate){
24580                 s.resizingEl.setWidth(newSize);
24581                 if(onComplete){
24582                     onComplete(s, newSize);
24583                 }
24584             }else{
24585                 s.resizingEl.setWidth(newSize, true, .1, onComplete, 'easeOut');
24586             }
24587         }else{
24588             
24589             if(!s.animate){
24590                 s.resizingEl.setHeight(newSize);
24591                 if(onComplete){
24592                     onComplete(s, newSize);
24593                 }
24594             }else{
24595                 s.resizingEl.setHeight(newSize, true, .1, onComplete, 'easeOut');
24596             }
24597         }
24598     }
24599 };
24600
24601 /** 
24602  *@class Roo.SplitBar.AbsoluteLayoutAdapter
24603  * @extends Roo.SplitBar.BasicLayoutAdapter
24604  * Adapter that  moves the splitter element to align with the resized sizing element. 
24605  * Used with an absolute positioned SplitBar.
24606  * @param {String/HTMLElement/Roo.Element} container The container that wraps around the absolute positioned content. If it's
24607  * document.body, make sure you assign an id to the body element.
24608  */
24609 Roo.SplitBar.AbsoluteLayoutAdapter = function(container){
24610     this.basic = new Roo.SplitBar.BasicLayoutAdapter();
24611     this.container = Roo.get(container);
24612 };
24613
24614 Roo.SplitBar.AbsoluteLayoutAdapter.prototype = {
24615     init : function(s){
24616         this.basic.init(s);
24617     },
24618     
24619     getElementSize : function(s){
24620         return this.basic.getElementSize(s);
24621     },
24622     
24623     setElementSize : function(s, newSize, onComplete){
24624         this.basic.setElementSize(s, newSize, this.moveSplitter.createDelegate(this, [s]));
24625     },
24626     
24627     moveSplitter : function(s){
24628         var yes = Roo.SplitBar;
24629         switch(s.placement){
24630             case yes.LEFT:
24631                 s.el.setX(s.resizingEl.getRight());
24632                 break;
24633             case yes.RIGHT:
24634                 s.el.setStyle("right", (this.container.getWidth() - s.resizingEl.getLeft()) + "px");
24635                 break;
24636             case yes.TOP:
24637                 s.el.setY(s.resizingEl.getBottom());
24638                 break;
24639             case yes.BOTTOM:
24640                 s.el.setY(s.resizingEl.getTop() - s.el.getHeight());
24641                 break;
24642         }
24643     }
24644 };
24645
24646 /**
24647  * Orientation constant - Create a vertical SplitBar
24648  * @static
24649  * @type Number
24650  */
24651 Roo.SplitBar.VERTICAL = 1;
24652
24653 /**
24654  * Orientation constant - Create a horizontal SplitBar
24655  * @static
24656  * @type Number
24657  */
24658 Roo.SplitBar.HORIZONTAL = 2;
24659
24660 /**
24661  * Placement constant - The resizing element is to the left of the splitter element
24662  * @static
24663  * @type Number
24664  */
24665 Roo.SplitBar.LEFT = 1;
24666
24667 /**
24668  * Placement constant - The resizing element is to the right of the splitter element
24669  * @static
24670  * @type Number
24671  */
24672 Roo.SplitBar.RIGHT = 2;
24673
24674 /**
24675  * Placement constant - The resizing element is positioned above the splitter element
24676  * @static
24677  * @type Number
24678  */
24679 Roo.SplitBar.TOP = 3;
24680
24681 /**
24682  * Placement constant - The resizing element is positioned under splitter element
24683  * @static
24684  * @type Number
24685  */
24686 Roo.SplitBar.BOTTOM = 4;
24687 /*
24688  * Based on:
24689  * Ext JS Library 1.1.1
24690  * Copyright(c) 2006-2007, Ext JS, LLC.
24691  *
24692  * Originally Released Under LGPL - original licence link has changed is not relivant.
24693  *
24694  * Fork - LGPL
24695  * <script type="text/javascript">
24696  */
24697
24698 /**
24699  * @class Roo.View
24700  * @extends Roo.util.Observable
24701  * Create a "View" for an element based on a data model or UpdateManager and the supplied DomHelper template. 
24702  * This class also supports single and multi selection modes. <br>
24703  * Create a data model bound view:
24704  <pre><code>
24705  var store = new Roo.data.Store(...);
24706
24707  var view = new Roo.View({
24708     el : "my-element",
24709     tpl : '&lt;div id="{0}"&gt;{2} - {1}&lt;/div&gt;', // auto create template
24710  
24711     singleSelect: true,
24712     selectedClass: "ydataview-selected",
24713     store: store
24714  });
24715
24716  // listen for node click?
24717  view.on("click", function(vw, index, node, e){
24718  alert('Node "' + node.id + '" at index: ' + index + " was clicked.");
24719  });
24720
24721  // load XML data
24722  dataModel.load("foobar.xml");
24723  </code></pre>
24724  For an example of creating a JSON/UpdateManager view, see {@link Roo.JsonView}.
24725  * <br><br>
24726  * <b>Note: The root of your template must be a single node. Table/row implementations may work but are not supported due to
24727  * IE"s limited insertion support with tables and Opera"s faulty event bubbling.</b>
24728  * 
24729  * Note: old style constructor is still suported (container, template, config)
24730  * 
24731  * @constructor
24732  * Create a new View
24733  * @param {Object} config The config object
24734  * 
24735  */
24736 Roo.View = function(config, depreciated_tpl, depreciated_config){
24737     
24738     if (typeof(depreciated_tpl) == 'undefined') {
24739         // new way.. - universal constructor.
24740         Roo.apply(this, config);
24741         this.el  = Roo.get(this.el);
24742     } else {
24743         // old format..
24744         this.el  = Roo.get(config);
24745         this.tpl = depreciated_tpl;
24746         Roo.apply(this, depreciated_config);
24747     }
24748     this.wrapEl  = this.el.wrap().wrap();
24749     ///this.el = this.wrapEla.appendChild(document.createElement("div"));
24750     
24751     
24752     if(typeof(this.tpl) == "string"){
24753         this.tpl = new Roo.Template(this.tpl);
24754     } else {
24755         // support xtype ctors..
24756         this.tpl = new Roo.factory(this.tpl, Roo);
24757     }
24758     
24759     
24760     this.tpl.compile();
24761    
24762   
24763     
24764      
24765     /** @private */
24766     this.addEvents({
24767         /**
24768          * @event beforeclick
24769          * Fires before a click is processed. Returns false to cancel the default action.
24770          * @param {Roo.View} this
24771          * @param {Number} index The index of the target node
24772          * @param {HTMLElement} node The target node
24773          * @param {Roo.EventObject} e The raw event object
24774          */
24775             "beforeclick" : true,
24776         /**
24777          * @event click
24778          * Fires when a template node is clicked.
24779          * @param {Roo.View} this
24780          * @param {Number} index The index of the target node
24781          * @param {HTMLElement} node The target node
24782          * @param {Roo.EventObject} e The raw event object
24783          */
24784             "click" : true,
24785         /**
24786          * @event dblclick
24787          * Fires when a template node is double clicked.
24788          * @param {Roo.View} this
24789          * @param {Number} index The index of the target node
24790          * @param {HTMLElement} node The target node
24791          * @param {Roo.EventObject} e The raw event object
24792          */
24793             "dblclick" : true,
24794         /**
24795          * @event contextmenu
24796          * Fires when a template node is right clicked.
24797          * @param {Roo.View} this
24798          * @param {Number} index The index of the target node
24799          * @param {HTMLElement} node The target node
24800          * @param {Roo.EventObject} e The raw event object
24801          */
24802             "contextmenu" : true,
24803         /**
24804          * @event selectionchange
24805          * Fires when the selected nodes change.
24806          * @param {Roo.View} this
24807          * @param {Array} selections Array of the selected nodes
24808          */
24809             "selectionchange" : true,
24810     
24811         /**
24812          * @event beforeselect
24813          * Fires before a selection is made. If any handlers return false, the selection is cancelled.
24814          * @param {Roo.View} this
24815          * @param {HTMLElement} node The node to be selected
24816          * @param {Array} selections Array of currently selected nodes
24817          */
24818             "beforeselect" : true,
24819         /**
24820          * @event preparedata
24821          * Fires on every row to render, to allow you to change the data.
24822          * @param {Roo.View} this
24823          * @param {Object} data to be rendered (change this)
24824          */
24825           "preparedata" : true
24826           
24827           
24828         });
24829
24830
24831
24832     this.el.on({
24833         "click": this.onClick,
24834         "dblclick": this.onDblClick,
24835         "contextmenu": this.onContextMenu,
24836         scope:this
24837     });
24838
24839     this.selections = [];
24840     this.nodes = [];
24841     this.cmp = new Roo.CompositeElementLite([]);
24842     if(this.store){
24843         this.store = Roo.factory(this.store, Roo.data);
24844         this.setStore(this.store, true);
24845     }
24846     
24847     if ( this.footer && this.footer.xtype) {
24848            
24849          var fctr = this.wrapEl.appendChild(document.createElement("div"));
24850         
24851         this.footer.dataSource = this.store
24852         this.footer.container = fctr;
24853         this.footer = Roo.factory(this.footer, Roo);
24854         fctr.insertFirst(this.el);
24855         
24856         // this is a bit insane - as the paging toolbar seems to detach the el..
24857 //        dom.parentNode.parentNode.parentNode
24858          // they get detached?
24859     }
24860     
24861     
24862     Roo.View.superclass.constructor.call(this);
24863     
24864     
24865 };
24866
24867 Roo.extend(Roo.View, Roo.util.Observable, {
24868     
24869      /**
24870      * @cfg {Roo.data.Store} store Data store to load data from.
24871      */
24872     store : false,
24873     
24874     /**
24875      * @cfg {String|Roo.Element} el The container element.
24876      */
24877     el : '',
24878     
24879     /**
24880      * @cfg {String|Roo.Template} tpl The template used by this View 
24881      */
24882     tpl : false,
24883     /**
24884      * @cfg {String} dataName the named area of the template to use as the data area
24885      *                          Works with domtemplates roo-name="name"
24886      */
24887     dataName: false,
24888     /**
24889      * @cfg {String} selectedClass The css class to add to selected nodes
24890      */
24891     selectedClass : "x-view-selected",
24892      /**
24893      * @cfg {String} emptyText The empty text to show when nothing is loaded.
24894      */
24895     emptyText : "",
24896     
24897     /**
24898      * @cfg {String} text to display on mask (default Loading)
24899      */
24900     mask : false,
24901     /**
24902      * @cfg {Boolean} multiSelect Allow multiple selection
24903      */
24904     multiSelect : false,
24905     /**
24906      * @cfg {Boolean} singleSelect Allow single selection
24907      */
24908     singleSelect:  false,
24909     
24910     /**
24911      * @cfg {Boolean} toggleSelect - selecting 
24912      */
24913     toggleSelect : false,
24914     
24915     /**
24916      * Returns the element this view is bound to.
24917      * @return {Roo.Element}
24918      */
24919     getEl : function(){
24920         return this.wrapEl;
24921     },
24922     
24923     
24924
24925     /**
24926      * Refreshes the view. - called by datachanged on the store. - do not call directly.
24927      */
24928     refresh : function(){
24929         var t = this.tpl;
24930         
24931         // if we are using something like 'domtemplate', then
24932         // the what gets used is:
24933         // t.applySubtemplate(NAME, data, wrapping data..)
24934         // the outer template then get' applied with
24935         //     the store 'extra data'
24936         // and the body get's added to the
24937         //      roo-name="data" node?
24938         //      <span class='roo-tpl-{name}'></span> ?????
24939         
24940         
24941         
24942         this.clearSelections();
24943         this.el.update("");
24944         var html = [];
24945         var records = this.store.getRange();
24946         if(records.length < 1) {
24947             
24948             // is this valid??  = should it render a template??
24949             
24950             this.el.update(this.emptyText);
24951             return;
24952         }
24953         var el = this.el;
24954         if (this.dataName) {
24955             this.el.update(t.apply(this.store.meta)); //????
24956             el = this.el.child('.roo-tpl-' + this.dataName);
24957         }
24958         
24959         for(var i = 0, len = records.length; i < len; i++){
24960             var data = this.prepareData(records[i].data, i, records[i]);
24961             this.fireEvent("preparedata", this, data, i, records[i]);
24962             html[html.length] = Roo.util.Format.trim(
24963                 this.dataName ?
24964                     t.applySubtemplate(this.dataName, data, this.store.meta) :
24965                     t.apply(data)
24966             );
24967         }
24968         
24969         
24970         
24971         el.update(html.join(""));
24972         this.nodes = el.dom.childNodes;
24973         this.updateIndexes(0);
24974     },
24975
24976     /**
24977      * Function to override to reformat the data that is sent to
24978      * the template for each node.
24979      * DEPRICATED - use the preparedata event handler.
24980      * @param {Array/Object} data The raw data (array of colData for a data model bound view or
24981      * a JSON object for an UpdateManager bound view).
24982      */
24983     prepareData : function(data, index, record)
24984     {
24985         this.fireEvent("preparedata", this, data, index, record);
24986         return data;
24987     },
24988
24989     onUpdate : function(ds, record){
24990         this.clearSelections();
24991         var index = this.store.indexOf(record);
24992         var n = this.nodes[index];
24993         this.tpl.insertBefore(n, this.prepareData(record.data, index, record));
24994         n.parentNode.removeChild(n);
24995         this.updateIndexes(index, index);
24996     },
24997
24998     
24999     
25000 // --------- FIXME     
25001     onAdd : function(ds, records, index)
25002     {
25003         this.clearSelections();
25004         if(this.nodes.length == 0){
25005             this.refresh();
25006             return;
25007         }
25008         var n = this.nodes[index];
25009         for(var i = 0, len = records.length; i < len; i++){
25010             var d = this.prepareData(records[i].data, i, records[i]);
25011             if(n){
25012                 this.tpl.insertBefore(n, d);
25013             }else{
25014                 
25015                 this.tpl.append(this.el, d);
25016             }
25017         }
25018         this.updateIndexes(index);
25019     },
25020
25021     onRemove : function(ds, record, index){
25022         this.clearSelections();
25023         var el = this.dataName  ?
25024             this.el.child('.roo-tpl-' + this.dataName) :
25025             this.el; 
25026         el.dom.removeChild(this.nodes[index]);
25027         this.updateIndexes(index);
25028     },
25029
25030     /**
25031      * Refresh an individual node.
25032      * @param {Number} index
25033      */
25034     refreshNode : function(index){
25035         this.onUpdate(this.store, this.store.getAt(index));
25036     },
25037
25038     updateIndexes : function(startIndex, endIndex){
25039         var ns = this.nodes;
25040         startIndex = startIndex || 0;
25041         endIndex = endIndex || ns.length - 1;
25042         for(var i = startIndex; i <= endIndex; i++){
25043             ns[i].nodeIndex = i;
25044         }
25045     },
25046
25047     /**
25048      * Changes the data store this view uses and refresh the view.
25049      * @param {Store} store
25050      */
25051     setStore : function(store, initial){
25052         if(!initial && this.store){
25053             this.store.un("datachanged", this.refresh);
25054             this.store.un("add", this.onAdd);
25055             this.store.un("remove", this.onRemove);
25056             this.store.un("update", this.onUpdate);
25057             this.store.un("clear", this.refresh);
25058             this.store.un("beforeload", this.onBeforeLoad);
25059             this.store.un("load", this.onLoad);
25060             this.store.un("loadexception", this.onLoad);
25061         }
25062         if(store){
25063           
25064             store.on("datachanged", this.refresh, this);
25065             store.on("add", this.onAdd, this);
25066             store.on("remove", this.onRemove, this);
25067             store.on("update", this.onUpdate, this);
25068             store.on("clear", this.refresh, this);
25069             store.on("beforeload", this.onBeforeLoad, this);
25070             store.on("load", this.onLoad, this);
25071             store.on("loadexception", this.onLoad, this);
25072         }
25073         
25074         if(store){
25075             this.refresh();
25076         }
25077     },
25078     /**
25079      * onbeforeLoad - masks the loading area.
25080      *
25081      */
25082     onBeforeLoad : function()
25083     {
25084         this.el.update("");
25085         this.el.mask(this.mask ? this.mask : "Loading" ); 
25086     },
25087     onLoad : function ()
25088     {
25089         this.el.unmask();
25090     },
25091     
25092
25093     /**
25094      * Returns the template node the passed child belongs to or null if it doesn't belong to one.
25095      * @param {HTMLElement} node
25096      * @return {HTMLElement} The template node
25097      */
25098     findItemFromChild : function(node){
25099         var el = this.dataName  ?
25100             this.el.child('.roo-tpl-' + this.dataName,true) :
25101             this.el.dom; 
25102         
25103         if(!node || node.parentNode == el){
25104                     return node;
25105             }
25106             var p = node.parentNode;
25107             while(p && p != el){
25108             if(p.parentNode == el){
25109                 return p;
25110             }
25111             p = p.parentNode;
25112         }
25113             return null;
25114     },
25115
25116     /** @ignore */
25117     onClick : function(e){
25118         var item = this.findItemFromChild(e.getTarget());
25119         if(item){
25120             var index = this.indexOf(item);
25121             if(this.onItemClick(item, index, e) !== false){
25122                 this.fireEvent("click", this, index, item, e);
25123             }
25124         }else{
25125             this.clearSelections();
25126         }
25127     },
25128
25129     /** @ignore */
25130     onContextMenu : function(e){
25131         var item = this.findItemFromChild(e.getTarget());
25132         if(item){
25133             this.fireEvent("contextmenu", this, this.indexOf(item), item, e);
25134         }
25135     },
25136
25137     /** @ignore */
25138     onDblClick : function(e){
25139         var item = this.findItemFromChild(e.getTarget());
25140         if(item){
25141             this.fireEvent("dblclick", this, this.indexOf(item), item, e);
25142         }
25143     },
25144
25145     onItemClick : function(item, index, e)
25146     {
25147         if(this.fireEvent("beforeclick", this, index, item, e) === false){
25148             return false;
25149         }
25150         if (this.toggleSelect) {
25151             var m = this.isSelected(item) ? 'unselect' : 'select';
25152             Roo.log(m);
25153             var _t = this;
25154             _t[m](item, true, false);
25155             return true;
25156         }
25157         if(this.multiSelect || this.singleSelect){
25158             if(this.multiSelect && e.shiftKey && this.lastSelection){
25159                 this.select(this.getNodes(this.indexOf(this.lastSelection), index), false);
25160             }else{
25161                 this.select(item, this.multiSelect && e.ctrlKey);
25162                 this.lastSelection = item;
25163             }
25164             e.preventDefault();
25165         }
25166         return true;
25167     },
25168
25169     /**
25170      * Get the number of selected nodes.
25171      * @return {Number}
25172      */
25173     getSelectionCount : function(){
25174         return this.selections.length;
25175     },
25176
25177     /**
25178      * Get the currently selected nodes.
25179      * @return {Array} An array of HTMLElements
25180      */
25181     getSelectedNodes : function(){
25182         return this.selections;
25183     },
25184
25185     /**
25186      * Get the indexes of the selected nodes.
25187      * @return {Array}
25188      */
25189     getSelectedIndexes : function(){
25190         var indexes = [], s = this.selections;
25191         for(var i = 0, len = s.length; i < len; i++){
25192             indexes.push(s[i].nodeIndex);
25193         }
25194         return indexes;
25195     },
25196
25197     /**
25198      * Clear all selections
25199      * @param {Boolean} suppressEvent (optional) true to skip firing of the selectionchange event
25200      */
25201     clearSelections : function(suppressEvent){
25202         if(this.nodes && (this.multiSelect || this.singleSelect) && this.selections.length > 0){
25203             this.cmp.elements = this.selections;
25204             this.cmp.removeClass(this.selectedClass);
25205             this.selections = [];
25206             if(!suppressEvent){
25207                 this.fireEvent("selectionchange", this, this.selections);
25208             }
25209         }
25210     },
25211
25212     /**
25213      * Returns true if the passed node is selected
25214      * @param {HTMLElement/Number} node The node or node index
25215      * @return {Boolean}
25216      */
25217     isSelected : function(node){
25218         var s = this.selections;
25219         if(s.length < 1){
25220             return false;
25221         }
25222         node = this.getNode(node);
25223         return s.indexOf(node) !== -1;
25224     },
25225
25226     /**
25227      * Selects nodes.
25228      * @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
25229      * @param {Boolean} keepExisting (optional) true to keep existing selections
25230      * @param {Boolean} suppressEvent (optional) true to skip firing of the selectionchange vent
25231      */
25232     select : function(nodeInfo, keepExisting, suppressEvent){
25233         if(nodeInfo instanceof Array){
25234             if(!keepExisting){
25235                 this.clearSelections(true);
25236             }
25237             for(var i = 0, len = nodeInfo.length; i < len; i++){
25238                 this.select(nodeInfo[i], true, true);
25239             }
25240             return;
25241         } 
25242         var node = this.getNode(nodeInfo);
25243         if(!node || this.isSelected(node)){
25244             return; // already selected.
25245         }
25246         if(!keepExisting){
25247             this.clearSelections(true);
25248         }
25249         if(this.fireEvent("beforeselect", this, node, this.selections) !== false){
25250             Roo.fly(node).addClass(this.selectedClass);
25251             this.selections.push(node);
25252             if(!suppressEvent){
25253                 this.fireEvent("selectionchange", this, this.selections);
25254             }
25255         }
25256         
25257         
25258     },
25259       /**
25260      * Unselects nodes.
25261      * @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
25262      * @param {Boolean} keepExisting (optional) true IGNORED (for campatibility with select)
25263      * @param {Boolean} suppressEvent (optional) true to skip firing of the selectionchange vent
25264      */
25265     unselect : function(nodeInfo, keepExisting, suppressEvent)
25266     {
25267         if(nodeInfo instanceof Array){
25268             Roo.each(this.selections, function(s) {
25269                 this.unselect(s, nodeInfo);
25270             }, this);
25271             return;
25272         }
25273         var node = this.getNode(nodeInfo);
25274         if(!node || !this.isSelected(node)){
25275             Roo.log("not selected");
25276             return; // not selected.
25277         }
25278         // fireevent???
25279         var ns = [];
25280         Roo.each(this.selections, function(s) {
25281             if (s == node ) {
25282                 Roo.fly(node).removeClass(this.selectedClass);
25283
25284                 return;
25285             }
25286             ns.push(s);
25287         },this);
25288         
25289         this.selections= ns;
25290         this.fireEvent("selectionchange", this, this.selections);
25291     },
25292
25293     /**
25294      * Gets a template node.
25295      * @param {HTMLElement/String/Number} nodeInfo An HTMLElement template node, index of a template node or the id of a template node
25296      * @return {HTMLElement} The node or null if it wasn't found
25297      */
25298     getNode : function(nodeInfo){
25299         if(typeof nodeInfo == "string"){
25300             return document.getElementById(nodeInfo);
25301         }else if(typeof nodeInfo == "number"){
25302             return this.nodes[nodeInfo];
25303         }
25304         return nodeInfo;
25305     },
25306
25307     /**
25308      * Gets a range template nodes.
25309      * @param {Number} startIndex
25310      * @param {Number} endIndex
25311      * @return {Array} An array of nodes
25312      */
25313     getNodes : function(start, end){
25314         var ns = this.nodes;
25315         start = start || 0;
25316         end = typeof end == "undefined" ? ns.length - 1 : end;
25317         var nodes = [];
25318         if(start <= end){
25319             for(var i = start; i <= end; i++){
25320                 nodes.push(ns[i]);
25321             }
25322         } else{
25323             for(var i = start; i >= end; i--){
25324                 nodes.push(ns[i]);
25325             }
25326         }
25327         return nodes;
25328     },
25329
25330     /**
25331      * Finds the index of the passed node
25332      * @param {HTMLElement/String/Number} nodeInfo An HTMLElement template node, index of a template node or the id of a template node
25333      * @return {Number} The index of the node or -1
25334      */
25335     indexOf : function(node){
25336         node = this.getNode(node);
25337         if(typeof node.nodeIndex == "number"){
25338             return node.nodeIndex;
25339         }
25340         var ns = this.nodes;
25341         for(var i = 0, len = ns.length; i < len; i++){
25342             if(ns[i] == node){
25343                 return i;
25344             }
25345         }
25346         return -1;
25347     }
25348 });
25349 /*
25350  * Based on:
25351  * Ext JS Library 1.1.1
25352  * Copyright(c) 2006-2007, Ext JS, LLC.
25353  *
25354  * Originally Released Under LGPL - original licence link has changed is not relivant.
25355  *
25356  * Fork - LGPL
25357  * <script type="text/javascript">
25358  */
25359
25360 /**
25361  * @class Roo.JsonView
25362  * @extends Roo.View
25363  * Shortcut class to create a JSON + {@link Roo.UpdateManager} template view. Usage:
25364 <pre><code>
25365 var view = new Roo.JsonView({
25366     container: "my-element",
25367     tpl: '&lt;div id="{id}"&gt;{foo} - {bar}&lt;/div&gt;', // auto create template
25368     multiSelect: true, 
25369     jsonRoot: "data" 
25370 });
25371
25372 // listen for node click?
25373 view.on("click", function(vw, index, node, e){
25374     alert('Node "' + node.id + '" at index: ' + index + " was clicked.");
25375 });
25376
25377 // direct load of JSON data
25378 view.load("foobar.php");
25379
25380 // Example from my blog list
25381 var tpl = new Roo.Template(
25382     '&lt;div class="entry"&gt;' +
25383     '&lt;a class="entry-title" href="{link}"&gt;{title}&lt;/a&gt;' +
25384     "&lt;h4&gt;{date} by {author} | {comments} Comments&lt;/h4&gt;{description}" +
25385     "&lt;/div&gt;&lt;hr /&gt;"
25386 );
25387
25388 var moreView = new Roo.JsonView({
25389     container :  "entry-list", 
25390     template : tpl,
25391     jsonRoot: "posts"
25392 });
25393 moreView.on("beforerender", this.sortEntries, this);
25394 moreView.load({
25395     url: "/blog/get-posts.php",
25396     params: "allposts=true",
25397     text: "Loading Blog Entries..."
25398 });
25399 </code></pre>
25400
25401 * Note: old code is supported with arguments : (container, template, config)
25402
25403
25404  * @constructor
25405  * Create a new JsonView
25406  * 
25407  * @param {Object} config The config object
25408  * 
25409  */
25410 Roo.JsonView = function(config, depreciated_tpl, depreciated_config){
25411     
25412     
25413     Roo.JsonView.superclass.constructor.call(this, config, depreciated_tpl, depreciated_config);
25414
25415     var um = this.el.getUpdateManager();
25416     um.setRenderer(this);
25417     um.on("update", this.onLoad, this);
25418     um.on("failure", this.onLoadException, this);
25419
25420     /**
25421      * @event beforerender
25422      * Fires before rendering of the downloaded JSON data.
25423      * @param {Roo.JsonView} this
25424      * @param {Object} data The JSON data loaded
25425      */
25426     /**
25427      * @event load
25428      * Fires when data is loaded.
25429      * @param {Roo.JsonView} this
25430      * @param {Object} data The JSON data loaded
25431      * @param {Object} response The raw Connect response object
25432      */
25433     /**
25434      * @event loadexception
25435      * Fires when loading fails.
25436      * @param {Roo.JsonView} this
25437      * @param {Object} response The raw Connect response object
25438      */
25439     this.addEvents({
25440         'beforerender' : true,
25441         'load' : true,
25442         'loadexception' : true
25443     });
25444 };
25445 Roo.extend(Roo.JsonView, Roo.View, {
25446     /**
25447      * @type {String} The root property in the loaded JSON object that contains the data
25448      */
25449     jsonRoot : "",
25450
25451     /**
25452      * Refreshes the view.
25453      */
25454     refresh : function(){
25455         this.clearSelections();
25456         this.el.update("");
25457         var html = [];
25458         var o = this.jsonData;
25459         if(o && o.length > 0){
25460             for(var i = 0, len = o.length; i < len; i++){
25461                 var data = this.prepareData(o[i], i, o);
25462                 html[html.length] = this.tpl.apply(data);
25463             }
25464         }else{
25465             html.push(this.emptyText);
25466         }
25467         this.el.update(html.join(""));
25468         this.nodes = this.el.dom.childNodes;
25469         this.updateIndexes(0);
25470     },
25471
25472     /**
25473      * 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.
25474      * @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:
25475      <pre><code>
25476      view.load({
25477          url: "your-url.php",
25478          params: {param1: "foo", param2: "bar"}, // or a URL encoded string
25479          callback: yourFunction,
25480          scope: yourObject, //(optional scope)
25481          discardUrl: false,
25482          nocache: false,
25483          text: "Loading...",
25484          timeout: 30,
25485          scripts: false
25486      });
25487      </code></pre>
25488      * The only required property is <i>url</i>. The optional properties <i>nocache</i>, <i>text</i> and <i>scripts</i>
25489      * 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.
25490      * @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}
25491      * @param {Function} callback (optional) Callback when transaction is complete - called with signature (oElement, bSuccess)
25492      * @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.
25493      */
25494     load : function(){
25495         var um = this.el.getUpdateManager();
25496         um.update.apply(um, arguments);
25497     },
25498
25499     render : function(el, response){
25500         this.clearSelections();
25501         this.el.update("");
25502         var o;
25503         try{
25504             o = Roo.util.JSON.decode(response.responseText);
25505             if(this.jsonRoot){
25506                 
25507                 o = o[this.jsonRoot];
25508             }
25509         } catch(e){
25510         }
25511         /**
25512          * The current JSON data or null
25513          */
25514         this.jsonData = o;
25515         this.beforeRender();
25516         this.refresh();
25517     },
25518
25519 /**
25520  * Get the number of records in the current JSON dataset
25521  * @return {Number}
25522  */
25523     getCount : function(){
25524         return this.jsonData ? this.jsonData.length : 0;
25525     },
25526
25527 /**
25528  * Returns the JSON object for the specified node(s)
25529  * @param {HTMLElement/Array} node The node or an array of nodes
25530  * @return {Object/Array} If you pass in an array, you get an array back, otherwise
25531  * you get the JSON object for the node
25532  */
25533     getNodeData : function(node){
25534         if(node instanceof Array){
25535             var data = [];
25536             for(var i = 0, len = node.length; i < len; i++){
25537                 data.push(this.getNodeData(node[i]));
25538             }
25539             return data;
25540         }
25541         return this.jsonData[this.indexOf(node)] || null;
25542     },
25543
25544     beforeRender : function(){
25545         this.snapshot = this.jsonData;
25546         if(this.sortInfo){
25547             this.sort.apply(this, this.sortInfo);
25548         }
25549         this.fireEvent("beforerender", this, this.jsonData);
25550     },
25551
25552     onLoad : function(el, o){
25553         this.fireEvent("load", this, this.jsonData, o);
25554     },
25555
25556     onLoadException : function(el, o){
25557         this.fireEvent("loadexception", this, o);
25558     },
25559
25560 /**
25561  * Filter the data by a specific property.
25562  * @param {String} property A property on your JSON objects
25563  * @param {String/RegExp} value Either string that the property values
25564  * should start with, or a RegExp to test against the property
25565  */
25566     filter : function(property, value){
25567         if(this.jsonData){
25568             var data = [];
25569             var ss = this.snapshot;
25570             if(typeof value == "string"){
25571                 var vlen = value.length;
25572                 if(vlen == 0){
25573                     this.clearFilter();
25574                     return;
25575                 }
25576                 value = value.toLowerCase();
25577                 for(var i = 0, len = ss.length; i < len; i++){
25578                     var o = ss[i];
25579                     if(o[property].substr(0, vlen).toLowerCase() == value){
25580                         data.push(o);
25581                     }
25582                 }
25583             } else if(value.exec){ // regex?
25584                 for(var i = 0, len = ss.length; i < len; i++){
25585                     var o = ss[i];
25586                     if(value.test(o[property])){
25587                         data.push(o);
25588                     }
25589                 }
25590             } else{
25591                 return;
25592             }
25593             this.jsonData = data;
25594             this.refresh();
25595         }
25596     },
25597
25598 /**
25599  * Filter by a function. The passed function will be called with each
25600  * object in the current dataset. If the function returns true the value is kept,
25601  * otherwise it is filtered.
25602  * @param {Function} fn
25603  * @param {Object} scope (optional) The scope of the function (defaults to this JsonView)
25604  */
25605     filterBy : function(fn, scope){
25606         if(this.jsonData){
25607             var data = [];
25608             var ss = this.snapshot;
25609             for(var i = 0, len = ss.length; i < len; i++){
25610                 var o = ss[i];
25611                 if(fn.call(scope || this, o)){
25612                     data.push(o);
25613                 }
25614             }
25615             this.jsonData = data;
25616             this.refresh();
25617         }
25618     },
25619
25620 /**
25621  * Clears the current filter.
25622  */
25623     clearFilter : function(){
25624         if(this.snapshot && this.jsonData != this.snapshot){
25625             this.jsonData = this.snapshot;
25626             this.refresh();
25627         }
25628     },
25629
25630
25631 /**
25632  * Sorts the data for this view and refreshes it.
25633  * @param {String} property A property on your JSON objects to sort on
25634  * @param {String} direction (optional) "desc" or "asc" (defaults to "asc")
25635  * @param {Function} sortType (optional) A function to call to convert the data to a sortable value.
25636  */
25637     sort : function(property, dir, sortType){
25638         this.sortInfo = Array.prototype.slice.call(arguments, 0);
25639         if(this.jsonData){
25640             var p = property;
25641             var dsc = dir && dir.toLowerCase() == "desc";
25642             var f = function(o1, o2){
25643                 var v1 = sortType ? sortType(o1[p]) : o1[p];
25644                 var v2 = sortType ? sortType(o2[p]) : o2[p];
25645                 ;
25646                 if(v1 < v2){
25647                     return dsc ? +1 : -1;
25648                 } else if(v1 > v2){
25649                     return dsc ? -1 : +1;
25650                 } else{
25651                     return 0;
25652                 }
25653             };
25654             this.jsonData.sort(f);
25655             this.refresh();
25656             if(this.jsonData != this.snapshot){
25657                 this.snapshot.sort(f);
25658             }
25659         }
25660     }
25661 });/*
25662  * Based on:
25663  * Ext JS Library 1.1.1
25664  * Copyright(c) 2006-2007, Ext JS, LLC.
25665  *
25666  * Originally Released Under LGPL - original licence link has changed is not relivant.
25667  *
25668  * Fork - LGPL
25669  * <script type="text/javascript">
25670  */
25671  
25672
25673 /**
25674  * @class Roo.ColorPalette
25675  * @extends Roo.Component
25676  * Simple color palette class for choosing colors.  The palette can be rendered to any container.<br />
25677  * Here's an example of typical usage:
25678  * <pre><code>
25679 var cp = new Roo.ColorPalette({value:'993300'});  // initial selected color
25680 cp.render('my-div');
25681
25682 cp.on('select', function(palette, selColor){
25683     // do something with selColor
25684 });
25685 </code></pre>
25686  * @constructor
25687  * Create a new ColorPalette
25688  * @param {Object} config The config object
25689  */
25690 Roo.ColorPalette = function(config){
25691     Roo.ColorPalette.superclass.constructor.call(this, config);
25692     this.addEvents({
25693         /**
25694              * @event select
25695              * Fires when a color is selected
25696              * @param {ColorPalette} this
25697              * @param {String} color The 6-digit color hex code (without the # symbol)
25698              */
25699         select: true
25700     });
25701
25702     if(this.handler){
25703         this.on("select", this.handler, this.scope, true);
25704     }
25705 };
25706 Roo.extend(Roo.ColorPalette, Roo.Component, {
25707     /**
25708      * @cfg {String} itemCls
25709      * The CSS class to apply to the containing element (defaults to "x-color-palette")
25710      */
25711     itemCls : "x-color-palette",
25712     /**
25713      * @cfg {String} value
25714      * The initial color to highlight (should be a valid 6-digit color hex code without the # symbol).  Note that
25715      * the hex codes are case-sensitive.
25716      */
25717     value : null,
25718     clickEvent:'click',
25719     // private
25720     ctype: "Roo.ColorPalette",
25721
25722     /**
25723      * @cfg {Boolean} allowReselect If set to true then reselecting a color that is already selected fires the selection event
25724      */
25725     allowReselect : false,
25726
25727     /**
25728      * <p>An array of 6-digit color hex code strings (without the # symbol).  This array can contain any number
25729      * of colors, and each hex code should be unique.  The width of the palette is controlled via CSS by adjusting
25730      * the width property of the 'x-color-palette' class (or assigning a custom class), so you can balance the number
25731      * of colors with the width setting until the box is symmetrical.</p>
25732      * <p>You can override individual colors if needed:</p>
25733      * <pre><code>
25734 var cp = new Roo.ColorPalette();
25735 cp.colors[0] = "FF0000";  // change the first box to red
25736 </code></pre>
25737
25738 Or you can provide a custom array of your own for complete control:
25739 <pre><code>
25740 var cp = new Roo.ColorPalette();
25741 cp.colors = ["000000", "993300", "333300"];
25742 </code></pre>
25743      * @type Array
25744      */
25745     colors : [
25746         "000000", "993300", "333300", "003300", "003366", "000080", "333399", "333333",
25747         "800000", "FF6600", "808000", "008000", "008080", "0000FF", "666699", "808080",
25748         "FF0000", "FF9900", "99CC00", "339966", "33CCCC", "3366FF", "800080", "969696",
25749         "FF00FF", "FFCC00", "FFFF00", "00FF00", "00FFFF", "00CCFF", "993366", "C0C0C0",
25750         "FF99CC", "FFCC99", "FFFF99", "CCFFCC", "CCFFFF", "99CCFF", "CC99FF", "FFFFFF"
25751     ],
25752
25753     // private
25754     onRender : function(container, position){
25755         var t = new Roo.MasterTemplate(
25756             '<tpl><a href="#" class="color-{0}" hidefocus="on"><em><span style="background:#{0}" unselectable="on">&#160;</span></em></a></tpl>'
25757         );
25758         var c = this.colors;
25759         for(var i = 0, len = c.length; i < len; i++){
25760             t.add([c[i]]);
25761         }
25762         var el = document.createElement("div");
25763         el.className = this.itemCls;
25764         t.overwrite(el);
25765         container.dom.insertBefore(el, position);
25766         this.el = Roo.get(el);
25767         this.el.on(this.clickEvent, this.handleClick,  this, {delegate: "a"});
25768         if(this.clickEvent != 'click'){
25769             this.el.on('click', Roo.emptyFn,  this, {delegate: "a", preventDefault:true});
25770         }
25771     },
25772
25773     // private
25774     afterRender : function(){
25775         Roo.ColorPalette.superclass.afterRender.call(this);
25776         if(this.value){
25777             var s = this.value;
25778             this.value = null;
25779             this.select(s);
25780         }
25781     },
25782
25783     // private
25784     handleClick : function(e, t){
25785         e.preventDefault();
25786         if(!this.disabled){
25787             var c = t.className.match(/(?:^|\s)color-(.{6})(?:\s|$)/)[1];
25788             this.select(c.toUpperCase());
25789         }
25790     },
25791
25792     /**
25793      * Selects the specified color in the palette (fires the select event)
25794      * @param {String} color A valid 6-digit color hex code (# will be stripped if included)
25795      */
25796     select : function(color){
25797         color = color.replace("#", "");
25798         if(color != this.value || this.allowReselect){
25799             var el = this.el;
25800             if(this.value){
25801                 el.child("a.color-"+this.value).removeClass("x-color-palette-sel");
25802             }
25803             el.child("a.color-"+color).addClass("x-color-palette-sel");
25804             this.value = color;
25805             this.fireEvent("select", this, color);
25806         }
25807     }
25808 });/*
25809  * Based on:
25810  * Ext JS Library 1.1.1
25811  * Copyright(c) 2006-2007, Ext JS, LLC.
25812  *
25813  * Originally Released Under LGPL - original licence link has changed is not relivant.
25814  *
25815  * Fork - LGPL
25816  * <script type="text/javascript">
25817  */
25818  
25819 /**
25820  * @class Roo.DatePicker
25821  * @extends Roo.Component
25822  * Simple date picker class.
25823  * @constructor
25824  * Create a new DatePicker
25825  * @param {Object} config The config object
25826  */
25827 Roo.DatePicker = function(config){
25828     Roo.DatePicker.superclass.constructor.call(this, config);
25829
25830     this.value = config && config.value ?
25831                  config.value.clearTime() : new Date().clearTime();
25832
25833     this.addEvents({
25834         /**
25835              * @event select
25836              * Fires when a date is selected
25837              * @param {DatePicker} this
25838              * @param {Date} date The selected date
25839              */
25840         'select': true,
25841         /**
25842              * @event monthchange
25843              * Fires when the displayed month changes 
25844              * @param {DatePicker} this
25845              * @param {Date} date The selected month
25846              */
25847         'monthchange': true
25848     });
25849
25850     if(this.handler){
25851         this.on("select", this.handler,  this.scope || this);
25852     }
25853     // build the disabledDatesRE
25854     if(!this.disabledDatesRE && this.disabledDates){
25855         var dd = this.disabledDates;
25856         var re = "(?:";
25857         for(var i = 0; i < dd.length; i++){
25858             re += dd[i];
25859             if(i != dd.length-1) re += "|";
25860         }
25861         this.disabledDatesRE = new RegExp(re + ")");
25862     }
25863 };
25864
25865 Roo.extend(Roo.DatePicker, Roo.Component, {
25866     /**
25867      * @cfg {String} todayText
25868      * The text to display on the button that selects the current date (defaults to "Today")
25869      */
25870     todayText : "Today",
25871     /**
25872      * @cfg {String} okText
25873      * The text to display on the ok button
25874      */
25875     okText : "&#160;OK&#160;", // &#160; to give the user extra clicking room
25876     /**
25877      * @cfg {String} cancelText
25878      * The text to display on the cancel button
25879      */
25880     cancelText : "Cancel",
25881     /**
25882      * @cfg {String} todayTip
25883      * The tooltip to display for the button that selects the current date (defaults to "{current date} (Spacebar)")
25884      */
25885     todayTip : "{0} (Spacebar)",
25886     /**
25887      * @cfg {Date} minDate
25888      * Minimum allowable date (JavaScript date object, defaults to null)
25889      */
25890     minDate : null,
25891     /**
25892      * @cfg {Date} maxDate
25893      * Maximum allowable date (JavaScript date object, defaults to null)
25894      */
25895     maxDate : null,
25896     /**
25897      * @cfg {String} minText
25898      * The error text to display if the minDate validation fails (defaults to "This date is before the minimum date")
25899      */
25900     minText : "This date is before the minimum date",
25901     /**
25902      * @cfg {String} maxText
25903      * The error text to display if the maxDate validation fails (defaults to "This date is after the maximum date")
25904      */
25905     maxText : "This date is after the maximum date",
25906     /**
25907      * @cfg {String} format
25908      * The default date format string which can be overriden for localization support.  The format must be
25909      * valid according to {@link Date#parseDate} (defaults to 'm/d/y').
25910      */
25911     format : "m/d/y",
25912     /**
25913      * @cfg {Array} disabledDays
25914      * An array of days to disable, 0-based. For example, [0, 6] disables Sunday and Saturday (defaults to null).
25915      */
25916     disabledDays : null,
25917     /**
25918      * @cfg {String} disabledDaysText
25919      * The tooltip to display when the date falls on a disabled day (defaults to "")
25920      */
25921     disabledDaysText : "",
25922     /**
25923      * @cfg {RegExp} disabledDatesRE
25924      * JavaScript regular expression used to disable a pattern of dates (defaults to null)
25925      */
25926     disabledDatesRE : null,
25927     /**
25928      * @cfg {String} disabledDatesText
25929      * The tooltip text to display when the date falls on a disabled date (defaults to "")
25930      */
25931     disabledDatesText : "",
25932     /**
25933      * @cfg {Boolean} constrainToViewport
25934      * True to constrain the date picker to the viewport (defaults to true)
25935      */
25936     constrainToViewport : true,
25937     /**
25938      * @cfg {Array} monthNames
25939      * An array of textual month names which can be overriden for localization support (defaults to Date.monthNames)
25940      */
25941     monthNames : Date.monthNames,
25942     /**
25943      * @cfg {Array} dayNames
25944      * An array of textual day names which can be overriden for localization support (defaults to Date.dayNames)
25945      */
25946     dayNames : Date.dayNames,
25947     /**
25948      * @cfg {String} nextText
25949      * The next month navigation button tooltip (defaults to 'Next Month (Control+Right)')
25950      */
25951     nextText: 'Next Month (Control+Right)',
25952     /**
25953      * @cfg {String} prevText
25954      * The previous month navigation button tooltip (defaults to 'Previous Month (Control+Left)')
25955      */
25956     prevText: 'Previous Month (Control+Left)',
25957     /**
25958      * @cfg {String} monthYearText
25959      * The header month selector tooltip (defaults to 'Choose a month (Control+Up/Down to move years)')
25960      */
25961     monthYearText: 'Choose a month (Control+Up/Down to move years)',
25962     /**
25963      * @cfg {Number} startDay
25964      * Day index at which the week should begin, 0-based (defaults to 0, which is Sunday)
25965      */
25966     startDay : 0,
25967     /**
25968      * @cfg {Bool} showClear
25969      * Show a clear button (usefull for date form elements that can be blank.)
25970      */
25971     
25972     showClear: false,
25973     
25974     /**
25975      * Sets the value of the date field
25976      * @param {Date} value The date to set
25977      */
25978     setValue : function(value){
25979         var old = this.value;
25980         
25981         if (typeof(value) == 'string') {
25982          
25983             value = Date.parseDate(value, this.format);
25984         }
25985         if (!value) {
25986             value = new Date();
25987         }
25988         
25989         this.value = value.clearTime(true);
25990         if(this.el){
25991             this.update(this.value);
25992         }
25993     },
25994
25995     /**
25996      * Gets the current selected value of the date field
25997      * @return {Date} The selected date
25998      */
25999     getValue : function(){
26000         return this.value;
26001     },
26002
26003     // private
26004     focus : function(){
26005         if(this.el){
26006             this.update(this.activeDate);
26007         }
26008     },
26009
26010     // privateval
26011     onRender : function(container, position){
26012         
26013         var m = [
26014              '<table cellspacing="0">',
26015                 '<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>',
26016                 '<tr><td colspan="3"><table class="x-date-inner" cellspacing="0"><thead><tr>'];
26017         var dn = this.dayNames;
26018         for(var i = 0; i < 7; i++){
26019             var d = this.startDay+i;
26020             if(d > 6){
26021                 d = d-7;
26022             }
26023             m.push("<th><span>", dn[d].substr(0,1), "</span></th>");
26024         }
26025         m[m.length] = "</tr></thead><tbody><tr>";
26026         for(var i = 0; i < 42; i++) {
26027             if(i % 7 == 0 && i != 0){
26028                 m[m.length] = "</tr><tr>";
26029             }
26030             m[m.length] = '<td><a href="#" hidefocus="on" class="x-date-date" tabIndex="1"><em><span></span></em></a></td>';
26031         }
26032         m[m.length] = '</tr></tbody></table></td></tr><tr>'+
26033             '<td colspan="3" class="x-date-bottom" align="center"></td></tr></table><div class="x-date-mp"></div>';
26034
26035         var el = document.createElement("div");
26036         el.className = "x-date-picker";
26037         el.innerHTML = m.join("");
26038
26039         container.dom.insertBefore(el, position);
26040
26041         this.el = Roo.get(el);
26042         this.eventEl = Roo.get(el.firstChild);
26043
26044         new Roo.util.ClickRepeater(this.el.child("td.x-date-left a"), {
26045             handler: this.showPrevMonth,
26046             scope: this,
26047             preventDefault:true,
26048             stopDefault:true
26049         });
26050
26051         new Roo.util.ClickRepeater(this.el.child("td.x-date-right a"), {
26052             handler: this.showNextMonth,
26053             scope: this,
26054             preventDefault:true,
26055             stopDefault:true
26056         });
26057
26058         this.eventEl.on("mousewheel", this.handleMouseWheel,  this);
26059
26060         this.monthPicker = this.el.down('div.x-date-mp');
26061         this.monthPicker.enableDisplayMode('block');
26062         
26063         var kn = new Roo.KeyNav(this.eventEl, {
26064             "left" : function(e){
26065                 e.ctrlKey ?
26066                     this.showPrevMonth() :
26067                     this.update(this.activeDate.add("d", -1));
26068             },
26069
26070             "right" : function(e){
26071                 e.ctrlKey ?
26072                     this.showNextMonth() :
26073                     this.update(this.activeDate.add("d", 1));
26074             },
26075
26076             "up" : function(e){
26077                 e.ctrlKey ?
26078                     this.showNextYear() :
26079                     this.update(this.activeDate.add("d", -7));
26080             },
26081
26082             "down" : function(e){
26083                 e.ctrlKey ?
26084                     this.showPrevYear() :
26085                     this.update(this.activeDate.add("d", 7));
26086             },
26087
26088             "pageUp" : function(e){
26089                 this.showNextMonth();
26090             },
26091
26092             "pageDown" : function(e){
26093                 this.showPrevMonth();
26094             },
26095
26096             "enter" : function(e){
26097                 e.stopPropagation();
26098                 return true;
26099             },
26100
26101             scope : this
26102         });
26103
26104         this.eventEl.on("click", this.handleDateClick,  this, {delegate: "a.x-date-date"});
26105
26106         this.eventEl.addKeyListener(Roo.EventObject.SPACE, this.selectToday,  this);
26107
26108         this.el.unselectable();
26109         
26110         this.cells = this.el.select("table.x-date-inner tbody td");
26111         this.textNodes = this.el.query("table.x-date-inner tbody span");
26112
26113         this.mbtn = new Roo.Button(this.el.child("td.x-date-middle", true), {
26114             text: "&#160;",
26115             tooltip: this.monthYearText
26116         });
26117
26118         this.mbtn.on('click', this.showMonthPicker, this);
26119         this.mbtn.el.child(this.mbtn.menuClassTarget).addClass("x-btn-with-menu");
26120
26121
26122         var today = (new Date()).dateFormat(this.format);
26123         
26124         var baseTb = new Roo.Toolbar(this.el.child("td.x-date-bottom", true));
26125         if (this.showClear) {
26126             baseTb.add( new Roo.Toolbar.Fill());
26127         }
26128         baseTb.add({
26129             text: String.format(this.todayText, today),
26130             tooltip: String.format(this.todayTip, today),
26131             handler: this.selectToday,
26132             scope: this
26133         });
26134         
26135         //var todayBtn = new Roo.Button(this.el.child("td.x-date-bottom", true), {
26136             
26137         //});
26138         if (this.showClear) {
26139             
26140             baseTb.add( new Roo.Toolbar.Fill());
26141             baseTb.add({
26142                 text: '&#160;',
26143                 cls: 'x-btn-icon x-btn-clear',
26144                 handler: function() {
26145                     //this.value = '';
26146                     this.fireEvent("select", this, '');
26147                 },
26148                 scope: this
26149             });
26150         }
26151         
26152         
26153         if(Roo.isIE){
26154             this.el.repaint();
26155         }
26156         this.update(this.value);
26157     },
26158
26159     createMonthPicker : function(){
26160         if(!this.monthPicker.dom.firstChild){
26161             var buf = ['<table border="0" cellspacing="0">'];
26162             for(var i = 0; i < 6; i++){
26163                 buf.push(
26164                     '<tr><td class="x-date-mp-month"><a href="#">', this.monthNames[i].substr(0, 3), '</a></td>',
26165                     '<td class="x-date-mp-month x-date-mp-sep"><a href="#">', this.monthNames[i+6].substr(0, 3), '</a></td>',
26166                     i == 0 ?
26167                     '<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>' :
26168                     '<td class="x-date-mp-year"><a href="#"></a></td><td class="x-date-mp-year"><a href="#"></a></td></tr>'
26169                 );
26170             }
26171             buf.push(
26172                 '<tr class="x-date-mp-btns"><td colspan="4"><button type="button" class="x-date-mp-ok">',
26173                     this.okText,
26174                     '</button><button type="button" class="x-date-mp-cancel">',
26175                     this.cancelText,
26176                     '</button></td></tr>',
26177                 '</table>'
26178             );
26179             this.monthPicker.update(buf.join(''));
26180             this.monthPicker.on('click', this.onMonthClick, this);
26181             this.monthPicker.on('dblclick', this.onMonthDblClick, this);
26182
26183             this.mpMonths = this.monthPicker.select('td.x-date-mp-month');
26184             this.mpYears = this.monthPicker.select('td.x-date-mp-year');
26185
26186             this.mpMonths.each(function(m, a, i){
26187                 i += 1;
26188                 if((i%2) == 0){
26189                     m.dom.xmonth = 5 + Math.round(i * .5);
26190                 }else{
26191                     m.dom.xmonth = Math.round((i-1) * .5);
26192                 }
26193             });
26194         }
26195     },
26196
26197     showMonthPicker : function(){
26198         this.createMonthPicker();
26199         var size = this.el.getSize();
26200         this.monthPicker.setSize(size);
26201         this.monthPicker.child('table').setSize(size);
26202
26203         this.mpSelMonth = (this.activeDate || this.value).getMonth();
26204         this.updateMPMonth(this.mpSelMonth);
26205         this.mpSelYear = (this.activeDate || this.value).getFullYear();
26206         this.updateMPYear(this.mpSelYear);
26207
26208         this.monthPicker.slideIn('t', {duration:.2});
26209     },
26210
26211     updateMPYear : function(y){
26212         this.mpyear = y;
26213         var ys = this.mpYears.elements;
26214         for(var i = 1; i <= 10; i++){
26215             var td = ys[i-1], y2;
26216             if((i%2) == 0){
26217                 y2 = y + Math.round(i * .5);
26218                 td.firstChild.innerHTML = y2;
26219                 td.xyear = y2;
26220             }else{
26221                 y2 = y - (5-Math.round(i * .5));
26222                 td.firstChild.innerHTML = y2;
26223                 td.xyear = y2;
26224             }
26225             this.mpYears.item(i-1)[y2 == this.mpSelYear ? 'addClass' : 'removeClass']('x-date-mp-sel');
26226         }
26227     },
26228
26229     updateMPMonth : function(sm){
26230         this.mpMonths.each(function(m, a, i){
26231             m[m.dom.xmonth == sm ? 'addClass' : 'removeClass']('x-date-mp-sel');
26232         });
26233     },
26234
26235     selectMPMonth: function(m){
26236         
26237     },
26238
26239     onMonthClick : function(e, t){
26240         e.stopEvent();
26241         var el = new Roo.Element(t), pn;
26242         if(el.is('button.x-date-mp-cancel')){
26243             this.hideMonthPicker();
26244         }
26245         else if(el.is('button.x-date-mp-ok')){
26246             this.update(new Date(this.mpSelYear, this.mpSelMonth, (this.activeDate || this.value).getDate()));
26247             this.hideMonthPicker();
26248         }
26249         else if(pn = el.up('td.x-date-mp-month', 2)){
26250             this.mpMonths.removeClass('x-date-mp-sel');
26251             pn.addClass('x-date-mp-sel');
26252             this.mpSelMonth = pn.dom.xmonth;
26253         }
26254         else if(pn = el.up('td.x-date-mp-year', 2)){
26255             this.mpYears.removeClass('x-date-mp-sel');
26256             pn.addClass('x-date-mp-sel');
26257             this.mpSelYear = pn.dom.xyear;
26258         }
26259         else if(el.is('a.x-date-mp-prev')){
26260             this.updateMPYear(this.mpyear-10);
26261         }
26262         else if(el.is('a.x-date-mp-next')){
26263             this.updateMPYear(this.mpyear+10);
26264         }
26265     },
26266
26267     onMonthDblClick : function(e, t){
26268         e.stopEvent();
26269         var el = new Roo.Element(t), pn;
26270         if(pn = el.up('td.x-date-mp-month', 2)){
26271             this.update(new Date(this.mpSelYear, pn.dom.xmonth, (this.activeDate || this.value).getDate()));
26272             this.hideMonthPicker();
26273         }
26274         else if(pn = el.up('td.x-date-mp-year', 2)){
26275             this.update(new Date(pn.dom.xyear, this.mpSelMonth, (this.activeDate || this.value).getDate()));
26276             this.hideMonthPicker();
26277         }
26278     },
26279
26280     hideMonthPicker : function(disableAnim){
26281         if(this.monthPicker){
26282             if(disableAnim === true){
26283                 this.monthPicker.hide();
26284             }else{
26285                 this.monthPicker.slideOut('t', {duration:.2});
26286             }
26287         }
26288     },
26289
26290     // private
26291     showPrevMonth : function(e){
26292         this.update(this.activeDate.add("mo", -1));
26293     },
26294
26295     // private
26296     showNextMonth : function(e){
26297         this.update(this.activeDate.add("mo", 1));
26298     },
26299
26300     // private
26301     showPrevYear : function(){
26302         this.update(this.activeDate.add("y", -1));
26303     },
26304
26305     // private
26306     showNextYear : function(){
26307         this.update(this.activeDate.add("y", 1));
26308     },
26309
26310     // private
26311     handleMouseWheel : function(e){
26312         var delta = e.getWheelDelta();
26313         if(delta > 0){
26314             this.showPrevMonth();
26315             e.stopEvent();
26316         } else if(delta < 0){
26317             this.showNextMonth();
26318             e.stopEvent();
26319         }
26320     },
26321
26322     // private
26323     handleDateClick : function(e, t){
26324         e.stopEvent();
26325         if(t.dateValue && !Roo.fly(t.parentNode).hasClass("x-date-disabled")){
26326             this.setValue(new Date(t.dateValue));
26327             this.fireEvent("select", this, this.value);
26328         }
26329     },
26330
26331     // private
26332     selectToday : function(){
26333         this.setValue(new Date().clearTime());
26334         this.fireEvent("select", this, this.value);
26335     },
26336
26337     // private
26338     update : function(date)
26339     {
26340         var vd = this.activeDate;
26341         this.activeDate = date;
26342         if(vd && this.el){
26343             var t = date.getTime();
26344             if(vd.getMonth() == date.getMonth() && vd.getFullYear() == date.getFullYear()){
26345                 this.cells.removeClass("x-date-selected");
26346                 this.cells.each(function(c){
26347                    if(c.dom.firstChild.dateValue == t){
26348                        c.addClass("x-date-selected");
26349                        setTimeout(function(){
26350                             try{c.dom.firstChild.focus();}catch(e){}
26351                        }, 50);
26352                        return false;
26353                    }
26354                 });
26355                 return;
26356             }
26357         }
26358         
26359         var days = date.getDaysInMonth();
26360         var firstOfMonth = date.getFirstDateOfMonth();
26361         var startingPos = firstOfMonth.getDay()-this.startDay;
26362
26363         if(startingPos <= this.startDay){
26364             startingPos += 7;
26365         }
26366
26367         var pm = date.add("mo", -1);
26368         var prevStart = pm.getDaysInMonth()-startingPos;
26369
26370         var cells = this.cells.elements;
26371         var textEls = this.textNodes;
26372         days += startingPos;
26373
26374         // convert everything to numbers so it's fast
26375         var day = 86400000;
26376         var d = (new Date(pm.getFullYear(), pm.getMonth(), prevStart)).clearTime();
26377         var today = new Date().clearTime().getTime();
26378         var sel = date.clearTime().getTime();
26379         var min = this.minDate ? this.minDate.clearTime() : Number.NEGATIVE_INFINITY;
26380         var max = this.maxDate ? this.maxDate.clearTime() : Number.POSITIVE_INFINITY;
26381         var ddMatch = this.disabledDatesRE;
26382         var ddText = this.disabledDatesText;
26383         var ddays = this.disabledDays ? this.disabledDays.join("") : false;
26384         var ddaysText = this.disabledDaysText;
26385         var format = this.format;
26386
26387         var setCellClass = function(cal, cell){
26388             cell.title = "";
26389             var t = d.getTime();
26390             cell.firstChild.dateValue = t;
26391             if(t == today){
26392                 cell.className += " x-date-today";
26393                 cell.title = cal.todayText;
26394             }
26395             if(t == sel){
26396                 cell.className += " x-date-selected";
26397                 setTimeout(function(){
26398                     try{cell.firstChild.focus();}catch(e){}
26399                 }, 50);
26400             }
26401             // disabling
26402             if(t < min) {
26403                 cell.className = " x-date-disabled";
26404                 cell.title = cal.minText;
26405                 return;
26406             }
26407             if(t > max) {
26408                 cell.className = " x-date-disabled";
26409                 cell.title = cal.maxText;
26410                 return;
26411             }
26412             if(ddays){
26413                 if(ddays.indexOf(d.getDay()) != -1){
26414                     cell.title = ddaysText;
26415                     cell.className = " x-date-disabled";
26416                 }
26417             }
26418             if(ddMatch && format){
26419                 var fvalue = d.dateFormat(format);
26420                 if(ddMatch.test(fvalue)){
26421                     cell.title = ddText.replace("%0", fvalue);
26422                     cell.className = " x-date-disabled";
26423                 }
26424             }
26425         };
26426
26427         var i = 0;
26428         for(; i < startingPos; i++) {
26429             textEls[i].innerHTML = (++prevStart);
26430             d.setDate(d.getDate()+1);
26431             cells[i].className = "x-date-prevday";
26432             setCellClass(this, cells[i]);
26433         }
26434         for(; i < days; i++){
26435             intDay = i - startingPos + 1;
26436             textEls[i].innerHTML = (intDay);
26437             d.setDate(d.getDate()+1);
26438             cells[i].className = "x-date-active";
26439             setCellClass(this, cells[i]);
26440         }
26441         var extraDays = 0;
26442         for(; i < 42; i++) {
26443              textEls[i].innerHTML = (++extraDays);
26444              d.setDate(d.getDate()+1);
26445              cells[i].className = "x-date-nextday";
26446              setCellClass(this, cells[i]);
26447         }
26448
26449         this.mbtn.setText(this.monthNames[date.getMonth()] + " " + date.getFullYear());
26450         this.fireEvent('monthchange', this, date);
26451         
26452         if(!this.internalRender){
26453             var main = this.el.dom.firstChild;
26454             var w = main.offsetWidth;
26455             this.el.setWidth(w + this.el.getBorderWidth("lr"));
26456             Roo.fly(main).setWidth(w);
26457             this.internalRender = true;
26458             // opera does not respect the auto grow header center column
26459             // then, after it gets a width opera refuses to recalculate
26460             // without a second pass
26461             if(Roo.isOpera && !this.secondPass){
26462                 main.rows[0].cells[1].style.width = (w - (main.rows[0].cells[0].offsetWidth+main.rows[0].cells[2].offsetWidth)) + "px";
26463                 this.secondPass = true;
26464                 this.update.defer(10, this, [date]);
26465             }
26466         }
26467         
26468         
26469     }
26470 });        /*
26471  * Based on:
26472  * Ext JS Library 1.1.1
26473  * Copyright(c) 2006-2007, Ext JS, LLC.
26474  *
26475  * Originally Released Under LGPL - original licence link has changed is not relivant.
26476  *
26477  * Fork - LGPL
26478  * <script type="text/javascript">
26479  */
26480 /**
26481  * @class Roo.TabPanel
26482  * @extends Roo.util.Observable
26483  * A lightweight tab container.
26484  * <br><br>
26485  * Usage:
26486  * <pre><code>
26487 // basic tabs 1, built from existing content
26488 var tabs = new Roo.TabPanel("tabs1");
26489 tabs.addTab("script", "View Script");
26490 tabs.addTab("markup", "View Markup");
26491 tabs.activate("script");
26492
26493 // more advanced tabs, built from javascript
26494 var jtabs = new Roo.TabPanel("jtabs");
26495 jtabs.addTab("jtabs-1", "Normal Tab", "My content was added during construction.");
26496
26497 // set up the UpdateManager
26498 var tab2 = jtabs.addTab("jtabs-2", "Ajax Tab 1");
26499 var updater = tab2.getUpdateManager();
26500 updater.setDefaultUrl("ajax1.htm");
26501 tab2.on('activate', updater.refresh, updater, true);
26502
26503 // Use setUrl for Ajax loading
26504 var tab3 = jtabs.addTab("jtabs-3", "Ajax Tab 2");
26505 tab3.setUrl("ajax2.htm", null, true);
26506
26507 // Disabled tab
26508 var tab4 = jtabs.addTab("tabs1-5", "Disabled Tab", "Can't see me cause I'm disabled");
26509 tab4.disable();
26510
26511 jtabs.activate("jtabs-1");
26512  * </code></pre>
26513  * @constructor
26514  * Create a new TabPanel.
26515  * @param {String/HTMLElement/Roo.Element} container The id, DOM element or Roo.Element container where this TabPanel is to be rendered.
26516  * @param {Object/Boolean} config Config object to set any properties for this TabPanel, or true to render the tabs on the bottom.
26517  */
26518 Roo.TabPanel = function(container, config){
26519     /**
26520     * The container element for this TabPanel.
26521     * @type Roo.Element
26522     */
26523     this.el = Roo.get(container, true);
26524     if(config){
26525         if(typeof config == "boolean"){
26526             this.tabPosition = config ? "bottom" : "top";
26527         }else{
26528             Roo.apply(this, config);
26529         }
26530     }
26531     if(this.tabPosition == "bottom"){
26532         this.bodyEl = Roo.get(this.createBody(this.el.dom));
26533         this.el.addClass("x-tabs-bottom");
26534     }
26535     this.stripWrap = Roo.get(this.createStrip(this.el.dom), true);
26536     this.stripEl = Roo.get(this.createStripList(this.stripWrap.dom), true);
26537     this.stripBody = Roo.get(this.stripWrap.dom.firstChild.firstChild, true);
26538     if(Roo.isIE){
26539         Roo.fly(this.stripWrap.dom.firstChild).setStyle("overflow-x", "hidden");
26540     }
26541     if(this.tabPosition != "bottom"){
26542         /** The body element that contains {@link Roo.TabPanelItem} bodies. +
26543          * @type Roo.Element
26544          */
26545         this.bodyEl = Roo.get(this.createBody(this.el.dom));
26546         this.el.addClass("x-tabs-top");
26547     }
26548     this.items = [];
26549
26550     this.bodyEl.setStyle("position", "relative");
26551
26552     this.active = null;
26553     this.activateDelegate = this.activate.createDelegate(this);
26554
26555     this.addEvents({
26556         /**
26557          * @event tabchange
26558          * Fires when the active tab changes
26559          * @param {Roo.TabPanel} this
26560          * @param {Roo.TabPanelItem} activePanel The new active tab
26561          */
26562         "tabchange": true,
26563         /**
26564          * @event beforetabchange
26565          * Fires before the active tab changes, set cancel to true on the "e" parameter to cancel the change
26566          * @param {Roo.TabPanel} this
26567          * @param {Object} e Set cancel to true on this object to cancel the tab change
26568          * @param {Roo.TabPanelItem} tab The tab being changed to
26569          */
26570         "beforetabchange" : true
26571     });
26572
26573     Roo.EventManager.onWindowResize(this.onResize, this);
26574     this.cpad = this.el.getPadding("lr");
26575     this.hiddenCount = 0;
26576
26577
26578     // toolbar on the tabbar support...
26579     if (this.toolbar) {
26580         var tcfg = this.toolbar;
26581         tcfg.container = this.stripEl.child('td.x-tab-strip-toolbar');  
26582         this.toolbar = new Roo.Toolbar(tcfg);
26583         if (Roo.isSafari) {
26584             var tbl = tcfg.container.child('table', true);
26585             tbl.setAttribute('width', '100%');
26586         }
26587         
26588     }
26589    
26590
26591
26592     Roo.TabPanel.superclass.constructor.call(this);
26593 };
26594
26595 Roo.extend(Roo.TabPanel, Roo.util.Observable, {
26596     /*
26597      *@cfg {String} tabPosition "top" or "bottom" (defaults to "top")
26598      */
26599     tabPosition : "top",
26600     /*
26601      *@cfg {Number} currentTabWidth The width of the current tab (defaults to 0)
26602      */
26603     currentTabWidth : 0,
26604     /*
26605      *@cfg {Number} minTabWidth The minimum width of a tab (defaults to 40) (ignored if {@link #resizeTabs} is not true)
26606      */
26607     minTabWidth : 40,
26608     /*
26609      *@cfg {Number} maxTabWidth The maximum width of a tab (defaults to 250) (ignored if {@link #resizeTabs} is not true)
26610      */
26611     maxTabWidth : 250,
26612     /*
26613      *@cfg {Number} preferredTabWidth The preferred (default) width of a tab (defaults to 175) (ignored if {@link #resizeTabs} is not true)
26614      */
26615     preferredTabWidth : 175,
26616     /*
26617      *@cfg {Boolean} resizeTabs True to enable dynamic tab resizing (defaults to false)
26618      */
26619     resizeTabs : false,
26620     /*
26621      *@cfg {Boolean} monitorResize Set this to true to turn on window resize monitoring (ignored if {@link #resizeTabs} is not true) (defaults to true)
26622      */
26623     monitorResize : true,
26624     /*
26625      *@cfg {Object} toolbar xtype description of toolbar to show at the right of the tab bar. 
26626      */
26627     toolbar : false,
26628
26629     /**
26630      * Creates a new {@link Roo.TabPanelItem} by looking for an existing element with the provided id -- if it's not found it creates one.
26631      * @param {String} id The id of the div to use <b>or create</b>
26632      * @param {String} text The text for the tab
26633      * @param {String} content (optional) Content to put in the TabPanelItem body
26634      * @param {Boolean} closable (optional) True to create a close icon on the tab
26635      * @return {Roo.TabPanelItem} The created TabPanelItem
26636      */
26637     addTab : function(id, text, content, closable){
26638         var item = new Roo.TabPanelItem(this, id, text, closable);
26639         this.addTabItem(item);
26640         if(content){
26641             item.setContent(content);
26642         }
26643         return item;
26644     },
26645
26646     /**
26647      * Returns the {@link Roo.TabPanelItem} with the specified id/index
26648      * @param {String/Number} id The id or index of the TabPanelItem to fetch.
26649      * @return {Roo.TabPanelItem}
26650      */
26651     getTab : function(id){
26652         return this.items[id];
26653     },
26654
26655     /**
26656      * Hides the {@link Roo.TabPanelItem} with the specified id/index
26657      * @param {String/Number} id The id or index of the TabPanelItem to hide.
26658      */
26659     hideTab : function(id){
26660         var t = this.items[id];
26661         if(!t.isHidden()){
26662            t.setHidden(true);
26663            this.hiddenCount++;
26664            this.autoSizeTabs();
26665         }
26666     },
26667
26668     /**
26669      * "Unhides" the {@link Roo.TabPanelItem} with the specified id/index.
26670      * @param {String/Number} id The id or index of the TabPanelItem to unhide.
26671      */
26672     unhideTab : function(id){
26673         var t = this.items[id];
26674         if(t.isHidden()){
26675            t.setHidden(false);
26676            this.hiddenCount--;
26677            this.autoSizeTabs();
26678         }
26679     },
26680
26681     /**
26682      * Adds an existing {@link Roo.TabPanelItem}.
26683      * @param {Roo.TabPanelItem} item The TabPanelItem to add
26684      */
26685     addTabItem : function(item){
26686         this.items[item.id] = item;
26687         this.items.push(item);
26688         if(this.resizeTabs){
26689            item.setWidth(this.currentTabWidth || this.preferredTabWidth);
26690            this.autoSizeTabs();
26691         }else{
26692             item.autoSize();
26693         }
26694     },
26695
26696     /**
26697      * Removes a {@link Roo.TabPanelItem}.
26698      * @param {String/Number} id The id or index of the TabPanelItem to remove.
26699      */
26700     removeTab : function(id){
26701         var items = this.items;
26702         var tab = items[id];
26703         if(!tab) { return; }
26704         var index = items.indexOf(tab);
26705         if(this.active == tab && items.length > 1){
26706             var newTab = this.getNextAvailable(index);
26707             if(newTab) {
26708                 newTab.activate();
26709             }
26710         }
26711         this.stripEl.dom.removeChild(tab.pnode.dom);
26712         if(tab.bodyEl.dom.parentNode == this.bodyEl.dom){ // if it was moved already prevent error
26713             this.bodyEl.dom.removeChild(tab.bodyEl.dom);
26714         }
26715         items.splice(index, 1);
26716         delete this.items[tab.id];
26717         tab.fireEvent("close", tab);
26718         tab.purgeListeners();
26719         this.autoSizeTabs();
26720     },
26721
26722     getNextAvailable : function(start){
26723         var items = this.items;
26724         var index = start;
26725         // look for a next tab that will slide over to
26726         // replace the one being removed
26727         while(index < items.length){
26728             var item = items[++index];
26729             if(item && !item.isHidden()){
26730                 return item;
26731             }
26732         }
26733         // if one isn't found select the previous tab (on the left)
26734         index = start;
26735         while(index >= 0){
26736             var item = items[--index];
26737             if(item && !item.isHidden()){
26738                 return item;
26739             }
26740         }
26741         return null;
26742     },
26743
26744     /**
26745      * Disables a {@link Roo.TabPanelItem}. It cannot be the active tab, if it is this call is ignored.
26746      * @param {String/Number} id The id or index of the TabPanelItem to disable.
26747      */
26748     disableTab : function(id){
26749         var tab = this.items[id];
26750         if(tab && this.active != tab){
26751             tab.disable();
26752         }
26753     },
26754
26755     /**
26756      * Enables a {@link Roo.TabPanelItem} that is disabled.
26757      * @param {String/Number} id The id or index of the TabPanelItem to enable.
26758      */
26759     enableTab : function(id){
26760         var tab = this.items[id];
26761         tab.enable();
26762     },
26763
26764     /**
26765      * Activates a {@link Roo.TabPanelItem}. The currently active one will be deactivated.
26766      * @param {String/Number} id The id or index of the TabPanelItem to activate.
26767      * @return {Roo.TabPanelItem} The TabPanelItem.
26768      */
26769     activate : function(id){
26770         var tab = this.items[id];
26771         if(!tab){
26772             return null;
26773         }
26774         if(tab == this.active || tab.disabled){
26775             return tab;
26776         }
26777         var e = {};
26778         this.fireEvent("beforetabchange", this, e, tab);
26779         if(e.cancel !== true && !tab.disabled){
26780             if(this.active){
26781                 this.active.hide();
26782             }
26783             this.active = this.items[id];
26784             this.active.show();
26785             this.fireEvent("tabchange", this, this.active);
26786         }
26787         return tab;
26788     },
26789
26790     /**
26791      * Gets the active {@link Roo.TabPanelItem}.
26792      * @return {Roo.TabPanelItem} The active TabPanelItem or null if none are active.
26793      */
26794     getActiveTab : function(){
26795         return this.active;
26796     },
26797
26798     /**
26799      * Updates the tab body element to fit the height of the container element
26800      * for overflow scrolling
26801      * @param {Number} targetHeight (optional) Override the starting height from the elements height
26802      */
26803     syncHeight : function(targetHeight){
26804         var height = (targetHeight || this.el.getHeight())-this.el.getBorderWidth("tb")-this.el.getPadding("tb");
26805         var bm = this.bodyEl.getMargins();
26806         var newHeight = height-(this.stripWrap.getHeight()||0)-(bm.top+bm.bottom);
26807         this.bodyEl.setHeight(newHeight);
26808         return newHeight;
26809     },
26810
26811     onResize : function(){
26812         if(this.monitorResize){
26813             this.autoSizeTabs();
26814         }
26815     },
26816
26817     /**
26818      * Disables tab resizing while tabs are being added (if {@link #resizeTabs} is false this does nothing)
26819      */
26820     beginUpdate : function(){
26821         this.updating = true;
26822     },
26823
26824     /**
26825      * Stops an update and resizes the tabs (if {@link #resizeTabs} is false this does nothing)
26826      */
26827     endUpdate : function(){
26828         this.updating = false;
26829         this.autoSizeTabs();
26830     },
26831
26832     /**
26833      * Manual call to resize the tabs (if {@link #resizeTabs} is false this does nothing)
26834      */
26835     autoSizeTabs : function(){
26836         var count = this.items.length;
26837         var vcount = count - this.hiddenCount;
26838         if(!this.resizeTabs || count < 1 || vcount < 1 || this.updating) return;
26839         var w = Math.max(this.el.getWidth() - this.cpad, 10);
26840         var availWidth = Math.floor(w / vcount);
26841         var b = this.stripBody;
26842         if(b.getWidth() > w){
26843             var tabs = this.items;
26844             this.setTabWidth(Math.max(availWidth, this.minTabWidth)-2);
26845             if(availWidth < this.minTabWidth){
26846                 /*if(!this.sleft){    // incomplete scrolling code
26847                     this.createScrollButtons();
26848                 }
26849                 this.showScroll();
26850                 this.stripClip.setWidth(w - (this.sleft.getWidth()+this.sright.getWidth()));*/
26851             }
26852         }else{
26853             if(this.currentTabWidth < this.preferredTabWidth){
26854                 this.setTabWidth(Math.min(availWidth, this.preferredTabWidth)-2);
26855             }
26856         }
26857     },
26858
26859     /**
26860      * Returns the number of tabs in this TabPanel.
26861      * @return {Number}
26862      */
26863      getCount : function(){
26864          return this.items.length;
26865      },
26866
26867     /**
26868      * Resizes all the tabs to the passed width
26869      * @param {Number} The new width
26870      */
26871     setTabWidth : function(width){
26872         this.currentTabWidth = width;
26873         for(var i = 0, len = this.items.length; i < len; i++) {
26874                 if(!this.items[i].isHidden())this.items[i].setWidth(width);
26875         }
26876     },
26877
26878     /**
26879      * Destroys this TabPanel
26880      * @param {Boolean} removeEl (optional) True to remove the element from the DOM as well (defaults to undefined)
26881      */
26882     destroy : function(removeEl){
26883         Roo.EventManager.removeResizeListener(this.onResize, this);
26884         for(var i = 0, len = this.items.length; i < len; i++){
26885             this.items[i].purgeListeners();
26886         }
26887         if(removeEl === true){
26888             this.el.update("");
26889             this.el.remove();
26890         }
26891     }
26892 });
26893
26894 /**
26895  * @class Roo.TabPanelItem
26896  * @extends Roo.util.Observable
26897  * Represents an individual item (tab plus body) in a TabPanel.
26898  * @param {Roo.TabPanel} tabPanel The {@link Roo.TabPanel} this TabPanelItem belongs to
26899  * @param {String} id The id of this TabPanelItem
26900  * @param {String} text The text for the tab of this TabPanelItem
26901  * @param {Boolean} closable True to allow this TabPanelItem to be closable (defaults to false)
26902  */
26903 Roo.TabPanelItem = function(tabPanel, id, text, closable){
26904     /**
26905      * The {@link Roo.TabPanel} this TabPanelItem belongs to
26906      * @type Roo.TabPanel
26907      */
26908     this.tabPanel = tabPanel;
26909     /**
26910      * The id for this TabPanelItem
26911      * @type String
26912      */
26913     this.id = id;
26914     /** @private */
26915     this.disabled = false;
26916     /** @private */
26917     this.text = text;
26918     /** @private */
26919     this.loaded = false;
26920     this.closable = closable;
26921
26922     /**
26923      * The body element for this TabPanelItem.
26924      * @type Roo.Element
26925      */
26926     this.bodyEl = Roo.get(tabPanel.createItemBody(tabPanel.bodyEl.dom, id));
26927     this.bodyEl.setVisibilityMode(Roo.Element.VISIBILITY);
26928     this.bodyEl.setStyle("display", "block");
26929     this.bodyEl.setStyle("zoom", "1");
26930     this.hideAction();
26931
26932     var els = tabPanel.createStripElements(tabPanel.stripEl.dom, text, closable);
26933     /** @private */
26934     this.el = Roo.get(els.el, true);
26935     this.inner = Roo.get(els.inner, true);
26936     this.textEl = Roo.get(this.el.dom.firstChild.firstChild.firstChild, true);
26937     this.pnode = Roo.get(els.el.parentNode, true);
26938     this.el.on("mousedown", this.onTabMouseDown, this);
26939     this.el.on("click", this.onTabClick, this);
26940     /** @private */
26941     if(closable){
26942         var c = Roo.get(els.close, true);
26943         c.dom.title = this.closeText;
26944         c.addClassOnOver("close-over");
26945         c.on("click", this.closeClick, this);
26946      }
26947
26948     this.addEvents({
26949          /**
26950          * @event activate
26951          * Fires when this tab becomes the active tab.
26952          * @param {Roo.TabPanel} tabPanel The parent TabPanel
26953          * @param {Roo.TabPanelItem} this
26954          */
26955         "activate": true,
26956         /**
26957          * @event beforeclose
26958          * Fires before this tab is closed. To cancel the close, set cancel to true on e (e.cancel = true).
26959          * @param {Roo.TabPanelItem} this
26960          * @param {Object} e Set cancel to true on this object to cancel the close.
26961          */
26962         "beforeclose": true,
26963         /**
26964          * @event close
26965          * Fires when this tab is closed.
26966          * @param {Roo.TabPanelItem} this
26967          */
26968          "close": true,
26969         /**
26970          * @event deactivate
26971          * Fires when this tab is no longer the active tab.
26972          * @param {Roo.TabPanel} tabPanel The parent TabPanel
26973          * @param {Roo.TabPanelItem} this
26974          */
26975          "deactivate" : true
26976     });
26977     this.hidden = false;
26978
26979     Roo.TabPanelItem.superclass.constructor.call(this);
26980 };
26981
26982 Roo.extend(Roo.TabPanelItem, Roo.util.Observable, {
26983     purgeListeners : function(){
26984        Roo.util.Observable.prototype.purgeListeners.call(this);
26985        this.el.removeAllListeners();
26986     },
26987     /**
26988      * Shows this TabPanelItem -- this <b>does not</b> deactivate the currently active TabPanelItem.
26989      */
26990     show : function(){
26991         this.pnode.addClass("on");
26992         this.showAction();
26993         if(Roo.isOpera){
26994             this.tabPanel.stripWrap.repaint();
26995         }
26996         this.fireEvent("activate", this.tabPanel, this);
26997     },
26998
26999     /**
27000      * Returns true if this tab is the active tab.
27001      * @return {Boolean}
27002      */
27003     isActive : function(){
27004         return this.tabPanel.getActiveTab() == this;
27005     },
27006
27007     /**
27008      * Hides this TabPanelItem -- if you don't activate another TabPanelItem this could look odd.
27009      */
27010     hide : function(){
27011         this.pnode.removeClass("on");
27012         this.hideAction();
27013         this.fireEvent("deactivate", this.tabPanel, this);
27014     },
27015
27016     hideAction : function(){
27017         this.bodyEl.hide();
27018         this.bodyEl.setStyle("position", "absolute");
27019         this.bodyEl.setLeft("-20000px");
27020         this.bodyEl.setTop("-20000px");
27021     },
27022
27023     showAction : function(){
27024         this.bodyEl.setStyle("position", "relative");
27025         this.bodyEl.setTop("");
27026         this.bodyEl.setLeft("");
27027         this.bodyEl.show();
27028     },
27029
27030     /**
27031      * Set the tooltip for the tab.
27032      * @param {String} tooltip The tab's tooltip
27033      */
27034     setTooltip : function(text){
27035         if(Roo.QuickTips && Roo.QuickTips.isEnabled()){
27036             this.textEl.dom.qtip = text;
27037             this.textEl.dom.removeAttribute('title');
27038         }else{
27039             this.textEl.dom.title = text;
27040         }
27041     },
27042
27043     onTabClick : function(e){
27044         e.preventDefault();
27045         this.tabPanel.activate(this.id);
27046     },
27047
27048     onTabMouseDown : function(e){
27049         e.preventDefault();
27050         this.tabPanel.activate(this.id);
27051     },
27052
27053     getWidth : function(){
27054         return this.inner.getWidth();
27055     },
27056
27057     setWidth : function(width){
27058         var iwidth = width - this.pnode.getPadding("lr");
27059         this.inner.setWidth(iwidth);
27060         this.textEl.setWidth(iwidth-this.inner.getPadding("lr"));
27061         this.pnode.setWidth(width);
27062     },
27063
27064     /**
27065      * Show or hide the tab
27066      * @param {Boolean} hidden True to hide or false to show.
27067      */
27068     setHidden : function(hidden){
27069         this.hidden = hidden;
27070         this.pnode.setStyle("display", hidden ? "none" : "");
27071     },
27072
27073     /**
27074      * Returns true if this tab is "hidden"
27075      * @return {Boolean}
27076      */
27077     isHidden : function(){
27078         return this.hidden;
27079     },
27080
27081     /**
27082      * Returns the text for this tab
27083      * @return {String}
27084      */
27085     getText : function(){
27086         return this.text;
27087     },
27088
27089     autoSize : function(){
27090         //this.el.beginMeasure();
27091         this.textEl.setWidth(1);
27092         this.setWidth(this.textEl.dom.scrollWidth+this.pnode.getPadding("lr")+this.inner.getPadding("lr"));
27093         //this.el.endMeasure();
27094     },
27095
27096     /**
27097      * Sets the text for the tab (Note: this also sets the tooltip text)
27098      * @param {String} text The tab's text and tooltip
27099      */
27100     setText : function(text){
27101         this.text = text;
27102         this.textEl.update(text);
27103         this.setTooltip(text);
27104         if(!this.tabPanel.resizeTabs){
27105             this.autoSize();
27106         }
27107     },
27108     /**
27109      * Activates this TabPanelItem -- this <b>does</b> deactivate the currently active TabPanelItem.
27110      */
27111     activate : function(){
27112         this.tabPanel.activate(this.id);
27113     },
27114
27115     /**
27116      * Disables this TabPanelItem -- this does nothing if this is the active TabPanelItem.
27117      */
27118     disable : function(){
27119         if(this.tabPanel.active != this){
27120             this.disabled = true;
27121             this.pnode.addClass("disabled");
27122         }
27123     },
27124
27125     /**
27126      * Enables this TabPanelItem if it was previously disabled.
27127      */
27128     enable : function(){
27129         this.disabled = false;
27130         this.pnode.removeClass("disabled");
27131     },
27132
27133     /**
27134      * Sets the content for this TabPanelItem.
27135      * @param {String} content The content
27136      * @param {Boolean} loadScripts true to look for and load scripts
27137      */
27138     setContent : function(content, loadScripts){
27139         this.bodyEl.update(content, loadScripts);
27140     },
27141
27142     /**
27143      * Gets the {@link Roo.UpdateManager} for the body of this TabPanelItem. Enables you to perform Ajax updates.
27144      * @return {Roo.UpdateManager} The UpdateManager
27145      */
27146     getUpdateManager : function(){
27147         return this.bodyEl.getUpdateManager();
27148     },
27149
27150     /**
27151      * Set a URL to be used to load the content for this TabPanelItem.
27152      * @param {String/Function} url The URL to load the content from, or a function to call to get the URL
27153      * @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)
27154      * @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)
27155      * @return {Roo.UpdateManager} The UpdateManager
27156      */
27157     setUrl : function(url, params, loadOnce){
27158         if(this.refreshDelegate){
27159             this.un('activate', this.refreshDelegate);
27160         }
27161         this.refreshDelegate = this._handleRefresh.createDelegate(this, [url, params, loadOnce]);
27162         this.on("activate", this.refreshDelegate);
27163         return this.bodyEl.getUpdateManager();
27164     },
27165
27166     /** @private */
27167     _handleRefresh : function(url, params, loadOnce){
27168         if(!loadOnce || !this.loaded){
27169             var updater = this.bodyEl.getUpdateManager();
27170             updater.update(url, params, this._setLoaded.createDelegate(this));
27171         }
27172     },
27173
27174     /**
27175      *   Forces a content refresh from the URL specified in the {@link #setUrl} method.
27176      *   Will fail silently if the setUrl method has not been called.
27177      *   This does not activate the panel, just updates its content.
27178      */
27179     refresh : function(){
27180         if(this.refreshDelegate){
27181            this.loaded = false;
27182            this.refreshDelegate();
27183         }
27184     },
27185
27186     /** @private */
27187     _setLoaded : function(){
27188         this.loaded = true;
27189     },
27190
27191     /** @private */
27192     closeClick : function(e){
27193         var o = {};
27194         e.stopEvent();
27195         this.fireEvent("beforeclose", this, o);
27196         if(o.cancel !== true){
27197             this.tabPanel.removeTab(this.id);
27198         }
27199     },
27200     /**
27201      * The text displayed in the tooltip for the close icon.
27202      * @type String
27203      */
27204     closeText : "Close this tab"
27205 });
27206
27207 /** @private */
27208 Roo.TabPanel.prototype.createStrip = function(container){
27209     var strip = document.createElement("div");
27210     strip.className = "x-tabs-wrap";
27211     container.appendChild(strip);
27212     return strip;
27213 };
27214 /** @private */
27215 Roo.TabPanel.prototype.createStripList = function(strip){
27216     // div wrapper for retard IE
27217     // returns the "tr" element.
27218     strip.innerHTML = '<div class="x-tabs-strip-wrap">'+
27219         '<table class="x-tabs-strip" cellspacing="0" cellpadding="0" border="0"><tbody><tr>'+
27220         '<td class="x-tab-strip-toolbar"></td></tr></tbody></table></div>';
27221     return strip.firstChild.firstChild.firstChild.firstChild;
27222 };
27223 /** @private */
27224 Roo.TabPanel.prototype.createBody = function(container){
27225     var body = document.createElement("div");
27226     Roo.id(body, "tab-body");
27227     Roo.fly(body).addClass("x-tabs-body");
27228     container.appendChild(body);
27229     return body;
27230 };
27231 /** @private */
27232 Roo.TabPanel.prototype.createItemBody = function(bodyEl, id){
27233     var body = Roo.getDom(id);
27234     if(!body){
27235         body = document.createElement("div");
27236         body.id = id;
27237     }
27238     Roo.fly(body).addClass("x-tabs-item-body");
27239     bodyEl.insertBefore(body, bodyEl.firstChild);
27240     return body;
27241 };
27242 /** @private */
27243 Roo.TabPanel.prototype.createStripElements = function(stripEl, text, closable){
27244     var td = document.createElement("td");
27245     stripEl.insertBefore(td, stripEl.childNodes[stripEl.childNodes.length-1]);
27246     //stripEl.appendChild(td);
27247     if(closable){
27248         td.className = "x-tabs-closable";
27249         if(!this.closeTpl){
27250             this.closeTpl = new Roo.Template(
27251                '<a href="#" class="x-tabs-right"><span class="x-tabs-left"><em class="x-tabs-inner">' +
27252                '<span unselectable="on"' + (this.disableTooltips ? '' : ' title="{text}"') +' class="x-tabs-text">{text}</span>' +
27253                '<div unselectable="on" class="close-icon">&#160;</div></em></span></a>'
27254             );
27255         }
27256         var el = this.closeTpl.overwrite(td, {"text": text});
27257         var close = el.getElementsByTagName("div")[0];
27258         var inner = el.getElementsByTagName("em")[0];
27259         return {"el": el, "close": close, "inner": inner};
27260     } else {
27261         if(!this.tabTpl){
27262             this.tabTpl = new Roo.Template(
27263                '<a href="#" class="x-tabs-right"><span class="x-tabs-left"><em class="x-tabs-inner">' +
27264                '<span unselectable="on"' + (this.disableTooltips ? '' : ' title="{text}"') +' class="x-tabs-text">{text}</span></em></span></a>'
27265             );
27266         }
27267         var el = this.tabTpl.overwrite(td, {"text": text});
27268         var inner = el.getElementsByTagName("em")[0];
27269         return {"el": el, "inner": inner};
27270     }
27271 };/*
27272  * Based on:
27273  * Ext JS Library 1.1.1
27274  * Copyright(c) 2006-2007, Ext JS, LLC.
27275  *
27276  * Originally Released Under LGPL - original licence link has changed is not relivant.
27277  *
27278  * Fork - LGPL
27279  * <script type="text/javascript">
27280  */
27281
27282 /**
27283  * @class Roo.Button
27284  * @extends Roo.util.Observable
27285  * Simple Button class
27286  * @cfg {String} text The button text
27287  * @cfg {String} icon The path to an image to display in the button (the image will be set as the background-image
27288  * CSS property of the button by default, so if you want a mixed icon/text button, set cls:"x-btn-text-icon")
27289  * @cfg {Function} handler A function called when the button is clicked (can be used instead of click event)
27290  * @cfg {Object} scope The scope of the handler
27291  * @cfg {Number} minWidth The minimum width for this button (used to give a set of buttons a common width)
27292  * @cfg {String/Object} tooltip The tooltip for the button - can be a string or QuickTips config object
27293  * @cfg {Boolean} hidden True to start hidden (defaults to false)
27294  * @cfg {Boolean} disabled True to start disabled (defaults to false)
27295  * @cfg {Boolean} pressed True to start pressed (only if enableToggle = true)
27296  * @cfg {String} toggleGroup The group this toggle button is a member of (only 1 per group can be pressed, only
27297    applies if enableToggle = true)
27298  * @cfg {String/HTMLElement/Element} renderTo The element to append the button to
27299  * @cfg {Boolean/Object} repeat True to repeat fire the click event while the mouse is down. This can also be
27300   an {@link Roo.util.ClickRepeater} config object (defaults to false).
27301  * @constructor
27302  * Create a new button
27303  * @param {Object} config The config object
27304  */
27305 Roo.Button = function(renderTo, config)
27306 {
27307     if (!config) {
27308         config = renderTo;
27309         renderTo = config.renderTo || false;
27310     }
27311     
27312     Roo.apply(this, config);
27313     this.addEvents({
27314         /**
27315              * @event click
27316              * Fires when this button is clicked
27317              * @param {Button} this
27318              * @param {EventObject} e The click event
27319              */
27320             "click" : true,
27321         /**
27322              * @event toggle
27323              * Fires when the "pressed" state of this button changes (only if enableToggle = true)
27324              * @param {Button} this
27325              * @param {Boolean} pressed
27326              */
27327             "toggle" : true,
27328         /**
27329              * @event mouseover
27330              * Fires when the mouse hovers over the button
27331              * @param {Button} this
27332              * @param {Event} e The event object
27333              */
27334         'mouseover' : true,
27335         /**
27336              * @event mouseout
27337              * Fires when the mouse exits the button
27338              * @param {Button} this
27339              * @param {Event} e The event object
27340              */
27341         'mouseout': true,
27342          /**
27343              * @event render
27344              * Fires when the button is rendered
27345              * @param {Button} this
27346              */
27347         'render': true
27348     });
27349     if(this.menu){
27350         this.menu = Roo.menu.MenuMgr.get(this.menu);
27351     }
27352     // register listeners first!!  - so render can be captured..
27353     Roo.util.Observable.call(this);
27354     if(renderTo){
27355         this.render(renderTo);
27356     }
27357     
27358   
27359 };
27360
27361 Roo.extend(Roo.Button, Roo.util.Observable, {
27362     /**
27363      * 
27364      */
27365     
27366     /**
27367      * Read-only. True if this button is hidden
27368      * @type Boolean
27369      */
27370     hidden : false,
27371     /**
27372      * Read-only. True if this button is disabled
27373      * @type Boolean
27374      */
27375     disabled : false,
27376     /**
27377      * Read-only. True if this button is pressed (only if enableToggle = true)
27378      * @type Boolean
27379      */
27380     pressed : false,
27381
27382     /**
27383      * @cfg {Number} tabIndex 
27384      * The DOM tabIndex for this button (defaults to undefined)
27385      */
27386     tabIndex : undefined,
27387
27388     /**
27389      * @cfg {Boolean} enableToggle
27390      * True to enable pressed/not pressed toggling (defaults to false)
27391      */
27392     enableToggle: false,
27393     /**
27394      * @cfg {Mixed} menu
27395      * Standard menu attribute consisting of a reference to a menu object, a menu id or a menu config blob (defaults to undefined).
27396      */
27397     menu : undefined,
27398     /**
27399      * @cfg {String} menuAlign
27400      * The position to align the menu to (see {@link Roo.Element#alignTo} for more details, defaults to 'tl-bl?').
27401      */
27402     menuAlign : "tl-bl?",
27403
27404     /**
27405      * @cfg {String} iconCls
27406      * A css class which sets a background image to be used as the icon for this button (defaults to undefined).
27407      */
27408     iconCls : undefined,
27409     /**
27410      * @cfg {String} type
27411      * The button's type, corresponding to the DOM input element type attribute.  Either "submit," "reset" or "button" (default).
27412      */
27413     type : 'button',
27414
27415     // private
27416     menuClassTarget: 'tr',
27417
27418     /**
27419      * @cfg {String} clickEvent
27420      * The type of event to map to the button's event handler (defaults to 'click')
27421      */
27422     clickEvent : 'click',
27423
27424     /**
27425      * @cfg {Boolean} handleMouseEvents
27426      * False to disable visual cues on mouseover, mouseout and mousedown (defaults to true)
27427      */
27428     handleMouseEvents : true,
27429
27430     /**
27431      * @cfg {String} tooltipType
27432      * The type of tooltip to use. Either "qtip" (default) for QuickTips or "title" for title attribute.
27433      */
27434     tooltipType : 'qtip',
27435
27436     /**
27437      * @cfg {String} cls
27438      * A CSS class to apply to the button's main element.
27439      */
27440     
27441     /**
27442      * @cfg {Roo.Template} template (Optional)
27443      * An {@link Roo.Template} with which to create the Button's main element. This Template must
27444      * contain numeric substitution parameter 0 if it is to display the tRoo property. Changing the template could
27445      * require code modifications if required elements (e.g. a button) aren't present.
27446      */
27447
27448     // private
27449     render : function(renderTo){
27450         var btn;
27451         if(this.hideParent){
27452             this.parentEl = Roo.get(renderTo);
27453         }
27454         if(!this.dhconfig){
27455             if(!this.template){
27456                 if(!Roo.Button.buttonTemplate){
27457                     // hideous table template
27458                     Roo.Button.buttonTemplate = new Roo.Template(
27459                         '<table border="0" cellpadding="0" cellspacing="0" class="x-btn-wrap"><tbody><tr>',
27460                         '<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>',
27461                         "</tr></tbody></table>");
27462                 }
27463                 this.template = Roo.Button.buttonTemplate;
27464             }
27465             btn = this.template.append(renderTo, [this.text || '&#160;', this.type], true);
27466             var btnEl = btn.child("button:first");
27467             btnEl.on('focus', this.onFocus, this);
27468             btnEl.on('blur', this.onBlur, this);
27469             if(this.cls){
27470                 btn.addClass(this.cls);
27471             }
27472             if(this.icon){
27473                 btnEl.setStyle('background-image', 'url(' +this.icon +')');
27474             }
27475             if(this.iconCls){
27476                 btnEl.addClass(this.iconCls);
27477                 if(!this.cls){
27478                     btn.addClass(this.text ? 'x-btn-text-icon' : 'x-btn-icon');
27479                 }
27480             }
27481             if(this.tabIndex !== undefined){
27482                 btnEl.dom.tabIndex = this.tabIndex;
27483             }
27484             if(this.tooltip){
27485                 if(typeof this.tooltip == 'object'){
27486                     Roo.QuickTips.tips(Roo.apply({
27487                           target: btnEl.id
27488                     }, this.tooltip));
27489                 } else {
27490                     btnEl.dom[this.tooltipType] = this.tooltip;
27491                 }
27492             }
27493         }else{
27494             btn = Roo.DomHelper.append(Roo.get(renderTo).dom, this.dhconfig, true);
27495         }
27496         this.el = btn;
27497         if(this.id){
27498             this.el.dom.id = this.el.id = this.id;
27499         }
27500         if(this.menu){
27501             this.el.child(this.menuClassTarget).addClass("x-btn-with-menu");
27502             this.menu.on("show", this.onMenuShow, this);
27503             this.menu.on("hide", this.onMenuHide, this);
27504         }
27505         btn.addClass("x-btn");
27506         if(Roo.isIE && !Roo.isIE7){
27507             this.autoWidth.defer(1, this);
27508         }else{
27509             this.autoWidth();
27510         }
27511         if(this.handleMouseEvents){
27512             btn.on("mouseover", this.onMouseOver, this);
27513             btn.on("mouseout", this.onMouseOut, this);
27514             btn.on("mousedown", this.onMouseDown, this);
27515         }
27516         btn.on(this.clickEvent, this.onClick, this);
27517         //btn.on("mouseup", this.onMouseUp, this);
27518         if(this.hidden){
27519             this.hide();
27520         }
27521         if(this.disabled){
27522             this.disable();
27523         }
27524         Roo.ButtonToggleMgr.register(this);
27525         if(this.pressed){
27526             this.el.addClass("x-btn-pressed");
27527         }
27528         if(this.repeat){
27529             var repeater = new Roo.util.ClickRepeater(btn,
27530                 typeof this.repeat == "object" ? this.repeat : {}
27531             );
27532             repeater.on("click", this.onClick,  this);
27533         }
27534         
27535         this.fireEvent('render', this);
27536         
27537     },
27538     /**
27539      * Returns the button's underlying element
27540      * @return {Roo.Element} The element
27541      */
27542     getEl : function(){
27543         return this.el;  
27544     },
27545     
27546     /**
27547      * Destroys this Button and removes any listeners.
27548      */
27549     destroy : function(){
27550         Roo.ButtonToggleMgr.unregister(this);
27551         this.el.removeAllListeners();
27552         this.purgeListeners();
27553         this.el.remove();
27554     },
27555
27556     // private
27557     autoWidth : function(){
27558         if(this.el){
27559             this.el.setWidth("auto");
27560             if(Roo.isIE7 && Roo.isStrict){
27561                 var ib = this.el.child('button');
27562                 if(ib && ib.getWidth() > 20){
27563                     ib.clip();
27564                     ib.setWidth(Roo.util.TextMetrics.measure(ib, this.text).width+ib.getFrameWidth('lr'));
27565                 }
27566             }
27567             if(this.minWidth){
27568                 if(this.hidden){
27569                     this.el.beginMeasure();
27570                 }
27571                 if(this.el.getWidth() < this.minWidth){
27572                     this.el.setWidth(this.minWidth);
27573                 }
27574                 if(this.hidden){
27575                     this.el.endMeasure();
27576                 }
27577             }
27578         }
27579     },
27580
27581     /**
27582      * Assigns this button's click handler
27583      * @param {Function} handler The function to call when the button is clicked
27584      * @param {Object} scope (optional) Scope for the function passed in
27585      */
27586     setHandler : function(handler, scope){
27587         this.handler = handler;
27588         this.scope = scope;  
27589     },
27590     
27591     /**
27592      * Sets this button's text
27593      * @param {String} text The button text
27594      */
27595     setText : function(text){
27596         this.text = text;
27597         if(this.el){
27598             this.el.child("td.x-btn-center button.x-btn-text").update(text);
27599         }
27600         this.autoWidth();
27601     },
27602     
27603     /**
27604      * Gets the text for this button
27605      * @return {String} The button text
27606      */
27607     getText : function(){
27608         return this.text;  
27609     },
27610     
27611     /**
27612      * Show this button
27613      */
27614     show: function(){
27615         this.hidden = false;
27616         if(this.el){
27617             this[this.hideParent? 'parentEl' : 'el'].setStyle("display", "");
27618         }
27619     },
27620     
27621     /**
27622      * Hide this button
27623      */
27624     hide: function(){
27625         this.hidden = true;
27626         if(this.el){
27627             this[this.hideParent? 'parentEl' : 'el'].setStyle("display", "none");
27628         }
27629     },
27630     
27631     /**
27632      * Convenience function for boolean show/hide
27633      * @param {Boolean} visible True to show, false to hide
27634      */
27635     setVisible: function(visible){
27636         if(visible) {
27637             this.show();
27638         }else{
27639             this.hide();
27640         }
27641     },
27642     
27643     /**
27644      * If a state it passed, it becomes the pressed state otherwise the current state is toggled.
27645      * @param {Boolean} state (optional) Force a particular state
27646      */
27647     toggle : function(state){
27648         state = state === undefined ? !this.pressed : state;
27649         if(state != this.pressed){
27650             if(state){
27651                 this.el.addClass("x-btn-pressed");
27652                 this.pressed = true;
27653                 this.fireEvent("toggle", this, true);
27654             }else{
27655                 this.el.removeClass("x-btn-pressed");
27656                 this.pressed = false;
27657                 this.fireEvent("toggle", this, false);
27658             }
27659             if(this.toggleHandler){
27660                 this.toggleHandler.call(this.scope || this, this, state);
27661             }
27662         }
27663     },
27664     
27665     /**
27666      * Focus the button
27667      */
27668     focus : function(){
27669         this.el.child('button:first').focus();
27670     },
27671     
27672     /**
27673      * Disable this button
27674      */
27675     disable : function(){
27676         if(this.el){
27677             this.el.addClass("x-btn-disabled");
27678         }
27679         this.disabled = true;
27680     },
27681     
27682     /**
27683      * Enable this button
27684      */
27685     enable : function(){
27686         if(this.el){
27687             this.el.removeClass("x-btn-disabled");
27688         }
27689         this.disabled = false;
27690     },
27691
27692     /**
27693      * Convenience function for boolean enable/disable
27694      * @param {Boolean} enabled True to enable, false to disable
27695      */
27696     setDisabled : function(v){
27697         this[v !== true ? "enable" : "disable"]();
27698     },
27699
27700     // private
27701     onClick : function(e){
27702         if(e){
27703             e.preventDefault();
27704         }
27705         if(e.button != 0){
27706             return;
27707         }
27708         if(!this.disabled){
27709             if(this.enableToggle){
27710                 this.toggle();
27711             }
27712             if(this.menu && !this.menu.isVisible()){
27713                 this.menu.show(this.el, this.menuAlign);
27714             }
27715             this.fireEvent("click", this, e);
27716             if(this.handler){
27717                 this.el.removeClass("x-btn-over");
27718                 this.handler.call(this.scope || this, this, e);
27719             }
27720         }
27721     },
27722     // private
27723     onMouseOver : function(e){
27724         if(!this.disabled){
27725             this.el.addClass("x-btn-over");
27726             this.fireEvent('mouseover', this, e);
27727         }
27728     },
27729     // private
27730     onMouseOut : function(e){
27731         if(!e.within(this.el,  true)){
27732             this.el.removeClass("x-btn-over");
27733             this.fireEvent('mouseout', this, e);
27734         }
27735     },
27736     // private
27737     onFocus : function(e){
27738         if(!this.disabled){
27739             this.el.addClass("x-btn-focus");
27740         }
27741     },
27742     // private
27743     onBlur : function(e){
27744         this.el.removeClass("x-btn-focus");
27745     },
27746     // private
27747     onMouseDown : function(e){
27748         if(!this.disabled && e.button == 0){
27749             this.el.addClass("x-btn-click");
27750             Roo.get(document).on('mouseup', this.onMouseUp, this);
27751         }
27752     },
27753     // private
27754     onMouseUp : function(e){
27755         if(e.button == 0){
27756             this.el.removeClass("x-btn-click");
27757             Roo.get(document).un('mouseup', this.onMouseUp, this);
27758         }
27759     },
27760     // private
27761     onMenuShow : function(e){
27762         this.el.addClass("x-btn-menu-active");
27763     },
27764     // private
27765     onMenuHide : function(e){
27766         this.el.removeClass("x-btn-menu-active");
27767     }   
27768 });
27769
27770 // Private utility class used by Button
27771 Roo.ButtonToggleMgr = function(){
27772    var groups = {};
27773    
27774    function toggleGroup(btn, state){
27775        if(state){
27776            var g = groups[btn.toggleGroup];
27777            for(var i = 0, l = g.length; i < l; i++){
27778                if(g[i] != btn){
27779                    g[i].toggle(false);
27780                }
27781            }
27782        }
27783    }
27784    
27785    return {
27786        register : function(btn){
27787            if(!btn.toggleGroup){
27788                return;
27789            }
27790            var g = groups[btn.toggleGroup];
27791            if(!g){
27792                g = groups[btn.toggleGroup] = [];
27793            }
27794            g.push(btn);
27795            btn.on("toggle", toggleGroup);
27796        },
27797        
27798        unregister : function(btn){
27799            if(!btn.toggleGroup){
27800                return;
27801            }
27802            var g = groups[btn.toggleGroup];
27803            if(g){
27804                g.remove(btn);
27805                btn.un("toggle", toggleGroup);
27806            }
27807        }
27808    };
27809 }();/*
27810  * Based on:
27811  * Ext JS Library 1.1.1
27812  * Copyright(c) 2006-2007, Ext JS, LLC.
27813  *
27814  * Originally Released Under LGPL - original licence link has changed is not relivant.
27815  *
27816  * Fork - LGPL
27817  * <script type="text/javascript">
27818  */
27819  
27820 /**
27821  * @class Roo.SplitButton
27822  * @extends Roo.Button
27823  * A split button that provides a built-in dropdown arrow that can fire an event separately from the default
27824  * click event of the button.  Typically this would be used to display a dropdown menu that provides additional
27825  * options to the primary button action, but any custom handler can provide the arrowclick implementation.
27826  * @cfg {Function} arrowHandler A function called when the arrow button is clicked (can be used instead of click event)
27827  * @cfg {String} arrowTooltip The title attribute of the arrow
27828  * @constructor
27829  * Create a new menu button
27830  * @param {String/HTMLElement/Element} renderTo The element to append the button to
27831  * @param {Object} config The config object
27832  */
27833 Roo.SplitButton = function(renderTo, config){
27834     Roo.SplitButton.superclass.constructor.call(this, renderTo, config);
27835     /**
27836      * @event arrowclick
27837      * Fires when this button's arrow is clicked
27838      * @param {SplitButton} this
27839      * @param {EventObject} e The click event
27840      */
27841     this.addEvents({"arrowclick":true});
27842 };
27843
27844 Roo.extend(Roo.SplitButton, Roo.Button, {
27845     render : function(renderTo){
27846         // this is one sweet looking template!
27847         var tpl = new Roo.Template(
27848             '<table cellspacing="0" class="x-btn-menu-wrap x-btn"><tr><td>',
27849             '<table cellspacing="0" class="x-btn-wrap x-btn-menu-text-wrap"><tbody>',
27850             '<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>',
27851             "</tbody></table></td><td>",
27852             '<table cellspacing="0" class="x-btn-wrap x-btn-menu-arrow-wrap"><tbody>',
27853             '<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>',
27854             "</tbody></table></td></tr></table>"
27855         );
27856         var btn = tpl.append(renderTo, [this.text, this.type], true);
27857         var btnEl = btn.child("button");
27858         if(this.cls){
27859             btn.addClass(this.cls);
27860         }
27861         if(this.icon){
27862             btnEl.setStyle('background-image', 'url(' +this.icon +')');
27863         }
27864         if(this.iconCls){
27865             btnEl.addClass(this.iconCls);
27866             if(!this.cls){
27867                 btn.addClass(this.text ? 'x-btn-text-icon' : 'x-btn-icon');
27868             }
27869         }
27870         this.el = btn;
27871         if(this.handleMouseEvents){
27872             btn.on("mouseover", this.onMouseOver, this);
27873             btn.on("mouseout", this.onMouseOut, this);
27874             btn.on("mousedown", this.onMouseDown, this);
27875             btn.on("mouseup", this.onMouseUp, this);
27876         }
27877         btn.on(this.clickEvent, this.onClick, this);
27878         if(this.tooltip){
27879             if(typeof this.tooltip == 'object'){
27880                 Roo.QuickTips.tips(Roo.apply({
27881                       target: btnEl.id
27882                 }, this.tooltip));
27883             } else {
27884                 btnEl.dom[this.tooltipType] = this.tooltip;
27885             }
27886         }
27887         if(this.arrowTooltip){
27888             btn.child("button:nth(2)").dom[this.tooltipType] = this.arrowTooltip;
27889         }
27890         if(this.hidden){
27891             this.hide();
27892         }
27893         if(this.disabled){
27894             this.disable();
27895         }
27896         if(this.pressed){
27897             this.el.addClass("x-btn-pressed");
27898         }
27899         if(Roo.isIE && !Roo.isIE7){
27900             this.autoWidth.defer(1, this);
27901         }else{
27902             this.autoWidth();
27903         }
27904         if(this.menu){
27905             this.menu.on("show", this.onMenuShow, this);
27906             this.menu.on("hide", this.onMenuHide, this);
27907         }
27908         this.fireEvent('render', this);
27909     },
27910
27911     // private
27912     autoWidth : function(){
27913         if(this.el){
27914             var tbl = this.el.child("table:first");
27915             var tbl2 = this.el.child("table:last");
27916             this.el.setWidth("auto");
27917             tbl.setWidth("auto");
27918             if(Roo.isIE7 && Roo.isStrict){
27919                 var ib = this.el.child('button:first');
27920                 if(ib && ib.getWidth() > 20){
27921                     ib.clip();
27922                     ib.setWidth(Roo.util.TextMetrics.measure(ib, this.text).width+ib.getFrameWidth('lr'));
27923                 }
27924             }
27925             if(this.minWidth){
27926                 if(this.hidden){
27927                     this.el.beginMeasure();
27928                 }
27929                 if((tbl.getWidth()+tbl2.getWidth()) < this.minWidth){
27930                     tbl.setWidth(this.minWidth-tbl2.getWidth());
27931                 }
27932                 if(this.hidden){
27933                     this.el.endMeasure();
27934                 }
27935             }
27936             this.el.setWidth(tbl.getWidth()+tbl2.getWidth());
27937         } 
27938     },
27939     /**
27940      * Sets this button's click handler
27941      * @param {Function} handler The function to call when the button is clicked
27942      * @param {Object} scope (optional) Scope for the function passed above
27943      */
27944     setHandler : function(handler, scope){
27945         this.handler = handler;
27946         this.scope = scope;  
27947     },
27948     
27949     /**
27950      * Sets this button's arrow click handler
27951      * @param {Function} handler The function to call when the arrow is clicked
27952      * @param {Object} scope (optional) Scope for the function passed above
27953      */
27954     setArrowHandler : function(handler, scope){
27955         this.arrowHandler = handler;
27956         this.scope = scope;  
27957     },
27958     
27959     /**
27960      * Focus the button
27961      */
27962     focus : function(){
27963         if(this.el){
27964             this.el.child("button:first").focus();
27965         }
27966     },
27967
27968     // private
27969     onClick : function(e){
27970         e.preventDefault();
27971         if(!this.disabled){
27972             if(e.getTarget(".x-btn-menu-arrow-wrap")){
27973                 if(this.menu && !this.menu.isVisible()){
27974                     this.menu.show(this.el, this.menuAlign);
27975                 }
27976                 this.fireEvent("arrowclick", this, e);
27977                 if(this.arrowHandler){
27978                     this.arrowHandler.call(this.scope || this, this, e);
27979                 }
27980             }else{
27981                 this.fireEvent("click", this, e);
27982                 if(this.handler){
27983                     this.handler.call(this.scope || this, this, e);
27984                 }
27985             }
27986         }
27987     },
27988     // private
27989     onMouseDown : function(e){
27990         if(!this.disabled){
27991             Roo.fly(e.getTarget("table")).addClass("x-btn-click");
27992         }
27993     },
27994     // private
27995     onMouseUp : function(e){
27996         Roo.fly(e.getTarget("table")).removeClass("x-btn-click");
27997     }   
27998 });
27999
28000
28001 // backwards compat
28002 Roo.MenuButton = Roo.SplitButton;/*
28003  * Based on:
28004  * Ext JS Library 1.1.1
28005  * Copyright(c) 2006-2007, Ext JS, LLC.
28006  *
28007  * Originally Released Under LGPL - original licence link has changed is not relivant.
28008  *
28009  * Fork - LGPL
28010  * <script type="text/javascript">
28011  */
28012
28013 /**
28014  * @class Roo.Toolbar
28015  * Basic Toolbar class.
28016  * @constructor
28017  * Creates a new Toolbar
28018  * @param {Object} container The config object
28019  */ 
28020 Roo.Toolbar = function(container, buttons, config)
28021 {
28022     /// old consturctor format still supported..
28023     if(container instanceof Array){ // omit the container for later rendering
28024         buttons = container;
28025         config = buttons;
28026         container = null;
28027     }
28028     if (typeof(container) == 'object' && container.xtype) {
28029         config = container;
28030         container = config.container;
28031         buttons = config.buttons || []; // not really - use items!!
28032     }
28033     var xitems = [];
28034     if (config && config.items) {
28035         xitems = config.items;
28036         delete config.items;
28037     }
28038     Roo.apply(this, config);
28039     this.buttons = buttons;
28040     
28041     if(container){
28042         this.render(container);
28043     }
28044     this.xitems = xitems;
28045     Roo.each(xitems, function(b) {
28046         this.add(b);
28047     }, this);
28048     
28049 };
28050
28051 Roo.Toolbar.prototype = {
28052     /**
28053      * @cfg {Array} items
28054      * array of button configs or elements to add (will be converted to a MixedCollection)
28055      */
28056     
28057     /**
28058      * @cfg {String/HTMLElement/Element} container
28059      * The id or element that will contain the toolbar
28060      */
28061     // private
28062     render : function(ct){
28063         this.el = Roo.get(ct);
28064         if(this.cls){
28065             this.el.addClass(this.cls);
28066         }
28067         // using a table allows for vertical alignment
28068         // 100% width is needed by Safari...
28069         this.el.update('<div class="x-toolbar x-small-editor"><table cellspacing="0"><tr></tr></table></div>');
28070         this.tr = this.el.child("tr", true);
28071         var autoId = 0;
28072         this.items = new Roo.util.MixedCollection(false, function(o){
28073             return o.id || ("item" + (++autoId));
28074         });
28075         if(this.buttons){
28076             this.add.apply(this, this.buttons);
28077             delete this.buttons;
28078         }
28079     },
28080
28081     /**
28082      * Adds element(s) to the toolbar -- this function takes a variable number of 
28083      * arguments of mixed type and adds them to the toolbar.
28084      * @param {Mixed} arg1 The following types of arguments are all valid:<br />
28085      * <ul>
28086      * <li>{@link Roo.Toolbar.Button} config: A valid button config object (equivalent to {@link #addButton})</li>
28087      * <li>HtmlElement: Any standard HTML element (equivalent to {@link #addElement})</li>
28088      * <li>Field: Any form field (equivalent to {@link #addField})</li>
28089      * <li>Item: Any subclass of {@link Roo.Toolbar.Item} (equivalent to {@link #addItem})</li>
28090      * <li>String: Any generic string (gets wrapped in a {@link Roo.Toolbar.TextItem}, equivalent to {@link #addText}).
28091      * Note that there are a few special strings that are treated differently as explained nRoo.</li>
28092      * <li>'separator' or '-': Creates a separator element (equivalent to {@link #addSeparator})</li>
28093      * <li>' ': Creates a spacer element (equivalent to {@link #addSpacer})</li>
28094      * <li>'->': Creates a fill element (equivalent to {@link #addFill})</li>
28095      * </ul>
28096      * @param {Mixed} arg2
28097      * @param {Mixed} etc.
28098      */
28099     add : function(){
28100         var a = arguments, l = a.length;
28101         for(var i = 0; i < l; i++){
28102             this._add(a[i]);
28103         }
28104     },
28105     // private..
28106     _add : function(el) {
28107         
28108         if (el.xtype) {
28109             el = Roo.factory(el, typeof(Roo.Toolbar[el.xtype]) == 'undefined' ? Roo.form : Roo.Toolbar);
28110         }
28111         
28112         if (el.applyTo){ // some kind of form field
28113             return this.addField(el);
28114         } 
28115         if (el.render){ // some kind of Toolbar.Item
28116             return this.addItem(el);
28117         }
28118         if (typeof el == "string"){ // string
28119             if(el == "separator" || el == "-"){
28120                 return this.addSeparator();
28121             }
28122             if (el == " "){
28123                 return this.addSpacer();
28124             }
28125             if(el == "->"){
28126                 return this.addFill();
28127             }
28128             return this.addText(el);
28129             
28130         }
28131         if(el.tagName){ // element
28132             return this.addElement(el);
28133         }
28134         if(typeof el == "object"){ // must be button config?
28135             return this.addButton(el);
28136         }
28137         // and now what?!?!
28138         return false;
28139         
28140     },
28141     
28142     /**
28143      * Add an Xtype element
28144      * @param {Object} xtype Xtype Object
28145      * @return {Object} created Object
28146      */
28147     addxtype : function(e){
28148         return this.add(e);  
28149     },
28150     
28151     /**
28152      * Returns the Element for this toolbar.
28153      * @return {Roo.Element}
28154      */
28155     getEl : function(){
28156         return this.el;  
28157     },
28158     
28159     /**
28160      * Adds a separator
28161      * @return {Roo.Toolbar.Item} The separator item
28162      */
28163     addSeparator : function(){
28164         return this.addItem(new Roo.Toolbar.Separator());
28165     },
28166
28167     /**
28168      * Adds a spacer element
28169      * @return {Roo.Toolbar.Spacer} The spacer item
28170      */
28171     addSpacer : function(){
28172         return this.addItem(new Roo.Toolbar.Spacer());
28173     },
28174
28175     /**
28176      * Adds a fill element that forces subsequent additions to the right side of the toolbar
28177      * @return {Roo.Toolbar.Fill} The fill item
28178      */
28179     addFill : function(){
28180         return this.addItem(new Roo.Toolbar.Fill());
28181     },
28182
28183     /**
28184      * Adds any standard HTML element to the toolbar
28185      * @param {String/HTMLElement/Element} el The element or id of the element to add
28186      * @return {Roo.Toolbar.Item} The element's item
28187      */
28188     addElement : function(el){
28189         return this.addItem(new Roo.Toolbar.Item(el));
28190     },
28191     /**
28192      * Collection of items on the toolbar.. (only Toolbar Items, so use fields to retrieve fields)
28193      * @type Roo.util.MixedCollection  
28194      */
28195     items : false,
28196      
28197     /**
28198      * Adds any Toolbar.Item or subclass
28199      * @param {Roo.Toolbar.Item} item
28200      * @return {Roo.Toolbar.Item} The item
28201      */
28202     addItem : function(item){
28203         var td = this.nextBlock();
28204         item.render(td);
28205         this.items.add(item);
28206         return item;
28207     },
28208     
28209     /**
28210      * Adds a button (or buttons). See {@link Roo.Toolbar.Button} for more info on the config.
28211      * @param {Object/Array} config A button config or array of configs
28212      * @return {Roo.Toolbar.Button/Array}
28213      */
28214     addButton : function(config){
28215         if(config instanceof Array){
28216             var buttons = [];
28217             for(var i = 0, len = config.length; i < len; i++) {
28218                 buttons.push(this.addButton(config[i]));
28219             }
28220             return buttons;
28221         }
28222         var b = config;
28223         if(!(config instanceof Roo.Toolbar.Button)){
28224             b = config.split ?
28225                 new Roo.Toolbar.SplitButton(config) :
28226                 new Roo.Toolbar.Button(config);
28227         }
28228         var td = this.nextBlock();
28229         b.render(td);
28230         this.items.add(b);
28231         return b;
28232     },
28233     
28234     /**
28235      * Adds text to the toolbar
28236      * @param {String} text The text to add
28237      * @return {Roo.Toolbar.Item} The element's item
28238      */
28239     addText : function(text){
28240         return this.addItem(new Roo.Toolbar.TextItem(text));
28241     },
28242     
28243     /**
28244      * Inserts any {@link Roo.Toolbar.Item}/{@link Roo.Toolbar.Button} at the specified index.
28245      * @param {Number} index The index where the item is to be inserted
28246      * @param {Object/Roo.Toolbar.Item/Roo.Toolbar.Button (may be Array)} item The button, or button config object to be inserted.
28247      * @return {Roo.Toolbar.Button/Item}
28248      */
28249     insertButton : function(index, item){
28250         if(item instanceof Array){
28251             var buttons = [];
28252             for(var i = 0, len = item.length; i < len; i++) {
28253                buttons.push(this.insertButton(index + i, item[i]));
28254             }
28255             return buttons;
28256         }
28257         if (!(item instanceof Roo.Toolbar.Button)){
28258            item = new Roo.Toolbar.Button(item);
28259         }
28260         var td = document.createElement("td");
28261         this.tr.insertBefore(td, this.tr.childNodes[index]);
28262         item.render(td);
28263         this.items.insert(index, item);
28264         return item;
28265     },
28266     
28267     /**
28268      * Adds a new element to the toolbar from the passed {@link Roo.DomHelper} config.
28269      * @param {Object} config
28270      * @return {Roo.Toolbar.Item} The element's item
28271      */
28272     addDom : function(config, returnEl){
28273         var td = this.nextBlock();
28274         Roo.DomHelper.overwrite(td, config);
28275         var ti = new Roo.Toolbar.Item(td.firstChild);
28276         ti.render(td);
28277         this.items.add(ti);
28278         return ti;
28279     },
28280
28281     /**
28282      * Collection of fields on the toolbar.. usefull for quering (value is false if there are no fields)
28283      * @type Roo.util.MixedCollection  
28284      */
28285     fields : false,
28286     
28287     /**
28288      * Adds a dynamically rendered Roo.form field (TextField, ComboBox, etc).
28289      * Note: the field should not have been rendered yet. For a field that has already been
28290      * rendered, use {@link #addElement}.
28291      * @param {Roo.form.Field} field
28292      * @return {Roo.ToolbarItem}
28293      */
28294      
28295       
28296     addField : function(field) {
28297         if (!this.fields) {
28298             var autoId = 0;
28299             this.fields = new Roo.util.MixedCollection(false, function(o){
28300                 return o.id || ("item" + (++autoId));
28301             });
28302
28303         }
28304         
28305         var td = this.nextBlock();
28306         field.render(td);
28307         var ti = new Roo.Toolbar.Item(td.firstChild);
28308         ti.render(td);
28309         this.items.add(ti);
28310         this.fields.add(field);
28311         return ti;
28312     },
28313     /**
28314      * Hide the toolbar
28315      * @method hide
28316      */
28317      
28318       
28319     hide : function()
28320     {
28321         this.el.child('div').setVisibilityMode(Roo.Element.DISPLAY);
28322         this.el.child('div').hide();
28323     },
28324     /**
28325      * Show the toolbar
28326      * @method show
28327      */
28328     show : function()
28329     {
28330         this.el.child('div').show();
28331     },
28332       
28333     // private
28334     nextBlock : function(){
28335         var td = document.createElement("td");
28336         this.tr.appendChild(td);
28337         return td;
28338     },
28339
28340     // private
28341     destroy : function(){
28342         if(this.items){ // rendered?
28343             Roo.destroy.apply(Roo, this.items.items);
28344         }
28345         if(this.fields){ // rendered?
28346             Roo.destroy.apply(Roo, this.fields.items);
28347         }
28348         Roo.Element.uncache(this.el, this.tr);
28349     }
28350 };
28351
28352 /**
28353  * @class Roo.Toolbar.Item
28354  * The base class that other classes should extend in order to get some basic common toolbar item functionality.
28355  * @constructor
28356  * Creates a new Item
28357  * @param {HTMLElement} el 
28358  */
28359 Roo.Toolbar.Item = function(el){
28360     this.el = Roo.getDom(el);
28361     this.id = Roo.id(this.el);
28362     this.hidden = false;
28363 };
28364
28365 Roo.Toolbar.Item.prototype = {
28366     
28367     /**
28368      * Get this item's HTML Element
28369      * @return {HTMLElement}
28370      */
28371     getEl : function(){
28372        return this.el;  
28373     },
28374
28375     // private
28376     render : function(td){
28377         this.td = td;
28378         td.appendChild(this.el);
28379     },
28380     
28381     /**
28382      * Removes and destroys this item.
28383      */
28384     destroy : function(){
28385         this.td.parentNode.removeChild(this.td);
28386     },
28387     
28388     /**
28389      * Shows this item.
28390      */
28391     show: function(){
28392         this.hidden = false;
28393         this.td.style.display = "";
28394     },
28395     
28396     /**
28397      * Hides this item.
28398      */
28399     hide: function(){
28400         this.hidden = true;
28401         this.td.style.display = "none";
28402     },
28403     
28404     /**
28405      * Convenience function for boolean show/hide.
28406      * @param {Boolean} visible true to show/false to hide
28407      */
28408     setVisible: function(visible){
28409         if(visible) {
28410             this.show();
28411         }else{
28412             this.hide();
28413         }
28414     },
28415     
28416     /**
28417      * Try to focus this item.
28418      */
28419     focus : function(){
28420         Roo.fly(this.el).focus();
28421     },
28422     
28423     /**
28424      * Disables this item.
28425      */
28426     disable : function(){
28427         Roo.fly(this.td).addClass("x-item-disabled");
28428         this.disabled = true;
28429         this.el.disabled = true;
28430     },
28431     
28432     /**
28433      * Enables this item.
28434      */
28435     enable : function(){
28436         Roo.fly(this.td).removeClass("x-item-disabled");
28437         this.disabled = false;
28438         this.el.disabled = false;
28439     }
28440 };
28441
28442
28443 /**
28444  * @class Roo.Toolbar.Separator
28445  * @extends Roo.Toolbar.Item
28446  * A simple toolbar separator class
28447  * @constructor
28448  * Creates a new Separator
28449  */
28450 Roo.Toolbar.Separator = function(){
28451     var s = document.createElement("span");
28452     s.className = "ytb-sep";
28453     Roo.Toolbar.Separator.superclass.constructor.call(this, s);
28454 };
28455 Roo.extend(Roo.Toolbar.Separator, Roo.Toolbar.Item, {
28456     enable:Roo.emptyFn,
28457     disable:Roo.emptyFn,
28458     focus:Roo.emptyFn
28459 });
28460
28461 /**
28462  * @class Roo.Toolbar.Spacer
28463  * @extends Roo.Toolbar.Item
28464  * A simple element that adds extra horizontal space to a toolbar.
28465  * @constructor
28466  * Creates a new Spacer
28467  */
28468 Roo.Toolbar.Spacer = function(){
28469     var s = document.createElement("div");
28470     s.className = "ytb-spacer";
28471     Roo.Toolbar.Spacer.superclass.constructor.call(this, s);
28472 };
28473 Roo.extend(Roo.Toolbar.Spacer, Roo.Toolbar.Item, {
28474     enable:Roo.emptyFn,
28475     disable:Roo.emptyFn,
28476     focus:Roo.emptyFn
28477 });
28478
28479 /**
28480  * @class Roo.Toolbar.Fill
28481  * @extends Roo.Toolbar.Spacer
28482  * A simple element that adds a greedy (100% width) horizontal space to a toolbar.
28483  * @constructor
28484  * Creates a new Spacer
28485  */
28486 Roo.Toolbar.Fill = Roo.extend(Roo.Toolbar.Spacer, {
28487     // private
28488     render : function(td){
28489         td.style.width = '100%';
28490         Roo.Toolbar.Fill.superclass.render.call(this, td);
28491     }
28492 });
28493
28494 /**
28495  * @class Roo.Toolbar.TextItem
28496  * @extends Roo.Toolbar.Item
28497  * A simple class that renders text directly into a toolbar.
28498  * @constructor
28499  * Creates a new TextItem
28500  * @param {String} text
28501  */
28502 Roo.Toolbar.TextItem = function(text){
28503     if (typeof(text) == 'object') {
28504         text = text.text;
28505     }
28506     var s = document.createElement("span");
28507     s.className = "ytb-text";
28508     s.innerHTML = text;
28509     Roo.Toolbar.TextItem.superclass.constructor.call(this, s);
28510 };
28511 Roo.extend(Roo.Toolbar.TextItem, Roo.Toolbar.Item, {
28512     enable:Roo.emptyFn,
28513     disable:Roo.emptyFn,
28514     focus:Roo.emptyFn
28515 });
28516
28517 /**
28518  * @class Roo.Toolbar.Button
28519  * @extends Roo.Button
28520  * A button that renders into a toolbar.
28521  * @constructor
28522  * Creates a new Button
28523  * @param {Object} config A standard {@link Roo.Button} config object
28524  */
28525 Roo.Toolbar.Button = function(config){
28526     Roo.Toolbar.Button.superclass.constructor.call(this, null, config);
28527 };
28528 Roo.extend(Roo.Toolbar.Button, Roo.Button, {
28529     render : function(td){
28530         this.td = td;
28531         Roo.Toolbar.Button.superclass.render.call(this, td);
28532     },
28533     
28534     /**
28535      * Removes and destroys this button
28536      */
28537     destroy : function(){
28538         Roo.Toolbar.Button.superclass.destroy.call(this);
28539         this.td.parentNode.removeChild(this.td);
28540     },
28541     
28542     /**
28543      * Shows this button
28544      */
28545     show: function(){
28546         this.hidden = false;
28547         this.td.style.display = "";
28548     },
28549     
28550     /**
28551      * Hides this button
28552      */
28553     hide: function(){
28554         this.hidden = true;
28555         this.td.style.display = "none";
28556     },
28557
28558     /**
28559      * Disables this item
28560      */
28561     disable : function(){
28562         Roo.fly(this.td).addClass("x-item-disabled");
28563         this.disabled = true;
28564     },
28565
28566     /**
28567      * Enables this item
28568      */
28569     enable : function(){
28570         Roo.fly(this.td).removeClass("x-item-disabled");
28571         this.disabled = false;
28572     }
28573 });
28574 // backwards compat
28575 Roo.ToolbarButton = Roo.Toolbar.Button;
28576
28577 /**
28578  * @class Roo.Toolbar.SplitButton
28579  * @extends Roo.SplitButton
28580  * A menu button that renders into a toolbar.
28581  * @constructor
28582  * Creates a new SplitButton
28583  * @param {Object} config A standard {@link Roo.SplitButton} config object
28584  */
28585 Roo.Toolbar.SplitButton = function(config){
28586     Roo.Toolbar.SplitButton.superclass.constructor.call(this, null, config);
28587 };
28588 Roo.extend(Roo.Toolbar.SplitButton, Roo.SplitButton, {
28589     render : function(td){
28590         this.td = td;
28591         Roo.Toolbar.SplitButton.superclass.render.call(this, td);
28592     },
28593     
28594     /**
28595      * Removes and destroys this button
28596      */
28597     destroy : function(){
28598         Roo.Toolbar.SplitButton.superclass.destroy.call(this);
28599         this.td.parentNode.removeChild(this.td);
28600     },
28601     
28602     /**
28603      * Shows this button
28604      */
28605     show: function(){
28606         this.hidden = false;
28607         this.td.style.display = "";
28608     },
28609     
28610     /**
28611      * Hides this button
28612      */
28613     hide: function(){
28614         this.hidden = true;
28615         this.td.style.display = "none";
28616     }
28617 });
28618
28619 // backwards compat
28620 Roo.Toolbar.MenuButton = Roo.Toolbar.SplitButton;/*
28621  * Based on:
28622  * Ext JS Library 1.1.1
28623  * Copyright(c) 2006-2007, Ext JS, LLC.
28624  *
28625  * Originally Released Under LGPL - original licence link has changed is not relivant.
28626  *
28627  * Fork - LGPL
28628  * <script type="text/javascript">
28629  */
28630  
28631 /**
28632  * @class Roo.PagingToolbar
28633  * @extends Roo.Toolbar
28634  * A specialized toolbar that is bound to a {@link Roo.data.Store} and provides automatic paging controls.
28635  * @constructor
28636  * Create a new PagingToolbar
28637  * @param {Object} config The config object
28638  */
28639 Roo.PagingToolbar = function(el, ds, config)
28640 {
28641     // old args format still supported... - xtype is prefered..
28642     if (typeof(el) == 'object' && el.xtype) {
28643         // created from xtype...
28644         config = el;
28645         ds = el.dataSource;
28646         el = config.container;
28647     }
28648     var items = [];
28649     if (config.items) {
28650         items = config.items;
28651         config.items = [];
28652     }
28653     
28654     Roo.PagingToolbar.superclass.constructor.call(this, el, null, config);
28655     this.ds = ds;
28656     this.cursor = 0;
28657     this.renderButtons(this.el);
28658     this.bind(ds);
28659     
28660     // supprot items array.
28661    
28662     Roo.each(items, function(e) {
28663         this.add(Roo.factory(e));
28664     },this);
28665     
28666 };
28667
28668 Roo.extend(Roo.PagingToolbar, Roo.Toolbar, {
28669     /**
28670      * @cfg {Roo.data.Store} dataSource
28671      * The underlying data store providing the paged data
28672      */
28673     /**
28674      * @cfg {String/HTMLElement/Element} container
28675      * container The id or element that will contain the toolbar
28676      */
28677     /**
28678      * @cfg {Boolean} displayInfo
28679      * True to display the displayMsg (defaults to false)
28680      */
28681     /**
28682      * @cfg {Number} pageSize
28683      * The number of records to display per page (defaults to 20)
28684      */
28685     pageSize: 20,
28686     /**
28687      * @cfg {String} displayMsg
28688      * The paging status message to display (defaults to "Displaying {start} - {end} of {total}")
28689      */
28690     displayMsg : 'Displaying {0} - {1} of {2}',
28691     /**
28692      * @cfg {String} emptyMsg
28693      * The message to display when no records are found (defaults to "No data to display")
28694      */
28695     emptyMsg : 'No data to display',
28696     /**
28697      * Customizable piece of the default paging text (defaults to "Page")
28698      * @type String
28699      */
28700     beforePageText : "Page",
28701     /**
28702      * Customizable piece of the default paging text (defaults to "of %0")
28703      * @type String
28704      */
28705     afterPageText : "of {0}",
28706     /**
28707      * Customizable piece of the default paging text (defaults to "First Page")
28708      * @type String
28709      */
28710     firstText : "First Page",
28711     /**
28712      * Customizable piece of the default paging text (defaults to "Previous Page")
28713      * @type String
28714      */
28715     prevText : "Previous Page",
28716     /**
28717      * Customizable piece of the default paging text (defaults to "Next Page")
28718      * @type String
28719      */
28720     nextText : "Next Page",
28721     /**
28722      * Customizable piece of the default paging text (defaults to "Last Page")
28723      * @type String
28724      */
28725     lastText : "Last Page",
28726     /**
28727      * Customizable piece of the default paging text (defaults to "Refresh")
28728      * @type String
28729      */
28730     refreshText : "Refresh",
28731
28732     // private
28733     renderButtons : function(el){
28734         Roo.PagingToolbar.superclass.render.call(this, el);
28735         this.first = this.addButton({
28736             tooltip: this.firstText,
28737             cls: "x-btn-icon x-grid-page-first",
28738             disabled: true,
28739             handler: this.onClick.createDelegate(this, ["first"])
28740         });
28741         this.prev = this.addButton({
28742             tooltip: this.prevText,
28743             cls: "x-btn-icon x-grid-page-prev",
28744             disabled: true,
28745             handler: this.onClick.createDelegate(this, ["prev"])
28746         });
28747         //this.addSeparator();
28748         this.add(this.beforePageText);
28749         this.field = Roo.get(this.addDom({
28750            tag: "input",
28751            type: "text",
28752            size: "3",
28753            value: "1",
28754            cls: "x-grid-page-number"
28755         }).el);
28756         this.field.on("keydown", this.onPagingKeydown, this);
28757         this.field.on("focus", function(){this.dom.select();});
28758         this.afterTextEl = this.addText(String.format(this.afterPageText, 1));
28759         this.field.setHeight(18);
28760         //this.addSeparator();
28761         this.next = this.addButton({
28762             tooltip: this.nextText,
28763             cls: "x-btn-icon x-grid-page-next",
28764             disabled: true,
28765             handler: this.onClick.createDelegate(this, ["next"])
28766         });
28767         this.last = this.addButton({
28768             tooltip: this.lastText,
28769             cls: "x-btn-icon x-grid-page-last",
28770             disabled: true,
28771             handler: this.onClick.createDelegate(this, ["last"])
28772         });
28773         //this.addSeparator();
28774         this.loading = this.addButton({
28775             tooltip: this.refreshText,
28776             cls: "x-btn-icon x-grid-loading",
28777             handler: this.onClick.createDelegate(this, ["refresh"])
28778         });
28779
28780         if(this.displayInfo){
28781             this.displayEl = Roo.fly(this.el.dom.firstChild).createChild({cls:'x-paging-info'});
28782         }
28783     },
28784
28785     // private
28786     updateInfo : function(){
28787         if(this.displayEl){
28788             var count = this.ds.getCount();
28789             var msg = count == 0 ?
28790                 this.emptyMsg :
28791                 String.format(
28792                     this.displayMsg,
28793                     this.cursor+1, this.cursor+count, this.ds.getTotalCount()    
28794                 );
28795             this.displayEl.update(msg);
28796         }
28797     },
28798
28799     // private
28800     onLoad : function(ds, r, o){
28801        this.cursor = o.params ? o.params.start : 0;
28802        var d = this.getPageData(), ap = d.activePage, ps = d.pages;
28803
28804        this.afterTextEl.el.innerHTML = String.format(this.afterPageText, d.pages);
28805        this.field.dom.value = ap;
28806        this.first.setDisabled(ap == 1);
28807        this.prev.setDisabled(ap == 1);
28808        this.next.setDisabled(ap == ps);
28809        this.last.setDisabled(ap == ps);
28810        this.loading.enable();
28811        this.updateInfo();
28812     },
28813
28814     // private
28815     getPageData : function(){
28816         var total = this.ds.getTotalCount();
28817         return {
28818             total : total,
28819             activePage : Math.ceil((this.cursor+this.pageSize)/this.pageSize),
28820             pages :  total < this.pageSize ? 1 : Math.ceil(total/this.pageSize)
28821         };
28822     },
28823
28824     // private
28825     onLoadError : function(){
28826         this.loading.enable();
28827     },
28828
28829     // private
28830     onPagingKeydown : function(e){
28831         var k = e.getKey();
28832         var d = this.getPageData();
28833         if(k == e.RETURN){
28834             var v = this.field.dom.value, pageNum;
28835             if(!v || isNaN(pageNum = parseInt(v, 10))){
28836                 this.field.dom.value = d.activePage;
28837                 return;
28838             }
28839             pageNum = Math.min(Math.max(1, pageNum), d.pages) - 1;
28840             this.ds.load({params:{start: pageNum * this.pageSize, limit: this.pageSize}});
28841             e.stopEvent();
28842         }
28843         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))
28844         {
28845           var pageNum = (k == e.HOME || (k == e.DOWN && e.ctrlKey) || (k == e.LEFT && e.ctrlKey) || (k == e.PAGEDOWN && e.ctrlKey)) ? 1 : d.pages;
28846           this.field.dom.value = pageNum;
28847           this.ds.load({params:{start: (pageNum - 1) * this.pageSize, limit: this.pageSize}});
28848           e.stopEvent();
28849         }
28850         else if(k == e.UP || k == e.RIGHT || k == e.PAGEUP || k == e.DOWN || k == e.LEFT || k == e.PAGEDOWN)
28851         {
28852           var v = this.field.dom.value, pageNum; 
28853           var increment = (e.shiftKey) ? 10 : 1;
28854           if(k == e.DOWN || k == e.LEFT || k == e.PAGEDOWN)
28855             increment *= -1;
28856           if(!v || isNaN(pageNum = parseInt(v, 10))) {
28857             this.field.dom.value = d.activePage;
28858             return;
28859           }
28860           else if(parseInt(v, 10) + increment >= 1 & parseInt(v, 10) + increment <= d.pages)
28861           {
28862             this.field.dom.value = parseInt(v, 10) + increment;
28863             pageNum = Math.min(Math.max(1, pageNum + increment), d.pages) - 1;
28864             this.ds.load({params:{start: pageNum * this.pageSize, limit: this.pageSize}});
28865           }
28866           e.stopEvent();
28867         }
28868     },
28869
28870     // private
28871     beforeLoad : function(){
28872         if(this.loading){
28873             this.loading.disable();
28874         }
28875     },
28876
28877     // private
28878     onClick : function(which){
28879         var ds = this.ds;
28880         switch(which){
28881             case "first":
28882                 ds.load({params:{start: 0, limit: this.pageSize}});
28883             break;
28884             case "prev":
28885                 ds.load({params:{start: Math.max(0, this.cursor-this.pageSize), limit: this.pageSize}});
28886             break;
28887             case "next":
28888                 ds.load({params:{start: this.cursor+this.pageSize, limit: this.pageSize}});
28889             break;
28890             case "last":
28891                 var total = ds.getTotalCount();
28892                 var extra = total % this.pageSize;
28893                 var lastStart = extra ? (total - extra) : total-this.pageSize;
28894                 ds.load({params:{start: lastStart, limit: this.pageSize}});
28895             break;
28896             case "refresh":
28897                 ds.load({params:{start: this.cursor, limit: this.pageSize}});
28898             break;
28899         }
28900     },
28901
28902     /**
28903      * Unbinds the paging toolbar from the specified {@link Roo.data.Store}
28904      * @param {Roo.data.Store} store The data store to unbind
28905      */
28906     unbind : function(ds){
28907         ds.un("beforeload", this.beforeLoad, this);
28908         ds.un("load", this.onLoad, this);
28909         ds.un("loadexception", this.onLoadError, this);
28910         ds.un("remove", this.updateInfo, this);
28911         ds.un("add", this.updateInfo, this);
28912         this.ds = undefined;
28913     },
28914
28915     /**
28916      * Binds the paging toolbar to the specified {@link Roo.data.Store}
28917      * @param {Roo.data.Store} store The data store to bind
28918      */
28919     bind : function(ds){
28920         ds.on("beforeload", this.beforeLoad, this);
28921         ds.on("load", this.onLoad, this);
28922         ds.on("loadexception", this.onLoadError, this);
28923         ds.on("remove", this.updateInfo, this);
28924         ds.on("add", this.updateInfo, this);
28925         this.ds = ds;
28926     }
28927 });/*
28928  * Based on:
28929  * Ext JS Library 1.1.1
28930  * Copyright(c) 2006-2007, Ext JS, LLC.
28931  *
28932  * Originally Released Under LGPL - original licence link has changed is not relivant.
28933  *
28934  * Fork - LGPL
28935  * <script type="text/javascript">
28936  */
28937
28938 /**
28939  * @class Roo.Resizable
28940  * @extends Roo.util.Observable
28941  * <p>Applies drag handles to an element to make it resizable. The drag handles are inserted into the element
28942  * and positioned absolute. Some elements, such as a textarea or image, don't support this. To overcome that, you can wrap
28943  * 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
28944  * the element will be wrapped for you automatically.</p>
28945  * <p>Here is the list of valid resize handles:</p>
28946  * <pre>
28947 Value   Description
28948 ------  -------------------
28949  'n'     north
28950  's'     south
28951  'e'     east
28952  'w'     west
28953  'nw'    northwest
28954  'sw'    southwest
28955  'se'    southeast
28956  'ne'    northeast
28957  'hd'    horizontal drag
28958  'all'   all
28959 </pre>
28960  * <p>Here's an example showing the creation of a typical Resizable:</p>
28961  * <pre><code>
28962 var resizer = new Roo.Resizable("element-id", {
28963     handles: 'all',
28964     minWidth: 200,
28965     minHeight: 100,
28966     maxWidth: 500,
28967     maxHeight: 400,
28968     pinned: true
28969 });
28970 resizer.on("resize", myHandler);
28971 </code></pre>
28972  * <p>To hide a particular handle, set its display to none in CSS, or through script:<br>
28973  * resizer.east.setDisplayed(false);</p>
28974  * @cfg {Boolean/String/Element} resizeChild True to resize the first child, or id/element to resize (defaults to false)
28975  * @cfg {Array/String} adjustments String "auto" or an array [width, height] with values to be <b>added</b> to the
28976  * resize operation's new size (defaults to [0, 0])
28977  * @cfg {Number} minWidth The minimum width for the element (defaults to 5)
28978  * @cfg {Number} minHeight The minimum height for the element (defaults to 5)
28979  * @cfg {Number} maxWidth The maximum width for the element (defaults to 10000)
28980  * @cfg {Number} maxHeight The maximum height for the element (defaults to 10000)
28981  * @cfg {Boolean} enabled False to disable resizing (defaults to true)
28982  * @cfg {Boolean} wrap True to wrap an element with a div if needed (required for textareas and images, defaults to false)
28983  * @cfg {Number} width The width of the element in pixels (defaults to null)
28984  * @cfg {Number} height The height of the element in pixels (defaults to null)
28985  * @cfg {Boolean} animate True to animate the resize (not compatible with dynamic sizing, defaults to false)
28986  * @cfg {Number} duration Animation duration if animate = true (defaults to .35)
28987  * @cfg {Boolean} dynamic True to resize the element while dragging instead of using a proxy (defaults to false)
28988  * @cfg {String} handles String consisting of the resize handles to display (defaults to undefined)
28989  * @cfg {Boolean} multiDirectional <b>Deprecated</b>.  The old style of adding multi-direction resize handles, deprecated
28990  * in favor of the handles config option (defaults to false)
28991  * @cfg {Boolean} disableTrackOver True to disable mouse tracking. This is only applied at config time. (defaults to false)
28992  * @cfg {String} easing Animation easing if animate = true (defaults to 'easingOutStrong')
28993  * @cfg {Number} widthIncrement The increment to snap the width resize in pixels (dynamic must be true, defaults to 0)
28994  * @cfg {Number} heightIncrement The increment to snap the height resize in pixels (dynamic must be true, defaults to 0)
28995  * @cfg {Boolean} pinned True to ensure that the resize handles are always visible, false to display them only when the
28996  * user mouses over the resizable borders. This is only applied at config time. (defaults to false)
28997  * @cfg {Boolean} preserveRatio True to preserve the original ratio between height and width during resize (defaults to false)
28998  * @cfg {Boolean} transparent True for transparent handles. This is only applied at config time. (defaults to false)
28999  * @cfg {Number} minX The minimum allowed page X for the element (only used for west resizing, defaults to 0)
29000  * @cfg {Number} minY The minimum allowed page Y for the element (only used for north resizing, defaults to 0)
29001  * @cfg {Boolean} draggable Convenience to initialize drag drop (defaults to false)
29002  * @constructor
29003  * Create a new resizable component
29004  * @param {String/HTMLElement/Roo.Element} el The id or element to resize
29005  * @param {Object} config configuration options
29006   */
29007 Roo.Resizable = function(el, config)
29008 {
29009     this.el = Roo.get(el);
29010
29011     if(config && config.wrap){
29012         config.resizeChild = this.el;
29013         this.el = this.el.wrap(typeof config.wrap == "object" ? config.wrap : {cls:"xresizable-wrap"});
29014         this.el.id = this.el.dom.id = config.resizeChild.id + "-rzwrap";
29015         this.el.setStyle("overflow", "hidden");
29016         this.el.setPositioning(config.resizeChild.getPositioning());
29017         config.resizeChild.clearPositioning();
29018         if(!config.width || !config.height){
29019             var csize = config.resizeChild.getSize();
29020             this.el.setSize(csize.width, csize.height);
29021         }
29022         if(config.pinned && !config.adjustments){
29023             config.adjustments = "auto";
29024         }
29025     }
29026
29027     this.proxy = this.el.createProxy({tag: "div", cls: "x-resizable-proxy", id: this.el.id + "-rzproxy"});
29028     this.proxy.unselectable();
29029     this.proxy.enableDisplayMode('block');
29030
29031     Roo.apply(this, config);
29032
29033     if(this.pinned){
29034         this.disableTrackOver = true;
29035         this.el.addClass("x-resizable-pinned");
29036     }
29037     // if the element isn't positioned, make it relative
29038     var position = this.el.getStyle("position");
29039     if(position != "absolute" && position != "fixed"){
29040         this.el.setStyle("position", "relative");
29041     }
29042     if(!this.handles){ // no handles passed, must be legacy style
29043         this.handles = 's,e,se';
29044         if(this.multiDirectional){
29045             this.handles += ',n,w';
29046         }
29047     }
29048     if(this.handles == "all"){
29049         this.handles = "n s e w ne nw se sw";
29050     }
29051     var hs = this.handles.split(/\s*?[,;]\s*?| /);
29052     var ps = Roo.Resizable.positions;
29053     for(var i = 0, len = hs.length; i < len; i++){
29054         if(hs[i] && ps[hs[i]]){
29055             var pos = ps[hs[i]];
29056             this[pos] = new Roo.Resizable.Handle(this, pos, this.disableTrackOver, this.transparent);
29057         }
29058     }
29059     // legacy
29060     this.corner = this.southeast;
29061     
29062     // updateBox = the box can move..
29063     if(this.handles.indexOf("n") != -1 || this.handles.indexOf("w") != -1 || this.handles.indexOf("hd") != -1) {
29064         this.updateBox = true;
29065     }
29066
29067     this.activeHandle = null;
29068
29069     if(this.resizeChild){
29070         if(typeof this.resizeChild == "boolean"){
29071             this.resizeChild = Roo.get(this.el.dom.firstChild, true);
29072         }else{
29073             this.resizeChild = Roo.get(this.resizeChild, true);
29074         }
29075     }
29076     
29077     if(this.adjustments == "auto"){
29078         var rc = this.resizeChild;
29079         var hw = this.west, he = this.east, hn = this.north, hs = this.south;
29080         if(rc && (hw || hn)){
29081             rc.position("relative");
29082             rc.setLeft(hw ? hw.el.getWidth() : 0);
29083             rc.setTop(hn ? hn.el.getHeight() : 0);
29084         }
29085         this.adjustments = [
29086             (he ? -he.el.getWidth() : 0) + (hw ? -hw.el.getWidth() : 0),
29087             (hn ? -hn.el.getHeight() : 0) + (hs ? -hs.el.getHeight() : 0) -1
29088         ];
29089     }
29090
29091     if(this.draggable){
29092         this.dd = this.dynamic ?
29093             this.el.initDD(null) : this.el.initDDProxy(null, {dragElId: this.proxy.id});
29094         this.dd.setHandleElId(this.resizeChild ? this.resizeChild.id : this.el.id);
29095     }
29096
29097     // public events
29098     this.addEvents({
29099         /**
29100          * @event beforeresize
29101          * Fired before resize is allowed. Set enabled to false to cancel resize.
29102          * @param {Roo.Resizable} this
29103          * @param {Roo.EventObject} e The mousedown event
29104          */
29105         "beforeresize" : true,
29106         /**
29107          * @event resizing
29108          * Fired a resizing.
29109          * @param {Roo.Resizable} this
29110          * @param {Number} x The new x position
29111          * @param {Number} y The new y position
29112          * @param {Number} w The new w width
29113          * @param {Number} h The new h hight
29114          * @param {Roo.EventObject} e The mouseup event
29115          */
29116         "resizing" : true,
29117         /**
29118          * @event resize
29119          * Fired after a resize.
29120          * @param {Roo.Resizable} this
29121          * @param {Number} width The new width
29122          * @param {Number} height The new height
29123          * @param {Roo.EventObject} e The mouseup event
29124          */
29125         "resize" : true
29126     });
29127
29128     if(this.width !== null && this.height !== null){
29129         this.resizeTo(this.width, this.height);
29130     }else{
29131         this.updateChildSize();
29132     }
29133     if(Roo.isIE){
29134         this.el.dom.style.zoom = 1;
29135     }
29136     Roo.Resizable.superclass.constructor.call(this);
29137 };
29138
29139 Roo.extend(Roo.Resizable, Roo.util.Observable, {
29140         resizeChild : false,
29141         adjustments : [0, 0],
29142         minWidth : 5,
29143         minHeight : 5,
29144         maxWidth : 10000,
29145         maxHeight : 10000,
29146         enabled : true,
29147         animate : false,
29148         duration : .35,
29149         dynamic : false,
29150         handles : false,
29151         multiDirectional : false,
29152         disableTrackOver : false,
29153         easing : 'easeOutStrong',
29154         widthIncrement : 0,
29155         heightIncrement : 0,
29156         pinned : false,
29157         width : null,
29158         height : null,
29159         preserveRatio : false,
29160         transparent: false,
29161         minX: 0,
29162         minY: 0,
29163         draggable: false,
29164
29165         /**
29166          * @cfg {String/HTMLElement/Element} constrainTo Constrain the resize to a particular element
29167          */
29168         constrainTo: undefined,
29169         /**
29170          * @cfg {Roo.lib.Region} resizeRegion Constrain the resize to a particular region
29171          */
29172         resizeRegion: undefined,
29173
29174
29175     /**
29176      * Perform a manual resize
29177      * @param {Number} width
29178      * @param {Number} height
29179      */
29180     resizeTo : function(width, height){
29181         this.el.setSize(width, height);
29182         this.updateChildSize();
29183         this.fireEvent("resize", this, width, height, null);
29184     },
29185
29186     // private
29187     startSizing : function(e, handle){
29188         this.fireEvent("beforeresize", this, e);
29189         if(this.enabled){ // 2nd enabled check in case disabled before beforeresize handler
29190
29191             if(!this.overlay){
29192                 this.overlay = this.el.createProxy({tag: "div", cls: "x-resizable-overlay", html: "&#160;"});
29193                 this.overlay.unselectable();
29194                 this.overlay.enableDisplayMode("block");
29195                 this.overlay.on("mousemove", this.onMouseMove, this);
29196                 this.overlay.on("mouseup", this.onMouseUp, this);
29197             }
29198             this.overlay.setStyle("cursor", handle.el.getStyle("cursor"));
29199
29200             this.resizing = true;
29201             this.startBox = this.el.getBox();
29202             this.startPoint = e.getXY();
29203             this.offsets = [(this.startBox.x + this.startBox.width) - this.startPoint[0],
29204                             (this.startBox.y + this.startBox.height) - this.startPoint[1]];
29205
29206             this.overlay.setSize(Roo.lib.Dom.getViewWidth(true), Roo.lib.Dom.getViewHeight(true));
29207             this.overlay.show();
29208
29209             if(this.constrainTo) {
29210                 var ct = Roo.get(this.constrainTo);
29211                 this.resizeRegion = ct.getRegion().adjust(
29212                     ct.getFrameWidth('t'),
29213                     ct.getFrameWidth('l'),
29214                     -ct.getFrameWidth('b'),
29215                     -ct.getFrameWidth('r')
29216                 );
29217             }
29218
29219             this.proxy.setStyle('visibility', 'hidden'); // workaround display none
29220             this.proxy.show();
29221             this.proxy.setBox(this.startBox);
29222             if(!this.dynamic){
29223                 this.proxy.setStyle('visibility', 'visible');
29224             }
29225         }
29226     },
29227
29228     // private
29229     onMouseDown : function(handle, e){
29230         if(this.enabled){
29231             e.stopEvent();
29232             this.activeHandle = handle;
29233             this.startSizing(e, handle);
29234         }
29235     },
29236
29237     // private
29238     onMouseUp : function(e){
29239         var size = this.resizeElement();
29240         this.resizing = false;
29241         this.handleOut();
29242         this.overlay.hide();
29243         this.proxy.hide();
29244         this.fireEvent("resize", this, size.width, size.height, e);
29245     },
29246
29247     // private
29248     updateChildSize : function(){
29249         
29250         if(this.resizeChild){
29251             var el = this.el;
29252             var child = this.resizeChild;
29253             var adj = this.adjustments;
29254             if(el.dom.offsetWidth){
29255                 var b = el.getSize(true);
29256                 child.setSize(b.width+adj[0], b.height+adj[1]);
29257             }
29258             // Second call here for IE
29259             // The first call enables instant resizing and
29260             // the second call corrects scroll bars if they
29261             // exist
29262             if(Roo.isIE){
29263                 setTimeout(function(){
29264                     if(el.dom.offsetWidth){
29265                         var b = el.getSize(true);
29266                         child.setSize(b.width+adj[0], b.height+adj[1]);
29267                     }
29268                 }, 10);
29269             }
29270         }
29271     },
29272
29273     // private
29274     snap : function(value, inc, min){
29275         if(!inc || !value) return value;
29276         var newValue = value;
29277         var m = value % inc;
29278         if(m > 0){
29279             if(m > (inc/2)){
29280                 newValue = value + (inc-m);
29281             }else{
29282                 newValue = value - m;
29283             }
29284         }
29285         return Math.max(min, newValue);
29286     },
29287
29288     // private
29289     resizeElement : function(){
29290         var box = this.proxy.getBox();
29291         if(this.updateBox){
29292             this.el.setBox(box, false, this.animate, this.duration, null, this.easing);
29293         }else{
29294             this.el.setSize(box.width, box.height, this.animate, this.duration, null, this.easing);
29295         }
29296         this.updateChildSize();
29297         if(!this.dynamic){
29298             this.proxy.hide();
29299         }
29300         return box;
29301     },
29302
29303     // private
29304     constrain : function(v, diff, m, mx){
29305         if(v - diff < m){
29306             diff = v - m;
29307         }else if(v - diff > mx){
29308             diff = mx - v;
29309         }
29310         return diff;
29311     },
29312
29313     // private
29314     onMouseMove : function(e){
29315         
29316         if(this.enabled){
29317             try{// try catch so if something goes wrong the user doesn't get hung
29318
29319             if(this.resizeRegion && !this.resizeRegion.contains(e.getPoint())) {
29320                 return;
29321             }
29322
29323             //var curXY = this.startPoint;
29324             var curSize = this.curSize || this.startBox;
29325             var x = this.startBox.x, y = this.startBox.y;
29326             var ox = x, oy = y;
29327             var w = curSize.width, h = curSize.height;
29328             var ow = w, oh = h;
29329             var mw = this.minWidth, mh = this.minHeight;
29330             var mxw = this.maxWidth, mxh = this.maxHeight;
29331             var wi = this.widthIncrement;
29332             var hi = this.heightIncrement;
29333
29334             var eventXY = e.getXY();
29335             var diffX = -(this.startPoint[0] - Math.max(this.minX, eventXY[0]));
29336             var diffY = -(this.startPoint[1] - Math.max(this.minY, eventXY[1]));
29337
29338             var pos = this.activeHandle.position;
29339
29340             switch(pos){
29341                 case "east":
29342                     w += diffX;
29343                     w = Math.min(Math.max(mw, w), mxw);
29344                     break;
29345              
29346                 case "south":
29347                     h += diffY;
29348                     h = Math.min(Math.max(mh, h), mxh);
29349                     break;
29350                 case "southeast":
29351                     w += diffX;
29352                     h += diffY;
29353                     w = Math.min(Math.max(mw, w), mxw);
29354                     h = Math.min(Math.max(mh, h), mxh);
29355                     break;
29356                 case "north":
29357                     diffY = this.constrain(h, diffY, mh, mxh);
29358                     y += diffY;
29359                     h -= diffY;
29360                     break;
29361                 case "hdrag":
29362                     
29363                     if (wi) {
29364                         var adiffX = Math.abs(diffX);
29365                         var sub = (adiffX % wi); // how much 
29366                         if (sub > (wi/2)) { // far enough to snap
29367                             diffX = (diffX > 0) ? diffX-sub + wi : diffX+sub - wi;
29368                         } else {
29369                             // remove difference.. 
29370                             diffX = (diffX > 0) ? diffX-sub : diffX+sub;
29371                         }
29372                     }
29373                     x += diffX;
29374                     x = Math.max(this.minX, x);
29375                     break;
29376                 case "west":
29377                     diffX = this.constrain(w, diffX, mw, mxw);
29378                     x += diffX;
29379                     w -= diffX;
29380                     break;
29381                 case "northeast":
29382                     w += diffX;
29383                     w = Math.min(Math.max(mw, w), mxw);
29384                     diffY = this.constrain(h, diffY, mh, mxh);
29385                     y += diffY;
29386                     h -= diffY;
29387                     break;
29388                 case "northwest":
29389                     diffX = this.constrain(w, diffX, mw, mxw);
29390                     diffY = this.constrain(h, diffY, mh, mxh);
29391                     y += diffY;
29392                     h -= diffY;
29393                     x += diffX;
29394                     w -= diffX;
29395                     break;
29396                case "southwest":
29397                     diffX = this.constrain(w, diffX, mw, mxw);
29398                     h += diffY;
29399                     h = Math.min(Math.max(mh, h), mxh);
29400                     x += diffX;
29401                     w -= diffX;
29402                     break;
29403             }
29404
29405             var sw = this.snap(w, wi, mw);
29406             var sh = this.snap(h, hi, mh);
29407             if(sw != w || sh != h){
29408                 switch(pos){
29409                     case "northeast":
29410                         y -= sh - h;
29411                     break;
29412                     case "north":
29413                         y -= sh - h;
29414                         break;
29415                     case "southwest":
29416                         x -= sw - w;
29417                     break;
29418                     case "west":
29419                         x -= sw - w;
29420                         break;
29421                     case "northwest":
29422                         x -= sw - w;
29423                         y -= sh - h;
29424                     break;
29425                 }
29426                 w = sw;
29427                 h = sh;
29428             }
29429
29430             if(this.preserveRatio){
29431                 switch(pos){
29432                     case "southeast":
29433                     case "east":
29434                         h = oh * (w/ow);
29435                         h = Math.min(Math.max(mh, h), mxh);
29436                         w = ow * (h/oh);
29437                        break;
29438                     case "south":
29439                         w = ow * (h/oh);
29440                         w = Math.min(Math.max(mw, w), mxw);
29441                         h = oh * (w/ow);
29442                         break;
29443                     case "northeast":
29444                         w = ow * (h/oh);
29445                         w = Math.min(Math.max(mw, w), mxw);
29446                         h = oh * (w/ow);
29447                     break;
29448                     case "north":
29449                         var tw = w;
29450                         w = ow * (h/oh);
29451                         w = Math.min(Math.max(mw, w), mxw);
29452                         h = oh * (w/ow);
29453                         x += (tw - w) / 2;
29454                         break;
29455                     case "southwest":
29456                         h = oh * (w/ow);
29457                         h = Math.min(Math.max(mh, h), mxh);
29458                         var tw = w;
29459                         w = ow * (h/oh);
29460                         x += tw - w;
29461                         break;
29462                     case "west":
29463                         var th = h;
29464                         h = oh * (w/ow);
29465                         h = Math.min(Math.max(mh, h), mxh);
29466                         y += (th - h) / 2;
29467                         var tw = w;
29468                         w = ow * (h/oh);
29469                         x += tw - w;
29470                        break;
29471                     case "northwest":
29472                         var tw = w;
29473                         var th = h;
29474                         h = oh * (w/ow);
29475                         h = Math.min(Math.max(mh, h), mxh);
29476                         w = ow * (h/oh);
29477                         y += th - h;
29478                         x += tw - w;
29479                        break;
29480
29481                 }
29482             }
29483             if (pos == 'hdrag') {
29484                 w = ow;
29485             }
29486             this.proxy.setBounds(x, y, w, h);
29487             if(this.dynamic){
29488                 this.resizeElement();
29489             }
29490             }catch(e){}
29491         }
29492         this.fireEvent("resizing", this, x, y, w, h, e);
29493     },
29494
29495     // private
29496     handleOver : function(){
29497         if(this.enabled){
29498             this.el.addClass("x-resizable-over");
29499         }
29500     },
29501
29502     // private
29503     handleOut : function(){
29504         if(!this.resizing){
29505             this.el.removeClass("x-resizable-over");
29506         }
29507     },
29508
29509     /**
29510      * Returns the element this component is bound to.
29511      * @return {Roo.Element}
29512      */
29513     getEl : function(){
29514         return this.el;
29515     },
29516
29517     /**
29518      * Returns the resizeChild element (or null).
29519      * @return {Roo.Element}
29520      */
29521     getResizeChild : function(){
29522         return this.resizeChild;
29523     },
29524     groupHandler : function()
29525     {
29526         
29527     },
29528     /**
29529      * Destroys this resizable. If the element was wrapped and
29530      * removeEl is not true then the element remains.
29531      * @param {Boolean} removeEl (optional) true to remove the element from the DOM
29532      */
29533     destroy : function(removeEl){
29534         this.proxy.remove();
29535         if(this.overlay){
29536             this.overlay.removeAllListeners();
29537             this.overlay.remove();
29538         }
29539         var ps = Roo.Resizable.positions;
29540         for(var k in ps){
29541             if(typeof ps[k] != "function" && this[ps[k]]){
29542                 var h = this[ps[k]];
29543                 h.el.removeAllListeners();
29544                 h.el.remove();
29545             }
29546         }
29547         if(removeEl){
29548             this.el.update("");
29549             this.el.remove();
29550         }
29551     }
29552 });
29553
29554 // private
29555 // hash to map config positions to true positions
29556 Roo.Resizable.positions = {
29557     n: "north", s: "south", e: "east", w: "west", se: "southeast", sw: "southwest", nw: "northwest", ne: "northeast", 
29558     hd: "hdrag"
29559 };
29560
29561 // private
29562 Roo.Resizable.Handle = function(rz, pos, disableTrackOver, transparent){
29563     if(!this.tpl){
29564         // only initialize the template if resizable is used
29565         var tpl = Roo.DomHelper.createTemplate(
29566             {tag: "div", cls: "x-resizable-handle x-resizable-handle-{0}"}
29567         );
29568         tpl.compile();
29569         Roo.Resizable.Handle.prototype.tpl = tpl;
29570     }
29571     this.position = pos;
29572     this.rz = rz;
29573     // show north drag fro topdra
29574     var handlepos = pos == 'hdrag' ? 'north' : pos;
29575     
29576     this.el = this.tpl.append(rz.el.dom, [handlepos], true);
29577     if (pos == 'hdrag') {
29578         this.el.setStyle('cursor', 'pointer');
29579     }
29580     this.el.unselectable();
29581     if(transparent){
29582         this.el.setOpacity(0);
29583     }
29584     this.el.on("mousedown", this.onMouseDown, this);
29585     if(!disableTrackOver){
29586         this.el.on("mouseover", this.onMouseOver, this);
29587         this.el.on("mouseout", this.onMouseOut, this);
29588     }
29589 };
29590
29591 // private
29592 Roo.Resizable.Handle.prototype = {
29593     afterResize : function(rz){
29594         Roo.log('after?');
29595         // do nothing
29596     },
29597     // private
29598     onMouseDown : function(e){
29599         this.rz.onMouseDown(this, e);
29600     },
29601     // private
29602     onMouseOver : function(e){
29603         this.rz.handleOver(this, e);
29604     },
29605     // private
29606     onMouseOut : function(e){
29607         this.rz.handleOut(this, e);
29608     }
29609 };/*
29610  * Based on:
29611  * Ext JS Library 1.1.1
29612  * Copyright(c) 2006-2007, Ext JS, LLC.
29613  *
29614  * Originally Released Under LGPL - original licence link has changed is not relivant.
29615  *
29616  * Fork - LGPL
29617  * <script type="text/javascript">
29618  */
29619
29620 /**
29621  * @class Roo.Editor
29622  * @extends Roo.Component
29623  * A base editor field that handles displaying/hiding on demand and has some built-in sizing and event handling logic.
29624  * @constructor
29625  * Create a new Editor
29626  * @param {Roo.form.Field} field The Field object (or descendant)
29627  * @param {Object} config The config object
29628  */
29629 Roo.Editor = function(field, config){
29630     Roo.Editor.superclass.constructor.call(this, config);
29631     this.field = field;
29632     this.addEvents({
29633         /**
29634              * @event beforestartedit
29635              * Fires when editing is initiated, but before the value changes.  Editing can be canceled by returning
29636              * false from the handler of this event.
29637              * @param {Editor} this
29638              * @param {Roo.Element} boundEl The underlying element bound to this editor
29639              * @param {Mixed} value The field value being set
29640              */
29641         "beforestartedit" : true,
29642         /**
29643              * @event startedit
29644              * Fires when this editor is displayed
29645              * @param {Roo.Element} boundEl The underlying element bound to this editor
29646              * @param {Mixed} value The starting field value
29647              */
29648         "startedit" : true,
29649         /**
29650              * @event beforecomplete
29651              * Fires after a change has been made to the field, but before the change is reflected in the underlying
29652              * field.  Saving the change to the field can be canceled by returning false from the handler of this event.
29653              * Note that if the value has not changed and ignoreNoChange = true, the editing will still end but this
29654              * event will not fire since no edit actually occurred.
29655              * @param {Editor} this
29656              * @param {Mixed} value The current field value
29657              * @param {Mixed} startValue The original field value
29658              */
29659         "beforecomplete" : true,
29660         /**
29661              * @event complete
29662              * Fires after editing is complete and any changed value has been written to the underlying field.
29663              * @param {Editor} this
29664              * @param {Mixed} value The current field value
29665              * @param {Mixed} startValue The original field value
29666              */
29667         "complete" : true,
29668         /**
29669          * @event specialkey
29670          * Fires when any key related to navigation (arrows, tab, enter, esc, etc.) is pressed.  You can check
29671          * {@link Roo.EventObject#getKey} to determine which key was pressed.
29672          * @param {Roo.form.Field} this
29673          * @param {Roo.EventObject} e The event object
29674          */
29675         "specialkey" : true
29676     });
29677 };
29678
29679 Roo.extend(Roo.Editor, Roo.Component, {
29680     /**
29681      * @cfg {Boolean/String} autosize
29682      * True for the editor to automatically adopt the size of the underlying field, "width" to adopt the width only,
29683      * or "height" to adopt the height only (defaults to false)
29684      */
29685     /**
29686      * @cfg {Boolean} revertInvalid
29687      * True to automatically revert the field value and cancel the edit when the user completes an edit and the field
29688      * validation fails (defaults to true)
29689      */
29690     /**
29691      * @cfg {Boolean} ignoreNoChange
29692      * True to skip the the edit completion process (no save, no events fired) if the user completes an edit and
29693      * the value has not changed (defaults to false).  Applies only to string values - edits for other data types
29694      * will never be ignored.
29695      */
29696     /**
29697      * @cfg {Boolean} hideEl
29698      * False to keep the bound element visible while the editor is displayed (defaults to true)
29699      */
29700     /**
29701      * @cfg {Mixed} value
29702      * The data value of the underlying field (defaults to "")
29703      */
29704     value : "",
29705     /**
29706      * @cfg {String} alignment
29707      * The position to align to (see {@link Roo.Element#alignTo} for more details, defaults to "c-c?").
29708      */
29709     alignment: "c-c?",
29710     /**
29711      * @cfg {Boolean/String} shadow "sides" for sides/bottom only, "frame" for 4-way shadow, and "drop"
29712      * for bottom-right shadow (defaults to "frame")
29713      */
29714     shadow : "frame",
29715     /**
29716      * @cfg {Boolean} constrain True to constrain the editor to the viewport
29717      */
29718     constrain : false,
29719     /**
29720      * @cfg {Boolean} completeOnEnter True to complete the edit when the enter key is pressed (defaults to false)
29721      */
29722     completeOnEnter : false,
29723     /**
29724      * @cfg {Boolean} cancelOnEsc True to cancel the edit when the escape key is pressed (defaults to false)
29725      */
29726     cancelOnEsc : false,
29727     /**
29728      * @cfg {Boolean} updateEl True to update the innerHTML of the bound element when the update completes (defaults to false)
29729      */
29730     updateEl : false,
29731
29732     // private
29733     onRender : function(ct, position){
29734         this.el = new Roo.Layer({
29735             shadow: this.shadow,
29736             cls: "x-editor",
29737             parentEl : ct,
29738             shim : this.shim,
29739             shadowOffset:4,
29740             id: this.id,
29741             constrain: this.constrain
29742         });
29743         this.el.setStyle("overflow", Roo.isGecko ? "auto" : "hidden");
29744         if(this.field.msgTarget != 'title'){
29745             this.field.msgTarget = 'qtip';
29746         }
29747         this.field.render(this.el);
29748         if(Roo.isGecko){
29749             this.field.el.dom.setAttribute('autocomplete', 'off');
29750         }
29751         this.field.on("specialkey", this.onSpecialKey, this);
29752         if(this.swallowKeys){
29753             this.field.el.swallowEvent(['keydown','keypress']);
29754         }
29755         this.field.show();
29756         this.field.on("blur", this.onBlur, this);
29757         if(this.field.grow){
29758             this.field.on("autosize", this.el.sync,  this.el, {delay:1});
29759         }
29760     },
29761
29762     onSpecialKey : function(field, e)
29763     {
29764         //Roo.log('editor onSpecialKey');
29765         if(this.completeOnEnter && e.getKey() == e.ENTER){
29766             e.stopEvent();
29767             this.completeEdit();
29768             return;
29769         }
29770         // do not fire special key otherwise it might hide close the editor...
29771         if(e.getKey() == e.ENTER){    
29772             return;
29773         }
29774         if(this.cancelOnEsc && e.getKey() == e.ESC){
29775             this.cancelEdit();
29776             return;
29777         } 
29778         this.fireEvent('specialkey', field, e);
29779     
29780     },
29781
29782     /**
29783      * Starts the editing process and shows the editor.
29784      * @param {String/HTMLElement/Element} el The element to edit
29785      * @param {String} value (optional) A value to initialize the editor with. If a value is not provided, it defaults
29786       * to the innerHTML of el.
29787      */
29788     startEdit : function(el, value){
29789         if(this.editing){
29790             this.completeEdit();
29791         }
29792         this.boundEl = Roo.get(el);
29793         var v = value !== undefined ? value : this.boundEl.dom.innerHTML;
29794         if(!this.rendered){
29795             this.render(this.parentEl || document.body);
29796         }
29797         if(this.fireEvent("beforestartedit", this, this.boundEl, v) === false){
29798             return;
29799         }
29800         this.startValue = v;
29801         this.field.setValue(v);
29802         if(this.autoSize){
29803             var sz = this.boundEl.getSize();
29804             switch(this.autoSize){
29805                 case "width":
29806                 this.setSize(sz.width,  "");
29807                 break;
29808                 case "height":
29809                 this.setSize("",  sz.height);
29810                 break;
29811                 default:
29812                 this.setSize(sz.width,  sz.height);
29813             }
29814         }
29815         this.el.alignTo(this.boundEl, this.alignment);
29816         this.editing = true;
29817         if(Roo.QuickTips){
29818             Roo.QuickTips.disable();
29819         }
29820         this.show();
29821     },
29822
29823     /**
29824      * Sets the height and width of this editor.
29825      * @param {Number} width The new width
29826      * @param {Number} height The new height
29827      */
29828     setSize : function(w, h){
29829         this.field.setSize(w, h);
29830         if(this.el){
29831             this.el.sync();
29832         }
29833     },
29834
29835     /**
29836      * Realigns the editor to the bound field based on the current alignment config value.
29837      */
29838     realign : function(){
29839         this.el.alignTo(this.boundEl, this.alignment);
29840     },
29841
29842     /**
29843      * Ends the editing process, persists the changed value to the underlying field, and hides the editor.
29844      * @param {Boolean} remainVisible Override the default behavior and keep the editor visible after edit (defaults to false)
29845      */
29846     completeEdit : function(remainVisible){
29847         if(!this.editing){
29848             return;
29849         }
29850         var v = this.getValue();
29851         if(this.revertInvalid !== false && !this.field.isValid()){
29852             v = this.startValue;
29853             this.cancelEdit(true);
29854         }
29855         if(String(v) === String(this.startValue) && this.ignoreNoChange){
29856             this.editing = false;
29857             this.hide();
29858             return;
29859         }
29860         if(this.fireEvent("beforecomplete", this, v, this.startValue) !== false){
29861             this.editing = false;
29862             if(this.updateEl && this.boundEl){
29863                 this.boundEl.update(v);
29864             }
29865             if(remainVisible !== true){
29866                 this.hide();
29867             }
29868             this.fireEvent("complete", this, v, this.startValue);
29869         }
29870     },
29871
29872     // private
29873     onShow : function(){
29874         this.el.show();
29875         if(this.hideEl !== false){
29876             this.boundEl.hide();
29877         }
29878         this.field.show();
29879         if(Roo.isIE && !this.fixIEFocus){ // IE has problems with focusing the first time
29880             this.fixIEFocus = true;
29881             this.deferredFocus.defer(50, this);
29882         }else{
29883             this.field.focus();
29884         }
29885         this.fireEvent("startedit", this.boundEl, this.startValue);
29886     },
29887
29888     deferredFocus : function(){
29889         if(this.editing){
29890             this.field.focus();
29891         }
29892     },
29893
29894     /**
29895      * Cancels the editing process and hides the editor without persisting any changes.  The field value will be
29896      * reverted to the original starting value.
29897      * @param {Boolean} remainVisible Override the default behavior and keep the editor visible after
29898      * cancel (defaults to false)
29899      */
29900     cancelEdit : function(remainVisible){
29901         if(this.editing){
29902             this.setValue(this.startValue);
29903             if(remainVisible !== true){
29904                 this.hide();
29905             }
29906         }
29907     },
29908
29909     // private
29910     onBlur : function(){
29911         if(this.allowBlur !== true && this.editing){
29912             this.completeEdit();
29913         }
29914     },
29915
29916     // private
29917     onHide : function(){
29918         if(this.editing){
29919             this.completeEdit();
29920             return;
29921         }
29922         this.field.blur();
29923         if(this.field.collapse){
29924             this.field.collapse();
29925         }
29926         this.el.hide();
29927         if(this.hideEl !== false){
29928             this.boundEl.show();
29929         }
29930         if(Roo.QuickTips){
29931             Roo.QuickTips.enable();
29932         }
29933     },
29934
29935     /**
29936      * Sets the data value of the editor
29937      * @param {Mixed} value Any valid value supported by the underlying field
29938      */
29939     setValue : function(v){
29940         this.field.setValue(v);
29941     },
29942
29943     /**
29944      * Gets the data value of the editor
29945      * @return {Mixed} The data value
29946      */
29947     getValue : function(){
29948         return this.field.getValue();
29949     }
29950 });/*
29951  * Based on:
29952  * Ext JS Library 1.1.1
29953  * Copyright(c) 2006-2007, Ext JS, LLC.
29954  *
29955  * Originally Released Under LGPL - original licence link has changed is not relivant.
29956  *
29957  * Fork - LGPL
29958  * <script type="text/javascript">
29959  */
29960  
29961 /**
29962  * @class Roo.BasicDialog
29963  * @extends Roo.util.Observable
29964  * Lightweight Dialog Class.  The code below shows the creation of a typical dialog using existing HTML markup:
29965  * <pre><code>
29966 var dlg = new Roo.BasicDialog("my-dlg", {
29967     height: 200,
29968     width: 300,
29969     minHeight: 100,
29970     minWidth: 150,
29971     modal: true,
29972     proxyDrag: true,
29973     shadow: true
29974 });
29975 dlg.addKeyListener(27, dlg.hide, dlg); // ESC can also close the dialog
29976 dlg.addButton('OK', dlg.hide, dlg);    // Could call a save function instead of hiding
29977 dlg.addButton('Cancel', dlg.hide, dlg);
29978 dlg.show();
29979 </code></pre>
29980   <b>A Dialog should always be a direct child of the body element.</b>
29981  * @cfg {Boolean/DomHelper} autoCreate True to auto create from scratch, or using a DomHelper Object (defaults to false)
29982  * @cfg {String} title Default text to display in the title bar (defaults to null)
29983  * @cfg {Number} width Width of the dialog in pixels (can also be set via CSS).  Determined by browser if unspecified.
29984  * @cfg {Number} height Height of the dialog in pixels (can also be set via CSS).  Determined by browser if unspecified.
29985  * @cfg {Number} x The default left page coordinate of the dialog (defaults to center screen)
29986  * @cfg {Number} y The default top page coordinate of the dialog (defaults to center screen)
29987  * @cfg {String/Element} animateTarget Id or element from which the dialog should animate while opening
29988  * (defaults to null with no animation)
29989  * @cfg {Boolean} resizable False to disable manual dialog resizing (defaults to true)
29990  * @cfg {String} resizeHandles Which resize handles to display - see the {@link Roo.Resizable} handles config
29991  * property for valid values (defaults to 'all')
29992  * @cfg {Number} minHeight The minimum allowable height for a resizable dialog (defaults to 80)
29993  * @cfg {Number} minWidth The minimum allowable width for a resizable dialog (defaults to 200)
29994  * @cfg {Boolean} modal True to show the dialog modally, preventing user interaction with the rest of the page (defaults to false)
29995  * @cfg {Boolean} autoScroll True to allow the dialog body contents to overflow and display scrollbars (defaults to false)
29996  * @cfg {Boolean} closable False to remove the built-in top-right corner close button (defaults to true)
29997  * @cfg {Boolean} collapsible False to remove the built-in top-right corner collapse button (defaults to true)
29998  * @cfg {Boolean} constraintoviewport True to keep the dialog constrained within the visible viewport boundaries (defaults to true)
29999  * @cfg {Boolean} syncHeightBeforeShow True to cause the dimensions to be recalculated before the dialog is shown (defaults to false)
30000  * @cfg {Boolean} draggable False to disable dragging of the dialog within the viewport (defaults to true)
30001  * @cfg {Boolean} autoTabs If true, all elements with class 'x-dlg-tab' will get automatically converted to tabs (defaults to false)
30002  * @cfg {String} tabTag The tag name of tab elements, used when autoTabs = true (defaults to 'div')
30003  * @cfg {Boolean} proxyDrag True to drag a lightweight proxy element rather than the dialog itself, used when
30004  * draggable = true (defaults to false)
30005  * @cfg {Boolean} fixedcenter True to ensure that anytime the dialog is shown or resized it gets centered (defaults to false)
30006  * @cfg {Boolean/String} shadow True or "sides" for the default effect, "frame" for 4-way shadow, and "drop" for bottom-right
30007  * shadow (defaults to false)
30008  * @cfg {Number} shadowOffset The number of pixels to offset the shadow if displayed (defaults to 5)
30009  * @cfg {String} buttonAlign Valid values are "left," "center" and "right" (defaults to "right")
30010  * @cfg {Number} minButtonWidth Minimum width of all dialog buttons (defaults to 75)
30011  * @cfg {Array} buttons Array of buttons
30012  * @cfg {Boolean} shim True to create an iframe shim that prevents selects from showing through (defaults to false)
30013  * @constructor
30014  * Create a new BasicDialog.
30015  * @param {String/HTMLElement/Roo.Element} el The container element or DOM node, or its id
30016  * @param {Object} config Configuration options
30017  */
30018 Roo.BasicDialog = function(el, config){
30019     this.el = Roo.get(el);
30020     var dh = Roo.DomHelper;
30021     if(!this.el && config && config.autoCreate){
30022         if(typeof config.autoCreate == "object"){
30023             if(!config.autoCreate.id){
30024                 config.autoCreate.id = el;
30025             }
30026             this.el = dh.append(document.body,
30027                         config.autoCreate, true);
30028         }else{
30029             this.el = dh.append(document.body,
30030                         {tag: "div", id: el, style:'visibility:hidden;'}, true);
30031         }
30032     }
30033     el = this.el;
30034     el.setDisplayed(true);
30035     el.hide = this.hideAction;
30036     this.id = el.id;
30037     el.addClass("x-dlg");
30038
30039     Roo.apply(this, config);
30040
30041     this.proxy = el.createProxy("x-dlg-proxy");
30042     this.proxy.hide = this.hideAction;
30043     this.proxy.setOpacity(.5);
30044     this.proxy.hide();
30045
30046     if(config.width){
30047         el.setWidth(config.width);
30048     }
30049     if(config.height){
30050         el.setHeight(config.height);
30051     }
30052     this.size = el.getSize();
30053     if(typeof config.x != "undefined" && typeof config.y != "undefined"){
30054         this.xy = [config.x,config.y];
30055     }else{
30056         this.xy = el.getCenterXY(true);
30057     }
30058     /** The header element @type Roo.Element */
30059     this.header = el.child("> .x-dlg-hd");
30060     /** The body element @type Roo.Element */
30061     this.body = el.child("> .x-dlg-bd");
30062     /** The footer element @type Roo.Element */
30063     this.footer = el.child("> .x-dlg-ft");
30064
30065     if(!this.header){
30066         this.header = el.createChild({tag: "div", cls:"x-dlg-hd", html: "&#160;"}, this.body ? this.body.dom : null);
30067     }
30068     if(!this.body){
30069         this.body = el.createChild({tag: "div", cls:"x-dlg-bd"});
30070     }
30071
30072     this.header.unselectable();
30073     if(this.title){
30074         this.header.update(this.title);
30075     }
30076     // this element allows the dialog to be focused for keyboard event
30077     this.focusEl = el.createChild({tag: "a", href:"#", cls:"x-dlg-focus", tabIndex:"-1"});
30078     this.focusEl.swallowEvent("click", true);
30079
30080     this.header.wrap({cls:"x-dlg-hd-right"}).wrap({cls:"x-dlg-hd-left"}, true);
30081
30082     // wrap the body and footer for special rendering
30083     this.bwrap = this.body.wrap({tag: "div", cls:"x-dlg-dlg-body"});
30084     if(this.footer){
30085         this.bwrap.dom.appendChild(this.footer.dom);
30086     }
30087
30088     this.bg = this.el.createChild({
30089         tag: "div", cls:"x-dlg-bg",
30090         html: '<div class="x-dlg-bg-left"><div class="x-dlg-bg-right"><div class="x-dlg-bg-center">&#160;</div></div></div>'
30091     });
30092     this.centerBg = this.bg.child("div.x-dlg-bg-center");
30093
30094
30095     if(this.autoScroll !== false && !this.autoTabs){
30096         this.body.setStyle("overflow", "auto");
30097     }
30098
30099     this.toolbox = this.el.createChild({cls: "x-dlg-toolbox"});
30100
30101     if(this.closable !== false){
30102         this.el.addClass("x-dlg-closable");
30103         this.close = this.toolbox.createChild({cls:"x-dlg-close"});
30104         this.close.on("click", this.closeClick, this);
30105         this.close.addClassOnOver("x-dlg-close-over");
30106     }
30107     if(this.collapsible !== false){
30108         this.collapseBtn = this.toolbox.createChild({cls:"x-dlg-collapse"});
30109         this.collapseBtn.on("click", this.collapseClick, this);
30110         this.collapseBtn.addClassOnOver("x-dlg-collapse-over");
30111         this.header.on("dblclick", this.collapseClick, this);
30112     }
30113     if(this.resizable !== false){
30114         this.el.addClass("x-dlg-resizable");
30115         this.resizer = new Roo.Resizable(el, {
30116             minWidth: this.minWidth || 80,
30117             minHeight:this.minHeight || 80,
30118             handles: this.resizeHandles || "all",
30119             pinned: true
30120         });
30121         this.resizer.on("beforeresize", this.beforeResize, this);
30122         this.resizer.on("resize", this.onResize, this);
30123     }
30124     if(this.draggable !== false){
30125         el.addClass("x-dlg-draggable");
30126         if (!this.proxyDrag) {
30127             var dd = new Roo.dd.DD(el.dom.id, "WindowDrag");
30128         }
30129         else {
30130             var dd = new Roo.dd.DDProxy(el.dom.id, "WindowDrag", {dragElId: this.proxy.id});
30131         }
30132         dd.setHandleElId(this.header.id);
30133         dd.endDrag = this.endMove.createDelegate(this);
30134         dd.startDrag = this.startMove.createDelegate(this);
30135         dd.onDrag = this.onDrag.createDelegate(this);
30136         dd.scroll = false;
30137         this.dd = dd;
30138     }
30139     if(this.modal){
30140         this.mask = dh.append(document.body, {tag: "div", cls:"x-dlg-mask"}, true);
30141         this.mask.enableDisplayMode("block");
30142         this.mask.hide();
30143         this.el.addClass("x-dlg-modal");
30144     }
30145     if(this.shadow){
30146         this.shadow = new Roo.Shadow({
30147             mode : typeof this.shadow == "string" ? this.shadow : "sides",
30148             offset : this.shadowOffset
30149         });
30150     }else{
30151         this.shadowOffset = 0;
30152     }
30153     if(Roo.useShims && this.shim !== false){
30154         this.shim = this.el.createShim();
30155         this.shim.hide = this.hideAction;
30156         this.shim.hide();
30157     }else{
30158         this.shim = false;
30159     }
30160     if(this.autoTabs){
30161         this.initTabs();
30162     }
30163     if (this.buttons) { 
30164         var bts= this.buttons;
30165         this.buttons = [];
30166         Roo.each(bts, function(b) {
30167             this.addButton(b);
30168         }, this);
30169     }
30170     
30171     
30172     this.addEvents({
30173         /**
30174          * @event keydown
30175          * Fires when a key is pressed
30176          * @param {Roo.BasicDialog} this
30177          * @param {Roo.EventObject} e
30178          */
30179         "keydown" : true,
30180         /**
30181          * @event move
30182          * Fires when this dialog is moved by the user.
30183          * @param {Roo.BasicDialog} this
30184          * @param {Number} x The new page X
30185          * @param {Number} y The new page Y
30186          */
30187         "move" : true,
30188         /**
30189          * @event resize
30190          * Fires when this dialog is resized by the user.
30191          * @param {Roo.BasicDialog} this
30192          * @param {Number} width The new width
30193          * @param {Number} height The new height
30194          */
30195         "resize" : true,
30196         /**
30197          * @event beforehide
30198          * Fires before this dialog is hidden.
30199          * @param {Roo.BasicDialog} this
30200          */
30201         "beforehide" : true,
30202         /**
30203          * @event hide
30204          * Fires when this dialog is hidden.
30205          * @param {Roo.BasicDialog} this
30206          */
30207         "hide" : true,
30208         /**
30209          * @event beforeshow
30210          * Fires before this dialog is shown.
30211          * @param {Roo.BasicDialog} this
30212          */
30213         "beforeshow" : true,
30214         /**
30215          * @event show
30216          * Fires when this dialog is shown.
30217          * @param {Roo.BasicDialog} this
30218          */
30219         "show" : true
30220     });
30221     el.on("keydown", this.onKeyDown, this);
30222     el.on("mousedown", this.toFront, this);
30223     Roo.EventManager.onWindowResize(this.adjustViewport, this, true);
30224     this.el.hide();
30225     Roo.DialogManager.register(this);
30226     Roo.BasicDialog.superclass.constructor.call(this);
30227 };
30228
30229 Roo.extend(Roo.BasicDialog, Roo.util.Observable, {
30230     shadowOffset: Roo.isIE ? 6 : 5,
30231     minHeight: 80,
30232     minWidth: 200,
30233     minButtonWidth: 75,
30234     defaultButton: null,
30235     buttonAlign: "right",
30236     tabTag: 'div',
30237     firstShow: true,
30238
30239     /**
30240      * Sets the dialog title text
30241      * @param {String} text The title text to display
30242      * @return {Roo.BasicDialog} this
30243      */
30244     setTitle : function(text){
30245         this.header.update(text);
30246         return this;
30247     },
30248
30249     // private
30250     closeClick : function(){
30251         this.hide();
30252     },
30253
30254     // private
30255     collapseClick : function(){
30256         this[this.collapsed ? "expand" : "collapse"]();
30257     },
30258
30259     /**
30260      * Collapses the dialog to its minimized state (only the title bar is visible).
30261      * Equivalent to the user clicking the collapse dialog button.
30262      */
30263     collapse : function(){
30264         if(!this.collapsed){
30265             this.collapsed = true;
30266             this.el.addClass("x-dlg-collapsed");
30267             this.restoreHeight = this.el.getHeight();
30268             this.resizeTo(this.el.getWidth(), this.header.getHeight());
30269         }
30270     },
30271
30272     /**
30273      * Expands a collapsed dialog back to its normal state.  Equivalent to the user
30274      * clicking the expand dialog button.
30275      */
30276     expand : function(){
30277         if(this.collapsed){
30278             this.collapsed = false;
30279             this.el.removeClass("x-dlg-collapsed");
30280             this.resizeTo(this.el.getWidth(), this.restoreHeight);
30281         }
30282     },
30283
30284     /**
30285      * Reinitializes the tabs component, clearing out old tabs and finding new ones.
30286      * @return {Roo.TabPanel} The tabs component
30287      */
30288     initTabs : function(){
30289         var tabs = this.getTabs();
30290         while(tabs.getTab(0)){
30291             tabs.removeTab(0);
30292         }
30293         this.el.select(this.tabTag+'.x-dlg-tab').each(function(el){
30294             var dom = el.dom;
30295             tabs.addTab(Roo.id(dom), dom.title);
30296             dom.title = "";
30297         });
30298         tabs.activate(0);
30299         return tabs;
30300     },
30301
30302     // private
30303     beforeResize : function(){
30304         this.resizer.minHeight = Math.max(this.minHeight, this.getHeaderFooterHeight(true)+40);
30305     },
30306
30307     // private
30308     onResize : function(){
30309         this.refreshSize();
30310         this.syncBodyHeight();
30311         this.adjustAssets();
30312         this.focus();
30313         this.fireEvent("resize", this, this.size.width, this.size.height);
30314     },
30315
30316     // private
30317     onKeyDown : function(e){
30318         if(this.isVisible()){
30319             this.fireEvent("keydown", this, e);
30320         }
30321     },
30322
30323     /**
30324      * Resizes the dialog.
30325      * @param {Number} width
30326      * @param {Number} height
30327      * @return {Roo.BasicDialog} this
30328      */
30329     resizeTo : function(width, height){
30330         this.el.setSize(width, height);
30331         this.size = {width: width, height: height};
30332         this.syncBodyHeight();
30333         if(this.fixedcenter){
30334             this.center();
30335         }
30336         if(this.isVisible()){
30337             this.constrainXY();
30338             this.adjustAssets();
30339         }
30340         this.fireEvent("resize", this, width, height);
30341         return this;
30342     },
30343
30344
30345     /**
30346      * Resizes the dialog to fit the specified content size.
30347      * @param {Number} width
30348      * @param {Number} height
30349      * @return {Roo.BasicDialog} this
30350      */
30351     setContentSize : function(w, h){
30352         h += this.getHeaderFooterHeight() + this.body.getMargins("tb");
30353         w += this.body.getMargins("lr") + this.bwrap.getMargins("lr") + this.centerBg.getPadding("lr");
30354         //if(!this.el.isBorderBox()){
30355             h +=  this.body.getPadding("tb") + this.bwrap.getBorderWidth("tb") + this.body.getBorderWidth("tb") + this.el.getBorderWidth("tb");
30356             w += this.body.getPadding("lr") + this.bwrap.getBorderWidth("lr") + this.body.getBorderWidth("lr") + this.bwrap.getPadding("lr") + this.el.getBorderWidth("lr");
30357         //}
30358         if(this.tabs){
30359             h += this.tabs.stripWrap.getHeight() + this.tabs.bodyEl.getMargins("tb") + this.tabs.bodyEl.getPadding("tb");
30360             w += this.tabs.bodyEl.getMargins("lr") + this.tabs.bodyEl.getPadding("lr");
30361         }
30362         this.resizeTo(w, h);
30363         return this;
30364     },
30365
30366     /**
30367      * Adds a key listener for when this dialog is displayed.  This allows you to hook in a function that will be
30368      * executed in response to a particular key being pressed while the dialog is active.
30369      * @param {Number/Array/Object} key Either the numeric key code, array of key codes or an object with the following options:
30370      *                                  {key: (number or array), shift: (true/false), ctrl: (true/false), alt: (true/false)}
30371      * @param {Function} fn The function to call
30372      * @param {Object} scope (optional) The scope of the function
30373      * @return {Roo.BasicDialog} this
30374      */
30375     addKeyListener : function(key, fn, scope){
30376         var keyCode, shift, ctrl, alt;
30377         if(typeof key == "object" && !(key instanceof Array)){
30378             keyCode = key["key"];
30379             shift = key["shift"];
30380             ctrl = key["ctrl"];
30381             alt = key["alt"];
30382         }else{
30383             keyCode = key;
30384         }
30385         var handler = function(dlg, e){
30386             if((!shift || e.shiftKey) && (!ctrl || e.ctrlKey) &&  (!alt || e.altKey)){
30387                 var k = e.getKey();
30388                 if(keyCode instanceof Array){
30389                     for(var i = 0, len = keyCode.length; i < len; i++){
30390                         if(keyCode[i] == k){
30391                           fn.call(scope || window, dlg, k, e);
30392                           return;
30393                         }
30394                     }
30395                 }else{
30396                     if(k == keyCode){
30397                         fn.call(scope || window, dlg, k, e);
30398                     }
30399                 }
30400             }
30401         };
30402         this.on("keydown", handler);
30403         return this;
30404     },
30405
30406     /**
30407      * Returns the TabPanel component (creates it if it doesn't exist).
30408      * Note: If you wish to simply check for the existence of tabs without creating them,
30409      * check for a null 'tabs' property.
30410      * @return {Roo.TabPanel} The tabs component
30411      */
30412     getTabs : function(){
30413         if(!this.tabs){
30414             this.el.addClass("x-dlg-auto-tabs");
30415             this.body.addClass(this.tabPosition == "bottom" ? "x-tabs-bottom" : "x-tabs-top");
30416             this.tabs = new Roo.TabPanel(this.body.dom, this.tabPosition == "bottom");
30417         }
30418         return this.tabs;
30419     },
30420
30421     /**
30422      * Adds a button to the footer section of the dialog.
30423      * @param {String/Object} config A string becomes the button text, an object can either be a Button config
30424      * object or a valid Roo.DomHelper element config
30425      * @param {Function} handler The function called when the button is clicked
30426      * @param {Object} scope (optional) The scope of the handler function (accepts position as a property)
30427      * @return {Roo.Button} The new button
30428      */
30429     addButton : function(config, handler, scope){
30430         var dh = Roo.DomHelper;
30431         if(!this.footer){
30432             this.footer = dh.append(this.bwrap, {tag: "div", cls:"x-dlg-ft"}, true);
30433         }
30434         if(!this.btnContainer){
30435             var tb = this.footer.createChild({
30436
30437                 cls:"x-dlg-btns x-dlg-btns-"+this.buttonAlign,
30438                 html:'<table cellspacing="0"><tbody><tr></tr></tbody></table><div class="x-clear"></div>'
30439             }, null, true);
30440             this.btnContainer = tb.firstChild.firstChild.firstChild;
30441         }
30442         var bconfig = {
30443             handler: handler,
30444             scope: scope,
30445             minWidth: this.minButtonWidth,
30446             hideParent:true
30447         };
30448         if(typeof config == "string"){
30449             bconfig.text = config;
30450         }else{
30451             if(config.tag){
30452                 bconfig.dhconfig = config;
30453             }else{
30454                 Roo.apply(bconfig, config);
30455             }
30456         }
30457         var fc = false;
30458         if ((typeof(bconfig.position) != 'undefined') && bconfig.position < this.btnContainer.childNodes.length-1) {
30459             bconfig.position = Math.max(0, bconfig.position);
30460             fc = this.btnContainer.childNodes[bconfig.position];
30461         }
30462          
30463         var btn = new Roo.Button(
30464             fc ? 
30465                 this.btnContainer.insertBefore(document.createElement("td"),fc)
30466                 : this.btnContainer.appendChild(document.createElement("td")),
30467             //Roo.get(this.btnContainer).createChild( { tag: 'td'},  fc ),
30468             bconfig
30469         );
30470         this.syncBodyHeight();
30471         if(!this.buttons){
30472             /**
30473              * Array of all the buttons that have been added to this dialog via addButton
30474              * @type Array
30475              */
30476             this.buttons = [];
30477         }
30478         this.buttons.push(btn);
30479         return btn;
30480     },
30481
30482     /**
30483      * Sets the default button to be focused when the dialog is displayed.
30484      * @param {Roo.BasicDialog.Button} btn The button object returned by {@link #addButton}
30485      * @return {Roo.BasicDialog} this
30486      */
30487     setDefaultButton : function(btn){
30488         this.defaultButton = btn;
30489         return this;
30490     },
30491
30492     // private
30493     getHeaderFooterHeight : function(safe){
30494         var height = 0;
30495         if(this.header){
30496            height += this.header.getHeight();
30497         }
30498         if(this.footer){
30499            var fm = this.footer.getMargins();
30500             height += (this.footer.getHeight()+fm.top+fm.bottom);
30501         }
30502         height += this.bwrap.getPadding("tb")+this.bwrap.getBorderWidth("tb");
30503         height += this.centerBg.getPadding("tb");
30504         return height;
30505     },
30506
30507     // private
30508     syncBodyHeight : function()
30509     {
30510         var bd = this.body, // the text
30511             cb = this.centerBg, // wrapper around bottom.. but does not seem to be used..
30512             bw = this.bwrap;
30513         var height = this.size.height - this.getHeaderFooterHeight(false);
30514         bd.setHeight(height-bd.getMargins("tb"));
30515         var hh = this.header.getHeight();
30516         var h = this.size.height-hh;
30517         cb.setHeight(h);
30518         
30519         bw.setLeftTop(cb.getPadding("l"), hh+cb.getPadding("t"));
30520         bw.setHeight(h-cb.getPadding("tb"));
30521         
30522         bw.setWidth(this.el.getWidth(true)-cb.getPadding("lr"));
30523         bd.setWidth(bw.getWidth(true));
30524         if(this.tabs){
30525             this.tabs.syncHeight();
30526             if(Roo.isIE){
30527                 this.tabs.el.repaint();
30528             }
30529         }
30530     },
30531
30532     /**
30533      * Restores the previous state of the dialog if Roo.state is configured.
30534      * @return {Roo.BasicDialog} this
30535      */
30536     restoreState : function(){
30537         var box = Roo.state.Manager.get(this.stateId || (this.el.id + "-state"));
30538         if(box && box.width){
30539             this.xy = [box.x, box.y];
30540             this.resizeTo(box.width, box.height);
30541         }
30542         return this;
30543     },
30544
30545     // private
30546     beforeShow : function(){
30547         this.expand();
30548         if(this.fixedcenter){
30549             this.xy = this.el.getCenterXY(true);
30550         }
30551         if(this.modal){
30552             Roo.get(document.body).addClass("x-body-masked");
30553             this.mask.setSize(Roo.lib.Dom.getViewWidth(true), Roo.lib.Dom.getViewHeight(true));
30554             this.mask.show();
30555         }
30556         this.constrainXY();
30557     },
30558
30559     // private
30560     animShow : function(){
30561         var b = Roo.get(this.animateTarget).getBox();
30562         this.proxy.setSize(b.width, b.height);
30563         this.proxy.setLocation(b.x, b.y);
30564         this.proxy.show();
30565         this.proxy.setBounds(this.xy[0], this.xy[1], this.size.width, this.size.height,
30566                     true, .35, this.showEl.createDelegate(this));
30567     },
30568
30569     /**
30570      * Shows the dialog.
30571      * @param {String/HTMLElement/Roo.Element} animateTarget (optional) Reset the animation target
30572      * @return {Roo.BasicDialog} this
30573      */
30574     show : function(animateTarget){
30575         if (this.fireEvent("beforeshow", this) === false){
30576             return;
30577         }
30578         if(this.syncHeightBeforeShow){
30579             this.syncBodyHeight();
30580         }else if(this.firstShow){
30581             this.firstShow = false;
30582             this.syncBodyHeight(); // sync the height on the first show instead of in the constructor
30583         }
30584         this.animateTarget = animateTarget || this.animateTarget;
30585         if(!this.el.isVisible()){
30586             this.beforeShow();
30587             if(this.animateTarget && Roo.get(this.animateTarget)){
30588                 this.animShow();
30589             }else{
30590                 this.showEl();
30591             }
30592         }
30593         return this;
30594     },
30595
30596     // private
30597     showEl : function(){
30598         this.proxy.hide();
30599         this.el.setXY(this.xy);
30600         this.el.show();
30601         this.adjustAssets(true);
30602         this.toFront();
30603         this.focus();
30604         // IE peekaboo bug - fix found by Dave Fenwick
30605         if(Roo.isIE){
30606             this.el.repaint();
30607         }
30608         this.fireEvent("show", this);
30609     },
30610
30611     /**
30612      * Focuses the dialog.  If a defaultButton is set, it will receive focus, otherwise the
30613      * dialog itself will receive focus.
30614      */
30615     focus : function(){
30616         if(this.defaultButton){
30617             this.defaultButton.focus();
30618         }else{
30619             this.focusEl.focus();
30620         }
30621     },
30622
30623     // private
30624     constrainXY : function(){
30625         if(this.constraintoviewport !== false){
30626             if(!this.viewSize){
30627                 if(this.container){
30628                     var s = this.container.getSize();
30629                     this.viewSize = [s.width, s.height];
30630                 }else{
30631                     this.viewSize = [Roo.lib.Dom.getViewWidth(),Roo.lib.Dom.getViewHeight()];
30632                 }
30633             }
30634             var s = Roo.get(this.container||document).getScroll();
30635
30636             var x = this.xy[0], y = this.xy[1];
30637             var w = this.size.width, h = this.size.height;
30638             var vw = this.viewSize[0], vh = this.viewSize[1];
30639             // only move it if it needs it
30640             var moved = false;
30641             // first validate right/bottom
30642             if(x + w > vw+s.left){
30643                 x = vw - w;
30644                 moved = true;
30645             }
30646             if(y + h > vh+s.top){
30647                 y = vh - h;
30648                 moved = true;
30649             }
30650             // then make sure top/left isn't negative
30651             if(x < s.left){
30652                 x = s.left;
30653                 moved = true;
30654             }
30655             if(y < s.top){
30656                 y = s.top;
30657                 moved = true;
30658             }
30659             if(moved){
30660                 // cache xy
30661                 this.xy = [x, y];
30662                 if(this.isVisible()){
30663                     this.el.setLocation(x, y);
30664                     this.adjustAssets();
30665                 }
30666             }
30667         }
30668     },
30669
30670     // private
30671     onDrag : function(){
30672         if(!this.proxyDrag){
30673             this.xy = this.el.getXY();
30674             this.adjustAssets();
30675         }
30676     },
30677
30678     // private
30679     adjustAssets : function(doShow){
30680         var x = this.xy[0], y = this.xy[1];
30681         var w = this.size.width, h = this.size.height;
30682         if(doShow === true){
30683             if(this.shadow){
30684                 this.shadow.show(this.el);
30685             }
30686             if(this.shim){
30687                 this.shim.show();
30688             }
30689         }
30690         if(this.shadow && this.shadow.isVisible()){
30691             this.shadow.show(this.el);
30692         }
30693         if(this.shim && this.shim.isVisible()){
30694             this.shim.setBounds(x, y, w, h);
30695         }
30696     },
30697
30698     // private
30699     adjustViewport : function(w, h){
30700         if(!w || !h){
30701             w = Roo.lib.Dom.getViewWidth();
30702             h = Roo.lib.Dom.getViewHeight();
30703         }
30704         // cache the size
30705         this.viewSize = [w, h];
30706         if(this.modal && this.mask.isVisible()){
30707             this.mask.setSize(w, h); // first make sure the mask isn't causing overflow
30708             this.mask.setSize(Roo.lib.Dom.getViewWidth(true), Roo.lib.Dom.getViewHeight(true));
30709         }
30710         if(this.isVisible()){
30711             this.constrainXY();
30712         }
30713     },
30714
30715     /**
30716      * Destroys this dialog and all its supporting elements (including any tabs, shim,
30717      * shadow, proxy, mask, etc.)  Also removes all event listeners.
30718      * @param {Boolean} removeEl (optional) true to remove the element from the DOM
30719      */
30720     destroy : function(removeEl){
30721         if(this.isVisible()){
30722             this.animateTarget = null;
30723             this.hide();
30724         }
30725         Roo.EventManager.removeResizeListener(this.adjustViewport, this);
30726         if(this.tabs){
30727             this.tabs.destroy(removeEl);
30728         }
30729         Roo.destroy(
30730              this.shim,
30731              this.proxy,
30732              this.resizer,
30733              this.close,
30734              this.mask
30735         );
30736         if(this.dd){
30737             this.dd.unreg();
30738         }
30739         if(this.buttons){
30740            for(var i = 0, len = this.buttons.length; i < len; i++){
30741                this.buttons[i].destroy();
30742            }
30743         }
30744         this.el.removeAllListeners();
30745         if(removeEl === true){
30746             this.el.update("");
30747             this.el.remove();
30748         }
30749         Roo.DialogManager.unregister(this);
30750     },
30751
30752     // private
30753     startMove : function(){
30754         if(this.proxyDrag){
30755             this.proxy.show();
30756         }
30757         if(this.constraintoviewport !== false){
30758             this.dd.constrainTo(document.body, {right: this.shadowOffset, bottom: this.shadowOffset});
30759         }
30760     },
30761
30762     // private
30763     endMove : function(){
30764         if(!this.proxyDrag){
30765             Roo.dd.DD.prototype.endDrag.apply(this.dd, arguments);
30766         }else{
30767             Roo.dd.DDProxy.prototype.endDrag.apply(this.dd, arguments);
30768             this.proxy.hide();
30769         }
30770         this.refreshSize();
30771         this.adjustAssets();
30772         this.focus();
30773         this.fireEvent("move", this, this.xy[0], this.xy[1]);
30774     },
30775
30776     /**
30777      * Brings this dialog to the front of any other visible dialogs
30778      * @return {Roo.BasicDialog} this
30779      */
30780     toFront : function(){
30781         Roo.DialogManager.bringToFront(this);
30782         return this;
30783     },
30784
30785     /**
30786      * Sends this dialog to the back (under) of any other visible dialogs
30787      * @return {Roo.BasicDialog} this
30788      */
30789     toBack : function(){
30790         Roo.DialogManager.sendToBack(this);
30791         return this;
30792     },
30793
30794     /**
30795      * Centers this dialog in the viewport
30796      * @return {Roo.BasicDialog} this
30797      */
30798     center : function(){
30799         var xy = this.el.getCenterXY(true);
30800         this.moveTo(xy[0], xy[1]);
30801         return this;
30802     },
30803
30804     /**
30805      * Moves the dialog's top-left corner to the specified point
30806      * @param {Number} x
30807      * @param {Number} y
30808      * @return {Roo.BasicDialog} this
30809      */
30810     moveTo : function(x, y){
30811         this.xy = [x,y];
30812         if(this.isVisible()){
30813             this.el.setXY(this.xy);
30814             this.adjustAssets();
30815         }
30816         return this;
30817     },
30818
30819     /**
30820      * Aligns the dialog to the specified element
30821      * @param {String/HTMLElement/Roo.Element} element The element to align to.
30822      * @param {String} position The position to align to (see {@link Roo.Element#alignTo} for more details).
30823      * @param {Array} offsets (optional) Offset the positioning by [x, y]
30824      * @return {Roo.BasicDialog} this
30825      */
30826     alignTo : function(element, position, offsets){
30827         this.xy = this.el.getAlignToXY(element, position, offsets);
30828         if(this.isVisible()){
30829             this.el.setXY(this.xy);
30830             this.adjustAssets();
30831         }
30832         return this;
30833     },
30834
30835     /**
30836      * Anchors an element to another element and realigns it when the window is resized.
30837      * @param {String/HTMLElement/Roo.Element} element The element to align to.
30838      * @param {String} position The position to align to (see {@link Roo.Element#alignTo} for more details)
30839      * @param {Array} offsets (optional) Offset the positioning by [x, y]
30840      * @param {Boolean/Number} monitorScroll (optional) true to monitor body scroll and reposition. If this parameter
30841      * is a number, it is used as the buffer delay (defaults to 50ms).
30842      * @return {Roo.BasicDialog} this
30843      */
30844     anchorTo : function(el, alignment, offsets, monitorScroll){
30845         var action = function(){
30846             this.alignTo(el, alignment, offsets);
30847         };
30848         Roo.EventManager.onWindowResize(action, this);
30849         var tm = typeof monitorScroll;
30850         if(tm != 'undefined'){
30851             Roo.EventManager.on(window, 'scroll', action, this,
30852                 {buffer: tm == 'number' ? monitorScroll : 50});
30853         }
30854         action.call(this);
30855         return this;
30856     },
30857
30858     /**
30859      * Returns true if the dialog is visible
30860      * @return {Boolean}
30861      */
30862     isVisible : function(){
30863         return this.el.isVisible();
30864     },
30865
30866     // private
30867     animHide : function(callback){
30868         var b = Roo.get(this.animateTarget).getBox();
30869         this.proxy.show();
30870         this.proxy.setBounds(this.xy[0], this.xy[1], this.size.width, this.size.height);
30871         this.el.hide();
30872         this.proxy.setBounds(b.x, b.y, b.width, b.height, true, .35,
30873                     this.hideEl.createDelegate(this, [callback]));
30874     },
30875
30876     /**
30877      * Hides the dialog.
30878      * @param {Function} callback (optional) Function to call when the dialog is hidden
30879      * @return {Roo.BasicDialog} this
30880      */
30881     hide : function(callback){
30882         if (this.fireEvent("beforehide", this) === false){
30883             return;
30884         }
30885         if(this.shadow){
30886             this.shadow.hide();
30887         }
30888         if(this.shim) {
30889           this.shim.hide();
30890         }
30891         // sometimes animateTarget seems to get set.. causing problems...
30892         // this just double checks..
30893         if(this.animateTarget && Roo.get(this.animateTarget)) {
30894            this.animHide(callback);
30895         }else{
30896             this.el.hide();
30897             this.hideEl(callback);
30898         }
30899         return this;
30900     },
30901
30902     // private
30903     hideEl : function(callback){
30904         this.proxy.hide();
30905         if(this.modal){
30906             this.mask.hide();
30907             Roo.get(document.body).removeClass("x-body-masked");
30908         }
30909         this.fireEvent("hide", this);
30910         if(typeof callback == "function"){
30911             callback();
30912         }
30913     },
30914
30915     // private
30916     hideAction : function(){
30917         this.setLeft("-10000px");
30918         this.setTop("-10000px");
30919         this.setStyle("visibility", "hidden");
30920     },
30921
30922     // private
30923     refreshSize : function(){
30924         this.size = this.el.getSize();
30925         this.xy = this.el.getXY();
30926         Roo.state.Manager.set(this.stateId || this.el.id + "-state", this.el.getBox());
30927     },
30928
30929     // private
30930     // z-index is managed by the DialogManager and may be overwritten at any time
30931     setZIndex : function(index){
30932         if(this.modal){
30933             this.mask.setStyle("z-index", index);
30934         }
30935         if(this.shim){
30936             this.shim.setStyle("z-index", ++index);
30937         }
30938         if(this.shadow){
30939             this.shadow.setZIndex(++index);
30940         }
30941         this.el.setStyle("z-index", ++index);
30942         if(this.proxy){
30943             this.proxy.setStyle("z-index", ++index);
30944         }
30945         if(this.resizer){
30946             this.resizer.proxy.setStyle("z-index", ++index);
30947         }
30948
30949         this.lastZIndex = index;
30950     },
30951
30952     /**
30953      * Returns the element for this dialog
30954      * @return {Roo.Element} The underlying dialog Element
30955      */
30956     getEl : function(){
30957         return this.el;
30958     }
30959 });
30960
30961 /**
30962  * @class Roo.DialogManager
30963  * Provides global access to BasicDialogs that have been created and
30964  * support for z-indexing (layering) multiple open dialogs.
30965  */
30966 Roo.DialogManager = function(){
30967     var list = {};
30968     var accessList = [];
30969     var front = null;
30970
30971     // private
30972     var sortDialogs = function(d1, d2){
30973         return (!d1._lastAccess || d1._lastAccess < d2._lastAccess) ? -1 : 1;
30974     };
30975
30976     // private
30977     var orderDialogs = function(){
30978         accessList.sort(sortDialogs);
30979         var seed = Roo.DialogManager.zseed;
30980         for(var i = 0, len = accessList.length; i < len; i++){
30981             var dlg = accessList[i];
30982             if(dlg){
30983                 dlg.setZIndex(seed + (i*10));
30984             }
30985         }
30986     };
30987
30988     return {
30989         /**
30990          * The starting z-index for BasicDialogs (defaults to 9000)
30991          * @type Number The z-index value
30992          */
30993         zseed : 9000,
30994
30995         // private
30996         register : function(dlg){
30997             list[dlg.id] = dlg;
30998             accessList.push(dlg);
30999         },
31000
31001         // private
31002         unregister : function(dlg){
31003             delete list[dlg.id];
31004             var i=0;
31005             var len=0;
31006             if(!accessList.indexOf){
31007                 for(  i = 0, len = accessList.length; i < len; i++){
31008                     if(accessList[i] == dlg){
31009                         accessList.splice(i, 1);
31010                         return;
31011                     }
31012                 }
31013             }else{
31014                  i = accessList.indexOf(dlg);
31015                 if(i != -1){
31016                     accessList.splice(i, 1);
31017                 }
31018             }
31019         },
31020
31021         /**
31022          * Gets a registered dialog by id
31023          * @param {String/Object} id The id of the dialog or a dialog
31024          * @return {Roo.BasicDialog} this
31025          */
31026         get : function(id){
31027             return typeof id == "object" ? id : list[id];
31028         },
31029
31030         /**
31031          * Brings the specified dialog to the front
31032          * @param {String/Object} dlg The id of the dialog or a dialog
31033          * @return {Roo.BasicDialog} this
31034          */
31035         bringToFront : function(dlg){
31036             dlg = this.get(dlg);
31037             if(dlg != front){
31038                 front = dlg;
31039                 dlg._lastAccess = new Date().getTime();
31040                 orderDialogs();
31041             }
31042             return dlg;
31043         },
31044
31045         /**
31046          * Sends the specified dialog to the back
31047          * @param {String/Object} dlg The id of the dialog or a dialog
31048          * @return {Roo.BasicDialog} this
31049          */
31050         sendToBack : function(dlg){
31051             dlg = this.get(dlg);
31052             dlg._lastAccess = -(new Date().getTime());
31053             orderDialogs();
31054             return dlg;
31055         },
31056
31057         /**
31058          * Hides all dialogs
31059          */
31060         hideAll : function(){
31061             for(var id in list){
31062                 if(list[id] && typeof list[id] != "function" && list[id].isVisible()){
31063                     list[id].hide();
31064                 }
31065             }
31066         }
31067     };
31068 }();
31069
31070 /**
31071  * @class Roo.LayoutDialog
31072  * @extends Roo.BasicDialog
31073  * Dialog which provides adjustments for working with a layout in a Dialog.
31074  * Add your necessary layout config options to the dialog's config.<br>
31075  * Example usage (including a nested layout):
31076  * <pre><code>
31077 if(!dialog){
31078     dialog = new Roo.LayoutDialog("download-dlg", {
31079         modal: true,
31080         width:600,
31081         height:450,
31082         shadow:true,
31083         minWidth:500,
31084         minHeight:350,
31085         autoTabs:true,
31086         proxyDrag:true,
31087         // layout config merges with the dialog config
31088         center:{
31089             tabPosition: "top",
31090             alwaysShowTabs: true
31091         }
31092     });
31093     dialog.addKeyListener(27, dialog.hide, dialog);
31094     dialog.setDefaultButton(dialog.addButton("Close", dialog.hide, dialog));
31095     dialog.addButton("Build It!", this.getDownload, this);
31096
31097     // we can even add nested layouts
31098     var innerLayout = new Roo.BorderLayout("dl-inner", {
31099         east: {
31100             initialSize: 200,
31101             autoScroll:true,
31102             split:true
31103         },
31104         center: {
31105             autoScroll:true
31106         }
31107     });
31108     innerLayout.beginUpdate();
31109     innerLayout.add("east", new Roo.ContentPanel("dl-details"));
31110     innerLayout.add("center", new Roo.ContentPanel("selection-panel"));
31111     innerLayout.endUpdate(true);
31112
31113     var layout = dialog.getLayout();
31114     layout.beginUpdate();
31115     layout.add("center", new Roo.ContentPanel("standard-panel",
31116                         {title: "Download the Source", fitToFrame:true}));
31117     layout.add("center", new Roo.NestedLayoutPanel(innerLayout,
31118                {title: "Build your own roo.js"}));
31119     layout.getRegion("center").showPanel(sp);
31120     layout.endUpdate();
31121 }
31122 </code></pre>
31123     * @constructor
31124     * @param {String/HTMLElement/Roo.Element} el The id of or container element, or config
31125     * @param {Object} config configuration options
31126   */
31127 Roo.LayoutDialog = function(el, cfg){
31128     
31129     var config=  cfg;
31130     if (typeof(cfg) == 'undefined') {
31131         config = Roo.apply({}, el);
31132         // not sure why we use documentElement here.. - it should always be body.
31133         // IE7 borks horribly if we use documentElement.
31134         // webkit also does not like documentElement - it creates a body element...
31135         el = Roo.get( document.body || document.documentElement ).createChild();
31136         //config.autoCreate = true;
31137     }
31138     
31139     
31140     config.autoTabs = false;
31141     Roo.LayoutDialog.superclass.constructor.call(this, el, config);
31142     this.body.setStyle({overflow:"hidden", position:"relative"});
31143     this.layout = new Roo.BorderLayout(this.body.dom, config);
31144     this.layout.monitorWindowResize = false;
31145     this.el.addClass("x-dlg-auto-layout");
31146     // fix case when center region overwrites center function
31147     this.center = Roo.BasicDialog.prototype.center;
31148     this.on("show", this.layout.layout, this.layout, true);
31149     if (config.items) {
31150         var xitems = config.items;
31151         delete config.items;
31152         Roo.each(xitems, this.addxtype, this);
31153     }
31154     
31155     
31156 };
31157 Roo.extend(Roo.LayoutDialog, Roo.BasicDialog, {
31158     /**
31159      * Ends update of the layout <strike>and resets display to none</strike>. Use standard beginUpdate/endUpdate on the layout.
31160      * @deprecated
31161      */
31162     endUpdate : function(){
31163         this.layout.endUpdate();
31164     },
31165
31166     /**
31167      * Begins an update of the layout <strike>and sets display to block and visibility to hidden</strike>. Use standard beginUpdate/endUpdate on the layout.
31168      *  @deprecated
31169      */
31170     beginUpdate : function(){
31171         this.layout.beginUpdate();
31172     },
31173
31174     /**
31175      * Get the BorderLayout for this dialog
31176      * @return {Roo.BorderLayout}
31177      */
31178     getLayout : function(){
31179         return this.layout;
31180     },
31181
31182     showEl : function(){
31183         Roo.LayoutDialog.superclass.showEl.apply(this, arguments);
31184         if(Roo.isIE7){
31185             this.layout.layout();
31186         }
31187     },
31188
31189     // private
31190     // Use the syncHeightBeforeShow config option to control this automatically
31191     syncBodyHeight : function(){
31192         Roo.LayoutDialog.superclass.syncBodyHeight.call(this);
31193         if(this.layout){this.layout.layout();}
31194     },
31195     
31196       /**
31197      * Add an xtype element (actually adds to the layout.)
31198      * @return {Object} xdata xtype object data.
31199      */
31200     
31201     addxtype : function(c) {
31202         return this.layout.addxtype(c);
31203     }
31204 });/*
31205  * Based on:
31206  * Ext JS Library 1.1.1
31207  * Copyright(c) 2006-2007, Ext JS, LLC.
31208  *
31209  * Originally Released Under LGPL - original licence link has changed is not relivant.
31210  *
31211  * Fork - LGPL
31212  * <script type="text/javascript">
31213  */
31214  
31215 /**
31216  * @class Roo.MessageBox
31217  * Utility class for generating different styles of message boxes.  The alias Roo.Msg can also be used.
31218  * Example usage:
31219  *<pre><code>
31220 // Basic alert:
31221 Roo.Msg.alert('Status', 'Changes saved successfully.');
31222
31223 // Prompt for user data:
31224 Roo.Msg.prompt('Name', 'Please enter your name:', function(btn, text){
31225     if (btn == 'ok'){
31226         // process text value...
31227     }
31228 });
31229
31230 // Show a dialog using config options:
31231 Roo.Msg.show({
31232    title:'Save Changes?',
31233    msg: 'Your are closing a tab that has unsaved changes. Would you like to save your changes?',
31234    buttons: Roo.Msg.YESNOCANCEL,
31235    fn: processResult,
31236    animEl: 'elId'
31237 });
31238 </code></pre>
31239  * @singleton
31240  */
31241 Roo.MessageBox = function(){
31242     var dlg, opt, mask, waitTimer;
31243     var bodyEl, msgEl, textboxEl, textareaEl, progressEl, pp;
31244     var buttons, activeTextEl, bwidth;
31245
31246     // private
31247     var handleButton = function(button){
31248         dlg.hide();
31249         Roo.callback(opt.fn, opt.scope||window, [button, activeTextEl.dom.value], 1);
31250     };
31251
31252     // private
31253     var handleHide = function(){
31254         if(opt && opt.cls){
31255             dlg.el.removeClass(opt.cls);
31256         }
31257         if(waitTimer){
31258             Roo.TaskMgr.stop(waitTimer);
31259             waitTimer = null;
31260         }
31261     };
31262
31263     // private
31264     var updateButtons = function(b){
31265         var width = 0;
31266         if(!b){
31267             buttons["ok"].hide();
31268             buttons["cancel"].hide();
31269             buttons["yes"].hide();
31270             buttons["no"].hide();
31271             dlg.footer.dom.style.display = 'none';
31272             return width;
31273         }
31274         dlg.footer.dom.style.display = '';
31275         for(var k in buttons){
31276             if(typeof buttons[k] != "function"){
31277                 if(b[k]){
31278                     buttons[k].show();
31279                     buttons[k].setText(typeof b[k] == "string" ? b[k] : Roo.MessageBox.buttonText[k]);
31280                     width += buttons[k].el.getWidth()+15;
31281                 }else{
31282                     buttons[k].hide();
31283                 }
31284             }
31285         }
31286         return width;
31287     };
31288
31289     // private
31290     var handleEsc = function(d, k, e){
31291         if(opt && opt.closable !== false){
31292             dlg.hide();
31293         }
31294         if(e){
31295             e.stopEvent();
31296         }
31297     };
31298
31299     return {
31300         /**
31301          * Returns a reference to the underlying {@link Roo.BasicDialog} element
31302          * @return {Roo.BasicDialog} The BasicDialog element
31303          */
31304         getDialog : function(){
31305            if(!dlg){
31306                 dlg = new Roo.BasicDialog("x-msg-box", {
31307                     autoCreate : true,
31308                     shadow: true,
31309                     draggable: true,
31310                     resizable:false,
31311                     constraintoviewport:false,
31312                     fixedcenter:true,
31313                     collapsible : false,
31314                     shim:true,
31315                     modal: true,
31316                     width:400, height:100,
31317                     buttonAlign:"center",
31318                     closeClick : function(){
31319                         if(opt && opt.buttons && opt.buttons.no && !opt.buttons.cancel){
31320                             handleButton("no");
31321                         }else{
31322                             handleButton("cancel");
31323                         }
31324                     }
31325                 });
31326                 dlg.on("hide", handleHide);
31327                 mask = dlg.mask;
31328                 dlg.addKeyListener(27, handleEsc);
31329                 buttons = {};
31330                 var bt = this.buttonText;
31331                 buttons["ok"] = dlg.addButton(bt["ok"], handleButton.createCallback("ok"));
31332                 buttons["yes"] = dlg.addButton(bt["yes"], handleButton.createCallback("yes"));
31333                 buttons["no"] = dlg.addButton(bt["no"], handleButton.createCallback("no"));
31334                 buttons["cancel"] = dlg.addButton(bt["cancel"], handleButton.createCallback("cancel"));
31335                 bodyEl = dlg.body.createChild({
31336
31337                     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>'
31338                 });
31339                 msgEl = bodyEl.dom.firstChild;
31340                 textboxEl = Roo.get(bodyEl.dom.childNodes[2]);
31341                 textboxEl.enableDisplayMode();
31342                 textboxEl.addKeyListener([10,13], function(){
31343                     if(dlg.isVisible() && opt && opt.buttons){
31344                         if(opt.buttons.ok){
31345                             handleButton("ok");
31346                         }else if(opt.buttons.yes){
31347                             handleButton("yes");
31348                         }
31349                     }
31350                 });
31351                 textareaEl = Roo.get(bodyEl.dom.childNodes[3]);
31352                 textareaEl.enableDisplayMode();
31353                 progressEl = Roo.get(bodyEl.dom.childNodes[4]);
31354                 progressEl.enableDisplayMode();
31355                 var pf = progressEl.dom.firstChild;
31356                 if (pf) {
31357                     pp = Roo.get(pf.firstChild);
31358                     pp.setHeight(pf.offsetHeight);
31359                 }
31360                 
31361             }
31362             return dlg;
31363         },
31364
31365         /**
31366          * Updates the message box body text
31367          * @param {String} text (optional) Replaces the message box element's innerHTML with the specified string (defaults to
31368          * the XHTML-compliant non-breaking space character '&amp;#160;')
31369          * @return {Roo.MessageBox} This message box
31370          */
31371         updateText : function(text){
31372             if(!dlg.isVisible() && !opt.width){
31373                 dlg.resizeTo(this.maxWidth, 100); // resize first so content is never clipped from previous shows
31374             }
31375             msgEl.innerHTML = text || '&#160;';
31376       
31377             var cw =  Math.max(msgEl.offsetWidth, msgEl.parentNode.scrollWidth);
31378             //Roo.log("guesed size: " + JSON.stringify([cw,msgEl.offsetWidth, msgEl.parentNode.scrollWidth]));
31379             var w = Math.max(
31380                     Math.min(opt.width || cw , this.maxWidth), 
31381                     Math.max(opt.minWidth || this.minWidth, bwidth)
31382             );
31383             if(opt.prompt){
31384                 activeTextEl.setWidth(w);
31385             }
31386             if(dlg.isVisible()){
31387                 dlg.fixedcenter = false;
31388             }
31389             // to big, make it scroll. = But as usual stupid IE does not support
31390             // !important..
31391             
31392             if ( bodyEl.getHeight() > (Roo.lib.Dom.getViewHeight() - 100)) {
31393                 bodyEl.setHeight ( Roo.lib.Dom.getViewHeight() - 100 );
31394                 bodyEl.dom.style.overflowY = 'auto' + ( Roo.isIE ? '' : ' !important');
31395             } else {
31396                 bodyEl.dom.style.height = '';
31397                 bodyEl.dom.style.overflowY = '';
31398             }
31399             if (cw > w) {
31400                 bodyEl.dom.style.get = 'auto' + ( Roo.isIE ? '' : ' !important');
31401             } else {
31402                 bodyEl.dom.style.overflowX = '';
31403             }
31404             
31405             dlg.setContentSize(w, bodyEl.getHeight());
31406             if(dlg.isVisible()){
31407                 dlg.fixedcenter = true;
31408             }
31409             return this;
31410         },
31411
31412         /**
31413          * Updates a progress-style message box's text and progress bar.  Only relevant on message boxes
31414          * initiated via {@link Roo.MessageBox#progress} or by calling {@link Roo.MessageBox#show} with progress: true.
31415          * @param {Number} value Any number between 0 and 1 (e.g., .5)
31416          * @param {String} text (optional) If defined, the message box's body text is replaced with the specified string (defaults to undefined)
31417          * @return {Roo.MessageBox} This message box
31418          */
31419         updateProgress : function(value, text){
31420             if(text){
31421                 this.updateText(text);
31422             }
31423             if (pp) { // weird bug on my firefox - for some reason this is not defined
31424                 pp.setWidth(Math.floor(value*progressEl.dom.firstChild.offsetWidth));
31425             }
31426             return this;
31427         },        
31428
31429         /**
31430          * Returns true if the message box is currently displayed
31431          * @return {Boolean} True if the message box is visible, else false
31432          */
31433         isVisible : function(){
31434             return dlg && dlg.isVisible();  
31435         },
31436
31437         /**
31438          * Hides the message box if it is displayed
31439          */
31440         hide : function(){
31441             if(this.isVisible()){
31442                 dlg.hide();
31443             }  
31444         },
31445
31446         /**
31447          * Displays a new message box, or reinitializes an existing message box, based on the config options
31448          * passed in. All functions (e.g. prompt, alert, etc) on MessageBox call this function internally.
31449          * The following config object properties are supported:
31450          * <pre>
31451 Property    Type             Description
31452 ----------  ---------------  ------------------------------------------------------------------------------------
31453 animEl            String/Element   An id or Element from which the message box should animate as it opens and
31454                                    closes (defaults to undefined)
31455 buttons           Object/Boolean   A button config object (e.g., Roo.MessageBox.OKCANCEL or {ok:'Foo',
31456                                    cancel:'Bar'}), or false to not show any buttons (defaults to false)
31457 closable          Boolean          False to hide the top-right close button (defaults to true).  Note that
31458                                    progress and wait dialogs will ignore this property and always hide the
31459                                    close button as they can only be closed programmatically.
31460 cls               String           A custom CSS class to apply to the message box element
31461 defaultTextHeight Number           The default height in pixels of the message box's multiline textarea if
31462                                    displayed (defaults to 75)
31463 fn                Function         A callback function to execute after closing the dialog.  The arguments to the
31464                                    function will be btn (the name of the button that was clicked, if applicable,
31465                                    e.g. "ok"), and text (the value of the active text field, if applicable).
31466                                    Progress and wait dialogs will ignore this option since they do not respond to
31467                                    user actions and can only be closed programmatically, so any required function
31468                                    should be called by the same code after it closes the dialog.
31469 icon              String           A CSS class that provides a background image to be used as an icon for
31470                                    the dialog (e.g., Roo.MessageBox.WARNING or 'custom-class', defaults to '')
31471 maxWidth          Number           The maximum width in pixels of the message box (defaults to 600)
31472 minWidth          Number           The minimum width in pixels of the message box (defaults to 100)
31473 modal             Boolean          False to allow user interaction with the page while the message box is
31474                                    displayed (defaults to true)
31475 msg               String           A string that will replace the existing message box body text (defaults
31476                                    to the XHTML-compliant non-breaking space character '&#160;')
31477 multiline         Boolean          True to prompt the user to enter multi-line text (defaults to false)
31478 progress          Boolean          True to display a progress bar (defaults to false)
31479 progressText      String           The text to display inside the progress bar if progress = true (defaults to '')
31480 prompt            Boolean          True to prompt the user to enter single-line text (defaults to false)
31481 proxyDrag         Boolean          True to display a lightweight proxy while dragging (defaults to false)
31482 title             String           The title text
31483 value             String           The string value to set into the active textbox element if displayed
31484 wait              Boolean          True to display a progress bar (defaults to false)
31485 width             Number           The width of the dialog in pixels
31486 </pre>
31487          *
31488          * Example usage:
31489          * <pre><code>
31490 Roo.Msg.show({
31491    title: 'Address',
31492    msg: 'Please enter your address:',
31493    width: 300,
31494    buttons: Roo.MessageBox.OKCANCEL,
31495    multiline: true,
31496    fn: saveAddress,
31497    animEl: 'addAddressBtn'
31498 });
31499 </code></pre>
31500          * @param {Object} config Configuration options
31501          * @return {Roo.MessageBox} This message box
31502          */
31503         show : function(options)
31504         {
31505             
31506             // this causes nightmares if you show one dialog after another
31507             // especially on callbacks..
31508              
31509             if(this.isVisible()){
31510                 
31511                 this.hide();
31512                 Roo.log("[Roo.Messagebox] Show called while message displayed:" );
31513                 Roo.log("Old Dialog Message:" +  msgEl.innerHTML );
31514                 Roo.log("New Dialog Message:" +  options.msg )
31515                 //this.alert("ERROR", "Multiple dialogs where displayed at the same time");
31516                 //throw "Roo.MessageBox ERROR : Multiple dialogs where displayed at the same time";
31517                 
31518             }
31519             var d = this.getDialog();
31520             opt = options;
31521             d.setTitle(opt.title || "&#160;");
31522             d.close.setDisplayed(opt.closable !== false);
31523             activeTextEl = textboxEl;
31524             opt.prompt = opt.prompt || (opt.multiline ? true : false);
31525             if(opt.prompt){
31526                 if(opt.multiline){
31527                     textboxEl.hide();
31528                     textareaEl.show();
31529                     textareaEl.setHeight(typeof opt.multiline == "number" ?
31530                         opt.multiline : this.defaultTextHeight);
31531                     activeTextEl = textareaEl;
31532                 }else{
31533                     textboxEl.show();
31534                     textareaEl.hide();
31535                 }
31536             }else{
31537                 textboxEl.hide();
31538                 textareaEl.hide();
31539             }
31540             progressEl.setDisplayed(opt.progress === true);
31541             this.updateProgress(0);
31542             activeTextEl.dom.value = opt.value || "";
31543             if(opt.prompt){
31544                 dlg.setDefaultButton(activeTextEl);
31545             }else{
31546                 var bs = opt.buttons;
31547                 var db = null;
31548                 if(bs && bs.ok){
31549                     db = buttons["ok"];
31550                 }else if(bs && bs.yes){
31551                     db = buttons["yes"];
31552                 }
31553                 dlg.setDefaultButton(db);
31554             }
31555             bwidth = updateButtons(opt.buttons);
31556             this.updateText(opt.msg);
31557             if(opt.cls){
31558                 d.el.addClass(opt.cls);
31559             }
31560             d.proxyDrag = opt.proxyDrag === true;
31561             d.modal = opt.modal !== false;
31562             d.mask = opt.modal !== false ? mask : false;
31563             if(!d.isVisible()){
31564                 // force it to the end of the z-index stack so it gets a cursor in FF
31565                 document.body.appendChild(dlg.el.dom);
31566                 d.animateTarget = null;
31567                 d.show(options.animEl);
31568             }
31569             return this;
31570         },
31571
31572         /**
31573          * Displays a message box with a progress bar.  This message box has no buttons and is not closeable by
31574          * the user.  You are responsible for updating the progress bar as needed via {@link Roo.MessageBox#updateProgress}
31575          * and closing the message box when the process is complete.
31576          * @param {String} title The title bar text
31577          * @param {String} msg The message box body text
31578          * @return {Roo.MessageBox} This message box
31579          */
31580         progress : function(title, msg){
31581             this.show({
31582                 title : title,
31583                 msg : msg,
31584                 buttons: false,
31585                 progress:true,
31586                 closable:false,
31587                 minWidth: this.minProgressWidth,
31588                 modal : true
31589             });
31590             return this;
31591         },
31592
31593         /**
31594          * Displays a standard read-only message box with an OK button (comparable to the basic JavaScript Window.alert).
31595          * If a callback function is passed it will be called after the user clicks the button, and the
31596          * id of the button that was clicked will be passed as the only parameter to the callback
31597          * (could also be the top-right close button).
31598          * @param {String} title The title bar text
31599          * @param {String} msg The message box body text
31600          * @param {Function} fn (optional) The callback function invoked after the message box is closed
31601          * @param {Object} scope (optional) The scope of the callback function
31602          * @return {Roo.MessageBox} This message box
31603          */
31604         alert : function(title, msg, fn, scope){
31605             this.show({
31606                 title : title,
31607                 msg : msg,
31608                 buttons: this.OK,
31609                 fn: fn,
31610                 scope : scope,
31611                 modal : true
31612             });
31613             return this;
31614         },
31615
31616         /**
31617          * Displays a message box with an infinitely auto-updating progress bar.  This can be used to block user
31618          * interaction while waiting for a long-running process to complete that does not have defined intervals.
31619          * You are responsible for closing the message box when the process is complete.
31620          * @param {String} msg The message box body text
31621          * @param {String} title (optional) The title bar text
31622          * @return {Roo.MessageBox} This message box
31623          */
31624         wait : function(msg, title){
31625             this.show({
31626                 title : title,
31627                 msg : msg,
31628                 buttons: false,
31629                 closable:false,
31630                 progress:true,
31631                 modal:true,
31632                 width:300,
31633                 wait:true
31634             });
31635             waitTimer = Roo.TaskMgr.start({
31636                 run: function(i){
31637                     Roo.MessageBox.updateProgress(((((i+20)%20)+1)*5)*.01);
31638                 },
31639                 interval: 1000
31640             });
31641             return this;
31642         },
31643
31644         /**
31645          * Displays a confirmation message box with Yes and No buttons (comparable to JavaScript's Window.confirm).
31646          * If a callback function is passed it will be called after the user clicks either button, and the id of the
31647          * button that was clicked will be passed as the only parameter to the callback (could also be the top-right close button).
31648          * @param {String} title The title bar text
31649          * @param {String} msg The message box body text
31650          * @param {Function} fn (optional) The callback function invoked after the message box is closed
31651          * @param {Object} scope (optional) The scope of the callback function
31652          * @return {Roo.MessageBox} This message box
31653          */
31654         confirm : function(title, msg, fn, scope){
31655             this.show({
31656                 title : title,
31657                 msg : msg,
31658                 buttons: this.YESNO,
31659                 fn: fn,
31660                 scope : scope,
31661                 modal : true
31662             });
31663             return this;
31664         },
31665
31666         /**
31667          * Displays a message box with OK and Cancel buttons prompting the user to enter some text (comparable to
31668          * JavaScript's Window.prompt).  The prompt can be a single-line or multi-line textbox.  If a callback function
31669          * is passed it will be called after the user clicks either button, and the id of the button that was clicked
31670          * (could also be the top-right close button) and the text that was entered will be passed as the two
31671          * parameters to the callback.
31672          * @param {String} title The title bar text
31673          * @param {String} msg The message box body text
31674          * @param {Function} fn (optional) The callback function invoked after the message box is closed
31675          * @param {Object} scope (optional) The scope of the callback function
31676          * @param {Boolean/Number} multiline (optional) True to create a multiline textbox using the defaultTextHeight
31677          * property, or the height in pixels to create the textbox (defaults to false / single-line)
31678          * @return {Roo.MessageBox} This message box
31679          */
31680         prompt : function(title, msg, fn, scope, multiline){
31681             this.show({
31682                 title : title,
31683                 msg : msg,
31684                 buttons: this.OKCANCEL,
31685                 fn: fn,
31686                 minWidth:250,
31687                 scope : scope,
31688                 prompt:true,
31689                 multiline: multiline,
31690                 modal : true
31691             });
31692             return this;
31693         },
31694
31695         /**
31696          * Button config that displays a single OK button
31697          * @type Object
31698          */
31699         OK : {ok:true},
31700         /**
31701          * Button config that displays Yes and No buttons
31702          * @type Object
31703          */
31704         YESNO : {yes:true, no:true},
31705         /**
31706          * Button config that displays OK and Cancel buttons
31707          * @type Object
31708          */
31709         OKCANCEL : {ok:true, cancel:true},
31710         /**
31711          * Button config that displays Yes, No and Cancel buttons
31712          * @type Object
31713          */
31714         YESNOCANCEL : {yes:true, no:true, cancel:true},
31715
31716         /**
31717          * The default height in pixels of the message box's multiline textarea if displayed (defaults to 75)
31718          * @type Number
31719          */
31720         defaultTextHeight : 75,
31721         /**
31722          * The maximum width in pixels of the message box (defaults to 600)
31723          * @type Number
31724          */
31725         maxWidth : 600,
31726         /**
31727          * The minimum width in pixels of the message box (defaults to 100)
31728          * @type Number
31729          */
31730         minWidth : 100,
31731         /**
31732          * The minimum width in pixels of the message box if it is a progress-style dialog.  This is useful
31733          * for setting a different minimum width than text-only dialogs may need (defaults to 250)
31734          * @type Number
31735          */
31736         minProgressWidth : 250,
31737         /**
31738          * An object containing the default button text strings that can be overriden for localized language support.
31739          * Supported properties are: ok, cancel, yes and no.
31740          * Customize the default text like so: Roo.MessageBox.buttonText.yes = "S?";
31741          * @type Object
31742          */
31743         buttonText : {
31744             ok : "OK",
31745             cancel : "Cancel",
31746             yes : "Yes",
31747             no : "No"
31748         }
31749     };
31750 }();
31751
31752 /**
31753  * Shorthand for {@link Roo.MessageBox}
31754  */
31755 Roo.Msg = Roo.MessageBox;/*
31756  * Based on:
31757  * Ext JS Library 1.1.1
31758  * Copyright(c) 2006-2007, Ext JS, LLC.
31759  *
31760  * Originally Released Under LGPL - original licence link has changed is not relivant.
31761  *
31762  * Fork - LGPL
31763  * <script type="text/javascript">
31764  */
31765 /**
31766  * @class Roo.QuickTips
31767  * Provides attractive and customizable tooltips for any element.
31768  * @singleton
31769  */
31770 Roo.QuickTips = function(){
31771     var el, tipBody, tipBodyText, tipTitle, tm, cfg, close, tagEls = {}, esc, removeCls = null, bdLeft, bdRight;
31772     var ce, bd, xy, dd;
31773     var visible = false, disabled = true, inited = false;
31774     var showProc = 1, hideProc = 1, dismissProc = 1, locks = [];
31775     
31776     var onOver = function(e){
31777         if(disabled){
31778             return;
31779         }
31780         var t = e.getTarget();
31781         if(!t || t.nodeType !== 1 || t == document || t == document.body){
31782             return;
31783         }
31784         if(ce && t == ce.el){
31785             clearTimeout(hideProc);
31786             return;
31787         }
31788         if(t && tagEls[t.id]){
31789             tagEls[t.id].el = t;
31790             showProc = show.defer(tm.showDelay, tm, [tagEls[t.id]]);
31791             return;
31792         }
31793         var ttp, et = Roo.fly(t);
31794         var ns = cfg.namespace;
31795         if(tm.interceptTitles && t.title){
31796             ttp = t.title;
31797             t.qtip = ttp;
31798             t.removeAttribute("title");
31799             e.preventDefault();
31800         }else{
31801             ttp = t.qtip || et.getAttributeNS(ns, cfg.attribute);
31802         }
31803         if(ttp){
31804             showProc = show.defer(tm.showDelay, tm, [{
31805                 el: t, 
31806                 text: ttp, 
31807                 width: et.getAttributeNS(ns, cfg.width),
31808                 autoHide: et.getAttributeNS(ns, cfg.hide) != "user",
31809                 title: et.getAttributeNS(ns, cfg.title),
31810                     cls: et.getAttributeNS(ns, cfg.cls)
31811             }]);
31812         }
31813     };
31814     
31815     var onOut = function(e){
31816         clearTimeout(showProc);
31817         var t = e.getTarget();
31818         if(t && ce && ce.el == t && (tm.autoHide && ce.autoHide !== false)){
31819             hideProc = setTimeout(hide, tm.hideDelay);
31820         }
31821     };
31822     
31823     var onMove = function(e){
31824         if(disabled){
31825             return;
31826         }
31827         xy = e.getXY();
31828         xy[1] += 18;
31829         if(tm.trackMouse && ce){
31830             el.setXY(xy);
31831         }
31832     };
31833     
31834     var onDown = function(e){
31835         clearTimeout(showProc);
31836         clearTimeout(hideProc);
31837         if(!e.within(el)){
31838             if(tm.hideOnClick){
31839                 hide();
31840                 tm.disable();
31841                 tm.enable.defer(100, tm);
31842             }
31843         }
31844     };
31845     
31846     var getPad = function(){
31847         return 2;//bdLeft.getPadding('l')+bdRight.getPadding('r');
31848     };
31849
31850     var show = function(o){
31851         if(disabled){
31852             return;
31853         }
31854         clearTimeout(dismissProc);
31855         ce = o;
31856         if(removeCls){ // in case manually hidden
31857             el.removeClass(removeCls);
31858             removeCls = null;
31859         }
31860         if(ce.cls){
31861             el.addClass(ce.cls);
31862             removeCls = ce.cls;
31863         }
31864         if(ce.title){
31865             tipTitle.update(ce.title);
31866             tipTitle.show();
31867         }else{
31868             tipTitle.update('');
31869             tipTitle.hide();
31870         }
31871         el.dom.style.width  = tm.maxWidth+'px';
31872         //tipBody.dom.style.width = '';
31873         tipBodyText.update(o.text);
31874         var p = getPad(), w = ce.width;
31875         if(!w){
31876             var td = tipBodyText.dom;
31877             var aw = Math.max(td.offsetWidth, td.clientWidth, td.scrollWidth);
31878             if(aw > tm.maxWidth){
31879                 w = tm.maxWidth;
31880             }else if(aw < tm.minWidth){
31881                 w = tm.minWidth;
31882             }else{
31883                 w = aw;
31884             }
31885         }
31886         //tipBody.setWidth(w);
31887         el.setWidth(parseInt(w, 10) + p);
31888         if(ce.autoHide === false){
31889             close.setDisplayed(true);
31890             if(dd){
31891                 dd.unlock();
31892             }
31893         }else{
31894             close.setDisplayed(false);
31895             if(dd){
31896                 dd.lock();
31897             }
31898         }
31899         if(xy){
31900             el.avoidY = xy[1]-18;
31901             el.setXY(xy);
31902         }
31903         if(tm.animate){
31904             el.setOpacity(.1);
31905             el.setStyle("visibility", "visible");
31906             el.fadeIn({callback: afterShow});
31907         }else{
31908             afterShow();
31909         }
31910     };
31911     
31912     var afterShow = function(){
31913         if(ce){
31914             el.show();
31915             esc.enable();
31916             if(tm.autoDismiss && ce.autoHide !== false){
31917                 dismissProc = setTimeout(hide, tm.autoDismissDelay);
31918             }
31919         }
31920     };
31921     
31922     var hide = function(noanim){
31923         clearTimeout(dismissProc);
31924         clearTimeout(hideProc);
31925         ce = null;
31926         if(el.isVisible()){
31927             esc.disable();
31928             if(noanim !== true && tm.animate){
31929                 el.fadeOut({callback: afterHide});
31930             }else{
31931                 afterHide();
31932             } 
31933         }
31934     };
31935     
31936     var afterHide = function(){
31937         el.hide();
31938         if(removeCls){
31939             el.removeClass(removeCls);
31940             removeCls = null;
31941         }
31942     };
31943     
31944     return {
31945         /**
31946         * @cfg {Number} minWidth
31947         * The minimum width of the quick tip (defaults to 40)
31948         */
31949        minWidth : 40,
31950         /**
31951         * @cfg {Number} maxWidth
31952         * The maximum width of the quick tip (defaults to 300)
31953         */
31954        maxWidth : 300,
31955         /**
31956         * @cfg {Boolean} interceptTitles
31957         * True to automatically use the element's DOM title value if available (defaults to false)
31958         */
31959        interceptTitles : false,
31960         /**
31961         * @cfg {Boolean} trackMouse
31962         * True to have the quick tip follow the mouse as it moves over the target element (defaults to false)
31963         */
31964        trackMouse : false,
31965         /**
31966         * @cfg {Boolean} hideOnClick
31967         * True to hide the quick tip if the user clicks anywhere in the document (defaults to true)
31968         */
31969        hideOnClick : true,
31970         /**
31971         * @cfg {Number} showDelay
31972         * Delay in milliseconds before the quick tip displays after the mouse enters the target element (defaults to 500)
31973         */
31974        showDelay : 500,
31975         /**
31976         * @cfg {Number} hideDelay
31977         * Delay in milliseconds before the quick tip hides when autoHide = true (defaults to 200)
31978         */
31979        hideDelay : 200,
31980         /**
31981         * @cfg {Boolean} autoHide
31982         * True to automatically hide the quick tip after the mouse exits the target element (defaults to true).
31983         * Used in conjunction with hideDelay.
31984         */
31985        autoHide : true,
31986         /**
31987         * @cfg {Boolean}
31988         * True to automatically hide the quick tip after a set period of time, regardless of the user's actions
31989         * (defaults to true).  Used in conjunction with autoDismissDelay.
31990         */
31991        autoDismiss : true,
31992         /**
31993         * @cfg {Number}
31994         * Delay in milliseconds before the quick tip hides when autoDismiss = true (defaults to 5000)
31995         */
31996        autoDismissDelay : 5000,
31997        /**
31998         * @cfg {Boolean} animate
31999         * True to turn on fade animation. Defaults to false (ClearType/scrollbar flicker issues in IE7).
32000         */
32001        animate : false,
32002
32003        /**
32004         * @cfg {String} title
32005         * Title text to display (defaults to '').  This can be any valid HTML markup.
32006         */
32007         title: '',
32008        /**
32009         * @cfg {String} text
32010         * Body text to display (defaults to '').  This can be any valid HTML markup.
32011         */
32012         text : '',
32013        /**
32014         * @cfg {String} cls
32015         * A CSS class to apply to the base quick tip element (defaults to '').
32016         */
32017         cls : '',
32018        /**
32019         * @cfg {Number} width
32020         * Width in pixels of the quick tip (defaults to auto).  Width will be ignored if it exceeds the bounds of
32021         * minWidth or maxWidth.
32022         */
32023         width : null,
32024
32025     /**
32026      * Initialize and enable QuickTips for first use.  This should be called once before the first attempt to access
32027      * or display QuickTips in a page.
32028      */
32029        init : function(){
32030           tm = Roo.QuickTips;
32031           cfg = tm.tagConfig;
32032           if(!inited){
32033               if(!Roo.isReady){ // allow calling of init() before onReady
32034                   Roo.onReady(Roo.QuickTips.init, Roo.QuickTips);
32035                   return;
32036               }
32037               el = new Roo.Layer({cls:"x-tip", shadow:"drop", shim: true, constrain:true, shadowOffset:4});
32038               el.fxDefaults = {stopFx: true};
32039               // maximum custom styling
32040               //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>');
32041               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>');              
32042               tipTitle = el.child('h3');
32043               tipTitle.enableDisplayMode("block");
32044               tipBody = el.child('div.x-tip-bd');
32045               tipBodyText = el.child('div.x-tip-bd-inner');
32046               //bdLeft = el.child('div.x-tip-bd-left');
32047               //bdRight = el.child('div.x-tip-bd-right');
32048               close = el.child('div.x-tip-close');
32049               close.enableDisplayMode("block");
32050               close.on("click", hide);
32051               var d = Roo.get(document);
32052               d.on("mousedown", onDown);
32053               d.on("mouseover", onOver);
32054               d.on("mouseout", onOut);
32055               d.on("mousemove", onMove);
32056               esc = d.addKeyListener(27, hide);
32057               esc.disable();
32058               if(Roo.dd.DD){
32059                   dd = el.initDD("default", null, {
32060                       onDrag : function(){
32061                           el.sync();  
32062                       }
32063                   });
32064                   dd.setHandleElId(tipTitle.id);
32065                   dd.lock();
32066               }
32067               inited = true;
32068           }
32069           this.enable(); 
32070        },
32071
32072     /**
32073      * Configures a new quick tip instance and assigns it to a target element.  The following config options
32074      * are supported:
32075      * <pre>
32076 Property    Type                   Description
32077 ----------  ---------------------  ------------------------------------------------------------------------
32078 target      Element/String/Array   An Element, id or array of ids that this quick tip should be tied to
32079      * </ul>
32080      * @param {Object} config The config object
32081      */
32082        register : function(config){
32083            var cs = config instanceof Array ? config : arguments;
32084            for(var i = 0, len = cs.length; i < len; i++) {
32085                var c = cs[i];
32086                var target = c.target;
32087                if(target){
32088                    if(target instanceof Array){
32089                        for(var j = 0, jlen = target.length; j < jlen; j++){
32090                            tagEls[target[j]] = c;
32091                        }
32092                    }else{
32093                        tagEls[typeof target == 'string' ? target : Roo.id(target)] = c;
32094                    }
32095                }
32096            }
32097        },
32098
32099     /**
32100      * Removes this quick tip from its element and destroys it.
32101      * @param {String/HTMLElement/Element} el The element from which the quick tip is to be removed.
32102      */
32103        unregister : function(el){
32104            delete tagEls[Roo.id(el)];
32105        },
32106
32107     /**
32108      * Enable this quick tip.
32109      */
32110        enable : function(){
32111            if(inited && disabled){
32112                locks.pop();
32113                if(locks.length < 1){
32114                    disabled = false;
32115                }
32116            }
32117        },
32118
32119     /**
32120      * Disable this quick tip.
32121      */
32122        disable : function(){
32123           disabled = true;
32124           clearTimeout(showProc);
32125           clearTimeout(hideProc);
32126           clearTimeout(dismissProc);
32127           if(ce){
32128               hide(true);
32129           }
32130           locks.push(1);
32131        },
32132
32133     /**
32134      * Returns true if the quick tip is enabled, else false.
32135      */
32136        isEnabled : function(){
32137             return !disabled;
32138        },
32139
32140         // private
32141        tagConfig : {
32142            namespace : "ext",
32143            attribute : "qtip",
32144            width : "width",
32145            target : "target",
32146            title : "qtitle",
32147            hide : "hide",
32148            cls : "qclass"
32149        }
32150    };
32151 }();
32152
32153 // backwards compat
32154 Roo.QuickTips.tips = Roo.QuickTips.register;/*
32155  * Based on:
32156  * Ext JS Library 1.1.1
32157  * Copyright(c) 2006-2007, Ext JS, LLC.
32158  *
32159  * Originally Released Under LGPL - original licence link has changed is not relivant.
32160  *
32161  * Fork - LGPL
32162  * <script type="text/javascript">
32163  */
32164  
32165
32166 /**
32167  * @class Roo.tree.TreePanel
32168  * @extends Roo.data.Tree
32169
32170  * @cfg {Boolean} rootVisible false to hide the root node (defaults to true)
32171  * @cfg {Boolean} lines false to disable tree lines (defaults to true)
32172  * @cfg {Boolean} enableDD true to enable drag and drop
32173  * @cfg {Boolean} enableDrag true to enable just drag
32174  * @cfg {Boolean} enableDrop true to enable just drop
32175  * @cfg {Object} dragConfig Custom config to pass to the {@link Roo.tree.TreeDragZone} instance
32176  * @cfg {Object} dropConfig Custom config to pass to the {@link Roo.tree.TreeDropZone} instance
32177  * @cfg {String} ddGroup The DD group this TreePanel belongs to
32178  * @cfg {String} ddAppendOnly True if the tree should only allow append drops (use for trees which are sorted)
32179  * @cfg {Boolean} ddScroll true to enable YUI body scrolling
32180  * @cfg {Boolean} containerScroll true to register this container with ScrollManager
32181  * @cfg {Boolean} hlDrop false to disable node highlight on drop (defaults to the value of Roo.enableFx)
32182  * @cfg {String} hlColor The color of the node highlight (defaults to C3DAF9)
32183  * @cfg {Boolean} animate true to enable animated expand/collapse (defaults to the value of Roo.enableFx)
32184  * @cfg {Boolean} singleExpand true if only 1 node per branch may be expanded
32185  * @cfg {Boolean} selModel A tree selection model to use with this TreePanel (defaults to a {@link Roo.tree.DefaultSelectionModel})
32186  * @cfg {Boolean} loader A TreeLoader for use with this TreePanel
32187  * @cfg {Object|Roo.tree.TreeEditor} editor The TreeEditor or xtype data to display when clicked.
32188  * @cfg {String} pathSeparator The token used to separate sub-paths in path strings (defaults to '/')
32189  * @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>
32190  * @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>
32191  * 
32192  * @constructor
32193  * @param {String/HTMLElement/Element} el The container element
32194  * @param {Object} config
32195  */
32196 Roo.tree.TreePanel = function(el, config){
32197     var root = false;
32198     var loader = false;
32199     if (config.root) {
32200         root = config.root;
32201         delete config.root;
32202     }
32203     if (config.loader) {
32204         loader = config.loader;
32205         delete config.loader;
32206     }
32207     
32208     Roo.apply(this, config);
32209     Roo.tree.TreePanel.superclass.constructor.call(this);
32210     this.el = Roo.get(el);
32211     this.el.addClass('x-tree');
32212     //console.log(root);
32213     if (root) {
32214         this.setRootNode( Roo.factory(root, Roo.tree));
32215     }
32216     if (loader) {
32217         this.loader = Roo.factory(loader, Roo.tree);
32218     }
32219    /**
32220     * Read-only. The id of the container element becomes this TreePanel's id.
32221     */
32222     this.id = this.el.id;
32223     this.addEvents({
32224         /**
32225         * @event beforeload
32226         * Fires before a node is loaded, return false to cancel
32227         * @param {Node} node The node being loaded
32228         */
32229         "beforeload" : true,
32230         /**
32231         * @event load
32232         * Fires when a node is loaded
32233         * @param {Node} node The node that was loaded
32234         */
32235         "load" : true,
32236         /**
32237         * @event textchange
32238         * Fires when the text for a node is changed
32239         * @param {Node} node The node
32240         * @param {String} text The new text
32241         * @param {String} oldText The old text
32242         */
32243         "textchange" : true,
32244         /**
32245         * @event beforeexpand
32246         * Fires before a node is expanded, return false to cancel.
32247         * @param {Node} node The node
32248         * @param {Boolean} deep
32249         * @param {Boolean} anim
32250         */
32251         "beforeexpand" : true,
32252         /**
32253         * @event beforecollapse
32254         * Fires before a node is collapsed, return false to cancel.
32255         * @param {Node} node The node
32256         * @param {Boolean} deep
32257         * @param {Boolean} anim
32258         */
32259         "beforecollapse" : true,
32260         /**
32261         * @event expand
32262         * Fires when a node is expanded
32263         * @param {Node} node The node
32264         */
32265         "expand" : true,
32266         /**
32267         * @event disabledchange
32268         * Fires when the disabled status of a node changes
32269         * @param {Node} node The node
32270         * @param {Boolean} disabled
32271         */
32272         "disabledchange" : true,
32273         /**
32274         * @event collapse
32275         * Fires when a node is collapsed
32276         * @param {Node} node The node
32277         */
32278         "collapse" : true,
32279         /**
32280         * @event beforeclick
32281         * Fires before click processing on a node. Return false to cancel the default action.
32282         * @param {Node} node The node
32283         * @param {Roo.EventObject} e The event object
32284         */
32285         "beforeclick":true,
32286         /**
32287         * @event checkchange
32288         * Fires when a node with a checkbox's checked property changes
32289         * @param {Node} this This node
32290         * @param {Boolean} checked
32291         */
32292         "checkchange":true,
32293         /**
32294         * @event click
32295         * Fires when a node is clicked
32296         * @param {Node} node The node
32297         * @param {Roo.EventObject} e The event object
32298         */
32299         "click":true,
32300         /**
32301         * @event dblclick
32302         * Fires when a node is double clicked
32303         * @param {Node} node The node
32304         * @param {Roo.EventObject} e The event object
32305         */
32306         "dblclick":true,
32307         /**
32308         * @event contextmenu
32309         * Fires when a node is right clicked
32310         * @param {Node} node The node
32311         * @param {Roo.EventObject} e The event object
32312         */
32313         "contextmenu":true,
32314         /**
32315         * @event beforechildrenrendered
32316         * Fires right before the child nodes for a node are rendered
32317         * @param {Node} node The node
32318         */
32319         "beforechildrenrendered":true,
32320         /**
32321         * @event startdrag
32322         * Fires when a node starts being dragged
32323         * @param {Roo.tree.TreePanel} this
32324         * @param {Roo.tree.TreeNode} node
32325         * @param {event} e The raw browser event
32326         */ 
32327        "startdrag" : true,
32328        /**
32329         * @event enddrag
32330         * Fires when a drag operation is complete
32331         * @param {Roo.tree.TreePanel} this
32332         * @param {Roo.tree.TreeNode} node
32333         * @param {event} e The raw browser event
32334         */
32335        "enddrag" : true,
32336        /**
32337         * @event dragdrop
32338         * Fires when a dragged node is dropped on a valid DD target
32339         * @param {Roo.tree.TreePanel} this
32340         * @param {Roo.tree.TreeNode} node
32341         * @param {DD} dd The dd it was dropped on
32342         * @param {event} e The raw browser event
32343         */
32344        "dragdrop" : true,
32345        /**
32346         * @event beforenodedrop
32347         * Fires when a DD object is dropped on a node in this tree for preprocessing. Return false to cancel the drop. The dropEvent
32348         * passed to handlers has the following properties:<br />
32349         * <ul style="padding:5px;padding-left:16px;">
32350         * <li>tree - The TreePanel</li>
32351         * <li>target - The node being targeted for the drop</li>
32352         * <li>data - The drag data from the drag source</li>
32353         * <li>point - The point of the drop - append, above or below</li>
32354         * <li>source - The drag source</li>
32355         * <li>rawEvent - Raw mouse event</li>
32356         * <li>dropNode - Drop node(s) provided by the source <b>OR</b> you can supply node(s)
32357         * to be inserted by setting them on this object.</li>
32358         * <li>cancel - Set this to true to cancel the drop.</li>
32359         * </ul>
32360         * @param {Object} dropEvent
32361         */
32362        "beforenodedrop" : true,
32363        /**
32364         * @event nodedrop
32365         * Fires after a DD object is dropped on a node in this tree. The dropEvent
32366         * passed to handlers has the following properties:<br />
32367         * <ul style="padding:5px;padding-left:16px;">
32368         * <li>tree - The TreePanel</li>
32369         * <li>target - The node being targeted for the drop</li>
32370         * <li>data - The drag data from the drag source</li>
32371         * <li>point - The point of the drop - append, above or below</li>
32372         * <li>source - The drag source</li>
32373         * <li>rawEvent - Raw mouse event</li>
32374         * <li>dropNode - Dropped node(s).</li>
32375         * </ul>
32376         * @param {Object} dropEvent
32377         */
32378        "nodedrop" : true,
32379         /**
32380         * @event nodedragover
32381         * Fires when a tree node is being targeted for a drag drop, return false to signal drop not allowed. The dragOverEvent
32382         * passed to handlers has the following properties:<br />
32383         * <ul style="padding:5px;padding-left:16px;">
32384         * <li>tree - The TreePanel</li>
32385         * <li>target - The node being targeted for the drop</li>
32386         * <li>data - The drag data from the drag source</li>
32387         * <li>point - The point of the drop - append, above or below</li>
32388         * <li>source - The drag source</li>
32389         * <li>rawEvent - Raw mouse event</li>
32390         * <li>dropNode - Drop node(s) provided by the source.</li>
32391         * <li>cancel - Set this to true to signal drop not allowed.</li>
32392         * </ul>
32393         * @param {Object} dragOverEvent
32394         */
32395        "nodedragover" : true
32396         
32397     });
32398     if(this.singleExpand){
32399        this.on("beforeexpand", this.restrictExpand, this);
32400     }
32401     if (this.editor) {
32402         this.editor.tree = this;
32403         this.editor = Roo.factory(this.editor, Roo.tree);
32404     }
32405     
32406     if (this.selModel) {
32407         this.selModel = Roo.factory(this.selModel, Roo.tree);
32408     }
32409    
32410 };
32411 Roo.extend(Roo.tree.TreePanel, Roo.data.Tree, {
32412     rootVisible : true,
32413     animate: Roo.enableFx,
32414     lines : true,
32415     enableDD : false,
32416     hlDrop : Roo.enableFx,
32417   
32418     renderer: false,
32419     
32420     rendererTip: false,
32421     // private
32422     restrictExpand : function(node){
32423         var p = node.parentNode;
32424         if(p){
32425             if(p.expandedChild && p.expandedChild.parentNode == p){
32426                 p.expandedChild.collapse();
32427             }
32428             p.expandedChild = node;
32429         }
32430     },
32431
32432     // private override
32433     setRootNode : function(node){
32434         Roo.tree.TreePanel.superclass.setRootNode.call(this, node);
32435         if(!this.rootVisible){
32436             node.ui = new Roo.tree.RootTreeNodeUI(node);
32437         }
32438         return node;
32439     },
32440
32441     /**
32442      * Returns the container element for this TreePanel
32443      */
32444     getEl : function(){
32445         return this.el;
32446     },
32447
32448     /**
32449      * Returns the default TreeLoader for this TreePanel
32450      */
32451     getLoader : function(){
32452         return this.loader;
32453     },
32454
32455     /**
32456      * Expand all nodes
32457      */
32458     expandAll : function(){
32459         this.root.expand(true);
32460     },
32461
32462     /**
32463      * Collapse all nodes
32464      */
32465     collapseAll : function(){
32466         this.root.collapse(true);
32467     },
32468
32469     /**
32470      * Returns the selection model used by this TreePanel
32471      */
32472     getSelectionModel : function(){
32473         if(!this.selModel){
32474             this.selModel = new Roo.tree.DefaultSelectionModel();
32475         }
32476         return this.selModel;
32477     },
32478
32479     /**
32480      * Retrieve an array of checked nodes, or an array of a specific attribute of checked nodes (e.g. "id")
32481      * @param {String} attribute (optional) Defaults to null (return the actual nodes)
32482      * @param {TreeNode} startNode (optional) The node to start from, defaults to the root
32483      * @return {Array}
32484      */
32485     getChecked : function(a, startNode){
32486         startNode = startNode || this.root;
32487         var r = [];
32488         var f = function(){
32489             if(this.attributes.checked){
32490                 r.push(!a ? this : (a == 'id' ? this.id : this.attributes[a]));
32491             }
32492         }
32493         startNode.cascade(f);
32494         return r;
32495     },
32496
32497     /**
32498      * Expands a specified path in this TreePanel. A path can be retrieved from a node with {@link Roo.data.Node#getPath}
32499      * @param {String} path
32500      * @param {String} attr (optional) The attribute used in the path (see {@link Roo.data.Node#getPath} for more info)
32501      * @param {Function} callback (optional) The callback to call when the expand is complete. The callback will be called with
32502      * (bSuccess, oLastNode) where bSuccess is if the expand was successful and oLastNode is the last node that was expanded.
32503      */
32504     expandPath : function(path, attr, callback){
32505         attr = attr || "id";
32506         var keys = path.split(this.pathSeparator);
32507         var curNode = this.root;
32508         if(curNode.attributes[attr] != keys[1]){ // invalid root
32509             if(callback){
32510                 callback(false, null);
32511             }
32512             return;
32513         }
32514         var index = 1;
32515         var f = function(){
32516             if(++index == keys.length){
32517                 if(callback){
32518                     callback(true, curNode);
32519                 }
32520                 return;
32521             }
32522             var c = curNode.findChild(attr, keys[index]);
32523             if(!c){
32524                 if(callback){
32525                     callback(false, curNode);
32526                 }
32527                 return;
32528             }
32529             curNode = c;
32530             c.expand(false, false, f);
32531         };
32532         curNode.expand(false, false, f);
32533     },
32534
32535     /**
32536      * Selects the node in this tree at the specified path. A path can be retrieved from a node with {@link Roo.data.Node#getPath}
32537      * @param {String} path
32538      * @param {String} attr (optional) The attribute used in the path (see {@link Roo.data.Node#getPath} for more info)
32539      * @param {Function} callback (optional) The callback to call when the selection is complete. The callback will be called with
32540      * (bSuccess, oSelNode) where bSuccess is if the selection was successful and oSelNode is the selected node.
32541      */
32542     selectPath : function(path, attr, callback){
32543         attr = attr || "id";
32544         var keys = path.split(this.pathSeparator);
32545         var v = keys.pop();
32546         if(keys.length > 0){
32547             var f = function(success, node){
32548                 if(success && node){
32549                     var n = node.findChild(attr, v);
32550                     if(n){
32551                         n.select();
32552                         if(callback){
32553                             callback(true, n);
32554                         }
32555                     }else if(callback){
32556                         callback(false, n);
32557                     }
32558                 }else{
32559                     if(callback){
32560                         callback(false, n);
32561                     }
32562                 }
32563             };
32564             this.expandPath(keys.join(this.pathSeparator), attr, f);
32565         }else{
32566             this.root.select();
32567             if(callback){
32568                 callback(true, this.root);
32569             }
32570         }
32571     },
32572
32573     getTreeEl : function(){
32574         return this.el;
32575     },
32576
32577     /**
32578      * Trigger rendering of this TreePanel
32579      */
32580     render : function(){
32581         if (this.innerCt) {
32582             return this; // stop it rendering more than once!!
32583         }
32584         
32585         this.innerCt = this.el.createChild({tag:"ul",
32586                cls:"x-tree-root-ct " +
32587                (this.lines ? "x-tree-lines" : "x-tree-no-lines")});
32588
32589         if(this.containerScroll){
32590             Roo.dd.ScrollManager.register(this.el);
32591         }
32592         if((this.enableDD || this.enableDrop) && !this.dropZone){
32593            /**
32594             * The dropZone used by this tree if drop is enabled
32595             * @type Roo.tree.TreeDropZone
32596             */
32597              this.dropZone = new Roo.tree.TreeDropZone(this, this.dropConfig || {
32598                ddGroup: this.ddGroup || "TreeDD", appendOnly: this.ddAppendOnly === true
32599            });
32600         }
32601         if((this.enableDD || this.enableDrag) && !this.dragZone){
32602            /**
32603             * The dragZone used by this tree if drag is enabled
32604             * @type Roo.tree.TreeDragZone
32605             */
32606             this.dragZone = new Roo.tree.TreeDragZone(this, this.dragConfig || {
32607                ddGroup: this.ddGroup || "TreeDD",
32608                scroll: this.ddScroll
32609            });
32610         }
32611         this.getSelectionModel().init(this);
32612         if (!this.root) {
32613             Roo.log("ROOT not set in tree");
32614             return this;
32615         }
32616         this.root.render();
32617         if(!this.rootVisible){
32618             this.root.renderChildren();
32619         }
32620         return this;
32621     }
32622 });/*
32623  * Based on:
32624  * Ext JS Library 1.1.1
32625  * Copyright(c) 2006-2007, Ext JS, LLC.
32626  *
32627  * Originally Released Under LGPL - original licence link has changed is not relivant.
32628  *
32629  * Fork - LGPL
32630  * <script type="text/javascript">
32631  */
32632  
32633
32634 /**
32635  * @class Roo.tree.DefaultSelectionModel
32636  * @extends Roo.util.Observable
32637  * The default single selection for a TreePanel.
32638  * @param {Object} cfg Configuration
32639  */
32640 Roo.tree.DefaultSelectionModel = function(cfg){
32641    this.selNode = null;
32642    
32643    
32644    
32645    this.addEvents({
32646        /**
32647         * @event selectionchange
32648         * Fires when the selected node changes
32649         * @param {DefaultSelectionModel} this
32650         * @param {TreeNode} node the new selection
32651         */
32652        "selectionchange" : true,
32653
32654        /**
32655         * @event beforeselect
32656         * Fires before the selected node changes, return false to cancel the change
32657         * @param {DefaultSelectionModel} this
32658         * @param {TreeNode} node the new selection
32659         * @param {TreeNode} node the old selection
32660         */
32661        "beforeselect" : true
32662    });
32663    
32664     Roo.tree.DefaultSelectionModel.superclass.constructor.call(this,cfg);
32665 };
32666
32667 Roo.extend(Roo.tree.DefaultSelectionModel, Roo.util.Observable, {
32668     init : function(tree){
32669         this.tree = tree;
32670         tree.getTreeEl().on("keydown", this.onKeyDown, this);
32671         tree.on("click", this.onNodeClick, this);
32672     },
32673     
32674     onNodeClick : function(node, e){
32675         if (e.ctrlKey && this.selNode == node)  {
32676             this.unselect(node);
32677             return;
32678         }
32679         this.select(node);
32680     },
32681     
32682     /**
32683      * Select a node.
32684      * @param {TreeNode} node The node to select
32685      * @return {TreeNode} The selected node
32686      */
32687     select : function(node){
32688         var last = this.selNode;
32689         if(last != node && this.fireEvent('beforeselect', this, node, last) !== false){
32690             if(last){
32691                 last.ui.onSelectedChange(false);
32692             }
32693             this.selNode = node;
32694             node.ui.onSelectedChange(true);
32695             this.fireEvent("selectionchange", this, node, last);
32696         }
32697         return node;
32698     },
32699     
32700     /**
32701      * Deselect a node.
32702      * @param {TreeNode} node The node to unselect
32703      */
32704     unselect : function(node){
32705         if(this.selNode == node){
32706             this.clearSelections();
32707         }    
32708     },
32709     
32710     /**
32711      * Clear all selections
32712      */
32713     clearSelections : function(){
32714         var n = this.selNode;
32715         if(n){
32716             n.ui.onSelectedChange(false);
32717             this.selNode = null;
32718             this.fireEvent("selectionchange", this, null);
32719         }
32720         return n;
32721     },
32722     
32723     /**
32724      * Get the selected node
32725      * @return {TreeNode} The selected node
32726      */
32727     getSelectedNode : function(){
32728         return this.selNode;    
32729     },
32730     
32731     /**
32732      * Returns true if the node is selected
32733      * @param {TreeNode} node The node to check
32734      * @return {Boolean}
32735      */
32736     isSelected : function(node){
32737         return this.selNode == node;  
32738     },
32739
32740     /**
32741      * Selects the node above the selected node in the tree, intelligently walking the nodes
32742      * @return TreeNode The new selection
32743      */
32744     selectPrevious : function(){
32745         var s = this.selNode || this.lastSelNode;
32746         if(!s){
32747             return null;
32748         }
32749         var ps = s.previousSibling;
32750         if(ps){
32751             if(!ps.isExpanded() || ps.childNodes.length < 1){
32752                 return this.select(ps);
32753             } else{
32754                 var lc = ps.lastChild;
32755                 while(lc && lc.isExpanded() && lc.childNodes.length > 0){
32756                     lc = lc.lastChild;
32757                 }
32758                 return this.select(lc);
32759             }
32760         } else if(s.parentNode && (this.tree.rootVisible || !s.parentNode.isRoot)){
32761             return this.select(s.parentNode);
32762         }
32763         return null;
32764     },
32765
32766     /**
32767      * Selects the node above the selected node in the tree, intelligently walking the nodes
32768      * @return TreeNode The new selection
32769      */
32770     selectNext : function(){
32771         var s = this.selNode || this.lastSelNode;
32772         if(!s){
32773             return null;
32774         }
32775         if(s.firstChild && s.isExpanded()){
32776              return this.select(s.firstChild);
32777          }else if(s.nextSibling){
32778              return this.select(s.nextSibling);
32779          }else if(s.parentNode){
32780             var newS = null;
32781             s.parentNode.bubble(function(){
32782                 if(this.nextSibling){
32783                     newS = this.getOwnerTree().selModel.select(this.nextSibling);
32784                     return false;
32785                 }
32786             });
32787             return newS;
32788          }
32789         return null;
32790     },
32791
32792     onKeyDown : function(e){
32793         var s = this.selNode || this.lastSelNode;
32794         // undesirable, but required
32795         var sm = this;
32796         if(!s){
32797             return;
32798         }
32799         var k = e.getKey();
32800         switch(k){
32801              case e.DOWN:
32802                  e.stopEvent();
32803                  this.selectNext();
32804              break;
32805              case e.UP:
32806                  e.stopEvent();
32807                  this.selectPrevious();
32808              break;
32809              case e.RIGHT:
32810                  e.preventDefault();
32811                  if(s.hasChildNodes()){
32812                      if(!s.isExpanded()){
32813                          s.expand();
32814                      }else if(s.firstChild){
32815                          this.select(s.firstChild, e);
32816                      }
32817                  }
32818              break;
32819              case e.LEFT:
32820                  e.preventDefault();
32821                  if(s.hasChildNodes() && s.isExpanded()){
32822                      s.collapse();
32823                  }else if(s.parentNode && (this.tree.rootVisible || s.parentNode != this.tree.getRootNode())){
32824                      this.select(s.parentNode, e);
32825                  }
32826              break;
32827         };
32828     }
32829 });
32830
32831 /**
32832  * @class Roo.tree.MultiSelectionModel
32833  * @extends Roo.util.Observable
32834  * Multi selection for a TreePanel.
32835  * @param {Object} cfg Configuration
32836  */
32837 Roo.tree.MultiSelectionModel = function(){
32838    this.selNodes = [];
32839    this.selMap = {};
32840    this.addEvents({
32841        /**
32842         * @event selectionchange
32843         * Fires when the selected nodes change
32844         * @param {MultiSelectionModel} this
32845         * @param {Array} nodes Array of the selected nodes
32846         */
32847        "selectionchange" : true
32848    });
32849    Roo.tree.MultiSelectionModel.superclass.constructor.call(this,cfg);
32850    
32851 };
32852
32853 Roo.extend(Roo.tree.MultiSelectionModel, Roo.util.Observable, {
32854     init : function(tree){
32855         this.tree = tree;
32856         tree.getTreeEl().on("keydown", this.onKeyDown, this);
32857         tree.on("click", this.onNodeClick, this);
32858     },
32859     
32860     onNodeClick : function(node, e){
32861         this.select(node, e, e.ctrlKey);
32862     },
32863     
32864     /**
32865      * Select a node.
32866      * @param {TreeNode} node The node to select
32867      * @param {EventObject} e (optional) An event associated with the selection
32868      * @param {Boolean} keepExisting True to retain existing selections
32869      * @return {TreeNode} The selected node
32870      */
32871     select : function(node, e, keepExisting){
32872         if(keepExisting !== true){
32873             this.clearSelections(true);
32874         }
32875         if(this.isSelected(node)){
32876             this.lastSelNode = node;
32877             return node;
32878         }
32879         this.selNodes.push(node);
32880         this.selMap[node.id] = node;
32881         this.lastSelNode = node;
32882         node.ui.onSelectedChange(true);
32883         this.fireEvent("selectionchange", this, this.selNodes);
32884         return node;
32885     },
32886     
32887     /**
32888      * Deselect a node.
32889      * @param {TreeNode} node The node to unselect
32890      */
32891     unselect : function(node){
32892         if(this.selMap[node.id]){
32893             node.ui.onSelectedChange(false);
32894             var sn = this.selNodes;
32895             var index = -1;
32896             if(sn.indexOf){
32897                 index = sn.indexOf(node);
32898             }else{
32899                 for(var i = 0, len = sn.length; i < len; i++){
32900                     if(sn[i] == node){
32901                         index = i;
32902                         break;
32903                     }
32904                 }
32905             }
32906             if(index != -1){
32907                 this.selNodes.splice(index, 1);
32908             }
32909             delete this.selMap[node.id];
32910             this.fireEvent("selectionchange", this, this.selNodes);
32911         }
32912     },
32913     
32914     /**
32915      * Clear all selections
32916      */
32917     clearSelections : function(suppressEvent){
32918         var sn = this.selNodes;
32919         if(sn.length > 0){
32920             for(var i = 0, len = sn.length; i < len; i++){
32921                 sn[i].ui.onSelectedChange(false);
32922             }
32923             this.selNodes = [];
32924             this.selMap = {};
32925             if(suppressEvent !== true){
32926                 this.fireEvent("selectionchange", this, this.selNodes);
32927             }
32928         }
32929     },
32930     
32931     /**
32932      * Returns true if the node is selected
32933      * @param {TreeNode} node The node to check
32934      * @return {Boolean}
32935      */
32936     isSelected : function(node){
32937         return this.selMap[node.id] ? true : false;  
32938     },
32939     
32940     /**
32941      * Returns an array of the selected nodes
32942      * @return {Array}
32943      */
32944     getSelectedNodes : function(){
32945         return this.selNodes;    
32946     },
32947
32948     onKeyDown : Roo.tree.DefaultSelectionModel.prototype.onKeyDown,
32949
32950     selectNext : Roo.tree.DefaultSelectionModel.prototype.selectNext,
32951
32952     selectPrevious : Roo.tree.DefaultSelectionModel.prototype.selectPrevious
32953 });/*
32954  * Based on:
32955  * Ext JS Library 1.1.1
32956  * Copyright(c) 2006-2007, Ext JS, LLC.
32957  *
32958  * Originally Released Under LGPL - original licence link has changed is not relivant.
32959  *
32960  * Fork - LGPL
32961  * <script type="text/javascript">
32962  */
32963  
32964 /**
32965  * @class Roo.tree.TreeNode
32966  * @extends Roo.data.Node
32967  * @cfg {String} text The text for this node
32968  * @cfg {Boolean} expanded true to start the node expanded
32969  * @cfg {Boolean} allowDrag false to make this node undraggable if DD is on (defaults to true)
32970  * @cfg {Boolean} allowDrop false if this node cannot be drop on
32971  * @cfg {Boolean} disabled true to start the node disabled
32972  * @cfg {String} icon The path to an icon for the node. The preferred way to do this
32973  * is to use the cls or iconCls attributes and add the icon via a CSS background image.
32974  * @cfg {String} cls A css class to be added to the node
32975  * @cfg {String} iconCls A css class to be added to the nodes icon element for applying css background images
32976  * @cfg {String} href URL of the link used for the node (defaults to #)
32977  * @cfg {String} hrefTarget target frame for the link
32978  * @cfg {String} qtip An Ext QuickTip for the node
32979  * @cfg {String} qtipCfg An Ext QuickTip config for the node (used instead of qtip)
32980  * @cfg {Boolean} singleClickExpand True for single click expand on this node
32981  * @cfg {Function} uiProvider A UI <b>class</b> to use for this node (defaults to Roo.tree.TreeNodeUI)
32982  * @cfg {Boolean} checked True to render a checked checkbox for this node, false to render an unchecked checkbox
32983  * (defaults to undefined with no checkbox rendered)
32984  * @constructor
32985  * @param {Object/String} attributes The attributes/config for the node or just a string with the text for the node
32986  */
32987 Roo.tree.TreeNode = function(attributes){
32988     attributes = attributes || {};
32989     if(typeof attributes == "string"){
32990         attributes = {text: attributes};
32991     }
32992     this.childrenRendered = false;
32993     this.rendered = false;
32994     Roo.tree.TreeNode.superclass.constructor.call(this, attributes);
32995     this.expanded = attributes.expanded === true;
32996     this.isTarget = attributes.isTarget !== false;
32997     this.draggable = attributes.draggable !== false && attributes.allowDrag !== false;
32998     this.allowChildren = attributes.allowChildren !== false && attributes.allowDrop !== false;
32999
33000     /**
33001      * Read-only. The text for this node. To change it use setText().
33002      * @type String
33003      */
33004     this.text = attributes.text;
33005     /**
33006      * True if this node is disabled.
33007      * @type Boolean
33008      */
33009     this.disabled = attributes.disabled === true;
33010
33011     this.addEvents({
33012         /**
33013         * @event textchange
33014         * Fires when the text for this node is changed
33015         * @param {Node} this This node
33016         * @param {String} text The new text
33017         * @param {String} oldText The old text
33018         */
33019         "textchange" : true,
33020         /**
33021         * @event beforeexpand
33022         * Fires before this node is expanded, return false to cancel.
33023         * @param {Node} this This node
33024         * @param {Boolean} deep
33025         * @param {Boolean} anim
33026         */
33027         "beforeexpand" : true,
33028         /**
33029         * @event beforecollapse
33030         * Fires before this node is collapsed, return false to cancel.
33031         * @param {Node} this This node
33032         * @param {Boolean} deep
33033         * @param {Boolean} anim
33034         */
33035         "beforecollapse" : true,
33036         /**
33037         * @event expand
33038         * Fires when this node is expanded
33039         * @param {Node} this This node
33040         */
33041         "expand" : true,
33042         /**
33043         * @event disabledchange
33044         * Fires when the disabled status of this node changes
33045         * @param {Node} this This node
33046         * @param {Boolean} disabled
33047         */
33048         "disabledchange" : true,
33049         /**
33050         * @event collapse
33051         * Fires when this node is collapsed
33052         * @param {Node} this This node
33053         */
33054         "collapse" : true,
33055         /**
33056         * @event beforeclick
33057         * Fires before click processing. Return false to cancel the default action.
33058         * @param {Node} this This node
33059         * @param {Roo.EventObject} e The event object
33060         */
33061         "beforeclick":true,
33062         /**
33063         * @event checkchange
33064         * Fires when a node with a checkbox's checked property changes
33065         * @param {Node} this This node
33066         * @param {Boolean} checked
33067         */
33068         "checkchange":true,
33069         /**
33070         * @event click
33071         * Fires when this node is clicked
33072         * @param {Node} this This node
33073         * @param {Roo.EventObject} e The event object
33074         */
33075         "click":true,
33076         /**
33077         * @event dblclick
33078         * Fires when this node is double clicked
33079         * @param {Node} this This node
33080         * @param {Roo.EventObject} e The event object
33081         */
33082         "dblclick":true,
33083         /**
33084         * @event contextmenu
33085         * Fires when this node is right clicked
33086         * @param {Node} this This node
33087         * @param {Roo.EventObject} e The event object
33088         */
33089         "contextmenu":true,
33090         /**
33091         * @event beforechildrenrendered
33092         * Fires right before the child nodes for this node are rendered
33093         * @param {Node} this This node
33094         */
33095         "beforechildrenrendered":true
33096     });
33097
33098     var uiClass = this.attributes.uiProvider || Roo.tree.TreeNodeUI;
33099
33100     /**
33101      * Read-only. The UI for this node
33102      * @type TreeNodeUI
33103      */
33104     this.ui = new uiClass(this);
33105     
33106     // finally support items[]
33107     if (typeof(this.attributes.items) == 'undefined' || !this.attributes.items) {
33108         return;
33109     }
33110     
33111     
33112     Roo.each(this.attributes.items, function(c) {
33113         this.appendChild(Roo.factory(c,Roo.Tree));
33114     }, this);
33115     delete this.attributes.items;
33116     
33117     
33118     
33119 };
33120 Roo.extend(Roo.tree.TreeNode, Roo.data.Node, {
33121     preventHScroll: true,
33122     /**
33123      * Returns true if this node is expanded
33124      * @return {Boolean}
33125      */
33126     isExpanded : function(){
33127         return this.expanded;
33128     },
33129
33130     /**
33131      * Returns the UI object for this node
33132      * @return {TreeNodeUI}
33133      */
33134     getUI : function(){
33135         return this.ui;
33136     },
33137
33138     // private override
33139     setFirstChild : function(node){
33140         var of = this.firstChild;
33141         Roo.tree.TreeNode.superclass.setFirstChild.call(this, node);
33142         if(this.childrenRendered && of && node != of){
33143             of.renderIndent(true, true);
33144         }
33145         if(this.rendered){
33146             this.renderIndent(true, true);
33147         }
33148     },
33149
33150     // private override
33151     setLastChild : function(node){
33152         var ol = this.lastChild;
33153         Roo.tree.TreeNode.superclass.setLastChild.call(this, node);
33154         if(this.childrenRendered && ol && node != ol){
33155             ol.renderIndent(true, true);
33156         }
33157         if(this.rendered){
33158             this.renderIndent(true, true);
33159         }
33160     },
33161
33162     // these methods are overridden to provide lazy rendering support
33163     // private override
33164     appendChild : function()
33165     {
33166         var node = Roo.tree.TreeNode.superclass.appendChild.apply(this, arguments);
33167         if(node && this.childrenRendered){
33168             node.render();
33169         }
33170         this.ui.updateExpandIcon();
33171         return node;
33172     },
33173
33174     // private override
33175     removeChild : function(node){
33176         this.ownerTree.getSelectionModel().unselect(node);
33177         Roo.tree.TreeNode.superclass.removeChild.apply(this, arguments);
33178         // if it's been rendered remove dom node
33179         if(this.childrenRendered){
33180             node.ui.remove();
33181         }
33182         if(this.childNodes.length < 1){
33183             this.collapse(false, false);
33184         }else{
33185             this.ui.updateExpandIcon();
33186         }
33187         if(!this.firstChild) {
33188             this.childrenRendered = false;
33189         }
33190         return node;
33191     },
33192
33193     // private override
33194     insertBefore : function(node, refNode){
33195         var newNode = Roo.tree.TreeNode.superclass.insertBefore.apply(this, arguments);
33196         if(newNode && refNode && this.childrenRendered){
33197             node.render();
33198         }
33199         this.ui.updateExpandIcon();
33200         return newNode;
33201     },
33202
33203     /**
33204      * Sets the text for this node
33205      * @param {String} text
33206      */
33207     setText : function(text){
33208         var oldText = this.text;
33209         this.text = text;
33210         this.attributes.text = text;
33211         if(this.rendered){ // event without subscribing
33212             this.ui.onTextChange(this, text, oldText);
33213         }
33214         this.fireEvent("textchange", this, text, oldText);
33215     },
33216
33217     /**
33218      * Triggers selection of this node
33219      */
33220     select : function(){
33221         this.getOwnerTree().getSelectionModel().select(this);
33222     },
33223
33224     /**
33225      * Triggers deselection of this node
33226      */
33227     unselect : function(){
33228         this.getOwnerTree().getSelectionModel().unselect(this);
33229     },
33230
33231     /**
33232      * Returns true if this node is selected
33233      * @return {Boolean}
33234      */
33235     isSelected : function(){
33236         return this.getOwnerTree().getSelectionModel().isSelected(this);
33237     },
33238
33239     /**
33240      * Expand this node.
33241      * @param {Boolean} deep (optional) True to expand all children as well
33242      * @param {Boolean} anim (optional) false to cancel the default animation
33243      * @param {Function} callback (optional) A callback to be called when
33244      * expanding this node completes (does not wait for deep expand to complete).
33245      * Called with 1 parameter, this node.
33246      */
33247     expand : function(deep, anim, callback){
33248         if(!this.expanded){
33249             if(this.fireEvent("beforeexpand", this, deep, anim) === false){
33250                 return;
33251             }
33252             if(!this.childrenRendered){
33253                 this.renderChildren();
33254             }
33255             this.expanded = true;
33256             if(!this.isHiddenRoot() && (this.getOwnerTree().animate && anim !== false) || anim){
33257                 this.ui.animExpand(function(){
33258                     this.fireEvent("expand", this);
33259                     if(typeof callback == "function"){
33260                         callback(this);
33261                     }
33262                     if(deep === true){
33263                         this.expandChildNodes(true);
33264                     }
33265                 }.createDelegate(this));
33266                 return;
33267             }else{
33268                 this.ui.expand();
33269                 this.fireEvent("expand", this);
33270                 if(typeof callback == "function"){
33271                     callback(this);
33272                 }
33273             }
33274         }else{
33275            if(typeof callback == "function"){
33276                callback(this);
33277            }
33278         }
33279         if(deep === true){
33280             this.expandChildNodes(true);
33281         }
33282     },
33283
33284     isHiddenRoot : function(){
33285         return this.isRoot && !this.getOwnerTree().rootVisible;
33286     },
33287
33288     /**
33289      * Collapse this node.
33290      * @param {Boolean} deep (optional) True to collapse all children as well
33291      * @param {Boolean} anim (optional) false to cancel the default animation
33292      */
33293     collapse : function(deep, anim){
33294         if(this.expanded && !this.isHiddenRoot()){
33295             if(this.fireEvent("beforecollapse", this, deep, anim) === false){
33296                 return;
33297             }
33298             this.expanded = false;
33299             if((this.getOwnerTree().animate && anim !== false) || anim){
33300                 this.ui.animCollapse(function(){
33301                     this.fireEvent("collapse", this);
33302                     if(deep === true){
33303                         this.collapseChildNodes(true);
33304                     }
33305                 }.createDelegate(this));
33306                 return;
33307             }else{
33308                 this.ui.collapse();
33309                 this.fireEvent("collapse", this);
33310             }
33311         }
33312         if(deep === true){
33313             var cs = this.childNodes;
33314             for(var i = 0, len = cs.length; i < len; i++) {
33315                 cs[i].collapse(true, false);
33316             }
33317         }
33318     },
33319
33320     // private
33321     delayedExpand : function(delay){
33322         if(!this.expandProcId){
33323             this.expandProcId = this.expand.defer(delay, this);
33324         }
33325     },
33326
33327     // private
33328     cancelExpand : function(){
33329         if(this.expandProcId){
33330             clearTimeout(this.expandProcId);
33331         }
33332         this.expandProcId = false;
33333     },
33334
33335     /**
33336      * Toggles expanded/collapsed state of the node
33337      */
33338     toggle : function(){
33339         if(this.expanded){
33340             this.collapse();
33341         }else{
33342             this.expand();
33343         }
33344     },
33345
33346     /**
33347      * Ensures all parent nodes are expanded
33348      */
33349     ensureVisible : function(callback){
33350         var tree = this.getOwnerTree();
33351         tree.expandPath(this.parentNode.getPath(), false, function(){
33352             tree.getTreeEl().scrollChildIntoView(this.ui.anchor);
33353             Roo.callback(callback);
33354         }.createDelegate(this));
33355     },
33356
33357     /**
33358      * Expand all child nodes
33359      * @param {Boolean} deep (optional) true if the child nodes should also expand their child nodes
33360      */
33361     expandChildNodes : function(deep){
33362         var cs = this.childNodes;
33363         for(var i = 0, len = cs.length; i < len; i++) {
33364                 cs[i].expand(deep);
33365         }
33366     },
33367
33368     /**
33369      * Collapse all child nodes
33370      * @param {Boolean} deep (optional) true if the child nodes should also collapse their child nodes
33371      */
33372     collapseChildNodes : function(deep){
33373         var cs = this.childNodes;
33374         for(var i = 0, len = cs.length; i < len; i++) {
33375                 cs[i].collapse(deep);
33376         }
33377     },
33378
33379     /**
33380      * Disables this node
33381      */
33382     disable : function(){
33383         this.disabled = true;
33384         this.unselect();
33385         if(this.rendered && this.ui.onDisableChange){ // event without subscribing
33386             this.ui.onDisableChange(this, true);
33387         }
33388         this.fireEvent("disabledchange", this, true);
33389     },
33390
33391     /**
33392      * Enables this node
33393      */
33394     enable : function(){
33395         this.disabled = false;
33396         if(this.rendered && this.ui.onDisableChange){ // event without subscribing
33397             this.ui.onDisableChange(this, false);
33398         }
33399         this.fireEvent("disabledchange", this, false);
33400     },
33401
33402     // private
33403     renderChildren : function(suppressEvent){
33404         if(suppressEvent !== false){
33405             this.fireEvent("beforechildrenrendered", this);
33406         }
33407         var cs = this.childNodes;
33408         for(var i = 0, len = cs.length; i < len; i++){
33409             cs[i].render(true);
33410         }
33411         this.childrenRendered = true;
33412     },
33413
33414     // private
33415     sort : function(fn, scope){
33416         Roo.tree.TreeNode.superclass.sort.apply(this, arguments);
33417         if(this.childrenRendered){
33418             var cs = this.childNodes;
33419             for(var i = 0, len = cs.length; i < len; i++){
33420                 cs[i].render(true);
33421             }
33422         }
33423     },
33424
33425     // private
33426     render : function(bulkRender){
33427         this.ui.render(bulkRender);
33428         if(!this.rendered){
33429             this.rendered = true;
33430             if(this.expanded){
33431                 this.expanded = false;
33432                 this.expand(false, false);
33433             }
33434         }
33435     },
33436
33437     // private
33438     renderIndent : function(deep, refresh){
33439         if(refresh){
33440             this.ui.childIndent = null;
33441         }
33442         this.ui.renderIndent();
33443         if(deep === true && this.childrenRendered){
33444             var cs = this.childNodes;
33445             for(var i = 0, len = cs.length; i < len; i++){
33446                 cs[i].renderIndent(true, refresh);
33447             }
33448         }
33449     }
33450 });/*
33451  * Based on:
33452  * Ext JS Library 1.1.1
33453  * Copyright(c) 2006-2007, Ext JS, LLC.
33454  *
33455  * Originally Released Under LGPL - original licence link has changed is not relivant.
33456  *
33457  * Fork - LGPL
33458  * <script type="text/javascript">
33459  */
33460  
33461 /**
33462  * @class Roo.tree.AsyncTreeNode
33463  * @extends Roo.tree.TreeNode
33464  * @cfg {TreeLoader} loader A TreeLoader to be used by this node (defaults to the loader defined on the tree)
33465  * @constructor
33466  * @param {Object/String} attributes The attributes/config for the node or just a string with the text for the node 
33467  */
33468  Roo.tree.AsyncTreeNode = function(config){
33469     this.loaded = false;
33470     this.loading = false;
33471     Roo.tree.AsyncTreeNode.superclass.constructor.apply(this, arguments);
33472     /**
33473     * @event beforeload
33474     * Fires before this node is loaded, return false to cancel
33475     * @param {Node} this This node
33476     */
33477     this.addEvents({'beforeload':true, 'load': true});
33478     /**
33479     * @event load
33480     * Fires when this node is loaded
33481     * @param {Node} this This node
33482     */
33483     /**
33484      * The loader used by this node (defaults to using the tree's defined loader)
33485      * @type TreeLoader
33486      * @property loader
33487      */
33488 };
33489 Roo.extend(Roo.tree.AsyncTreeNode, Roo.tree.TreeNode, {
33490     expand : function(deep, anim, callback){
33491         if(this.loading){ // if an async load is already running, waiting til it's done
33492             var timer;
33493             var f = function(){
33494                 if(!this.loading){ // done loading
33495                     clearInterval(timer);
33496                     this.expand(deep, anim, callback);
33497                 }
33498             }.createDelegate(this);
33499             timer = setInterval(f, 200);
33500             return;
33501         }
33502         if(!this.loaded){
33503             if(this.fireEvent("beforeload", this) === false){
33504                 return;
33505             }
33506             this.loading = true;
33507             this.ui.beforeLoad(this);
33508             var loader = this.loader || this.attributes.loader || this.getOwnerTree().getLoader();
33509             if(loader){
33510                 loader.load(this, this.loadComplete.createDelegate(this, [deep, anim, callback]));
33511                 return;
33512             }
33513         }
33514         Roo.tree.AsyncTreeNode.superclass.expand.call(this, deep, anim, callback);
33515     },
33516     
33517     /**
33518      * Returns true if this node is currently loading
33519      * @return {Boolean}
33520      */
33521     isLoading : function(){
33522         return this.loading;  
33523     },
33524     
33525     loadComplete : function(deep, anim, callback){
33526         this.loading = false;
33527         this.loaded = true;
33528         this.ui.afterLoad(this);
33529         this.fireEvent("load", this);
33530         this.expand(deep, anim, callback);
33531     },
33532     
33533     /**
33534      * Returns true if this node has been loaded
33535      * @return {Boolean}
33536      */
33537     isLoaded : function(){
33538         return this.loaded;
33539     },
33540     
33541     hasChildNodes : function(){
33542         if(!this.isLeaf() && !this.loaded){
33543             return true;
33544         }else{
33545             return Roo.tree.AsyncTreeNode.superclass.hasChildNodes.call(this);
33546         }
33547     },
33548
33549     /**
33550      * Trigger a reload for this node
33551      * @param {Function} callback
33552      */
33553     reload : function(callback){
33554         this.collapse(false, false);
33555         while(this.firstChild){
33556             this.removeChild(this.firstChild);
33557         }
33558         this.childrenRendered = false;
33559         this.loaded = false;
33560         if(this.isHiddenRoot()){
33561             this.expanded = false;
33562         }
33563         this.expand(false, false, callback);
33564     }
33565 });/*
33566  * Based on:
33567  * Ext JS Library 1.1.1
33568  * Copyright(c) 2006-2007, Ext JS, LLC.
33569  *
33570  * Originally Released Under LGPL - original licence link has changed is not relivant.
33571  *
33572  * Fork - LGPL
33573  * <script type="text/javascript">
33574  */
33575  
33576 /**
33577  * @class Roo.tree.TreeNodeUI
33578  * @constructor
33579  * @param {Object} node The node to render
33580  * The TreeNode UI implementation is separate from the
33581  * tree implementation. Unless you are customizing the tree UI,
33582  * you should never have to use this directly.
33583  */
33584 Roo.tree.TreeNodeUI = function(node){
33585     this.node = node;
33586     this.rendered = false;
33587     this.animating = false;
33588     this.emptyIcon = Roo.BLANK_IMAGE_URL;
33589 };
33590
33591 Roo.tree.TreeNodeUI.prototype = {
33592     removeChild : function(node){
33593         if(this.rendered){
33594             this.ctNode.removeChild(node.ui.getEl());
33595         }
33596     },
33597
33598     beforeLoad : function(){
33599          this.addClass("x-tree-node-loading");
33600     },
33601
33602     afterLoad : function(){
33603          this.removeClass("x-tree-node-loading");
33604     },
33605
33606     onTextChange : function(node, text, oldText){
33607         if(this.rendered){
33608             this.textNode.innerHTML = text;
33609         }
33610     },
33611
33612     onDisableChange : function(node, state){
33613         this.disabled = state;
33614         if(state){
33615             this.addClass("x-tree-node-disabled");
33616         }else{
33617             this.removeClass("x-tree-node-disabled");
33618         }
33619     },
33620
33621     onSelectedChange : function(state){
33622         if(state){
33623             this.focus();
33624             this.addClass("x-tree-selected");
33625         }else{
33626             //this.blur();
33627             this.removeClass("x-tree-selected");
33628         }
33629     },
33630
33631     onMove : function(tree, node, oldParent, newParent, index, refNode){
33632         this.childIndent = null;
33633         if(this.rendered){
33634             var targetNode = newParent.ui.getContainer();
33635             if(!targetNode){//target not rendered
33636                 this.holder = document.createElement("div");
33637                 this.holder.appendChild(this.wrap);
33638                 return;
33639             }
33640             var insertBefore = refNode ? refNode.ui.getEl() : null;
33641             if(insertBefore){
33642                 targetNode.insertBefore(this.wrap, insertBefore);
33643             }else{
33644                 targetNode.appendChild(this.wrap);
33645             }
33646             this.node.renderIndent(true);
33647         }
33648     },
33649
33650     addClass : function(cls){
33651         if(this.elNode){
33652             Roo.fly(this.elNode).addClass(cls);
33653         }
33654     },
33655
33656     removeClass : function(cls){
33657         if(this.elNode){
33658             Roo.fly(this.elNode).removeClass(cls);
33659         }
33660     },
33661
33662     remove : function(){
33663         if(this.rendered){
33664             this.holder = document.createElement("div");
33665             this.holder.appendChild(this.wrap);
33666         }
33667     },
33668
33669     fireEvent : function(){
33670         return this.node.fireEvent.apply(this.node, arguments);
33671     },
33672
33673     initEvents : function(){
33674         this.node.on("move", this.onMove, this);
33675         var E = Roo.EventManager;
33676         var a = this.anchor;
33677
33678         var el = Roo.fly(a, '_treeui');
33679
33680         if(Roo.isOpera){ // opera render bug ignores the CSS
33681             el.setStyle("text-decoration", "none");
33682         }
33683
33684         el.on("click", this.onClick, this);
33685         el.on("dblclick", this.onDblClick, this);
33686
33687         if(this.checkbox){
33688             Roo.EventManager.on(this.checkbox,
33689                     Roo.isIE ? 'click' : 'change', this.onCheckChange, this);
33690         }
33691
33692         el.on("contextmenu", this.onContextMenu, this);
33693
33694         var icon = Roo.fly(this.iconNode);
33695         icon.on("click", this.onClick, this);
33696         icon.on("dblclick", this.onDblClick, this);
33697         icon.on("contextmenu", this.onContextMenu, this);
33698         E.on(this.ecNode, "click", this.ecClick, this, true);
33699
33700         if(this.node.disabled){
33701             this.addClass("x-tree-node-disabled");
33702         }
33703         if(this.node.hidden){
33704             this.addClass("x-tree-node-disabled");
33705         }
33706         var ot = this.node.getOwnerTree();
33707         var dd = ot.enableDD || ot.enableDrag || ot.enableDrop;
33708         if(dd && (!this.node.isRoot || ot.rootVisible)){
33709             Roo.dd.Registry.register(this.elNode, {
33710                 node: this.node,
33711                 handles: this.getDDHandles(),
33712                 isHandle: false
33713             });
33714         }
33715     },
33716
33717     getDDHandles : function(){
33718         return [this.iconNode, this.textNode];
33719     },
33720
33721     hide : function(){
33722         if(this.rendered){
33723             this.wrap.style.display = "none";
33724         }
33725     },
33726
33727     show : function(){
33728         if(this.rendered){
33729             this.wrap.style.display = "";
33730         }
33731     },
33732
33733     onContextMenu : function(e){
33734         if (this.node.hasListener("contextmenu") || this.node.getOwnerTree().hasListener("contextmenu")) {
33735             e.preventDefault();
33736             this.focus();
33737             this.fireEvent("contextmenu", this.node, e);
33738         }
33739     },
33740
33741     onClick : function(e){
33742         if(this.dropping){
33743             e.stopEvent();
33744             return;
33745         }
33746         if(this.fireEvent("beforeclick", this.node, e) !== false){
33747             if(!this.disabled && this.node.attributes.href){
33748                 this.fireEvent("click", this.node, e);
33749                 return;
33750             }
33751             e.preventDefault();
33752             if(this.disabled){
33753                 return;
33754             }
33755
33756             if(this.node.attributes.singleClickExpand && !this.animating && this.node.hasChildNodes()){
33757                 this.node.toggle();
33758             }
33759
33760             this.fireEvent("click", this.node, e);
33761         }else{
33762             e.stopEvent();
33763         }
33764     },
33765
33766     onDblClick : function(e){
33767         e.preventDefault();
33768         if(this.disabled){
33769             return;
33770         }
33771         if(this.checkbox){
33772             this.toggleCheck();
33773         }
33774         if(!this.animating && this.node.hasChildNodes()){
33775             this.node.toggle();
33776         }
33777         this.fireEvent("dblclick", this.node, e);
33778     },
33779
33780     onCheckChange : function(){
33781         var checked = this.checkbox.checked;
33782         this.node.attributes.checked = checked;
33783         this.fireEvent('checkchange', this.node, checked);
33784     },
33785
33786     ecClick : function(e){
33787         if(!this.animating && this.node.hasChildNodes()){
33788             this.node.toggle();
33789         }
33790     },
33791
33792     startDrop : function(){
33793         this.dropping = true;
33794     },
33795
33796     // delayed drop so the click event doesn't get fired on a drop
33797     endDrop : function(){
33798        setTimeout(function(){
33799            this.dropping = false;
33800        }.createDelegate(this), 50);
33801     },
33802
33803     expand : function(){
33804         this.updateExpandIcon();
33805         this.ctNode.style.display = "";
33806     },
33807
33808     focus : function(){
33809         if(!this.node.preventHScroll){
33810             try{this.anchor.focus();
33811             }catch(e){}
33812         }else if(!Roo.isIE){
33813             try{
33814                 var noscroll = this.node.getOwnerTree().getTreeEl().dom;
33815                 var l = noscroll.scrollLeft;
33816                 this.anchor.focus();
33817                 noscroll.scrollLeft = l;
33818             }catch(e){}
33819         }
33820     },
33821
33822     toggleCheck : function(value){
33823         var cb = this.checkbox;
33824         if(cb){
33825             cb.checked = (value === undefined ? !cb.checked : value);
33826         }
33827     },
33828
33829     blur : function(){
33830         try{
33831             this.anchor.blur();
33832         }catch(e){}
33833     },
33834
33835     animExpand : function(callback){
33836         var ct = Roo.get(this.ctNode);
33837         ct.stopFx();
33838         if(!this.node.hasChildNodes()){
33839             this.updateExpandIcon();
33840             this.ctNode.style.display = "";
33841             Roo.callback(callback);
33842             return;
33843         }
33844         this.animating = true;
33845         this.updateExpandIcon();
33846
33847         ct.slideIn('t', {
33848            callback : function(){
33849                this.animating = false;
33850                Roo.callback(callback);
33851             },
33852             scope: this,
33853             duration: this.node.ownerTree.duration || .25
33854         });
33855     },
33856
33857     highlight : function(){
33858         var tree = this.node.getOwnerTree();
33859         Roo.fly(this.wrap).highlight(
33860             tree.hlColor || "C3DAF9",
33861             {endColor: tree.hlBaseColor}
33862         );
33863     },
33864
33865     collapse : function(){
33866         this.updateExpandIcon();
33867         this.ctNode.style.display = "none";
33868     },
33869
33870     animCollapse : function(callback){
33871         var ct = Roo.get(this.ctNode);
33872         ct.enableDisplayMode('block');
33873         ct.stopFx();
33874
33875         this.animating = true;
33876         this.updateExpandIcon();
33877
33878         ct.slideOut('t', {
33879             callback : function(){
33880                this.animating = false;
33881                Roo.callback(callback);
33882             },
33883             scope: this,
33884             duration: this.node.ownerTree.duration || .25
33885         });
33886     },
33887
33888     getContainer : function(){
33889         return this.ctNode;
33890     },
33891
33892     getEl : function(){
33893         return this.wrap;
33894     },
33895
33896     appendDDGhost : function(ghostNode){
33897         ghostNode.appendChild(this.elNode.cloneNode(true));
33898     },
33899
33900     getDDRepairXY : function(){
33901         return Roo.lib.Dom.getXY(this.iconNode);
33902     },
33903
33904     onRender : function(){
33905         this.render();
33906     },
33907
33908     render : function(bulkRender){
33909         var n = this.node, a = n.attributes;
33910         var targetNode = n.parentNode ?
33911               n.parentNode.ui.getContainer() : n.ownerTree.innerCt.dom;
33912
33913         if(!this.rendered){
33914             this.rendered = true;
33915
33916             this.renderElements(n, a, targetNode, bulkRender);
33917
33918             if(a.qtip){
33919                if(this.textNode.setAttributeNS){
33920                    this.textNode.setAttributeNS("ext", "qtip", a.qtip);
33921                    if(a.qtipTitle){
33922                        this.textNode.setAttributeNS("ext", "qtitle", a.qtipTitle);
33923                    }
33924                }else{
33925                    this.textNode.setAttribute("ext:qtip", a.qtip);
33926                    if(a.qtipTitle){
33927                        this.textNode.setAttribute("ext:qtitle", a.qtipTitle);
33928                    }
33929                }
33930             }else if(a.qtipCfg){
33931                 a.qtipCfg.target = Roo.id(this.textNode);
33932                 Roo.QuickTips.register(a.qtipCfg);
33933             }
33934             this.initEvents();
33935             if(!this.node.expanded){
33936                 this.updateExpandIcon();
33937             }
33938         }else{
33939             if(bulkRender === true) {
33940                 targetNode.appendChild(this.wrap);
33941             }
33942         }
33943     },
33944
33945     renderElements : function(n, a, targetNode, bulkRender)
33946     {
33947         // add some indent caching, this helps performance when rendering a large tree
33948         this.indentMarkup = n.parentNode ? n.parentNode.ui.getChildIndent() : '';
33949         var t = n.getOwnerTree();
33950         var txt = t.renderer ? t.renderer(n.attributes) : Roo.util.Format.htmlEncode(n.text);
33951         if (typeof(n.attributes.html) != 'undefined') {
33952             txt = n.attributes.html;
33953         }
33954         var tip = t.rendererTip ? t.rendererTip(n.attributes) : txt;
33955         var cb = typeof a.checked == 'boolean';
33956         var href = a.href ? a.href : Roo.isGecko ? "" : "#";
33957         var buf = ['<li class="x-tree-node"><div class="x-tree-node-el ', a.cls,'">',
33958             '<span class="x-tree-node-indent">',this.indentMarkup,"</span>",
33959             '<img src="', this.emptyIcon, '" class="x-tree-ec-icon" />',
33960             '<img src="', a.icon || this.emptyIcon, '" class="x-tree-node-icon',(a.icon ? " x-tree-node-inline-icon" : ""),(a.iconCls ? " "+a.iconCls : ""),'" unselectable="on" />',
33961             cb ? ('<input class="x-tree-node-cb" type="checkbox" ' + (a.checked ? 'checked="checked" />' : ' />')) : '',
33962             '<a hidefocus="on" href="',href,'" tabIndex="1" ',
33963              a.hrefTarget ? ' target="'+a.hrefTarget+'"' : "", 
33964                 '><span unselectable="on" qtip="' , tip ,'">',txt,"</span></a></div>",
33965             '<ul class="x-tree-node-ct" style="display:none;"></ul>',
33966             "</li>"];
33967
33968         if(bulkRender !== true && n.nextSibling && n.nextSibling.ui.getEl()){
33969             this.wrap = Roo.DomHelper.insertHtml("beforeBegin",
33970                                 n.nextSibling.ui.getEl(), buf.join(""));
33971         }else{
33972             this.wrap = Roo.DomHelper.insertHtml("beforeEnd", targetNode, buf.join(""));
33973         }
33974
33975         this.elNode = this.wrap.childNodes[0];
33976         this.ctNode = this.wrap.childNodes[1];
33977         var cs = this.elNode.childNodes;
33978         this.indentNode = cs[0];
33979         this.ecNode = cs[1];
33980         this.iconNode = cs[2];
33981         var index = 3;
33982         if(cb){
33983             this.checkbox = cs[3];
33984             index++;
33985         }
33986         this.anchor = cs[index];
33987         this.textNode = cs[index].firstChild;
33988     },
33989
33990     getAnchor : function(){
33991         return this.anchor;
33992     },
33993
33994     getTextEl : function(){
33995         return this.textNode;
33996     },
33997
33998     getIconEl : function(){
33999         return this.iconNode;
34000     },
34001
34002     isChecked : function(){
34003         return this.checkbox ? this.checkbox.checked : false;
34004     },
34005
34006     updateExpandIcon : function(){
34007         if(this.rendered){
34008             var n = this.node, c1, c2;
34009             var cls = n.isLast() ? "x-tree-elbow-end" : "x-tree-elbow";
34010             var hasChild = n.hasChildNodes();
34011             if(hasChild){
34012                 if(n.expanded){
34013                     cls += "-minus";
34014                     c1 = "x-tree-node-collapsed";
34015                     c2 = "x-tree-node-expanded";
34016                 }else{
34017                     cls += "-plus";
34018                     c1 = "x-tree-node-expanded";
34019                     c2 = "x-tree-node-collapsed";
34020                 }
34021                 if(this.wasLeaf){
34022                     this.removeClass("x-tree-node-leaf");
34023                     this.wasLeaf = false;
34024                 }
34025                 if(this.c1 != c1 || this.c2 != c2){
34026                     Roo.fly(this.elNode).replaceClass(c1, c2);
34027                     this.c1 = c1; this.c2 = c2;
34028                 }
34029             }else{
34030                 // this changes non-leafs into leafs if they have no children.
34031                 // it's not very rational behaviour..
34032                 
34033                 if(!this.wasLeaf && this.node.leaf){
34034                     Roo.fly(this.elNode).replaceClass("x-tree-node-expanded", "x-tree-node-leaf");
34035                     delete this.c1;
34036                     delete this.c2;
34037                     this.wasLeaf = true;
34038                 }
34039             }
34040             var ecc = "x-tree-ec-icon "+cls;
34041             if(this.ecc != ecc){
34042                 this.ecNode.className = ecc;
34043                 this.ecc = ecc;
34044             }
34045         }
34046     },
34047
34048     getChildIndent : function(){
34049         if(!this.childIndent){
34050             var buf = [];
34051             var p = this.node;
34052             while(p){
34053                 if(!p.isRoot || (p.isRoot && p.ownerTree.rootVisible)){
34054                     if(!p.isLast()) {
34055                         buf.unshift('<img src="'+this.emptyIcon+'" class="x-tree-elbow-line" />');
34056                     } else {
34057                         buf.unshift('<img src="'+this.emptyIcon+'" class="x-tree-icon" />');
34058                     }
34059                 }
34060                 p = p.parentNode;
34061             }
34062             this.childIndent = buf.join("");
34063         }
34064         return this.childIndent;
34065     },
34066
34067     renderIndent : function(){
34068         if(this.rendered){
34069             var indent = "";
34070             var p = this.node.parentNode;
34071             if(p){
34072                 indent = p.ui.getChildIndent();
34073             }
34074             if(this.indentMarkup != indent){ // don't rerender if not required
34075                 this.indentNode.innerHTML = indent;
34076                 this.indentMarkup = indent;
34077             }
34078             this.updateExpandIcon();
34079         }
34080     }
34081 };
34082
34083 Roo.tree.RootTreeNodeUI = function(){
34084     Roo.tree.RootTreeNodeUI.superclass.constructor.apply(this, arguments);
34085 };
34086 Roo.extend(Roo.tree.RootTreeNodeUI, Roo.tree.TreeNodeUI, {
34087     render : function(){
34088         if(!this.rendered){
34089             var targetNode = this.node.ownerTree.innerCt.dom;
34090             this.node.expanded = true;
34091             targetNode.innerHTML = '<div class="x-tree-root-node"></div>';
34092             this.wrap = this.ctNode = targetNode.firstChild;
34093         }
34094     },
34095     collapse : function(){
34096     },
34097     expand : function(){
34098     }
34099 });/*
34100  * Based on:
34101  * Ext JS Library 1.1.1
34102  * Copyright(c) 2006-2007, Ext JS, LLC.
34103  *
34104  * Originally Released Under LGPL - original licence link has changed is not relivant.
34105  *
34106  * Fork - LGPL
34107  * <script type="text/javascript">
34108  */
34109 /**
34110  * @class Roo.tree.TreeLoader
34111  * @extends Roo.util.Observable
34112  * A TreeLoader provides for lazy loading of an {@link Roo.tree.TreeNode}'s child
34113  * nodes from a specified URL. The response must be a javascript Array definition
34114  * who's elements are node definition objects. eg:
34115  * <pre><code>
34116 {  success : true,
34117    data :      [
34118    
34119     { 'id': 1, 'text': 'A folder Node', 'leaf': false },
34120     { 'id': 2, 'text': 'A leaf Node', 'leaf': true }
34121     ]
34122 }
34123
34124
34125 </code></pre>
34126  * <br><br>
34127  * The old style respose with just an array is still supported, but not recommended.
34128  * <br><br>
34129  *
34130  * A server request is sent, and child nodes are loaded only when a node is expanded.
34131  * The loading node's id is passed to the server under the parameter name "node" to
34132  * enable the server to produce the correct child nodes.
34133  * <br><br>
34134  * To pass extra parameters, an event handler may be attached to the "beforeload"
34135  * event, and the parameters specified in the TreeLoader's baseParams property:
34136  * <pre><code>
34137     myTreeLoader.on("beforeload", function(treeLoader, node) {
34138         this.baseParams.category = node.attributes.category;
34139     }, this);
34140 </code></pre><
34141  * This would pass an HTTP parameter called "category" to the server containing
34142  * the value of the Node's "category" attribute.
34143  * @constructor
34144  * Creates a new Treeloader.
34145  * @param {Object} config A config object containing config properties.
34146  */
34147 Roo.tree.TreeLoader = function(config){
34148     this.baseParams = {};
34149     this.requestMethod = "POST";
34150     Roo.apply(this, config);
34151
34152     this.addEvents({
34153     
34154         /**
34155          * @event beforeload
34156          * Fires before a network request is made to retrieve the Json text which specifies a node's children.
34157          * @param {Object} This TreeLoader object.
34158          * @param {Object} node The {@link Roo.tree.TreeNode} object being loaded.
34159          * @param {Object} callback The callback function specified in the {@link #load} call.
34160          */
34161         beforeload : true,
34162         /**
34163          * @event load
34164          * Fires when the node has been successfuly loaded.
34165          * @param {Object} This TreeLoader object.
34166          * @param {Object} node The {@link Roo.tree.TreeNode} object being loaded.
34167          * @param {Object} response The response object containing the data from the server.
34168          */
34169         load : true,
34170         /**
34171          * @event loadexception
34172          * Fires if the network request failed.
34173          * @param {Object} This TreeLoader object.
34174          * @param {Object} node The {@link Roo.tree.TreeNode} object being loaded.
34175          * @param {Object} response The response object containing the data from the server.
34176          */
34177         loadexception : true,
34178         /**
34179          * @event create
34180          * Fires before a node is created, enabling you to return custom Node types 
34181          * @param {Object} This TreeLoader object.
34182          * @param {Object} attr - the data returned from the AJAX call (modify it to suit)
34183          */
34184         create : true
34185     });
34186
34187     Roo.tree.TreeLoader.superclass.constructor.call(this);
34188 };
34189
34190 Roo.extend(Roo.tree.TreeLoader, Roo.util.Observable, {
34191     /**
34192     * @cfg {String} dataUrl The URL from which to request a Json string which
34193     * specifies an array of node definition object representing the child nodes
34194     * to be loaded.
34195     */
34196     /**
34197     * @cfg {String} requestMethod either GET or POST
34198     * defaults to POST (due to BC)
34199     * to be loaded.
34200     */
34201     /**
34202     * @cfg {Object} baseParams (optional) An object containing properties which
34203     * specify HTTP parameters to be passed to each request for child nodes.
34204     */
34205     /**
34206     * @cfg {Object} baseAttrs (optional) An object containing attributes to be added to all nodes
34207     * created by this loader. If the attributes sent by the server have an attribute in this object,
34208     * they take priority.
34209     */
34210     /**
34211     * @cfg {Object} uiProviders (optional) An object containing properties which
34212     * 
34213     * DEPRECATED - use 'create' event handler to modify attributes - which affect creation.
34214     * specify custom {@link Roo.tree.TreeNodeUI} implementations. If the optional
34215     * <i>uiProvider</i> attribute of a returned child node is a string rather
34216     * than a reference to a TreeNodeUI implementation, this that string value
34217     * is used as a property name in the uiProviders object. You can define the provider named
34218     * 'default' , and this will be used for all nodes (if no uiProvider is delivered by the node data)
34219     */
34220     uiProviders : {},
34221
34222     /**
34223     * @cfg {Boolean} clearOnLoad (optional) Default to true. Remove previously existing
34224     * child nodes before loading.
34225     */
34226     clearOnLoad : true,
34227
34228     /**
34229     * @cfg {String} root (optional) Default to false. Use this to read data from an object 
34230     * property on loading, rather than expecting an array. (eg. more compatible to a standard
34231     * Grid query { data : [ .....] }
34232     */
34233     
34234     root : false,
34235      /**
34236     * @cfg {String} queryParam (optional) 
34237     * Name of the query as it will be passed on the querystring (defaults to 'node')
34238     * eg. the request will be ?node=[id]
34239     */
34240     
34241     
34242     queryParam: false,
34243     
34244     /**
34245      * Load an {@link Roo.tree.TreeNode} from the URL specified in the constructor.
34246      * This is called automatically when a node is expanded, but may be used to reload
34247      * a node (or append new children if the {@link #clearOnLoad} option is false.)
34248      * @param {Roo.tree.TreeNode} node
34249      * @param {Function} callback
34250      */
34251     load : function(node, callback){
34252         if(this.clearOnLoad){
34253             while(node.firstChild){
34254                 node.removeChild(node.firstChild);
34255             }
34256         }
34257         if(node.attributes.children){ // preloaded json children
34258             var cs = node.attributes.children;
34259             for(var i = 0, len = cs.length; i < len; i++){
34260                 node.appendChild(this.createNode(cs[i]));
34261             }
34262             if(typeof callback == "function"){
34263                 callback();
34264             }
34265         }else if(this.dataUrl){
34266             this.requestData(node, callback);
34267         }
34268     },
34269
34270     getParams: function(node){
34271         var buf = [], bp = this.baseParams;
34272         for(var key in bp){
34273             if(typeof bp[key] != "function"){
34274                 buf.push(encodeURIComponent(key), "=", encodeURIComponent(bp[key]), "&");
34275             }
34276         }
34277         var n = this.queryParam === false ? 'node' : this.queryParam;
34278         buf.push(n + "=", encodeURIComponent(node.id));
34279         return buf.join("");
34280     },
34281
34282     requestData : function(node, callback){
34283         if(this.fireEvent("beforeload", this, node, callback) !== false){
34284             this.transId = Roo.Ajax.request({
34285                 method:this.requestMethod,
34286                 url: this.dataUrl||this.url,
34287                 success: this.handleResponse,
34288                 failure: this.handleFailure,
34289                 scope: this,
34290                 argument: {callback: callback, node: node},
34291                 params: this.getParams(node)
34292             });
34293         }else{
34294             // if the load is cancelled, make sure we notify
34295             // the node that we are done
34296             if(typeof callback == "function"){
34297                 callback();
34298             }
34299         }
34300     },
34301
34302     isLoading : function(){
34303         return this.transId ? true : false;
34304     },
34305
34306     abort : function(){
34307         if(this.isLoading()){
34308             Roo.Ajax.abort(this.transId);
34309         }
34310     },
34311
34312     // private
34313     createNode : function(attr)
34314     {
34315         // apply baseAttrs, nice idea Corey!
34316         if(this.baseAttrs){
34317             Roo.applyIf(attr, this.baseAttrs);
34318         }
34319         if(this.applyLoader !== false){
34320             attr.loader = this;
34321         }
34322         // uiProvider = depreciated..
34323         
34324         if(typeof(attr.uiProvider) == 'string'){
34325            attr.uiProvider = this.uiProviders[attr.uiProvider] || 
34326                 /**  eval:var:attr */ eval(attr.uiProvider);
34327         }
34328         if(typeof(this.uiProviders['default']) != 'undefined') {
34329             attr.uiProvider = this.uiProviders['default'];
34330         }
34331         
34332         this.fireEvent('create', this, attr);
34333         
34334         attr.leaf  = typeof(attr.leaf) == 'string' ? attr.leaf * 1 : attr.leaf;
34335         return(attr.leaf ?
34336                         new Roo.tree.TreeNode(attr) :
34337                         new Roo.tree.AsyncTreeNode(attr));
34338     },
34339
34340     processResponse : function(response, node, callback)
34341     {
34342         var json = response.responseText;
34343         try {
34344             
34345             var o = Roo.decode(json);
34346             
34347             if (this.root === false && typeof(o.success) != undefined) {
34348                 this.root = 'data'; // the default behaviour for list like data..
34349                 }
34350                 
34351             if (this.root !== false &&  !o.success) {
34352                 // it's a failure condition.
34353                 var a = response.argument;
34354                 this.fireEvent("loadexception", this, a.node, response);
34355                 Roo.log("Load failed - should have a handler really");
34356                 return;
34357             }
34358             
34359             
34360             
34361             if (this.root !== false) {
34362                  o = o[this.root];
34363             }
34364             
34365             for(var i = 0, len = o.length; i < len; i++){
34366                 var n = this.createNode(o[i]);
34367                 if(n){
34368                     node.appendChild(n);
34369                 }
34370             }
34371             if(typeof callback == "function"){
34372                 callback(this, node);
34373             }
34374         }catch(e){
34375             this.handleFailure(response);
34376         }
34377     },
34378
34379     handleResponse : function(response){
34380         this.transId = false;
34381         var a = response.argument;
34382         this.processResponse(response, a.node, a.callback);
34383         this.fireEvent("load", this, a.node, response);
34384     },
34385
34386     handleFailure : function(response)
34387     {
34388         // should handle failure better..
34389         this.transId = false;
34390         var a = response.argument;
34391         this.fireEvent("loadexception", this, a.node, response);
34392         if(typeof a.callback == "function"){
34393             a.callback(this, a.node);
34394         }
34395     }
34396 });/*
34397  * Based on:
34398  * Ext JS Library 1.1.1
34399  * Copyright(c) 2006-2007, Ext JS, LLC.
34400  *
34401  * Originally Released Under LGPL - original licence link has changed is not relivant.
34402  *
34403  * Fork - LGPL
34404  * <script type="text/javascript">
34405  */
34406
34407 /**
34408 * @class Roo.tree.TreeFilter
34409 * Note this class is experimental and doesn't update the indent (lines) or expand collapse icons of the nodes
34410 * @param {TreePanel} tree
34411 * @param {Object} config (optional)
34412  */
34413 Roo.tree.TreeFilter = function(tree, config){
34414     this.tree = tree;
34415     this.filtered = {};
34416     Roo.apply(this, config);
34417 };
34418
34419 Roo.tree.TreeFilter.prototype = {
34420     clearBlank:false,
34421     reverse:false,
34422     autoClear:false,
34423     remove:false,
34424
34425      /**
34426      * Filter the data by a specific attribute.
34427      * @param {String/RegExp} value Either string that the attribute value
34428      * should start with or a RegExp to test against the attribute
34429      * @param {String} attr (optional) The attribute passed in your node's attributes collection. Defaults to "text".
34430      * @param {TreeNode} startNode (optional) The node to start the filter at.
34431      */
34432     filter : function(value, attr, startNode){
34433         attr = attr || "text";
34434         var f;
34435         if(typeof value == "string"){
34436             var vlen = value.length;
34437             // auto clear empty filter
34438             if(vlen == 0 && this.clearBlank){
34439                 this.clear();
34440                 return;
34441             }
34442             value = value.toLowerCase();
34443             f = function(n){
34444                 return n.attributes[attr].substr(0, vlen).toLowerCase() == value;
34445             };
34446         }else if(value.exec){ // regex?
34447             f = function(n){
34448                 return value.test(n.attributes[attr]);
34449             };
34450         }else{
34451             throw 'Illegal filter type, must be string or regex';
34452         }
34453         this.filterBy(f, null, startNode);
34454         },
34455
34456     /**
34457      * Filter by a function. The passed function will be called with each
34458      * node in the tree (or from the startNode). If the function returns true, the node is kept
34459      * otherwise it is filtered. If a node is filtered, its children are also filtered.
34460      * @param {Function} fn The filter function
34461      * @param {Object} scope (optional) The scope of the function (defaults to the current node)
34462      */
34463     filterBy : function(fn, scope, startNode){
34464         startNode = startNode || this.tree.root;
34465         if(this.autoClear){
34466             this.clear();
34467         }
34468         var af = this.filtered, rv = this.reverse;
34469         var f = function(n){
34470             if(n == startNode){
34471                 return true;
34472             }
34473             if(af[n.id]){
34474                 return false;
34475             }
34476             var m = fn.call(scope || n, n);
34477             if(!m || rv){
34478                 af[n.id] = n;
34479                 n.ui.hide();
34480                 return false;
34481             }
34482             return true;
34483         };
34484         startNode.cascade(f);
34485         if(this.remove){
34486            for(var id in af){
34487                if(typeof id != "function"){
34488                    var n = af[id];
34489                    if(n && n.parentNode){
34490                        n.parentNode.removeChild(n);
34491                    }
34492                }
34493            }
34494         }
34495     },
34496
34497     /**
34498      * Clears the current filter. Note: with the "remove" option
34499      * set a filter cannot be cleared.
34500      */
34501     clear : function(){
34502         var t = this.tree;
34503         var af = this.filtered;
34504         for(var id in af){
34505             if(typeof id != "function"){
34506                 var n = af[id];
34507                 if(n){
34508                     n.ui.show();
34509                 }
34510             }
34511         }
34512         this.filtered = {};
34513     }
34514 };
34515 /*
34516  * Based on:
34517  * Ext JS Library 1.1.1
34518  * Copyright(c) 2006-2007, Ext JS, LLC.
34519  *
34520  * Originally Released Under LGPL - original licence link has changed is not relivant.
34521  *
34522  * Fork - LGPL
34523  * <script type="text/javascript">
34524  */
34525  
34526
34527 /**
34528  * @class Roo.tree.TreeSorter
34529  * Provides sorting of nodes in a TreePanel
34530  * 
34531  * @cfg {Boolean} folderSort True to sort leaf nodes under non leaf nodes
34532  * @cfg {String} property The named attribute on the node to sort by (defaults to text)
34533  * @cfg {String} dir The direction to sort (asc or desc) (defaults to asc)
34534  * @cfg {String} leafAttr The attribute used to determine leaf nodes in folder sort (defaults to "leaf")
34535  * @cfg {Boolean} caseSensitive true for case sensitive sort (defaults to false)
34536  * @cfg {Function} sortType A custom "casting" function used to convert node values before sorting
34537  * @constructor
34538  * @param {TreePanel} tree
34539  * @param {Object} config
34540  */
34541 Roo.tree.TreeSorter = function(tree, config){
34542     Roo.apply(this, config);
34543     tree.on("beforechildrenrendered", this.doSort, this);
34544     tree.on("append", this.updateSort, this);
34545     tree.on("insert", this.updateSort, this);
34546     
34547     var dsc = this.dir && this.dir.toLowerCase() == "desc";
34548     var p = this.property || "text";
34549     var sortType = this.sortType;
34550     var fs = this.folderSort;
34551     var cs = this.caseSensitive === true;
34552     var leafAttr = this.leafAttr || 'leaf';
34553
34554     this.sortFn = function(n1, n2){
34555         if(fs){
34556             if(n1.attributes[leafAttr] && !n2.attributes[leafAttr]){
34557                 return 1;
34558             }
34559             if(!n1.attributes[leafAttr] && n2.attributes[leafAttr]){
34560                 return -1;
34561             }
34562         }
34563         var v1 = sortType ? sortType(n1) : (cs ? n1.attributes[p] : n1.attributes[p].toUpperCase());
34564         var v2 = sortType ? sortType(n2) : (cs ? n2.attributes[p] : n2.attributes[p].toUpperCase());
34565         if(v1 < v2){
34566                         return dsc ? +1 : -1;
34567                 }else if(v1 > v2){
34568                         return dsc ? -1 : +1;
34569         }else{
34570                 return 0;
34571         }
34572     };
34573 };
34574
34575 Roo.tree.TreeSorter.prototype = {
34576     doSort : function(node){
34577         node.sort(this.sortFn);
34578     },
34579     
34580     compareNodes : function(n1, n2){
34581         return (n1.text.toUpperCase() > n2.text.toUpperCase() ? 1 : -1);
34582     },
34583     
34584     updateSort : function(tree, node){
34585         if(node.childrenRendered){
34586             this.doSort.defer(1, this, [node]);
34587         }
34588     }
34589 };/*
34590  * Based on:
34591  * Ext JS Library 1.1.1
34592  * Copyright(c) 2006-2007, Ext JS, LLC.
34593  *
34594  * Originally Released Under LGPL - original licence link has changed is not relivant.
34595  *
34596  * Fork - LGPL
34597  * <script type="text/javascript">
34598  */
34599
34600 if(Roo.dd.DropZone){
34601     
34602 Roo.tree.TreeDropZone = function(tree, config){
34603     this.allowParentInsert = false;
34604     this.allowContainerDrop = false;
34605     this.appendOnly = false;
34606     Roo.tree.TreeDropZone.superclass.constructor.call(this, tree.innerCt, config);
34607     this.tree = tree;
34608     this.lastInsertClass = "x-tree-no-status";
34609     this.dragOverData = {};
34610 };
34611
34612 Roo.extend(Roo.tree.TreeDropZone, Roo.dd.DropZone, {
34613     ddGroup : "TreeDD",
34614     scroll:  true,
34615     
34616     expandDelay : 1000,
34617     
34618     expandNode : function(node){
34619         if(node.hasChildNodes() && !node.isExpanded()){
34620             node.expand(false, null, this.triggerCacheRefresh.createDelegate(this));
34621         }
34622     },
34623     
34624     queueExpand : function(node){
34625         this.expandProcId = this.expandNode.defer(this.expandDelay, this, [node]);
34626     },
34627     
34628     cancelExpand : function(){
34629         if(this.expandProcId){
34630             clearTimeout(this.expandProcId);
34631             this.expandProcId = false;
34632         }
34633     },
34634     
34635     isValidDropPoint : function(n, pt, dd, e, data){
34636         if(!n || !data){ return false; }
34637         var targetNode = n.node;
34638         var dropNode = data.node;
34639         // default drop rules
34640         if(!(targetNode && targetNode.isTarget && pt)){
34641             return false;
34642         }
34643         if(pt == "append" && targetNode.allowChildren === false){
34644             return false;
34645         }
34646         if((pt == "above" || pt == "below") && (targetNode.parentNode && targetNode.parentNode.allowChildren === false)){
34647             return false;
34648         }
34649         if(dropNode && (targetNode == dropNode || dropNode.contains(targetNode))){
34650             return false;
34651         }
34652         // reuse the object
34653         var overEvent = this.dragOverData;
34654         overEvent.tree = this.tree;
34655         overEvent.target = targetNode;
34656         overEvent.data = data;
34657         overEvent.point = pt;
34658         overEvent.source = dd;
34659         overEvent.rawEvent = e;
34660         overEvent.dropNode = dropNode;
34661         overEvent.cancel = false;  
34662         var result = this.tree.fireEvent("nodedragover", overEvent);
34663         return overEvent.cancel === false && result !== false;
34664     },
34665     
34666     getDropPoint : function(e, n, dd)
34667     {
34668         var tn = n.node;
34669         if(tn.isRoot){
34670             return tn.allowChildren !== false ? "append" : false; // always append for root
34671         }
34672         var dragEl = n.ddel;
34673         var t = Roo.lib.Dom.getY(dragEl), b = t + dragEl.offsetHeight;
34674         var y = Roo.lib.Event.getPageY(e);
34675         //var noAppend = tn.allowChildren === false || tn.isLeaf();
34676         
34677         // we may drop nodes anywhere, as long as allowChildren has not been set to false..
34678         var noAppend = tn.allowChildren === false;
34679         if(this.appendOnly || tn.parentNode.allowChildren === false){
34680             return noAppend ? false : "append";
34681         }
34682         var noBelow = false;
34683         if(!this.allowParentInsert){
34684             noBelow = tn.hasChildNodes() && tn.isExpanded();
34685         }
34686         var q = (b - t) / (noAppend ? 2 : 3);
34687         if(y >= t && y < (t + q)){
34688             return "above";
34689         }else if(!noBelow && (noAppend || y >= b-q && y <= b)){
34690             return "below";
34691         }else{
34692             return "append";
34693         }
34694     },
34695     
34696     onNodeEnter : function(n, dd, e, data)
34697     {
34698         this.cancelExpand();
34699     },
34700     
34701     onNodeOver : function(n, dd, e, data)
34702     {
34703        
34704         var pt = this.getDropPoint(e, n, dd);
34705         var node = n.node;
34706         
34707         // auto node expand check
34708         if(!this.expandProcId && pt == "append" && node.hasChildNodes() && !n.node.isExpanded()){
34709             this.queueExpand(node);
34710         }else if(pt != "append"){
34711             this.cancelExpand();
34712         }
34713         
34714         // set the insert point style on the target node
34715         var returnCls = this.dropNotAllowed;
34716         if(this.isValidDropPoint(n, pt, dd, e, data)){
34717            if(pt){
34718                var el = n.ddel;
34719                var cls;
34720                if(pt == "above"){
34721                    returnCls = n.node.isFirst() ? "x-tree-drop-ok-above" : "x-tree-drop-ok-between";
34722                    cls = "x-tree-drag-insert-above";
34723                }else if(pt == "below"){
34724                    returnCls = n.node.isLast() ? "x-tree-drop-ok-below" : "x-tree-drop-ok-between";
34725                    cls = "x-tree-drag-insert-below";
34726                }else{
34727                    returnCls = "x-tree-drop-ok-append";
34728                    cls = "x-tree-drag-append";
34729                }
34730                if(this.lastInsertClass != cls){
34731                    Roo.fly(el).replaceClass(this.lastInsertClass, cls);
34732                    this.lastInsertClass = cls;
34733                }
34734            }
34735        }
34736        return returnCls;
34737     },
34738     
34739     onNodeOut : function(n, dd, e, data){
34740         
34741         this.cancelExpand();
34742         this.removeDropIndicators(n);
34743     },
34744     
34745     onNodeDrop : function(n, dd, e, data){
34746         var point = this.getDropPoint(e, n, dd);
34747         var targetNode = n.node;
34748         targetNode.ui.startDrop();
34749         if(!this.isValidDropPoint(n, point, dd, e, data)){
34750             targetNode.ui.endDrop();
34751             return false;
34752         }
34753         // first try to find the drop node
34754         var dropNode = data.node || (dd.getTreeNode ? dd.getTreeNode(data, targetNode, point, e) : null);
34755         var dropEvent = {
34756             tree : this.tree,
34757             target: targetNode,
34758             data: data,
34759             point: point,
34760             source: dd,
34761             rawEvent: e,
34762             dropNode: dropNode,
34763             cancel: !dropNode   
34764         };
34765         var retval = this.tree.fireEvent("beforenodedrop", dropEvent);
34766         if(retval === false || dropEvent.cancel === true || !dropEvent.dropNode){
34767             targetNode.ui.endDrop();
34768             return false;
34769         }
34770         // allow target changing
34771         targetNode = dropEvent.target;
34772         if(point == "append" && !targetNode.isExpanded()){
34773             targetNode.expand(false, null, function(){
34774                 this.completeDrop(dropEvent);
34775             }.createDelegate(this));
34776         }else{
34777             this.completeDrop(dropEvent);
34778         }
34779         return true;
34780     },
34781     
34782     completeDrop : function(de){
34783         var ns = de.dropNode, p = de.point, t = de.target;
34784         if(!(ns instanceof Array)){
34785             ns = [ns];
34786         }
34787         var n;
34788         for(var i = 0, len = ns.length; i < len; i++){
34789             n = ns[i];
34790             if(p == "above"){
34791                 t.parentNode.insertBefore(n, t);
34792             }else if(p == "below"){
34793                 t.parentNode.insertBefore(n, t.nextSibling);
34794             }else{
34795                 t.appendChild(n);
34796             }
34797         }
34798         n.ui.focus();
34799         if(this.tree.hlDrop){
34800             n.ui.highlight();
34801         }
34802         t.ui.endDrop();
34803         this.tree.fireEvent("nodedrop", de);
34804     },
34805     
34806     afterNodeMoved : function(dd, data, e, targetNode, dropNode){
34807         if(this.tree.hlDrop){
34808             dropNode.ui.focus();
34809             dropNode.ui.highlight();
34810         }
34811         this.tree.fireEvent("nodedrop", this.tree, targetNode, data, dd, e);
34812     },
34813     
34814     getTree : function(){
34815         return this.tree;
34816     },
34817     
34818     removeDropIndicators : function(n){
34819         if(n && n.ddel){
34820             var el = n.ddel;
34821             Roo.fly(el).removeClass([
34822                     "x-tree-drag-insert-above",
34823                     "x-tree-drag-insert-below",
34824                     "x-tree-drag-append"]);
34825             this.lastInsertClass = "_noclass";
34826         }
34827     },
34828     
34829     beforeDragDrop : function(target, e, id){
34830         this.cancelExpand();
34831         return true;
34832     },
34833     
34834     afterRepair : function(data){
34835         if(data && Roo.enableFx){
34836             data.node.ui.highlight();
34837         }
34838         this.hideProxy();
34839     } 
34840     
34841 });
34842
34843 }
34844 /*
34845  * Based on:
34846  * Ext JS Library 1.1.1
34847  * Copyright(c) 2006-2007, Ext JS, LLC.
34848  *
34849  * Originally Released Under LGPL - original licence link has changed is not relivant.
34850  *
34851  * Fork - LGPL
34852  * <script type="text/javascript">
34853  */
34854  
34855
34856 if(Roo.dd.DragZone){
34857 Roo.tree.TreeDragZone = function(tree, config){
34858     Roo.tree.TreeDragZone.superclass.constructor.call(this, tree.getTreeEl(), config);
34859     this.tree = tree;
34860 };
34861
34862 Roo.extend(Roo.tree.TreeDragZone, Roo.dd.DragZone, {
34863     ddGroup : "TreeDD",
34864    
34865     onBeforeDrag : function(data, e){
34866         var n = data.node;
34867         return n && n.draggable && !n.disabled;
34868     },
34869      
34870     
34871     onInitDrag : function(e){
34872         var data = this.dragData;
34873         this.tree.getSelectionModel().select(data.node);
34874         this.proxy.update("");
34875         data.node.ui.appendDDGhost(this.proxy.ghost.dom);
34876         this.tree.fireEvent("startdrag", this.tree, data.node, e);
34877     },
34878     
34879     getRepairXY : function(e, data){
34880         return data.node.ui.getDDRepairXY();
34881     },
34882     
34883     onEndDrag : function(data, e){
34884         this.tree.fireEvent("enddrag", this.tree, data.node, e);
34885         
34886         
34887     },
34888     
34889     onValidDrop : function(dd, e, id){
34890         this.tree.fireEvent("dragdrop", this.tree, this.dragData.node, dd, e);
34891         this.hideProxy();
34892     },
34893     
34894     beforeInvalidDrop : function(e, id){
34895         // this scrolls the original position back into view
34896         var sm = this.tree.getSelectionModel();
34897         sm.clearSelections();
34898         sm.select(this.dragData.node);
34899     }
34900 });
34901 }/*
34902  * Based on:
34903  * Ext JS Library 1.1.1
34904  * Copyright(c) 2006-2007, Ext JS, LLC.
34905  *
34906  * Originally Released Under LGPL - original licence link has changed is not relivant.
34907  *
34908  * Fork - LGPL
34909  * <script type="text/javascript">
34910  */
34911 /**
34912  * @class Roo.tree.TreeEditor
34913  * @extends Roo.Editor
34914  * Provides editor functionality for inline tree node editing.  Any valid {@link Roo.form.Field} can be used
34915  * as the editor field.
34916  * @constructor
34917  * @param {Object} config (used to be the tree panel.)
34918  * @param {Object} oldconfig DEPRECIATED Either a prebuilt {@link Roo.form.Field} instance or a Field config object
34919  * 
34920  * @cfg {Roo.tree.TreePanel} tree The tree to bind to.
34921  * @cfg {Roo.form.TextField|Object} field The field configuration
34922  *
34923  * 
34924  */
34925 Roo.tree.TreeEditor = function(config, oldconfig) { // was -- (tree, config){
34926     var tree = config;
34927     var field;
34928     if (oldconfig) { // old style..
34929         field = oldconfig.events ? oldconfig : new Roo.form.TextField(oldconfig);
34930     } else {
34931         // new style..
34932         tree = config.tree;
34933         config.field = config.field  || {};
34934         config.field.xtype = 'TextField';
34935         field = Roo.factory(config.field, Roo.form);
34936     }
34937     config = config || {};
34938     
34939     
34940     this.addEvents({
34941         /**
34942          * @event beforenodeedit
34943          * Fires when editing is initiated, but before the value changes.  Editing can be canceled by returning
34944          * false from the handler of this event.
34945          * @param {Editor} this
34946          * @param {Roo.tree.Node} node 
34947          */
34948         "beforenodeedit" : true
34949     });
34950     
34951     //Roo.log(config);
34952     Roo.tree.TreeEditor.superclass.constructor.call(this, field, config);
34953
34954     this.tree = tree;
34955
34956     tree.on('beforeclick', this.beforeNodeClick, this);
34957     tree.getTreeEl().on('mousedown', this.hide, this);
34958     this.on('complete', this.updateNode, this);
34959     this.on('beforestartedit', this.fitToTree, this);
34960     this.on('startedit', this.bindScroll, this, {delay:10});
34961     this.on('specialkey', this.onSpecialKey, this);
34962 };
34963
34964 Roo.extend(Roo.tree.TreeEditor, Roo.Editor, {
34965     /**
34966      * @cfg {String} alignment
34967      * The position to align to (see {@link Roo.Element#alignTo} for more details, defaults to "l-l").
34968      */
34969     alignment: "l-l",
34970     // inherit
34971     autoSize: false,
34972     /**
34973      * @cfg {Boolean} hideEl
34974      * True to hide the bound element while the editor is displayed (defaults to false)
34975      */
34976     hideEl : false,
34977     /**
34978      * @cfg {String} cls
34979      * CSS class to apply to the editor (defaults to "x-small-editor x-tree-editor")
34980      */
34981     cls: "x-small-editor x-tree-editor",
34982     /**
34983      * @cfg {Boolean} shim
34984      * True to shim the editor if selects/iframes could be displayed beneath it (defaults to false)
34985      */
34986     shim:false,
34987     // inherit
34988     shadow:"frame",
34989     /**
34990      * @cfg {Number} maxWidth
34991      * The maximum width in pixels of the editor field (defaults to 250).  Note that if the maxWidth would exceed
34992      * the containing tree element's size, it will be automatically limited for you to the container width, taking
34993      * scroll and client offsets into account prior to each edit.
34994      */
34995     maxWidth: 250,
34996
34997     editDelay : 350,
34998
34999     // private
35000     fitToTree : function(ed, el){
35001         var td = this.tree.getTreeEl().dom, nd = el.dom;
35002         if(td.scrollLeft >  nd.offsetLeft){ // ensure the node left point is visible
35003             td.scrollLeft = nd.offsetLeft;
35004         }
35005         var w = Math.min(
35006                 this.maxWidth,
35007                 (td.clientWidth > 20 ? td.clientWidth : td.offsetWidth) - Math.max(0, nd.offsetLeft-td.scrollLeft) - /*cushion*/5);
35008         this.setSize(w, '');
35009         
35010         return this.fireEvent('beforenodeedit', this, this.editNode);
35011         
35012     },
35013
35014     // private
35015     triggerEdit : function(node){
35016         this.completeEdit();
35017         this.editNode = node;
35018         this.startEdit(node.ui.textNode, node.text);
35019     },
35020
35021     // private
35022     bindScroll : function(){
35023         this.tree.getTreeEl().on('scroll', this.cancelEdit, this);
35024     },
35025
35026     // private
35027     beforeNodeClick : function(node, e){
35028         var sinceLast = (this.lastClick ? this.lastClick.getElapsed() : 0);
35029         this.lastClick = new Date();
35030         if(sinceLast > this.editDelay && this.tree.getSelectionModel().isSelected(node)){
35031             e.stopEvent();
35032             this.triggerEdit(node);
35033             return false;
35034         }
35035         return true;
35036     },
35037
35038     // private
35039     updateNode : function(ed, value){
35040         this.tree.getTreeEl().un('scroll', this.cancelEdit, this);
35041         this.editNode.setText(value);
35042     },
35043
35044     // private
35045     onHide : function(){
35046         Roo.tree.TreeEditor.superclass.onHide.call(this);
35047         if(this.editNode){
35048             this.editNode.ui.focus();
35049         }
35050     },
35051
35052     // private
35053     onSpecialKey : function(field, e){
35054         var k = e.getKey();
35055         if(k == e.ESC){
35056             e.stopEvent();
35057             this.cancelEdit();
35058         }else if(k == e.ENTER && !e.hasModifier()){
35059             e.stopEvent();
35060             this.completeEdit();
35061         }
35062     }
35063 });//<Script type="text/javascript">
35064 /*
35065  * Based on:
35066  * Ext JS Library 1.1.1
35067  * Copyright(c) 2006-2007, Ext JS, LLC.
35068  *
35069  * Originally Released Under LGPL - original licence link has changed is not relivant.
35070  *
35071  * Fork - LGPL
35072  * <script type="text/javascript">
35073  */
35074  
35075 /**
35076  * Not documented??? - probably should be...
35077  */
35078
35079 Roo.tree.ColumnNodeUI = Roo.extend(Roo.tree.TreeNodeUI, {
35080     //focus: Roo.emptyFn, // prevent odd scrolling behavior
35081     
35082     renderElements : function(n, a, targetNode, bulkRender){
35083         //consel.log("renderElements?");
35084         this.indentMarkup = n.parentNode ? n.parentNode.ui.getChildIndent() : '';
35085
35086         var t = n.getOwnerTree();
35087         var tid = Pman.Tab.Document_TypesTree.tree.el.id;
35088         
35089         var cols = t.columns;
35090         var bw = t.borderWidth;
35091         var c = cols[0];
35092         var href = a.href ? a.href : Roo.isGecko ? "" : "#";
35093          var cb = typeof a.checked == "boolean";
35094         var tx = String.format('{0}',n.text || (c.renderer ? c.renderer(a[c.dataIndex], n, a) : a[c.dataIndex]));
35095         var colcls = 'x-t-' + tid + '-c0';
35096         var buf = [
35097             '<li class="x-tree-node">',
35098             
35099                 
35100                 '<div class="x-tree-node-el ', a.cls,'">',
35101                     // extran...
35102                     '<div class="x-tree-col ', colcls, '" style="width:', c.width-bw, 'px;">',
35103                 
35104                 
35105                         '<span class="x-tree-node-indent">',this.indentMarkup,'</span>',
35106                         '<img src="', this.emptyIcon, '" class="x-tree-ec-icon  " />',
35107                         '<img src="', a.icon || this.emptyIcon, '" class="x-tree-node-icon',
35108                            (a.icon ? ' x-tree-node-inline-icon' : ''),
35109                            (a.iconCls ? ' '+a.iconCls : ''),
35110                            '" unselectable="on" />',
35111                         (cb ? ('<input class="x-tree-node-cb" type="checkbox" ' + 
35112                              (a.checked ? 'checked="checked" />' : ' />')) : ''),
35113                              
35114                         '<a class="x-tree-node-anchor" hidefocus="on" href="',href,'" tabIndex="1" ',
35115                             (a.hrefTarget ? ' target="' +a.hrefTarget + '"' : ''), '>',
35116                             '<span unselectable="on" qtip="' + tx + '">',
35117                              tx,
35118                              '</span></a>' ,
35119                     '</div>',
35120                      '<a class="x-tree-node-anchor" hidefocus="on" href="',href,'" tabIndex="1" ',
35121                             (a.hrefTarget ? ' target="' +a.hrefTarget + '"' : ''), '>'
35122                  ];
35123         for(var i = 1, len = cols.length; i < len; i++){
35124             c = cols[i];
35125             colcls = 'x-t-' + tid + '-c' +i;
35126             tx = String.format('{0}', (c.renderer ? c.renderer(a[c.dataIndex], n, a) : a[c.dataIndex]));
35127             buf.push('<div class="x-tree-col ', colcls, ' ' ,(c.cls?c.cls:''),'" style="width:',c.width-bw,'px;">',
35128                         '<div class="x-tree-col-text" qtip="' + tx +'">',tx,"</div>",
35129                       "</div>");
35130          }
35131          
35132          buf.push(
35133             '</a>',
35134             '<div class="x-clear"></div></div>',
35135             '<ul class="x-tree-node-ct" style="display:none;"></ul>',
35136             "</li>");
35137         
35138         if(bulkRender !== true && n.nextSibling && n.nextSibling.ui.getEl()){
35139             this.wrap = Roo.DomHelper.insertHtml("beforeBegin",
35140                                 n.nextSibling.ui.getEl(), buf.join(""));
35141         }else{
35142             this.wrap = Roo.DomHelper.insertHtml("beforeEnd", targetNode, buf.join(""));
35143         }
35144         var el = this.wrap.firstChild;
35145         this.elRow = el;
35146         this.elNode = el.firstChild;
35147         this.ranchor = el.childNodes[1];
35148         this.ctNode = this.wrap.childNodes[1];
35149         var cs = el.firstChild.childNodes;
35150         this.indentNode = cs[0];
35151         this.ecNode = cs[1];
35152         this.iconNode = cs[2];
35153         var index = 3;
35154         if(cb){
35155             this.checkbox = cs[3];
35156             index++;
35157         }
35158         this.anchor = cs[index];
35159         
35160         this.textNode = cs[index].firstChild;
35161         
35162         //el.on("click", this.onClick, this);
35163         //el.on("dblclick", this.onDblClick, this);
35164         
35165         
35166        // console.log(this);
35167     },
35168     initEvents : function(){
35169         Roo.tree.ColumnNodeUI.superclass.initEvents.call(this);
35170         
35171             
35172         var a = this.ranchor;
35173
35174         var el = Roo.get(a);
35175
35176         if(Roo.isOpera){ // opera render bug ignores the CSS
35177             el.setStyle("text-decoration", "none");
35178         }
35179
35180         el.on("click", this.onClick, this);
35181         el.on("dblclick", this.onDblClick, this);
35182         el.on("contextmenu", this.onContextMenu, this);
35183         
35184     },
35185     
35186     /*onSelectedChange : function(state){
35187         if(state){
35188             this.focus();
35189             this.addClass("x-tree-selected");
35190         }else{
35191             //this.blur();
35192             this.removeClass("x-tree-selected");
35193         }
35194     },*/
35195     addClass : function(cls){
35196         if(this.elRow){
35197             Roo.fly(this.elRow).addClass(cls);
35198         }
35199         
35200     },
35201     
35202     
35203     removeClass : function(cls){
35204         if(this.elRow){
35205             Roo.fly(this.elRow).removeClass(cls);
35206         }
35207     }
35208
35209     
35210     
35211 });//<Script type="text/javascript">
35212
35213 /*
35214  * Based on:
35215  * Ext JS Library 1.1.1
35216  * Copyright(c) 2006-2007, Ext JS, LLC.
35217  *
35218  * Originally Released Under LGPL - original licence link has changed is not relivant.
35219  *
35220  * Fork - LGPL
35221  * <script type="text/javascript">
35222  */
35223  
35224
35225 /**
35226  * @class Roo.tree.ColumnTree
35227  * @extends Roo.data.TreePanel
35228  * @cfg {Object} columns  Including width, header, renderer, cls, dataIndex 
35229  * @cfg {int} borderWidth  compined right/left border allowance
35230  * @constructor
35231  * @param {String/HTMLElement/Element} el The container element
35232  * @param {Object} config
35233  */
35234 Roo.tree.ColumnTree =  function(el, config)
35235 {
35236    Roo.tree.ColumnTree.superclass.constructor.call(this, el , config);
35237    this.addEvents({
35238         /**
35239         * @event resize
35240         * Fire this event on a container when it resizes
35241         * @param {int} w Width
35242         * @param {int} h Height
35243         */
35244        "resize" : true
35245     });
35246     this.on('resize', this.onResize, this);
35247 };
35248
35249 Roo.extend(Roo.tree.ColumnTree, Roo.tree.TreePanel, {
35250     //lines:false,
35251     
35252     
35253     borderWidth: Roo.isBorderBox ? 0 : 2, 
35254     headEls : false,
35255     
35256     render : function(){
35257         // add the header.....
35258        
35259         Roo.tree.ColumnTree.superclass.render.apply(this);
35260         
35261         this.el.addClass('x-column-tree');
35262         
35263         this.headers = this.el.createChild(
35264             {cls:'x-tree-headers'},this.innerCt.dom);
35265    
35266         var cols = this.columns, c;
35267         var totalWidth = 0;
35268         this.headEls = [];
35269         var  len = cols.length;
35270         for(var i = 0; i < len; i++){
35271              c = cols[i];
35272              totalWidth += c.width;
35273             this.headEls.push(this.headers.createChild({
35274                  cls:'x-tree-hd ' + (c.cls?c.cls+'-hd':''),
35275                  cn: {
35276                      cls:'x-tree-hd-text',
35277                      html: c.header
35278                  },
35279                  style:'width:'+(c.width-this.borderWidth)+'px;'
35280              }));
35281         }
35282         this.headers.createChild({cls:'x-clear'});
35283         // prevent floats from wrapping when clipped
35284         this.headers.setWidth(totalWidth);
35285         //this.innerCt.setWidth(totalWidth);
35286         this.innerCt.setStyle({ overflow: 'auto' });
35287         this.onResize(this.width, this.height);
35288              
35289         
35290     },
35291     onResize : function(w,h)
35292     {
35293         this.height = h;
35294         this.width = w;
35295         // resize cols..
35296         this.innerCt.setWidth(this.width);
35297         this.innerCt.setHeight(this.height-20);
35298         
35299         // headers...
35300         var cols = this.columns, c;
35301         var totalWidth = 0;
35302         var expEl = false;
35303         var len = cols.length;
35304         for(var i = 0; i < len; i++){
35305             c = cols[i];
35306             if (this.autoExpandColumn !== false && c.dataIndex == this.autoExpandColumn) {
35307                 // it's the expander..
35308                 expEl  = this.headEls[i];
35309                 continue;
35310             }
35311             totalWidth += c.width;
35312             
35313         }
35314         if (expEl) {
35315             expEl.setWidth(  ((w - totalWidth)-this.borderWidth - 20));
35316         }
35317         this.headers.setWidth(w-20);
35318
35319         
35320         
35321         
35322     }
35323 });
35324 /*
35325  * Based on:
35326  * Ext JS Library 1.1.1
35327  * Copyright(c) 2006-2007, Ext JS, LLC.
35328  *
35329  * Originally Released Under LGPL - original licence link has changed is not relivant.
35330  *
35331  * Fork - LGPL
35332  * <script type="text/javascript">
35333  */
35334  
35335 /**
35336  * @class Roo.menu.Menu
35337  * @extends Roo.util.Observable
35338  * A menu object.  This is the container to which you add all other menu items.  Menu can also serve a as a base class
35339  * when you want a specialzed menu based off of another component (like {@link Roo.menu.DateMenu} for example).
35340  * @constructor
35341  * Creates a new Menu
35342  * @param {Object} config Configuration options
35343  */
35344 Roo.menu.Menu = function(config){
35345     Roo.apply(this, config);
35346     this.id = this.id || Roo.id();
35347     this.addEvents({
35348         /**
35349          * @event beforeshow
35350          * Fires before this menu is displayed
35351          * @param {Roo.menu.Menu} this
35352          */
35353         beforeshow : true,
35354         /**
35355          * @event beforehide
35356          * Fires before this menu is hidden
35357          * @param {Roo.menu.Menu} this
35358          */
35359         beforehide : true,
35360         /**
35361          * @event show
35362          * Fires after this menu is displayed
35363          * @param {Roo.menu.Menu} this
35364          */
35365         show : true,
35366         /**
35367          * @event hide
35368          * Fires after this menu is hidden
35369          * @param {Roo.menu.Menu} this
35370          */
35371         hide : true,
35372         /**
35373          * @event click
35374          * Fires when this menu is clicked (or when the enter key is pressed while it is active)
35375          * @param {Roo.menu.Menu} this
35376          * @param {Roo.menu.Item} menuItem The menu item that was clicked
35377          * @param {Roo.EventObject} e
35378          */
35379         click : true,
35380         /**
35381          * @event mouseover
35382          * Fires when the mouse is hovering over this menu
35383          * @param {Roo.menu.Menu} this
35384          * @param {Roo.EventObject} e
35385          * @param {Roo.menu.Item} menuItem The menu item that was clicked
35386          */
35387         mouseover : true,
35388         /**
35389          * @event mouseout
35390          * Fires when the mouse exits this menu
35391          * @param {Roo.menu.Menu} this
35392          * @param {Roo.EventObject} e
35393          * @param {Roo.menu.Item} menuItem The menu item that was clicked
35394          */
35395         mouseout : true,
35396         /**
35397          * @event itemclick
35398          * Fires when a menu item contained in this menu is clicked
35399          * @param {Roo.menu.BaseItem} baseItem The BaseItem that was clicked
35400          * @param {Roo.EventObject} e
35401          */
35402         itemclick: true
35403     });
35404     if (this.registerMenu) {
35405         Roo.menu.MenuMgr.register(this);
35406     }
35407     
35408     var mis = this.items;
35409     this.items = new Roo.util.MixedCollection();
35410     if(mis){
35411         this.add.apply(this, mis);
35412     }
35413 };
35414
35415 Roo.extend(Roo.menu.Menu, Roo.util.Observable, {
35416     /**
35417      * @cfg {Number} minWidth The minimum width of the menu in pixels (defaults to 120)
35418      */
35419     minWidth : 120,
35420     /**
35421      * @cfg {Boolean/String} shadow True or "sides" for the default effect, "frame" for 4-way shadow, and "drop"
35422      * for bottom-right shadow (defaults to "sides")
35423      */
35424     shadow : "sides",
35425     /**
35426      * @cfg {String} subMenuAlign The {@link Roo.Element#alignTo} anchor position value to use for submenus of
35427      * this menu (defaults to "tl-tr?")
35428      */
35429     subMenuAlign : "tl-tr?",
35430     /**
35431      * @cfg {String} defaultAlign The default {@link Roo.Element#alignTo) anchor position value for this menu
35432      * relative to its element of origin (defaults to "tl-bl?")
35433      */
35434     defaultAlign : "tl-bl?",
35435     /**
35436      * @cfg {Boolean} allowOtherMenus True to allow multiple menus to be displayed at the same time (defaults to false)
35437      */
35438     allowOtherMenus : false,
35439     /**
35440      * @cfg {Boolean} registerMenu True (default) - means that clicking on screen etc. hides it.
35441      */
35442     registerMenu : true,
35443
35444     hidden:true,
35445
35446     // private
35447     render : function(){
35448         if(this.el){
35449             return;
35450         }
35451         var el = this.el = new Roo.Layer({
35452             cls: "x-menu",
35453             shadow:this.shadow,
35454             constrain: false,
35455             parentEl: this.parentEl || document.body,
35456             zindex:15000
35457         });
35458
35459         this.keyNav = new Roo.menu.MenuNav(this);
35460
35461         if(this.plain){
35462             el.addClass("x-menu-plain");
35463         }
35464         if(this.cls){
35465             el.addClass(this.cls);
35466         }
35467         // generic focus element
35468         this.focusEl = el.createChild({
35469             tag: "a", cls: "x-menu-focus", href: "#", onclick: "return false;", tabIndex:"-1"
35470         });
35471         var ul = el.createChild({tag: "ul", cls: "x-menu-list"});
35472         ul.on(Roo.isTouch ? 'touchstart' : 'click'   , this.onClick, this);
35473         
35474         ul.on("mouseover", this.onMouseOver, this);
35475         ul.on("mouseout", this.onMouseOut, this);
35476         this.items.each(function(item){
35477             if (item.hidden) {
35478                 return;
35479             }
35480             
35481             var li = document.createElement("li");
35482             li.className = "x-menu-list-item";
35483             ul.dom.appendChild(li);
35484             item.render(li, this);
35485         }, this);
35486         this.ul = ul;
35487         this.autoWidth();
35488     },
35489
35490     // private
35491     autoWidth : function(){
35492         var el = this.el, ul = this.ul;
35493         if(!el){
35494             return;
35495         }
35496         var w = this.width;
35497         if(w){
35498             el.setWidth(w);
35499         }else if(Roo.isIE){
35500             el.setWidth(this.minWidth);
35501             var t = el.dom.offsetWidth; // force recalc
35502             el.setWidth(ul.getWidth()+el.getFrameWidth("lr"));
35503         }
35504     },
35505
35506     // private
35507     delayAutoWidth : function(){
35508         if(this.rendered){
35509             if(!this.awTask){
35510                 this.awTask = new Roo.util.DelayedTask(this.autoWidth, this);
35511             }
35512             this.awTask.delay(20);
35513         }
35514     },
35515
35516     // private
35517     findTargetItem : function(e){
35518         var t = e.getTarget(".x-menu-list-item", this.ul,  true);
35519         if(t && t.menuItemId){
35520             return this.items.get(t.menuItemId);
35521         }
35522     },
35523
35524     // private
35525     onClick : function(e){
35526         Roo.log("menu.onClick");
35527         var t = this.findTargetItem(e);
35528         if(!t){
35529             return;
35530         }
35531         Roo.log(e);
35532         if (Roo.isTouch && e.type == 'touchstart' && t.menu  && !t.disabled) {
35533             if(t == this.activeItem && t.shouldDeactivate(e)){
35534                 this.activeItem.deactivate();
35535                 delete this.activeItem;
35536                 return;
35537             }
35538             if(t.canActivate){
35539                 this.setActiveItem(t, true);
35540             }
35541             return;
35542             
35543             
35544         }
35545         
35546         t.onClick(e);
35547         this.fireEvent("click", this, t, e);
35548     },
35549
35550     // private
35551     setActiveItem : function(item, autoExpand){
35552         if(item != this.activeItem){
35553             if(this.activeItem){
35554                 this.activeItem.deactivate();
35555             }
35556             this.activeItem = item;
35557             item.activate(autoExpand);
35558         }else if(autoExpand){
35559             item.expandMenu();
35560         }
35561     },
35562
35563     // private
35564     tryActivate : function(start, step){
35565         var items = this.items;
35566         for(var i = start, len = items.length; i >= 0 && i < len; i+= step){
35567             var item = items.get(i);
35568             if(!item.disabled && item.canActivate){
35569                 this.setActiveItem(item, false);
35570                 return item;
35571             }
35572         }
35573         return false;
35574     },
35575
35576     // private
35577     onMouseOver : function(e){
35578         var t;
35579         if(t = this.findTargetItem(e)){
35580             if(t.canActivate && !t.disabled){
35581                 this.setActiveItem(t, true);
35582             }
35583         }
35584         this.fireEvent("mouseover", this, e, t);
35585     },
35586
35587     // private
35588     onMouseOut : function(e){
35589         var t;
35590         if(t = this.findTargetItem(e)){
35591             if(t == this.activeItem && t.shouldDeactivate(e)){
35592                 this.activeItem.deactivate();
35593                 delete this.activeItem;
35594             }
35595         }
35596         this.fireEvent("mouseout", this, e, t);
35597     },
35598
35599     /**
35600      * Read-only.  Returns true if the menu is currently displayed, else false.
35601      * @type Boolean
35602      */
35603     isVisible : function(){
35604         return this.el && !this.hidden;
35605     },
35606
35607     /**
35608      * Displays this menu relative to another element
35609      * @param {String/HTMLElement/Roo.Element} element The element to align to
35610      * @param {String} position (optional) The {@link Roo.Element#alignTo} anchor position to use in aligning to
35611      * the element (defaults to this.defaultAlign)
35612      * @param {Roo.menu.Menu} parentMenu (optional) This menu's parent menu, if applicable (defaults to undefined)
35613      */
35614     show : function(el, pos, parentMenu){
35615         this.parentMenu = parentMenu;
35616         if(!this.el){
35617             this.render();
35618         }
35619         this.fireEvent("beforeshow", this);
35620         this.showAt(this.el.getAlignToXY(el, pos || this.defaultAlign), parentMenu, false);
35621     },
35622
35623     /**
35624      * Displays this menu at a specific xy position
35625      * @param {Array} xyPosition Contains X & Y [x, y] values for the position at which to show the menu (coordinates are page-based)
35626      * @param {Roo.menu.Menu} parentMenu (optional) This menu's parent menu, if applicable (defaults to undefined)
35627      */
35628     showAt : function(xy, parentMenu, /* private: */_e){
35629         this.parentMenu = parentMenu;
35630         if(!this.el){
35631             this.render();
35632         }
35633         if(_e !== false){
35634             this.fireEvent("beforeshow", this);
35635             xy = this.el.adjustForConstraints(xy);
35636         }
35637         this.el.setXY(xy);
35638         this.el.show();
35639         this.hidden = false;
35640         this.focus();
35641         this.fireEvent("show", this);
35642     },
35643
35644     focus : function(){
35645         if(!this.hidden){
35646             this.doFocus.defer(50, this);
35647         }
35648     },
35649
35650     doFocus : function(){
35651         if(!this.hidden){
35652             this.focusEl.focus();
35653         }
35654     },
35655
35656     /**
35657      * Hides this menu and optionally all parent menus
35658      * @param {Boolean} deep (optional) True to hide all parent menus recursively, if any (defaults to false)
35659      */
35660     hide : function(deep){
35661         if(this.el && this.isVisible()){
35662             this.fireEvent("beforehide", this);
35663             if(this.activeItem){
35664                 this.activeItem.deactivate();
35665                 this.activeItem = null;
35666             }
35667             this.el.hide();
35668             this.hidden = true;
35669             this.fireEvent("hide", this);
35670         }
35671         if(deep === true && this.parentMenu){
35672             this.parentMenu.hide(true);
35673         }
35674     },
35675
35676     /**
35677      * Addds one or more items of any type supported by the Menu class, or that can be converted into menu items.
35678      * Any of the following are valid:
35679      * <ul>
35680      * <li>Any menu item object based on {@link Roo.menu.Item}</li>
35681      * <li>An HTMLElement object which will be converted to a menu item</li>
35682      * <li>A menu item config object that will be created as a new menu item</li>
35683      * <li>A string, which can either be '-' or 'separator' to add a menu separator, otherwise
35684      * it will be converted into a {@link Roo.menu.TextItem} and added</li>
35685      * </ul>
35686      * Usage:
35687      * <pre><code>
35688 // Create the menu
35689 var menu = new Roo.menu.Menu();
35690
35691 // Create a menu item to add by reference
35692 var menuItem = new Roo.menu.Item({ text: 'New Item!' });
35693
35694 // Add a bunch of items at once using different methods.
35695 // Only the last item added will be returned.
35696 var item = menu.add(
35697     menuItem,                // add existing item by ref
35698     'Dynamic Item',          // new TextItem
35699     '-',                     // new separator
35700     { text: 'Config Item' }  // new item by config
35701 );
35702 </code></pre>
35703      * @param {Mixed} args One or more menu items, menu item configs or other objects that can be converted to menu items
35704      * @return {Roo.menu.Item} The menu item that was added, or the last one if multiple items were added
35705      */
35706     add : function(){
35707         var a = arguments, l = a.length, item;
35708         for(var i = 0; i < l; i++){
35709             var el = a[i];
35710             if ((typeof(el) == "object") && el.xtype && el.xns) {
35711                 el = Roo.factory(el, Roo.menu);
35712             }
35713             
35714             if(el.render){ // some kind of Item
35715                 item = this.addItem(el);
35716             }else if(typeof el == "string"){ // string
35717                 if(el == "separator" || el == "-"){
35718                     item = this.addSeparator();
35719                 }else{
35720                     item = this.addText(el);
35721                 }
35722             }else if(el.tagName || el.el){ // element
35723                 item = this.addElement(el);
35724             }else if(typeof el == "object"){ // must be menu item config?
35725                 item = this.addMenuItem(el);
35726             }
35727         }
35728         return item;
35729     },
35730
35731     /**
35732      * Returns this menu's underlying {@link Roo.Element} object
35733      * @return {Roo.Element} The element
35734      */
35735     getEl : function(){
35736         if(!this.el){
35737             this.render();
35738         }
35739         return this.el;
35740     },
35741
35742     /**
35743      * Adds a separator bar to the menu
35744      * @return {Roo.menu.Item} The menu item that was added
35745      */
35746     addSeparator : function(){
35747         return this.addItem(new Roo.menu.Separator());
35748     },
35749
35750     /**
35751      * Adds an {@link Roo.Element} object to the menu
35752      * @param {String/HTMLElement/Roo.Element} el The element or DOM node to add, or its id
35753      * @return {Roo.menu.Item} The menu item that was added
35754      */
35755     addElement : function(el){
35756         return this.addItem(new Roo.menu.BaseItem(el));
35757     },
35758
35759     /**
35760      * Adds an existing object based on {@link Roo.menu.Item} to the menu
35761      * @param {Roo.menu.Item} item The menu item to add
35762      * @return {Roo.menu.Item} The menu item that was added
35763      */
35764     addItem : function(item){
35765         this.items.add(item);
35766         if(this.ul){
35767             var li = document.createElement("li");
35768             li.className = "x-menu-list-item";
35769             this.ul.dom.appendChild(li);
35770             item.render(li, this);
35771             this.delayAutoWidth();
35772         }
35773         return item;
35774     },
35775
35776     /**
35777      * Creates a new {@link Roo.menu.Item} based an the supplied config object and adds it to the menu
35778      * @param {Object} config A MenuItem config object
35779      * @return {Roo.menu.Item} The menu item that was added
35780      */
35781     addMenuItem : function(config){
35782         if(!(config instanceof Roo.menu.Item)){
35783             if(typeof config.checked == "boolean"){ // must be check menu item config?
35784                 config = new Roo.menu.CheckItem(config);
35785             }else{
35786                 config = new Roo.menu.Item(config);
35787             }
35788         }
35789         return this.addItem(config);
35790     },
35791
35792     /**
35793      * Creates a new {@link Roo.menu.TextItem} with the supplied text and adds it to the menu
35794      * @param {String} text The text to display in the menu item
35795      * @return {Roo.menu.Item} The menu item that was added
35796      */
35797     addText : function(text){
35798         return this.addItem(new Roo.menu.TextItem({ text : text }));
35799     },
35800
35801     /**
35802      * Inserts an existing object based on {@link Roo.menu.Item} to the menu at a specified index
35803      * @param {Number} index The index in the menu's list of current items where the new item should be inserted
35804      * @param {Roo.menu.Item} item The menu item to add
35805      * @return {Roo.menu.Item} The menu item that was added
35806      */
35807     insert : function(index, item){
35808         this.items.insert(index, item);
35809         if(this.ul){
35810             var li = document.createElement("li");
35811             li.className = "x-menu-list-item";
35812             this.ul.dom.insertBefore(li, this.ul.dom.childNodes[index]);
35813             item.render(li, this);
35814             this.delayAutoWidth();
35815         }
35816         return item;
35817     },
35818
35819     /**
35820      * Removes an {@link Roo.menu.Item} from the menu and destroys the object
35821      * @param {Roo.menu.Item} item The menu item to remove
35822      */
35823     remove : function(item){
35824         this.items.removeKey(item.id);
35825         item.destroy();
35826     },
35827
35828     /**
35829      * Removes and destroys all items in the menu
35830      */
35831     removeAll : function(){
35832         var f;
35833         while(f = this.items.first()){
35834             this.remove(f);
35835         }
35836     }
35837 });
35838
35839 // MenuNav is a private utility class used internally by the Menu
35840 Roo.menu.MenuNav = function(menu){
35841     Roo.menu.MenuNav.superclass.constructor.call(this, menu.el);
35842     this.scope = this.menu = menu;
35843 };
35844
35845 Roo.extend(Roo.menu.MenuNav, Roo.KeyNav, {
35846     doRelay : function(e, h){
35847         var k = e.getKey();
35848         if(!this.menu.activeItem && e.isNavKeyPress() && k != e.SPACE && k != e.RETURN){
35849             this.menu.tryActivate(0, 1);
35850             return false;
35851         }
35852         return h.call(this.scope || this, e, this.menu);
35853     },
35854
35855     up : function(e, m){
35856         if(!m.tryActivate(m.items.indexOf(m.activeItem)-1, -1)){
35857             m.tryActivate(m.items.length-1, -1);
35858         }
35859     },
35860
35861     down : function(e, m){
35862         if(!m.tryActivate(m.items.indexOf(m.activeItem)+1, 1)){
35863             m.tryActivate(0, 1);
35864         }
35865     },
35866
35867     right : function(e, m){
35868         if(m.activeItem){
35869             m.activeItem.expandMenu(true);
35870         }
35871     },
35872
35873     left : function(e, m){
35874         m.hide();
35875         if(m.parentMenu && m.parentMenu.activeItem){
35876             m.parentMenu.activeItem.activate();
35877         }
35878     },
35879
35880     enter : function(e, m){
35881         if(m.activeItem){
35882             e.stopPropagation();
35883             m.activeItem.onClick(e);
35884             m.fireEvent("click", this, m.activeItem);
35885             return true;
35886         }
35887     }
35888 });/*
35889  * Based on:
35890  * Ext JS Library 1.1.1
35891  * Copyright(c) 2006-2007, Ext JS, LLC.
35892  *
35893  * Originally Released Under LGPL - original licence link has changed is not relivant.
35894  *
35895  * Fork - LGPL
35896  * <script type="text/javascript">
35897  */
35898  
35899 /**
35900  * @class Roo.menu.MenuMgr
35901  * Provides a common registry of all menu items on a page so that they can be easily accessed by id.
35902  * @singleton
35903  */
35904 Roo.menu.MenuMgr = function(){
35905    var menus, active, groups = {}, attached = false, lastShow = new Date();
35906
35907    // private - called when first menu is created
35908    function init(){
35909        menus = {};
35910        active = new Roo.util.MixedCollection();
35911        Roo.get(document).addKeyListener(27, function(){
35912            if(active.length > 0){
35913                hideAll();
35914            }
35915        });
35916    }
35917
35918    // private
35919    function hideAll(){
35920        if(active && active.length > 0){
35921            var c = active.clone();
35922            c.each(function(m){
35923                m.hide();
35924            });
35925        }
35926    }
35927
35928    // private
35929    function onHide(m){
35930        active.remove(m);
35931        if(active.length < 1){
35932            Roo.get(document).un("mousedown", onMouseDown);
35933            attached = false;
35934        }
35935    }
35936
35937    // private
35938    function onShow(m){
35939        var last = active.last();
35940        lastShow = new Date();
35941        active.add(m);
35942        if(!attached){
35943            Roo.get(document).on("mousedown", onMouseDown);
35944            attached = true;
35945        }
35946        if(m.parentMenu){
35947           m.getEl().setZIndex(parseInt(m.parentMenu.getEl().getStyle("z-index"), 10) + 3);
35948           m.parentMenu.activeChild = m;
35949        }else if(last && last.isVisible()){
35950           m.getEl().setZIndex(parseInt(last.getEl().getStyle("z-index"), 10) + 3);
35951        }
35952    }
35953
35954    // private
35955    function onBeforeHide(m){
35956        if(m.activeChild){
35957            m.activeChild.hide();
35958        }
35959        if(m.autoHideTimer){
35960            clearTimeout(m.autoHideTimer);
35961            delete m.autoHideTimer;
35962        }
35963    }
35964
35965    // private
35966    function onBeforeShow(m){
35967        var pm = m.parentMenu;
35968        if(!pm && !m.allowOtherMenus){
35969            hideAll();
35970        }else if(pm && pm.activeChild && active != m){
35971            pm.activeChild.hide();
35972        }
35973    }
35974
35975    // private
35976    function onMouseDown(e){
35977        if(lastShow.getElapsed() > 50 && active.length > 0 && !e.getTarget(".x-menu")){
35978            hideAll();
35979        }
35980    }
35981
35982    // private
35983    function onBeforeCheck(mi, state){
35984        if(state){
35985            var g = groups[mi.group];
35986            for(var i = 0, l = g.length; i < l; i++){
35987                if(g[i] != mi){
35988                    g[i].setChecked(false);
35989                }
35990            }
35991        }
35992    }
35993
35994    return {
35995
35996        /**
35997         * Hides all menus that are currently visible
35998         */
35999        hideAll : function(){
36000             hideAll();  
36001        },
36002
36003        // private
36004        register : function(menu){
36005            if(!menus){
36006                init();
36007            }
36008            menus[menu.id] = menu;
36009            menu.on("beforehide", onBeforeHide);
36010            menu.on("hide", onHide);
36011            menu.on("beforeshow", onBeforeShow);
36012            menu.on("show", onShow);
36013            var g = menu.group;
36014            if(g && menu.events["checkchange"]){
36015                if(!groups[g]){
36016                    groups[g] = [];
36017                }
36018                groups[g].push(menu);
36019                menu.on("checkchange", onCheck);
36020            }
36021        },
36022
36023         /**
36024          * Returns a {@link Roo.menu.Menu} object
36025          * @param {String/Object} menu The string menu id, an existing menu object reference, or a Menu config that will
36026          * be used to generate and return a new Menu instance.
36027          */
36028        get : function(menu){
36029            if(typeof menu == "string"){ // menu id
36030                return menus[menu];
36031            }else if(menu.events){  // menu instance
36032                return menu;
36033            }else if(typeof menu.length == 'number'){ // array of menu items?
36034                return new Roo.menu.Menu({items:menu});
36035            }else{ // otherwise, must be a config
36036                return new Roo.menu.Menu(menu);
36037            }
36038        },
36039
36040        // private
36041        unregister : function(menu){
36042            delete menus[menu.id];
36043            menu.un("beforehide", onBeforeHide);
36044            menu.un("hide", onHide);
36045            menu.un("beforeshow", onBeforeShow);
36046            menu.un("show", onShow);
36047            var g = menu.group;
36048            if(g && menu.events["checkchange"]){
36049                groups[g].remove(menu);
36050                menu.un("checkchange", onCheck);
36051            }
36052        },
36053
36054        // private
36055        registerCheckable : function(menuItem){
36056            var g = menuItem.group;
36057            if(g){
36058                if(!groups[g]){
36059                    groups[g] = [];
36060                }
36061                groups[g].push(menuItem);
36062                menuItem.on("beforecheckchange", onBeforeCheck);
36063            }
36064        },
36065
36066        // private
36067        unregisterCheckable : function(menuItem){
36068            var g = menuItem.group;
36069            if(g){
36070                groups[g].remove(menuItem);
36071                menuItem.un("beforecheckchange", onBeforeCheck);
36072            }
36073        }
36074    };
36075 }();/*
36076  * Based on:
36077  * Ext JS Library 1.1.1
36078  * Copyright(c) 2006-2007, Ext JS, LLC.
36079  *
36080  * Originally Released Under LGPL - original licence link has changed is not relivant.
36081  *
36082  * Fork - LGPL
36083  * <script type="text/javascript">
36084  */
36085  
36086
36087 /**
36088  * @class Roo.menu.BaseItem
36089  * @extends Roo.Component
36090  * The base class for all items that render into menus.  BaseItem provides default rendering, activated state
36091  * management and base configuration options shared by all menu components.
36092  * @constructor
36093  * Creates a new BaseItem
36094  * @param {Object} config Configuration options
36095  */
36096 Roo.menu.BaseItem = function(config){
36097     Roo.menu.BaseItem.superclass.constructor.call(this, config);
36098
36099     this.addEvents({
36100         /**
36101          * @event click
36102          * Fires when this item is clicked
36103          * @param {Roo.menu.BaseItem} this
36104          * @param {Roo.EventObject} e
36105          */
36106         click: true,
36107         /**
36108          * @event activate
36109          * Fires when this item is activated
36110          * @param {Roo.menu.BaseItem} this
36111          */
36112         activate : true,
36113         /**
36114          * @event deactivate
36115          * Fires when this item is deactivated
36116          * @param {Roo.menu.BaseItem} this
36117          */
36118         deactivate : true
36119     });
36120
36121     if(this.handler){
36122         this.on("click", this.handler, this.scope, true);
36123     }
36124 };
36125
36126 Roo.extend(Roo.menu.BaseItem, Roo.Component, {
36127     /**
36128      * @cfg {Function} handler
36129      * A function that will handle the click event of this menu item (defaults to undefined)
36130      */
36131     /**
36132      * @cfg {Boolean} canActivate True if this item can be visually activated (defaults to false)
36133      */
36134     canActivate : false,
36135     
36136      /**
36137      * @cfg {Boolean} hidden True to prevent creation of this menu item (defaults to false)
36138      */
36139     hidden: false,
36140     
36141     /**
36142      * @cfg {String} activeClass The CSS class to use when the item becomes activated (defaults to "x-menu-item-active")
36143      */
36144     activeClass : "x-menu-item-active",
36145     /**
36146      * @cfg {Boolean} hideOnClick True to hide the containing menu after this item is clicked (defaults to true)
36147      */
36148     hideOnClick : true,
36149     /**
36150      * @cfg {Number} hideDelay Length of time in milliseconds to wait before hiding after a click (defaults to 100)
36151      */
36152     hideDelay : 100,
36153
36154     // private
36155     ctype: "Roo.menu.BaseItem",
36156
36157     // private
36158     actionMode : "container",
36159
36160     // private
36161     render : function(container, parentMenu){
36162         this.parentMenu = parentMenu;
36163         Roo.menu.BaseItem.superclass.render.call(this, container);
36164         this.container.menuItemId = this.id;
36165     },
36166
36167     // private
36168     onRender : function(container, position){
36169         this.el = Roo.get(this.el);
36170         container.dom.appendChild(this.el.dom);
36171     },
36172
36173     // private
36174     onClick : function(e){
36175         if(!this.disabled && this.fireEvent("click", this, e) !== false
36176                 && this.parentMenu.fireEvent("itemclick", this, e) !== false){
36177             this.handleClick(e);
36178         }else{
36179             e.stopEvent();
36180         }
36181     },
36182
36183     // private
36184     activate : function(){
36185         if(this.disabled){
36186             return false;
36187         }
36188         var li = this.container;
36189         li.addClass(this.activeClass);
36190         this.region = li.getRegion().adjust(2, 2, -2, -2);
36191         this.fireEvent("activate", this);
36192         return true;
36193     },
36194
36195     // private
36196     deactivate : function(){
36197         this.container.removeClass(this.activeClass);
36198         this.fireEvent("deactivate", this);
36199     },
36200
36201     // private
36202     shouldDeactivate : function(e){
36203         return !this.region || !this.region.contains(e.getPoint());
36204     },
36205
36206     // private
36207     handleClick : function(e){
36208         if(this.hideOnClick){
36209             this.parentMenu.hide.defer(this.hideDelay, this.parentMenu, [true]);
36210         }
36211     },
36212
36213     // private
36214     expandMenu : function(autoActivate){
36215         // do nothing
36216     },
36217
36218     // private
36219     hideMenu : function(){
36220         // do nothing
36221     }
36222 });/*
36223  * Based on:
36224  * Ext JS Library 1.1.1
36225  * Copyright(c) 2006-2007, Ext JS, LLC.
36226  *
36227  * Originally Released Under LGPL - original licence link has changed is not relivant.
36228  *
36229  * Fork - LGPL
36230  * <script type="text/javascript">
36231  */
36232  
36233 /**
36234  * @class Roo.menu.Adapter
36235  * @extends Roo.menu.BaseItem
36236  * 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.
36237  * It provides basic rendering, activation management and enable/disable logic required to work in menus.
36238  * @constructor
36239  * Creates a new Adapter
36240  * @param {Object} config Configuration options
36241  */
36242 Roo.menu.Adapter = function(component, config){
36243     Roo.menu.Adapter.superclass.constructor.call(this, config);
36244     this.component = component;
36245 };
36246 Roo.extend(Roo.menu.Adapter, Roo.menu.BaseItem, {
36247     // private
36248     canActivate : true,
36249
36250     // private
36251     onRender : function(container, position){
36252         this.component.render(container);
36253         this.el = this.component.getEl();
36254     },
36255
36256     // private
36257     activate : function(){
36258         if(this.disabled){
36259             return false;
36260         }
36261         this.component.focus();
36262         this.fireEvent("activate", this);
36263         return true;
36264     },
36265
36266     // private
36267     deactivate : function(){
36268         this.fireEvent("deactivate", this);
36269     },
36270
36271     // private
36272     disable : function(){
36273         this.component.disable();
36274         Roo.menu.Adapter.superclass.disable.call(this);
36275     },
36276
36277     // private
36278     enable : function(){
36279         this.component.enable();
36280         Roo.menu.Adapter.superclass.enable.call(this);
36281     }
36282 });/*
36283  * Based on:
36284  * Ext JS Library 1.1.1
36285  * Copyright(c) 2006-2007, Ext JS, LLC.
36286  *
36287  * Originally Released Under LGPL - original licence link has changed is not relivant.
36288  *
36289  * Fork - LGPL
36290  * <script type="text/javascript">
36291  */
36292
36293 /**
36294  * @class Roo.menu.TextItem
36295  * @extends Roo.menu.BaseItem
36296  * Adds a static text string to a menu, usually used as either a heading or group separator.
36297  * Note: old style constructor with text is still supported.
36298  * 
36299  * @constructor
36300  * Creates a new TextItem
36301  * @param {Object} cfg Configuration
36302  */
36303 Roo.menu.TextItem = function(cfg){
36304     if (typeof(cfg) == 'string') {
36305         this.text = cfg;
36306     } else {
36307         Roo.apply(this,cfg);
36308     }
36309     
36310     Roo.menu.TextItem.superclass.constructor.call(this);
36311 };
36312
36313 Roo.extend(Roo.menu.TextItem, Roo.menu.BaseItem, {
36314     /**
36315      * @cfg {Boolean} text Text to show on item.
36316      */
36317     text : '',
36318     
36319     /**
36320      * @cfg {Boolean} hideOnClick True to hide the containing menu after this item is clicked (defaults to false)
36321      */
36322     hideOnClick : false,
36323     /**
36324      * @cfg {String} itemCls The default CSS class to use for text items (defaults to "x-menu-text")
36325      */
36326     itemCls : "x-menu-text",
36327
36328     // private
36329     onRender : function(){
36330         var s = document.createElement("span");
36331         s.className = this.itemCls;
36332         s.innerHTML = this.text;
36333         this.el = s;
36334         Roo.menu.TextItem.superclass.onRender.apply(this, arguments);
36335     }
36336 });/*
36337  * Based on:
36338  * Ext JS Library 1.1.1
36339  * Copyright(c) 2006-2007, Ext JS, LLC.
36340  *
36341  * Originally Released Under LGPL - original licence link has changed is not relivant.
36342  *
36343  * Fork - LGPL
36344  * <script type="text/javascript">
36345  */
36346
36347 /**
36348  * @class Roo.menu.Separator
36349  * @extends Roo.menu.BaseItem
36350  * Adds a separator bar to a menu, used to divide logical groups of menu items. Generally you will
36351  * add one of these by using "-" in you call to add() or in your items config rather than creating one directly.
36352  * @constructor
36353  * @param {Object} config Configuration options
36354  */
36355 Roo.menu.Separator = function(config){
36356     Roo.menu.Separator.superclass.constructor.call(this, config);
36357 };
36358
36359 Roo.extend(Roo.menu.Separator, Roo.menu.BaseItem, {
36360     /**
36361      * @cfg {String} itemCls The default CSS class to use for separators (defaults to "x-menu-sep")
36362      */
36363     itemCls : "x-menu-sep",
36364     /**
36365      * @cfg {Boolean} hideOnClick True to hide the containing menu after this item is clicked (defaults to false)
36366      */
36367     hideOnClick : false,
36368
36369     // private
36370     onRender : function(li){
36371         var s = document.createElement("span");
36372         s.className = this.itemCls;
36373         s.innerHTML = "&#160;";
36374         this.el = s;
36375         li.addClass("x-menu-sep-li");
36376         Roo.menu.Separator.superclass.onRender.apply(this, arguments);
36377     }
36378 });/*
36379  * Based on:
36380  * Ext JS Library 1.1.1
36381  * Copyright(c) 2006-2007, Ext JS, LLC.
36382  *
36383  * Originally Released Under LGPL - original licence link has changed is not relivant.
36384  *
36385  * Fork - LGPL
36386  * <script type="text/javascript">
36387  */
36388 /**
36389  * @class Roo.menu.Item
36390  * @extends Roo.menu.BaseItem
36391  * A base class for all menu items that require menu-related functionality (like sub-menus) and are not static
36392  * display items.  Item extends the base functionality of {@link Roo.menu.BaseItem} by adding menu-specific
36393  * activation and click handling.
36394  * @constructor
36395  * Creates a new Item
36396  * @param {Object} config Configuration options
36397  */
36398 Roo.menu.Item = function(config){
36399     Roo.menu.Item.superclass.constructor.call(this, config);
36400     if(this.menu){
36401         this.menu = Roo.menu.MenuMgr.get(this.menu);
36402     }
36403 };
36404 Roo.extend(Roo.menu.Item, Roo.menu.BaseItem, {
36405     
36406     /**
36407      * @cfg {String} text
36408      * The text to show on the menu item.
36409      */
36410     text: '',
36411      /**
36412      * @cfg {String} HTML to render in menu
36413      * The text to show on the menu item (HTML version).
36414      */
36415     html: '',
36416     /**
36417      * @cfg {String} icon
36418      * The path to an icon to display in this menu item (defaults to Roo.BLANK_IMAGE_URL)
36419      */
36420     icon: undefined,
36421     /**
36422      * @cfg {String} itemCls The default CSS class to use for menu items (defaults to "x-menu-item")
36423      */
36424     itemCls : "x-menu-item",
36425     /**
36426      * @cfg {Boolean} canActivate True if this item can be visually activated (defaults to true)
36427      */
36428     canActivate : true,
36429     /**
36430      * @cfg {Number} showDelay Length of time in milliseconds to wait before showing this item (defaults to 200)
36431      */
36432     showDelay: 200,
36433     // doc'd in BaseItem
36434     hideDelay: 200,
36435
36436     // private
36437     ctype: "Roo.menu.Item",
36438     
36439     // private
36440     onRender : function(container, position){
36441         var el = document.createElement("a");
36442         el.hideFocus = true;
36443         el.unselectable = "on";
36444         el.href = this.href || "#";
36445         if(this.hrefTarget){
36446             el.target = this.hrefTarget;
36447         }
36448         el.className = this.itemCls + (this.menu ?  " x-menu-item-arrow" : "") + (this.cls ?  " " + this.cls : "");
36449         
36450         var html = this.html.length ? this.html  : String.format('{0}',this.text);
36451         
36452         el.innerHTML = String.format(
36453                 '<img src="{0}" class="x-menu-item-icon {1}" />' + html,
36454                 this.icon || Roo.BLANK_IMAGE_URL, this.iconCls || '');
36455         this.el = el;
36456         Roo.menu.Item.superclass.onRender.call(this, container, position);
36457     },
36458
36459     /**
36460      * Sets the text to display in this menu item
36461      * @param {String} text The text to display
36462      * @param {Boolean} isHTML true to indicate text is pure html.
36463      */
36464     setText : function(text, isHTML){
36465         if (isHTML) {
36466             this.html = text;
36467         } else {
36468             this.text = text;
36469             this.html = '';
36470         }
36471         if(this.rendered){
36472             var html = this.html.length ? this.html  : String.format('{0}',this.text);
36473      
36474             this.el.update(String.format(
36475                 '<img src="{0}" class="x-menu-item-icon {2}">' + html,
36476                 this.icon || Roo.BLANK_IMAGE_URL, this.text, this.iconCls || ''));
36477             this.parentMenu.autoWidth();
36478         }
36479     },
36480
36481     // private
36482     handleClick : function(e){
36483         if(!this.href){ // if no link defined, stop the event automatically
36484             e.stopEvent();
36485         }
36486         Roo.menu.Item.superclass.handleClick.apply(this, arguments);
36487     },
36488
36489     // private
36490     activate : function(autoExpand){
36491         if(Roo.menu.Item.superclass.activate.apply(this, arguments)){
36492             this.focus();
36493             if(autoExpand){
36494                 this.expandMenu();
36495             }
36496         }
36497         return true;
36498     },
36499
36500     // private
36501     shouldDeactivate : function(e){
36502         if(Roo.menu.Item.superclass.shouldDeactivate.call(this, e)){
36503             if(this.menu && this.menu.isVisible()){
36504                 return !this.menu.getEl().getRegion().contains(e.getPoint());
36505             }
36506             return true;
36507         }
36508         return false;
36509     },
36510
36511     // private
36512     deactivate : function(){
36513         Roo.menu.Item.superclass.deactivate.apply(this, arguments);
36514         this.hideMenu();
36515     },
36516
36517     // private
36518     expandMenu : function(autoActivate){
36519         if(!this.disabled && this.menu){
36520             clearTimeout(this.hideTimer);
36521             delete this.hideTimer;
36522             if(!this.menu.isVisible() && !this.showTimer){
36523                 this.showTimer = this.deferExpand.defer(this.showDelay, this, [autoActivate]);
36524             }else if (this.menu.isVisible() && autoActivate){
36525                 this.menu.tryActivate(0, 1);
36526             }
36527         }
36528     },
36529
36530     // private
36531     deferExpand : function(autoActivate){
36532         delete this.showTimer;
36533         this.menu.show(this.container, this.parentMenu.subMenuAlign || "tl-tr?", this.parentMenu);
36534         if(autoActivate){
36535             this.menu.tryActivate(0, 1);
36536         }
36537     },
36538
36539     // private
36540     hideMenu : function(){
36541         clearTimeout(this.showTimer);
36542         delete this.showTimer;
36543         if(!this.hideTimer && this.menu && this.menu.isVisible()){
36544             this.hideTimer = this.deferHide.defer(this.hideDelay, this);
36545         }
36546     },
36547
36548     // private
36549     deferHide : function(){
36550         delete this.hideTimer;
36551         this.menu.hide();
36552     }
36553 });/*
36554  * Based on:
36555  * Ext JS Library 1.1.1
36556  * Copyright(c) 2006-2007, Ext JS, LLC.
36557  *
36558  * Originally Released Under LGPL - original licence link has changed is not relivant.
36559  *
36560  * Fork - LGPL
36561  * <script type="text/javascript">
36562  */
36563  
36564 /**
36565  * @class Roo.menu.CheckItem
36566  * @extends Roo.menu.Item
36567  * Adds a menu item that contains a checkbox by default, but can also be part of a radio group.
36568  * @constructor
36569  * Creates a new CheckItem
36570  * @param {Object} config Configuration options
36571  */
36572 Roo.menu.CheckItem = function(config){
36573     Roo.menu.CheckItem.superclass.constructor.call(this, config);
36574     this.addEvents({
36575         /**
36576          * @event beforecheckchange
36577          * Fires before the checked value is set, providing an opportunity to cancel if needed
36578          * @param {Roo.menu.CheckItem} this
36579          * @param {Boolean} checked The new checked value that will be set
36580          */
36581         "beforecheckchange" : true,
36582         /**
36583          * @event checkchange
36584          * Fires after the checked value has been set
36585          * @param {Roo.menu.CheckItem} this
36586          * @param {Boolean} checked The checked value that was set
36587          */
36588         "checkchange" : true
36589     });
36590     if(this.checkHandler){
36591         this.on('checkchange', this.checkHandler, this.scope);
36592     }
36593 };
36594 Roo.extend(Roo.menu.CheckItem, Roo.menu.Item, {
36595     /**
36596      * @cfg {String} group
36597      * All check items with the same group name will automatically be grouped into a single-select
36598      * radio button group (defaults to '')
36599      */
36600     /**
36601      * @cfg {String} itemCls The default CSS class to use for check items (defaults to "x-menu-item x-menu-check-item")
36602      */
36603     itemCls : "x-menu-item x-menu-check-item",
36604     /**
36605      * @cfg {String} groupClass The default CSS class to use for radio group check items (defaults to "x-menu-group-item")
36606      */
36607     groupClass : "x-menu-group-item",
36608
36609     /**
36610      * @cfg {Boolean} checked True to initialize this checkbox as checked (defaults to false).  Note that
36611      * if this checkbox is part of a radio group (group = true) only the last item in the group that is
36612      * initialized with checked = true will be rendered as checked.
36613      */
36614     checked: false,
36615
36616     // private
36617     ctype: "Roo.menu.CheckItem",
36618
36619     // private
36620     onRender : function(c){
36621         Roo.menu.CheckItem.superclass.onRender.apply(this, arguments);
36622         if(this.group){
36623             this.el.addClass(this.groupClass);
36624         }
36625         Roo.menu.MenuMgr.registerCheckable(this);
36626         if(this.checked){
36627             this.checked = false;
36628             this.setChecked(true, true);
36629         }
36630     },
36631
36632     // private
36633     destroy : function(){
36634         if(this.rendered){
36635             Roo.menu.MenuMgr.unregisterCheckable(this);
36636         }
36637         Roo.menu.CheckItem.superclass.destroy.apply(this, arguments);
36638     },
36639
36640     /**
36641      * Set the checked state of this item
36642      * @param {Boolean} checked The new checked value
36643      * @param {Boolean} suppressEvent (optional) True to prevent the checkchange event from firing (defaults to false)
36644      */
36645     setChecked : function(state, suppressEvent){
36646         if(this.checked != state && this.fireEvent("beforecheckchange", this, state) !== false){
36647             if(this.container){
36648                 this.container[state ? "addClass" : "removeClass"]("x-menu-item-checked");
36649             }
36650             this.checked = state;
36651             if(suppressEvent !== true){
36652                 this.fireEvent("checkchange", this, state);
36653             }
36654         }
36655     },
36656
36657     // private
36658     handleClick : function(e){
36659        if(!this.disabled && !(this.checked && this.group)){// disable unselect on radio item
36660            this.setChecked(!this.checked);
36661        }
36662        Roo.menu.CheckItem.superclass.handleClick.apply(this, arguments);
36663     }
36664 });/*
36665  * Based on:
36666  * Ext JS Library 1.1.1
36667  * Copyright(c) 2006-2007, Ext JS, LLC.
36668  *
36669  * Originally Released Under LGPL - original licence link has changed is not relivant.
36670  *
36671  * Fork - LGPL
36672  * <script type="text/javascript">
36673  */
36674  
36675 /**
36676  * @class Roo.menu.DateItem
36677  * @extends Roo.menu.Adapter
36678  * A menu item that wraps the {@link Roo.DatPicker} component.
36679  * @constructor
36680  * Creates a new DateItem
36681  * @param {Object} config Configuration options
36682  */
36683 Roo.menu.DateItem = function(config){
36684     Roo.menu.DateItem.superclass.constructor.call(this, new Roo.DatePicker(config), config);
36685     /** The Roo.DatePicker object @type Roo.DatePicker */
36686     this.picker = this.component;
36687     this.addEvents({select: true});
36688     
36689     this.picker.on("render", function(picker){
36690         picker.getEl().swallowEvent("click");
36691         picker.container.addClass("x-menu-date-item");
36692     });
36693
36694     this.picker.on("select", this.onSelect, this);
36695 };
36696
36697 Roo.extend(Roo.menu.DateItem, Roo.menu.Adapter, {
36698     // private
36699     onSelect : function(picker, date){
36700         this.fireEvent("select", this, date, picker);
36701         Roo.menu.DateItem.superclass.handleClick.call(this);
36702     }
36703 });/*
36704  * Based on:
36705  * Ext JS Library 1.1.1
36706  * Copyright(c) 2006-2007, Ext JS, LLC.
36707  *
36708  * Originally Released Under LGPL - original licence link has changed is not relivant.
36709  *
36710  * Fork - LGPL
36711  * <script type="text/javascript">
36712  */
36713  
36714 /**
36715  * @class Roo.menu.ColorItem
36716  * @extends Roo.menu.Adapter
36717  * A menu item that wraps the {@link Roo.ColorPalette} component.
36718  * @constructor
36719  * Creates a new ColorItem
36720  * @param {Object} config Configuration options
36721  */
36722 Roo.menu.ColorItem = function(config){
36723     Roo.menu.ColorItem.superclass.constructor.call(this, new Roo.ColorPalette(config), config);
36724     /** The Roo.ColorPalette object @type Roo.ColorPalette */
36725     this.palette = this.component;
36726     this.relayEvents(this.palette, ["select"]);
36727     if(this.selectHandler){
36728         this.on('select', this.selectHandler, this.scope);
36729     }
36730 };
36731 Roo.extend(Roo.menu.ColorItem, Roo.menu.Adapter);/*
36732  * Based on:
36733  * Ext JS Library 1.1.1
36734  * Copyright(c) 2006-2007, Ext JS, LLC.
36735  *
36736  * Originally Released Under LGPL - original licence link has changed is not relivant.
36737  *
36738  * Fork - LGPL
36739  * <script type="text/javascript">
36740  */
36741  
36742
36743 /**
36744  * @class Roo.menu.DateMenu
36745  * @extends Roo.menu.Menu
36746  * A menu containing a {@link Roo.menu.DateItem} component (which provides a date picker).
36747  * @constructor
36748  * Creates a new DateMenu
36749  * @param {Object} config Configuration options
36750  */
36751 Roo.menu.DateMenu = function(config){
36752     Roo.menu.DateMenu.superclass.constructor.call(this, config);
36753     this.plain = true;
36754     var di = new Roo.menu.DateItem(config);
36755     this.add(di);
36756     /**
36757      * The {@link Roo.DatePicker} instance for this DateMenu
36758      * @type DatePicker
36759      */
36760     this.picker = di.picker;
36761     /**
36762      * @event select
36763      * @param {DatePicker} picker
36764      * @param {Date} date
36765      */
36766     this.relayEvents(di, ["select"]);
36767     this.on('beforeshow', function(){
36768         if(this.picker){
36769             this.picker.hideMonthPicker(false);
36770         }
36771     }, this);
36772 };
36773 Roo.extend(Roo.menu.DateMenu, Roo.menu.Menu, {
36774     cls:'x-date-menu'
36775 });/*
36776  * Based on:
36777  * Ext JS Library 1.1.1
36778  * Copyright(c) 2006-2007, Ext JS, LLC.
36779  *
36780  * Originally Released Under LGPL - original licence link has changed is not relivant.
36781  *
36782  * Fork - LGPL
36783  * <script type="text/javascript">
36784  */
36785  
36786
36787 /**
36788  * @class Roo.menu.ColorMenu
36789  * @extends Roo.menu.Menu
36790  * A menu containing a {@link Roo.menu.ColorItem} component (which provides a basic color picker).
36791  * @constructor
36792  * Creates a new ColorMenu
36793  * @param {Object} config Configuration options
36794  */
36795 Roo.menu.ColorMenu = function(config){
36796     Roo.menu.ColorMenu.superclass.constructor.call(this, config);
36797     this.plain = true;
36798     var ci = new Roo.menu.ColorItem(config);
36799     this.add(ci);
36800     /**
36801      * The {@link Roo.ColorPalette} instance for this ColorMenu
36802      * @type ColorPalette
36803      */
36804     this.palette = ci.palette;
36805     /**
36806      * @event select
36807      * @param {ColorPalette} palette
36808      * @param {String} color
36809      */
36810     this.relayEvents(ci, ["select"]);
36811 };
36812 Roo.extend(Roo.menu.ColorMenu, Roo.menu.Menu);/*
36813  * Based on:
36814  * Ext JS Library 1.1.1
36815  * Copyright(c) 2006-2007, Ext JS, LLC.
36816  *
36817  * Originally Released Under LGPL - original licence link has changed is not relivant.
36818  *
36819  * Fork - LGPL
36820  * <script type="text/javascript">
36821  */
36822  
36823 /**
36824  * @class Roo.form.Field
36825  * @extends Roo.BoxComponent
36826  * Base class for form fields that provides default event handling, sizing, value handling and other functionality.
36827  * @constructor
36828  * Creates a new Field
36829  * @param {Object} config Configuration options
36830  */
36831 Roo.form.Field = function(config){
36832     Roo.form.Field.superclass.constructor.call(this, config);
36833 };
36834
36835 Roo.extend(Roo.form.Field, Roo.BoxComponent,  {
36836     /**
36837      * @cfg {String} fieldLabel Label to use when rendering a form.
36838      */
36839        /**
36840      * @cfg {String} qtip Mouse over tip
36841      */
36842      
36843     /**
36844      * @cfg {String} invalidClass The CSS class to use when marking a field invalid (defaults to "x-form-invalid")
36845      */
36846     invalidClass : "x-form-invalid",
36847     /**
36848      * @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")
36849      */
36850     invalidText : "The value in this field is invalid",
36851     /**
36852      * @cfg {String} focusClass The CSS class to use when the field receives focus (defaults to "x-form-focus")
36853      */
36854     focusClass : "x-form-focus",
36855     /**
36856      * @cfg {String/Boolean} validationEvent The event that should initiate field validation. Set to false to disable
36857       automatic validation (defaults to "keyup").
36858      */
36859     validationEvent : "keyup",
36860     /**
36861      * @cfg {Boolean} validateOnBlur Whether the field should validate when it loses focus (defaults to true).
36862      */
36863     validateOnBlur : true,
36864     /**
36865      * @cfg {Number} validationDelay The length of time in milliseconds after user input begins until validation is initiated (defaults to 250)
36866      */
36867     validationDelay : 250,
36868     /**
36869      * @cfg {String/Object} autoCreate A DomHelper element spec, or true for a default element spec (defaults to
36870      * {tag: "input", type: "text", size: "20", autocomplete: "off"})
36871      */
36872     defaultAutoCreate : {tag: "input", type: "text", size: "20", autocomplete: "off"},
36873     /**
36874      * @cfg {String} fieldClass The default CSS class for the field (defaults to "x-form-field")
36875      */
36876     fieldClass : "x-form-field",
36877     /**
36878      * @cfg {String} msgTarget The location where error text should display.  Should be one of the following values (defaults to 'qtip'):
36879      *<pre>
36880 Value         Description
36881 -----------   ----------------------------------------------------------------------
36882 qtip          Display a quick tip when the user hovers over the field
36883 title         Display a default browser title attribute popup
36884 under         Add a block div beneath the field containing the error text
36885 side          Add an error icon to the right of the field with a popup on hover
36886 [element id]  Add the error text directly to the innerHTML of the specified element
36887 </pre>
36888      */
36889     msgTarget : 'qtip',
36890     /**
36891      * @cfg {String} msgFx <b>Experimental</b> The effect used when displaying a validation message under the field (defaults to 'normal').
36892      */
36893     msgFx : 'normal',
36894
36895     /**
36896      * @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.
36897      */
36898     readOnly : false,
36899
36900     /**
36901      * @cfg {Boolean} disabled True to disable the field (defaults to false).
36902      */
36903     disabled : false,
36904
36905     /**
36906      * @cfg {String} inputType The type attribute for input fields -- e.g. radio, text, password (defaults to "text").
36907      */
36908     inputType : undefined,
36909     
36910     /**
36911      * @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).
36912          */
36913         tabIndex : undefined,
36914         
36915     // private
36916     isFormField : true,
36917
36918     // private
36919     hasFocus : false,
36920     /**
36921      * @property {Roo.Element} fieldEl
36922      * Element Containing the rendered Field (with label etc.)
36923      */
36924     /**
36925      * @cfg {Mixed} value A value to initialize this field with.
36926      */
36927     value : undefined,
36928
36929     /**
36930      * @cfg {String} name The field's HTML name attribute.
36931      */
36932     /**
36933      * @cfg {String} cls A CSS class to apply to the field's underlying element.
36934      */
36935
36936         // private ??
36937         initComponent : function(){
36938         Roo.form.Field.superclass.initComponent.call(this);
36939         this.addEvents({
36940             /**
36941              * @event focus
36942              * Fires when this field receives input focus.
36943              * @param {Roo.form.Field} this
36944              */
36945             focus : true,
36946             /**
36947              * @event blur
36948              * Fires when this field loses input focus.
36949              * @param {Roo.form.Field} this
36950              */
36951             blur : true,
36952             /**
36953              * @event specialkey
36954              * Fires when any key related to navigation (arrows, tab, enter, esc, etc.) is pressed.  You can check
36955              * {@link Roo.EventObject#getKey} to determine which key was pressed.
36956              * @param {Roo.form.Field} this
36957              * @param {Roo.EventObject} e The event object
36958              */
36959             specialkey : true,
36960             /**
36961              * @event change
36962              * Fires just before the field blurs if the field value has changed.
36963              * @param {Roo.form.Field} this
36964              * @param {Mixed} newValue The new value
36965              * @param {Mixed} oldValue The original value
36966              */
36967             change : true,
36968             /**
36969              * @event invalid
36970              * Fires after the field has been marked as invalid.
36971              * @param {Roo.form.Field} this
36972              * @param {String} msg The validation message
36973              */
36974             invalid : true,
36975             /**
36976              * @event valid
36977              * Fires after the field has been validated with no errors.
36978              * @param {Roo.form.Field} this
36979              */
36980             valid : true,
36981              /**
36982              * @event keyup
36983              * Fires after the key up
36984              * @param {Roo.form.Field} this
36985              * @param {Roo.EventObject}  e The event Object
36986              */
36987             keyup : true
36988         });
36989     },
36990
36991     /**
36992      * Returns the name attribute of the field if available
36993      * @return {String} name The field name
36994      */
36995     getName: function(){
36996          return this.rendered && this.el.dom.name ? this.el.dom.name : (this.hiddenName || '');
36997     },
36998
36999     // private
37000     onRender : function(ct, position){
37001         Roo.form.Field.superclass.onRender.call(this, ct, position);
37002         if(!this.el){
37003             var cfg = this.getAutoCreate();
37004             if(!cfg.name){
37005                 cfg.name = typeof(this.name) == 'undefined' ? this.id : this.name;
37006             }
37007             if (!cfg.name.length) {
37008                 delete cfg.name;
37009             }
37010             if(this.inputType){
37011                 cfg.type = this.inputType;
37012             }
37013             this.el = ct.createChild(cfg, position);
37014         }
37015         var type = this.el.dom.type;
37016         if(type){
37017             if(type == 'password'){
37018                 type = 'text';
37019             }
37020             this.el.addClass('x-form-'+type);
37021         }
37022         if(this.readOnly){
37023             this.el.dom.readOnly = true;
37024         }
37025         if(this.tabIndex !== undefined){
37026             this.el.dom.setAttribute('tabIndex', this.tabIndex);
37027         }
37028
37029         this.el.addClass([this.fieldClass, this.cls]);
37030         this.initValue();
37031     },
37032
37033     /**
37034      * Apply the behaviors of this component to an existing element. <b>This is used instead of render().</b>
37035      * @param {String/HTMLElement/Element} el The id of the node, a DOM node or an existing Element
37036      * @return {Roo.form.Field} this
37037      */
37038     applyTo : function(target){
37039         this.allowDomMove = false;
37040         this.el = Roo.get(target);
37041         this.render(this.el.dom.parentNode);
37042         return this;
37043     },
37044
37045     // private
37046     initValue : function(){
37047         if(this.value !== undefined){
37048             this.setValue(this.value);
37049         }else if(this.el.dom.value.length > 0){
37050             this.setValue(this.el.dom.value);
37051         }
37052     },
37053
37054     /**
37055      * Returns true if this field has been changed since it was originally loaded and is not disabled.
37056      */
37057     isDirty : function() {
37058         if(this.disabled) {
37059             return false;
37060         }
37061         return String(this.getValue()) !== String(this.originalValue);
37062     },
37063
37064     // private
37065     afterRender : function(){
37066         Roo.form.Field.superclass.afterRender.call(this);
37067         this.initEvents();
37068     },
37069
37070     // private
37071     fireKey : function(e){
37072         //Roo.log('field ' + e.getKey());
37073         if(e.isNavKeyPress()){
37074             this.fireEvent("specialkey", this, e);
37075         }
37076     },
37077
37078     /**
37079      * Resets the current field value to the originally loaded value and clears any validation messages
37080      */
37081     reset : function(){
37082         this.setValue(this.resetValue);
37083         this.clearInvalid();
37084     },
37085
37086     // private
37087     initEvents : function(){
37088         // safari killled keypress - so keydown is now used..
37089         this.el.on("keydown" , this.fireKey,  this);
37090         this.el.on("focus", this.onFocus,  this);
37091         this.el.on("blur", this.onBlur,  this);
37092         this.el.relayEvent('keyup', this);
37093
37094         // reference to original value for reset
37095         this.originalValue = this.getValue();
37096         this.resetValue =  this.getValue();
37097     },
37098
37099     // private
37100     onFocus : function(){
37101         if(!Roo.isOpera && this.focusClass){ // don't touch in Opera
37102             this.el.addClass(this.focusClass);
37103         }
37104         if(!this.hasFocus){
37105             this.hasFocus = true;
37106             this.startValue = this.getValue();
37107             this.fireEvent("focus", this);
37108         }
37109     },
37110
37111     beforeBlur : Roo.emptyFn,
37112
37113     // private
37114     onBlur : function(){
37115         this.beforeBlur();
37116         if(!Roo.isOpera && this.focusClass){ // don't touch in Opera
37117             this.el.removeClass(this.focusClass);
37118         }
37119         this.hasFocus = false;
37120         if(this.validationEvent !== false && this.validateOnBlur && this.validationEvent != "blur"){
37121             this.validate();
37122         }
37123         var v = this.getValue();
37124         if(String(v) !== String(this.startValue)){
37125             this.fireEvent('change', this, v, this.startValue);
37126         }
37127         this.fireEvent("blur", this);
37128     },
37129
37130     /**
37131      * Returns whether or not the field value is currently valid
37132      * @param {Boolean} preventMark True to disable marking the field invalid
37133      * @return {Boolean} True if the value is valid, else false
37134      */
37135     isValid : function(preventMark){
37136         if(this.disabled){
37137             return true;
37138         }
37139         var restore = this.preventMark;
37140         this.preventMark = preventMark === true;
37141         var v = this.validateValue(this.processValue(this.getRawValue()));
37142         this.preventMark = restore;
37143         return v;
37144     },
37145
37146     /**
37147      * Validates the field value
37148      * @return {Boolean} True if the value is valid, else false
37149      */
37150     validate : function(){
37151         if(this.disabled || this.validateValue(this.processValue(this.getRawValue()))){
37152             this.clearInvalid();
37153             return true;
37154         }
37155         return false;
37156     },
37157
37158     processValue : function(value){
37159         return value;
37160     },
37161
37162     // private
37163     // Subclasses should provide the validation implementation by overriding this
37164     validateValue : function(value){
37165         return true;
37166     },
37167
37168     /**
37169      * Mark this field as invalid
37170      * @param {String} msg The validation message
37171      */
37172     markInvalid : function(msg){
37173         if(!this.rendered || this.preventMark){ // not rendered
37174             return;
37175         }
37176         
37177         var obj = (typeof(this.combo) != 'undefined') ? this.combo : this; // fix the combox array!!
37178         
37179         obj.el.addClass(this.invalidClass);
37180         msg = msg || this.invalidText;
37181         switch(this.msgTarget){
37182             case 'qtip':
37183                 obj.el.dom.qtip = msg;
37184                 obj.el.dom.qclass = 'x-form-invalid-tip';
37185                 if(Roo.QuickTips){ // fix for floating editors interacting with DND
37186                     Roo.QuickTips.enable();
37187                 }
37188                 break;
37189             case 'title':
37190                 this.el.dom.title = msg;
37191                 break;
37192             case 'under':
37193                 if(!this.errorEl){
37194                     var elp = this.el.findParent('.x-form-element', 5, true);
37195                     this.errorEl = elp.createChild({cls:'x-form-invalid-msg'});
37196                     this.errorEl.setWidth(elp.getWidth(true)-20);
37197                 }
37198                 this.errorEl.update(msg);
37199                 Roo.form.Field.msgFx[this.msgFx].show(this.errorEl, this);
37200                 break;
37201             case 'side':
37202                 if(!this.errorIcon){
37203                     var elp = this.el.findParent('.x-form-element', 5, true);
37204                     this.errorIcon = elp.createChild({cls:'x-form-invalid-icon'});
37205                 }
37206                 this.alignErrorIcon();
37207                 this.errorIcon.dom.qtip = msg;
37208                 this.errorIcon.dom.qclass = 'x-form-invalid-tip';
37209                 this.errorIcon.show();
37210                 this.on('resize', this.alignErrorIcon, this);
37211                 break;
37212             default:
37213                 var t = Roo.getDom(this.msgTarget);
37214                 t.innerHTML = msg;
37215                 t.style.display = this.msgDisplay;
37216                 break;
37217         }
37218         this.fireEvent('invalid', this, msg);
37219     },
37220
37221     // private
37222     alignErrorIcon : function(){
37223         this.errorIcon.alignTo(this.el, 'tl-tr', [2, 0]);
37224     },
37225
37226     /**
37227      * Clear any invalid styles/messages for this field
37228      */
37229     clearInvalid : function(){
37230         if(!this.rendered || this.preventMark){ // not rendered
37231             return;
37232         }
37233         var obj = (typeof(this.combo) != 'undefined') ? this.combo : this; // fix the combox array!!
37234         
37235         obj.el.removeClass(this.invalidClass);
37236         switch(this.msgTarget){
37237             case 'qtip':
37238                 obj.el.dom.qtip = '';
37239                 break;
37240             case 'title':
37241                 this.el.dom.title = '';
37242                 break;
37243             case 'under':
37244                 if(this.errorEl){
37245                     Roo.form.Field.msgFx[this.msgFx].hide(this.errorEl, this);
37246                 }
37247                 break;
37248             case 'side':
37249                 if(this.errorIcon){
37250                     this.errorIcon.dom.qtip = '';
37251                     this.errorIcon.hide();
37252                     this.un('resize', this.alignErrorIcon, this);
37253                 }
37254                 break;
37255             default:
37256                 var t = Roo.getDom(this.msgTarget);
37257                 t.innerHTML = '';
37258                 t.style.display = 'none';
37259                 break;
37260         }
37261         this.fireEvent('valid', this);
37262     },
37263
37264     /**
37265      * Returns the raw data value which may or may not be a valid, defined value.  To return a normalized value see {@link #getValue}.
37266      * @return {Mixed} value The field value
37267      */
37268     getRawValue : function(){
37269         var v = this.el.getValue();
37270         
37271         return v;
37272     },
37273
37274     /**
37275      * Returns the normalized data value (undefined or emptyText will be returned as '').  To return the raw value see {@link #getRawValue}.
37276      * @return {Mixed} value The field value
37277      */
37278     getValue : function(){
37279         var v = this.el.getValue();
37280          
37281         return v;
37282     },
37283
37284     /**
37285      * Sets the underlying DOM field's value directly, bypassing validation.  To set the value with validation see {@link #setValue}.
37286      * @param {Mixed} value The value to set
37287      */
37288     setRawValue : function(v){
37289         return this.el.dom.value = (v === null || v === undefined ? '' : v);
37290     },
37291
37292     /**
37293      * Sets a data value into the field and validates it.  To set the value directly without validation see {@link #setRawValue}.
37294      * @param {Mixed} value The value to set
37295      */
37296     setValue : function(v){
37297         this.value = v;
37298         if(this.rendered){
37299             this.el.dom.value = (v === null || v === undefined ? '' : v);
37300              this.validate();
37301         }
37302     },
37303
37304     adjustSize : function(w, h){
37305         var s = Roo.form.Field.superclass.adjustSize.call(this, w, h);
37306         s.width = this.adjustWidth(this.el.dom.tagName, s.width);
37307         return s;
37308     },
37309
37310     adjustWidth : function(tag, w){
37311         tag = tag.toLowerCase();
37312         if(typeof w == 'number' && Roo.isStrict && !Roo.isSafari){
37313             if(Roo.isIE && (tag == 'input' || tag == 'textarea')){
37314                 if(tag == 'input'){
37315                     return w + 2;
37316                 }
37317                 if(tag == 'textarea'){
37318                     return w-2;
37319                 }
37320             }else if(Roo.isOpera){
37321                 if(tag == 'input'){
37322                     return w + 2;
37323                 }
37324                 if(tag == 'textarea'){
37325                     return w-2;
37326                 }
37327             }
37328         }
37329         return w;
37330     }
37331 });
37332
37333
37334 // anything other than normal should be considered experimental
37335 Roo.form.Field.msgFx = {
37336     normal : {
37337         show: function(msgEl, f){
37338             msgEl.setDisplayed('block');
37339         },
37340
37341         hide : function(msgEl, f){
37342             msgEl.setDisplayed(false).update('');
37343         }
37344     },
37345
37346     slide : {
37347         show: function(msgEl, f){
37348             msgEl.slideIn('t', {stopFx:true});
37349         },
37350
37351         hide : function(msgEl, f){
37352             msgEl.slideOut('t', {stopFx:true,useDisplay:true});
37353         }
37354     },
37355
37356     slideRight : {
37357         show: function(msgEl, f){
37358             msgEl.fixDisplay();
37359             msgEl.alignTo(f.el, 'tl-tr');
37360             msgEl.slideIn('l', {stopFx:true});
37361         },
37362
37363         hide : function(msgEl, f){
37364             msgEl.slideOut('l', {stopFx:true,useDisplay:true});
37365         }
37366     }
37367 };/*
37368  * Based on:
37369  * Ext JS Library 1.1.1
37370  * Copyright(c) 2006-2007, Ext JS, LLC.
37371  *
37372  * Originally Released Under LGPL - original licence link has changed is not relivant.
37373  *
37374  * Fork - LGPL
37375  * <script type="text/javascript">
37376  */
37377  
37378
37379 /**
37380  * @class Roo.form.TextField
37381  * @extends Roo.form.Field
37382  * Basic text field.  Can be used as a direct replacement for traditional text inputs, or as the base
37383  * class for more sophisticated input controls (like {@link Roo.form.TextArea} and {@link Roo.form.ComboBox}).
37384  * @constructor
37385  * Creates a new TextField
37386  * @param {Object} config Configuration options
37387  */
37388 Roo.form.TextField = function(config){
37389     Roo.form.TextField.superclass.constructor.call(this, config);
37390     this.addEvents({
37391         /**
37392          * @event autosize
37393          * Fires when the autosize function is triggered.  The field may or may not have actually changed size
37394          * according to the default logic, but this event provides a hook for the developer to apply additional
37395          * logic at runtime to resize the field if needed.
37396              * @param {Roo.form.Field} this This text field
37397              * @param {Number} width The new field width
37398              */
37399         autosize : true
37400     });
37401 };
37402
37403 Roo.extend(Roo.form.TextField, Roo.form.Field,  {
37404     /**
37405      * @cfg {Boolean} grow True if this field should automatically grow and shrink to its content
37406      */
37407     grow : false,
37408     /**
37409      * @cfg {Number} growMin The minimum width to allow when grow = true (defaults to 30)
37410      */
37411     growMin : 30,
37412     /**
37413      * @cfg {Number} growMax The maximum width to allow when grow = true (defaults to 800)
37414      */
37415     growMax : 800,
37416     /**
37417      * @cfg {String} vtype A validation type name as defined in {@link Roo.form.VTypes} (defaults to null)
37418      */
37419     vtype : null,
37420     /**
37421      * @cfg {String} maskRe An input mask regular expression that will be used to filter keystrokes that don't match (defaults to null)
37422      */
37423     maskRe : null,
37424     /**
37425      * @cfg {Boolean} disableKeyFilter True to disable input keystroke filtering (defaults to false)
37426      */
37427     disableKeyFilter : false,
37428     /**
37429      * @cfg {Boolean} allowBlank False to validate that the value length > 0 (defaults to true)
37430      */
37431     allowBlank : true,
37432     /**
37433      * @cfg {Number} minLength Minimum input field length required (defaults to 0)
37434      */
37435     minLength : 0,
37436     /**
37437      * @cfg {Number} maxLength Maximum input field length allowed (defaults to Number.MAX_VALUE)
37438      */
37439     maxLength : Number.MAX_VALUE,
37440     /**
37441      * @cfg {String} minLengthText Error text to display if the minimum length validation fails (defaults to "The minimum length for this field is {minLength}")
37442      */
37443     minLengthText : "The minimum length for this field is {0}",
37444     /**
37445      * @cfg {String} maxLengthText Error text to display if the maximum length validation fails (defaults to "The maximum length for this field is {maxLength}")
37446      */
37447     maxLengthText : "The maximum length for this field is {0}",
37448     /**
37449      * @cfg {Boolean} selectOnFocus True to automatically select any existing field text when the field receives input focus (defaults to false)
37450      */
37451     selectOnFocus : false,
37452     /**
37453      * @cfg {String} blankText Error text to display if the allow blank validation fails (defaults to "This field is required")
37454      */
37455     blankText : "This field is required",
37456     /**
37457      * @cfg {Function} validator A custom validation function to be called during field validation (defaults to null).
37458      * If available, this function will be called only after the basic validators all return true, and will be passed the
37459      * current field value and expected to return boolean true if the value is valid or a string error message if invalid.
37460      */
37461     validator : null,
37462     /**
37463      * @cfg {RegExp} regex A JavaScript RegExp object to be tested against the field value during validation (defaults to null).
37464      * If available, this regex will be evaluated only after the basic validators all return true, and will be passed the
37465      * current field value.  If the test fails, the field will be marked invalid using {@link #regexText}.
37466      */
37467     regex : null,
37468     /**
37469      * @cfg {String} regexText The error text to display if {@link #regex} is used and the test fails during validation (defaults to "")
37470      */
37471     regexText : "",
37472     /**
37473      * @cfg {String} emptyText The default text to display in an empty field - placeholder... (defaults to null).
37474      */
37475     emptyText : null,
37476    
37477
37478     // private
37479     initEvents : function()
37480     {
37481         if (this.emptyText) {
37482             this.el.attr('placeholder', this.emptyText);
37483         }
37484         
37485         Roo.form.TextField.superclass.initEvents.call(this);
37486         if(this.validationEvent == 'keyup'){
37487             this.validationTask = new Roo.util.DelayedTask(this.validate, this);
37488             this.el.on('keyup', this.filterValidation, this);
37489         }
37490         else if(this.validationEvent !== false){
37491             this.el.on(this.validationEvent, this.validate, this, {buffer: this.validationDelay});
37492         }
37493         
37494         if(this.selectOnFocus){
37495             this.on("focus", this.preFocus, this);
37496             
37497         }
37498         if(this.maskRe || (this.vtype && this.disableKeyFilter !== true && (this.maskRe = Roo.form.VTypes[this.vtype+'Mask']))){
37499             this.el.on("keypress", this.filterKeys, this);
37500         }
37501         if(this.grow){
37502             this.el.on("keyup", this.onKeyUp,  this, {buffer:50});
37503             this.el.on("click", this.autoSize,  this);
37504         }
37505         if(this.el.is('input[type=password]') && Roo.isSafari){
37506             this.el.on('keydown', this.SafariOnKeyDown, this);
37507         }
37508     },
37509
37510     processValue : function(value){
37511         if(this.stripCharsRe){
37512             var newValue = value.replace(this.stripCharsRe, '');
37513             if(newValue !== value){
37514                 this.setRawValue(newValue);
37515                 return newValue;
37516             }
37517         }
37518         return value;
37519     },
37520
37521     filterValidation : function(e){
37522         if(!e.isNavKeyPress()){
37523             this.validationTask.delay(this.validationDelay);
37524         }
37525     },
37526
37527     // private
37528     onKeyUp : function(e){
37529         if(!e.isNavKeyPress()){
37530             this.autoSize();
37531         }
37532     },
37533
37534     /**
37535      * Resets the current field value to the originally-loaded value and clears any validation messages.
37536      *  
37537      */
37538     reset : function(){
37539         Roo.form.TextField.superclass.reset.call(this);
37540        
37541     },
37542
37543     
37544     // private
37545     preFocus : function(){
37546         
37547         if(this.selectOnFocus){
37548             this.el.dom.select();
37549         }
37550     },
37551
37552     
37553     // private
37554     filterKeys : function(e){
37555         var k = e.getKey();
37556         if(!Roo.isIE && (e.isNavKeyPress() || k == e.BACKSPACE || (k == e.DELETE && e.button == -1))){
37557             return;
37558         }
37559         var c = e.getCharCode(), cc = String.fromCharCode(c);
37560         if(Roo.isIE && (e.isSpecialKey() || !cc)){
37561             return;
37562         }
37563         if(!this.maskRe.test(cc)){
37564             e.stopEvent();
37565         }
37566     },
37567
37568     setValue : function(v){
37569         
37570         Roo.form.TextField.superclass.setValue.apply(this, arguments);
37571         
37572         this.autoSize();
37573     },
37574
37575     /**
37576      * Validates a value according to the field's validation rules and marks the field as invalid
37577      * if the validation fails
37578      * @param {Mixed} value The value to validate
37579      * @return {Boolean} True if the value is valid, else false
37580      */
37581     validateValue : function(value){
37582         if(value.length < 1)  { // if it's blank
37583              if(this.allowBlank){
37584                 this.clearInvalid();
37585                 return true;
37586              }else{
37587                 this.markInvalid(this.blankText);
37588                 return false;
37589              }
37590         }
37591         if(value.length < this.minLength){
37592             this.markInvalid(String.format(this.minLengthText, this.minLength));
37593             return false;
37594         }
37595         if(value.length > this.maxLength){
37596             this.markInvalid(String.format(this.maxLengthText, this.maxLength));
37597             return false;
37598         }
37599         if(this.vtype){
37600             var vt = Roo.form.VTypes;
37601             if(!vt[this.vtype](value, this)){
37602                 this.markInvalid(this.vtypeText || vt[this.vtype +'Text']);
37603                 return false;
37604             }
37605         }
37606         if(typeof this.validator == "function"){
37607             var msg = this.validator(value);
37608             if(msg !== true){
37609                 this.markInvalid(msg);
37610                 return false;
37611             }
37612         }
37613         if(this.regex && !this.regex.test(value)){
37614             this.markInvalid(this.regexText);
37615             return false;
37616         }
37617         return true;
37618     },
37619
37620     /**
37621      * Selects text in this field
37622      * @param {Number} start (optional) The index where the selection should start (defaults to 0)
37623      * @param {Number} end (optional) The index where the selection should end (defaults to the text length)
37624      */
37625     selectText : function(start, end){
37626         var v = this.getRawValue();
37627         if(v.length > 0){
37628             start = start === undefined ? 0 : start;
37629             end = end === undefined ? v.length : end;
37630             var d = this.el.dom;
37631             if(d.setSelectionRange){
37632                 d.setSelectionRange(start, end);
37633             }else if(d.createTextRange){
37634                 var range = d.createTextRange();
37635                 range.moveStart("character", start);
37636                 range.moveEnd("character", v.length-end);
37637                 range.select();
37638             }
37639         }
37640     },
37641
37642     /**
37643      * Automatically grows the field to accomodate the width of the text up to the maximum field width allowed.
37644      * This only takes effect if grow = true, and fires the autosize event.
37645      */
37646     autoSize : function(){
37647         if(!this.grow || !this.rendered){
37648             return;
37649         }
37650         if(!this.metrics){
37651             this.metrics = Roo.util.TextMetrics.createInstance(this.el);
37652         }
37653         var el = this.el;
37654         var v = el.dom.value;
37655         var d = document.createElement('div');
37656         d.appendChild(document.createTextNode(v));
37657         v = d.innerHTML;
37658         d = null;
37659         v += "&#160;";
37660         var w = Math.min(this.growMax, Math.max(this.metrics.getWidth(v) + /* add extra padding */ 10, this.growMin));
37661         this.el.setWidth(w);
37662         this.fireEvent("autosize", this, w);
37663     },
37664     
37665     // private
37666     SafariOnKeyDown : function(event)
37667     {
37668         // this is a workaround for a password hang bug on chrome/ webkit.
37669         
37670         var isSelectAll = false;
37671         
37672         if(this.el.dom.selectionEnd > 0){
37673             isSelectAll = (this.el.dom.selectionEnd - this.el.dom.selectionStart - this.getValue().length == 0) ? true : false;
37674         }
37675         if(((event.getKey() == 8 || event.getKey() == 46) && this.getValue().length ==1)){ // backspace and delete key
37676             event.preventDefault();
37677             this.setValue('');
37678             return;
37679         }
37680         
37681         if(isSelectAll){ // backspace and delete key
37682             
37683             event.preventDefault();
37684             // this is very hacky as keydown always get's upper case.
37685             //
37686             var cc = String.fromCharCode(event.getCharCode());
37687             this.setValue( event.shiftKey ?  cc : cc.toLowerCase());
37688             
37689         }
37690         
37691         
37692     }
37693 });/*
37694  * Based on:
37695  * Ext JS Library 1.1.1
37696  * Copyright(c) 2006-2007, Ext JS, LLC.
37697  *
37698  * Originally Released Under LGPL - original licence link has changed is not relivant.
37699  *
37700  * Fork - LGPL
37701  * <script type="text/javascript">
37702  */
37703  
37704 /**
37705  * @class Roo.form.Hidden
37706  * @extends Roo.form.TextField
37707  * Simple Hidden element used on forms 
37708  * 
37709  * usage: form.add(new Roo.form.HiddenField({ 'name' : 'test1' }));
37710  * 
37711  * @constructor
37712  * Creates a new Hidden form element.
37713  * @param {Object} config Configuration options
37714  */
37715
37716
37717
37718 // easy hidden field...
37719 Roo.form.Hidden = function(config){
37720     Roo.form.Hidden.superclass.constructor.call(this, config);
37721 };
37722   
37723 Roo.extend(Roo.form.Hidden, Roo.form.TextField, {
37724     fieldLabel:      '',
37725     inputType:      'hidden',
37726     width:          50,
37727     allowBlank:     true,
37728     labelSeparator: '',
37729     hidden:         true,
37730     itemCls :       'x-form-item-display-none'
37731
37732
37733 });
37734
37735
37736 /*
37737  * Based on:
37738  * Ext JS Library 1.1.1
37739  * Copyright(c) 2006-2007, Ext JS, LLC.
37740  *
37741  * Originally Released Under LGPL - original licence link has changed is not relivant.
37742  *
37743  * Fork - LGPL
37744  * <script type="text/javascript">
37745  */
37746  
37747 /**
37748  * @class Roo.form.TriggerField
37749  * @extends Roo.form.TextField
37750  * Provides a convenient wrapper for TextFields that adds a clickable trigger button (looks like a combobox by default).
37751  * The trigger has no default action, so you must assign a function to implement the trigger click handler by
37752  * overriding {@link #onTriggerClick}. You can create a TriggerField directly, as it renders exactly like a combobox
37753  * for which you can provide a custom implementation.  For example:
37754  * <pre><code>
37755 var trigger = new Roo.form.TriggerField();
37756 trigger.onTriggerClick = myTriggerFn;
37757 trigger.applyTo('my-field');
37758 </code></pre>
37759  *
37760  * However, in general you will most likely want to use TriggerField as the base class for a reusable component.
37761  * {@link Roo.form.DateField} and {@link Roo.form.ComboBox} are perfect examples of this.
37762  * @cfg {String} triggerClass An additional CSS class used to style the trigger button.  The trigger will always get the
37763  * class 'x-form-trigger' by default and triggerClass will be <b>appended</b> if specified.
37764  * @constructor
37765  * Create a new TriggerField.
37766  * @param {Object} config Configuration options (valid {@Roo.form.TextField} config options will also be applied
37767  * to the base TextField)
37768  */
37769 Roo.form.TriggerField = function(config){
37770     this.mimicing = false;
37771     Roo.form.TriggerField.superclass.constructor.call(this, config);
37772 };
37773
37774 Roo.extend(Roo.form.TriggerField, Roo.form.TextField,  {
37775     /**
37776      * @cfg {String} triggerClass A CSS class to apply to the trigger
37777      */
37778     /**
37779      * @cfg {String/Object} autoCreate A DomHelper element spec, or true for a default element spec (defaults to
37780      * {tag: "input", type: "text", size: "16", autocomplete: "off"})
37781      */
37782     defaultAutoCreate : {tag: "input", type: "text", size: "16", autocomplete: "off"},
37783     /**
37784      * @cfg {Boolean} hideTrigger True to hide the trigger element and display only the base text field (defaults to false)
37785      */
37786     hideTrigger:false,
37787
37788     /** @cfg {Boolean} grow @hide */
37789     /** @cfg {Number} growMin @hide */
37790     /** @cfg {Number} growMax @hide */
37791
37792     /**
37793      * @hide 
37794      * @method
37795      */
37796     autoSize: Roo.emptyFn,
37797     // private
37798     monitorTab : true,
37799     // private
37800     deferHeight : true,
37801
37802     
37803     actionMode : 'wrap',
37804     // private
37805     onResize : function(w, h){
37806         Roo.form.TriggerField.superclass.onResize.apply(this, arguments);
37807         if(typeof w == 'number'){
37808             var x = w - this.trigger.getWidth();
37809             this.el.setWidth(this.adjustWidth('input', x));
37810             this.trigger.setStyle('left', x+'px');
37811         }
37812     },
37813
37814     // private
37815     adjustSize : Roo.BoxComponent.prototype.adjustSize,
37816
37817     // private
37818     getResizeEl : function(){
37819         return this.wrap;
37820     },
37821
37822     // private
37823     getPositionEl : function(){
37824         return this.wrap;
37825     },
37826
37827     // private
37828     alignErrorIcon : function(){
37829         this.errorIcon.alignTo(this.wrap, 'tl-tr', [2, 0]);
37830     },
37831
37832     // private
37833     onRender : function(ct, position){
37834         Roo.form.TriggerField.superclass.onRender.call(this, ct, position);
37835         this.wrap = this.el.wrap({cls: "x-form-field-wrap"});
37836         this.trigger = this.wrap.createChild(this.triggerConfig ||
37837                 {tag: "img", src: Roo.BLANK_IMAGE_URL, cls: "x-form-trigger " + this.triggerClass});
37838         if(this.hideTrigger){
37839             this.trigger.setDisplayed(false);
37840         }
37841         this.initTrigger();
37842         if(!this.width){
37843             this.wrap.setWidth(this.el.getWidth()+this.trigger.getWidth());
37844         }
37845     },
37846
37847     // private
37848     initTrigger : function(){
37849         this.trigger.on("click", this.onTriggerClick, this, {preventDefault:true});
37850         this.trigger.addClassOnOver('x-form-trigger-over');
37851         this.trigger.addClassOnClick('x-form-trigger-click');
37852     },
37853
37854     // private
37855     onDestroy : function(){
37856         if(this.trigger){
37857             this.trigger.removeAllListeners();
37858             this.trigger.remove();
37859         }
37860         if(this.wrap){
37861             this.wrap.remove();
37862         }
37863         Roo.form.TriggerField.superclass.onDestroy.call(this);
37864     },
37865
37866     // private
37867     onFocus : function(){
37868         Roo.form.TriggerField.superclass.onFocus.call(this);
37869         if(!this.mimicing){
37870             this.wrap.addClass('x-trigger-wrap-focus');
37871             this.mimicing = true;
37872             Roo.get(Roo.isIE ? document.body : document).on("mousedown", this.mimicBlur, this);
37873             if(this.monitorTab){
37874                 this.el.on("keydown", this.checkTab, this);
37875             }
37876         }
37877     },
37878
37879     // private
37880     checkTab : function(e){
37881         if(e.getKey() == e.TAB){
37882             this.triggerBlur();
37883         }
37884     },
37885
37886     // private
37887     onBlur : function(){
37888         // do nothing
37889     },
37890
37891     // private
37892     mimicBlur : function(e, t){
37893         if(!this.wrap.contains(t) && this.validateBlur()){
37894             this.triggerBlur();
37895         }
37896     },
37897
37898     // private
37899     triggerBlur : function(){
37900         this.mimicing = false;
37901         Roo.get(Roo.isIE ? document.body : document).un("mousedown", this.mimicBlur);
37902         if(this.monitorTab){
37903             this.el.un("keydown", this.checkTab, this);
37904         }
37905         this.wrap.removeClass('x-trigger-wrap-focus');
37906         Roo.form.TriggerField.superclass.onBlur.call(this);
37907     },
37908
37909     // private
37910     // This should be overriden by any subclass that needs to check whether or not the field can be blurred.
37911     validateBlur : function(e, t){
37912         return true;
37913     },
37914
37915     // private
37916     onDisable : function(){
37917         Roo.form.TriggerField.superclass.onDisable.call(this);
37918         if(this.wrap){
37919             this.wrap.addClass('x-item-disabled');
37920         }
37921     },
37922
37923     // private
37924     onEnable : function(){
37925         Roo.form.TriggerField.superclass.onEnable.call(this);
37926         if(this.wrap){
37927             this.wrap.removeClass('x-item-disabled');
37928         }
37929     },
37930
37931     // private
37932     onShow : function(){
37933         var ae = this.getActionEl();
37934         
37935         if(ae){
37936             ae.dom.style.display = '';
37937             ae.dom.style.visibility = 'visible';
37938         }
37939     },
37940
37941     // private
37942     
37943     onHide : function(){
37944         var ae = this.getActionEl();
37945         ae.dom.style.display = 'none';
37946     },
37947
37948     /**
37949      * The function that should handle the trigger's click event.  This method does nothing by default until overridden
37950      * by an implementing function.
37951      * @method
37952      * @param {EventObject} e
37953      */
37954     onTriggerClick : Roo.emptyFn
37955 });
37956
37957 // TwinTriggerField is not a public class to be used directly.  It is meant as an abstract base class
37958 // to be extended by an implementing class.  For an example of implementing this class, see the custom
37959 // SearchField implementation here: http://extjs.com/deploy/ext/examples/form/custom.html
37960 Roo.form.TwinTriggerField = Roo.extend(Roo.form.TriggerField, {
37961     initComponent : function(){
37962         Roo.form.TwinTriggerField.superclass.initComponent.call(this);
37963
37964         this.triggerConfig = {
37965             tag:'span', cls:'x-form-twin-triggers', cn:[
37966             {tag: "img", src: Roo.BLANK_IMAGE_URL, cls: "x-form-trigger " + this.trigger1Class},
37967             {tag: "img", src: Roo.BLANK_IMAGE_URL, cls: "x-form-trigger " + this.trigger2Class}
37968         ]};
37969     },
37970
37971     getTrigger : function(index){
37972         return this.triggers[index];
37973     },
37974
37975     initTrigger : function(){
37976         var ts = this.trigger.select('.x-form-trigger', true);
37977         this.wrap.setStyle('overflow', 'hidden');
37978         var triggerField = this;
37979         ts.each(function(t, all, index){
37980             t.hide = function(){
37981                 var w = triggerField.wrap.getWidth();
37982                 this.dom.style.display = 'none';
37983                 triggerField.el.setWidth(w-triggerField.trigger.getWidth());
37984             };
37985             t.show = function(){
37986                 var w = triggerField.wrap.getWidth();
37987                 this.dom.style.display = '';
37988                 triggerField.el.setWidth(w-triggerField.trigger.getWidth());
37989             };
37990             var triggerIndex = 'Trigger'+(index+1);
37991
37992             if(this['hide'+triggerIndex]){
37993                 t.dom.style.display = 'none';
37994             }
37995             t.on("click", this['on'+triggerIndex+'Click'], this, {preventDefault:true});
37996             t.addClassOnOver('x-form-trigger-over');
37997             t.addClassOnClick('x-form-trigger-click');
37998         }, this);
37999         this.triggers = ts.elements;
38000     },
38001
38002     onTrigger1Click : Roo.emptyFn,
38003     onTrigger2Click : Roo.emptyFn
38004 });/*
38005  * Based on:
38006  * Ext JS Library 1.1.1
38007  * Copyright(c) 2006-2007, Ext JS, LLC.
38008  *
38009  * Originally Released Under LGPL - original licence link has changed is not relivant.
38010  *
38011  * Fork - LGPL
38012  * <script type="text/javascript">
38013  */
38014  
38015 /**
38016  * @class Roo.form.TextArea
38017  * @extends Roo.form.TextField
38018  * Multiline text field.  Can be used as a direct replacement for traditional textarea fields, plus adds
38019  * support for auto-sizing.
38020  * @constructor
38021  * Creates a new TextArea
38022  * @param {Object} config Configuration options
38023  */
38024 Roo.form.TextArea = function(config){
38025     Roo.form.TextArea.superclass.constructor.call(this, config);
38026     // these are provided exchanges for backwards compat
38027     // minHeight/maxHeight were replaced by growMin/growMax to be
38028     // compatible with TextField growing config values
38029     if(this.minHeight !== undefined){
38030         this.growMin = this.minHeight;
38031     }
38032     if(this.maxHeight !== undefined){
38033         this.growMax = this.maxHeight;
38034     }
38035 };
38036
38037 Roo.extend(Roo.form.TextArea, Roo.form.TextField,  {
38038     /**
38039      * @cfg {Number} growMin The minimum height to allow when grow = true (defaults to 60)
38040      */
38041     growMin : 60,
38042     /**
38043      * @cfg {Number} growMax The maximum height to allow when grow = true (defaults to 1000)
38044      */
38045     growMax: 1000,
38046     /**
38047      * @cfg {Boolean} preventScrollbars True to prevent scrollbars from appearing regardless of how much text is
38048      * in the field (equivalent to setting overflow: hidden, defaults to false)
38049      */
38050     preventScrollbars: false,
38051     /**
38052      * @cfg {String/Object} autoCreate A DomHelper element spec, or true for a default element spec (defaults to
38053      * {tag: "textarea", style: "width:300px;height:60px;", autocomplete: "off"})
38054      */
38055
38056     // private
38057     onRender : function(ct, position){
38058         if(!this.el){
38059             this.defaultAutoCreate = {
38060                 tag: "textarea",
38061                 style:"width:300px;height:60px;",
38062                 autocomplete: "off"
38063             };
38064         }
38065         Roo.form.TextArea.superclass.onRender.call(this, ct, position);
38066         if(this.grow){
38067             this.textSizeEl = Roo.DomHelper.append(document.body, {
38068                 tag: "pre", cls: "x-form-grow-sizer"
38069             });
38070             if(this.preventScrollbars){
38071                 this.el.setStyle("overflow", "hidden");
38072             }
38073             this.el.setHeight(this.growMin);
38074         }
38075     },
38076
38077     onDestroy : function(){
38078         if(this.textSizeEl){
38079             this.textSizeEl.parentNode.removeChild(this.textSizeEl);
38080         }
38081         Roo.form.TextArea.superclass.onDestroy.call(this);
38082     },
38083
38084     // private
38085     onKeyUp : function(e){
38086         if(!e.isNavKeyPress() || e.getKey() == e.ENTER){
38087             this.autoSize();
38088         }
38089     },
38090
38091     /**
38092      * Automatically grows the field to accomodate the height of the text up to the maximum field height allowed.
38093      * This only takes effect if grow = true, and fires the autosize event if the height changes.
38094      */
38095     autoSize : function(){
38096         if(!this.grow || !this.textSizeEl){
38097             return;
38098         }
38099         var el = this.el;
38100         var v = el.dom.value;
38101         var ts = this.textSizeEl;
38102
38103         ts.innerHTML = '';
38104         ts.appendChild(document.createTextNode(v));
38105         v = ts.innerHTML;
38106
38107         Roo.fly(ts).setWidth(this.el.getWidth());
38108         if(v.length < 1){
38109             v = "&#160;&#160;";
38110         }else{
38111             if(Roo.isIE){
38112                 v = v.replace(/\n/g, '<p>&#160;</p>');
38113             }
38114             v += "&#160;\n&#160;";
38115         }
38116         ts.innerHTML = v;
38117         var h = Math.min(this.growMax, Math.max(ts.offsetHeight, this.growMin));
38118         if(h != this.lastHeight){
38119             this.lastHeight = h;
38120             this.el.setHeight(h);
38121             this.fireEvent("autosize", this, h);
38122         }
38123     }
38124 });/*
38125  * Based on:
38126  * Ext JS Library 1.1.1
38127  * Copyright(c) 2006-2007, Ext JS, LLC.
38128  *
38129  * Originally Released Under LGPL - original licence link has changed is not relivant.
38130  *
38131  * Fork - LGPL
38132  * <script type="text/javascript">
38133  */
38134  
38135
38136 /**
38137  * @class Roo.form.NumberField
38138  * @extends Roo.form.TextField
38139  * Numeric text field that provides automatic keystroke filtering and numeric validation.
38140  * @constructor
38141  * Creates a new NumberField
38142  * @param {Object} config Configuration options
38143  */
38144 Roo.form.NumberField = function(config){
38145     Roo.form.NumberField.superclass.constructor.call(this, config);
38146 };
38147
38148 Roo.extend(Roo.form.NumberField, Roo.form.TextField,  {
38149     /**
38150      * @cfg {String} fieldClass The default CSS class for the field (defaults to "x-form-field x-form-num-field")
38151      */
38152     fieldClass: "x-form-field x-form-num-field",
38153     /**
38154      * @cfg {Boolean} allowDecimals False to disallow decimal values (defaults to true)
38155      */
38156     allowDecimals : true,
38157     /**
38158      * @cfg {String} decimalSeparator Character(s) to allow as the decimal separator (defaults to '.')
38159      */
38160     decimalSeparator : ".",
38161     /**
38162      * @cfg {Number} decimalPrecision The maximum precision to display after the decimal separator (defaults to 2)
38163      */
38164     decimalPrecision : 2,
38165     /**
38166      * @cfg {Boolean} allowNegative False to prevent entering a negative sign (defaults to true)
38167      */
38168     allowNegative : true,
38169     /**
38170      * @cfg {Number} minValue The minimum allowed value (defaults to Number.NEGATIVE_INFINITY)
38171      */
38172     minValue : Number.NEGATIVE_INFINITY,
38173     /**
38174      * @cfg {Number} maxValue The maximum allowed value (defaults to Number.MAX_VALUE)
38175      */
38176     maxValue : Number.MAX_VALUE,
38177     /**
38178      * @cfg {String} minText Error text to display if the minimum value validation fails (defaults to "The minimum value for this field is {minValue}")
38179      */
38180     minText : "The minimum value for this field is {0}",
38181     /**
38182      * @cfg {String} maxText Error text to display if the maximum value validation fails (defaults to "The maximum value for this field is {maxValue}")
38183      */
38184     maxText : "The maximum value for this field is {0}",
38185     /**
38186      * @cfg {String} nanText Error text to display if the value is not a valid number.  For example, this can happen
38187      * if a valid character like '.' or '-' is left in the field with no number (defaults to "{value} is not a valid number")
38188      */
38189     nanText : "{0} is not a valid number",
38190
38191     // private
38192     initEvents : function(){
38193         Roo.form.NumberField.superclass.initEvents.call(this);
38194         var allowed = "0123456789";
38195         if(this.allowDecimals){
38196             allowed += this.decimalSeparator;
38197         }
38198         if(this.allowNegative){
38199             allowed += "-";
38200         }
38201         this.stripCharsRe = new RegExp('[^'+allowed+']', 'gi');
38202         var keyPress = function(e){
38203             var k = e.getKey();
38204             if(!Roo.isIE && (e.isSpecialKey() || k == e.BACKSPACE || k == e.DELETE)){
38205                 return;
38206             }
38207             var c = e.getCharCode();
38208             if(allowed.indexOf(String.fromCharCode(c)) === -1){
38209                 e.stopEvent();
38210             }
38211         };
38212         this.el.on("keypress", keyPress, this);
38213     },
38214
38215     // private
38216     validateValue : function(value){
38217         if(!Roo.form.NumberField.superclass.validateValue.call(this, value)){
38218             return false;
38219         }
38220         if(value.length < 1){ // if it's blank and textfield didn't flag it then it's valid
38221              return true;
38222         }
38223         var num = this.parseValue(value);
38224         if(isNaN(num)){
38225             this.markInvalid(String.format(this.nanText, value));
38226             return false;
38227         }
38228         if(num < this.minValue){
38229             this.markInvalid(String.format(this.minText, this.minValue));
38230             return false;
38231         }
38232         if(num > this.maxValue){
38233             this.markInvalid(String.format(this.maxText, this.maxValue));
38234             return false;
38235         }
38236         return true;
38237     },
38238
38239     getValue : function(){
38240         return this.fixPrecision(this.parseValue(Roo.form.NumberField.superclass.getValue.call(this)));
38241     },
38242
38243     // private
38244     parseValue : function(value){
38245         value = parseFloat(String(value).replace(this.decimalSeparator, "."));
38246         return isNaN(value) ? '' : value;
38247     },
38248
38249     // private
38250     fixPrecision : function(value){
38251         var nan = isNaN(value);
38252         if(!this.allowDecimals || this.decimalPrecision == -1 || nan || !value){
38253             return nan ? '' : value;
38254         }
38255         return parseFloat(value).toFixed(this.decimalPrecision);
38256     },
38257
38258     setValue : function(v){
38259         v = this.fixPrecision(v);
38260         Roo.form.NumberField.superclass.setValue.call(this, String(v).replace(".", this.decimalSeparator));
38261     },
38262
38263     // private
38264     decimalPrecisionFcn : function(v){
38265         return Math.floor(v);
38266     },
38267
38268     beforeBlur : function(){
38269         var v = this.parseValue(this.getRawValue());
38270         if(v){
38271             this.setValue(v);
38272         }
38273     }
38274 });/*
38275  * Based on:
38276  * Ext JS Library 1.1.1
38277  * Copyright(c) 2006-2007, Ext JS, LLC.
38278  *
38279  * Originally Released Under LGPL - original licence link has changed is not relivant.
38280  *
38281  * Fork - LGPL
38282  * <script type="text/javascript">
38283  */
38284  
38285 /**
38286  * @class Roo.form.DateField
38287  * @extends Roo.form.TriggerField
38288  * Provides a date input field with a {@link Roo.DatePicker} dropdown and automatic date validation.
38289 * @constructor
38290 * Create a new DateField
38291 * @param {Object} config
38292  */
38293 Roo.form.DateField = function(config){
38294     Roo.form.DateField.superclass.constructor.call(this, config);
38295     
38296       this.addEvents({
38297          
38298         /**
38299          * @event select
38300          * Fires when a date is selected
38301              * @param {Roo.form.DateField} combo This combo box
38302              * @param {Date} date The date selected
38303              */
38304         'select' : true
38305          
38306     });
38307     
38308     
38309     if(typeof this.minValue == "string") this.minValue = this.parseDate(this.minValue);
38310     if(typeof this.maxValue == "string") this.maxValue = this.parseDate(this.maxValue);
38311     this.ddMatch = null;
38312     if(this.disabledDates){
38313         var dd = this.disabledDates;
38314         var re = "(?:";
38315         for(var i = 0; i < dd.length; i++){
38316             re += dd[i];
38317             if(i != dd.length-1) re += "|";
38318         }
38319         this.ddMatch = new RegExp(re + ")");
38320     }
38321 };
38322
38323 Roo.extend(Roo.form.DateField, Roo.form.TriggerField,  {
38324     /**
38325      * @cfg {String} format
38326      * The default date format string which can be overriden for localization support.  The format must be
38327      * valid according to {@link Date#parseDate} (defaults to 'm/d/y').
38328      */
38329     format : "m/d/y",
38330     /**
38331      * @cfg {String} altFormats
38332      * Multiple date formats separated by "|" to try when parsing a user input value and it doesn't match the defined
38333      * format (defaults to 'm/d/Y|m-d-y|m-d-Y|m/d|m-d|d').
38334      */
38335     altFormats : "m/d/Y|m-d-y|m-d-Y|m/d|m-d|md|mdy|mdY|d",
38336     /**
38337      * @cfg {Array} disabledDays
38338      * An array of days to disable, 0 based. For example, [0, 6] disables Sunday and Saturday (defaults to null).
38339      */
38340     disabledDays : null,
38341     /**
38342      * @cfg {String} disabledDaysText
38343      * The tooltip to display when the date falls on a disabled day (defaults to 'Disabled')
38344      */
38345     disabledDaysText : "Disabled",
38346     /**
38347      * @cfg {Array} disabledDates
38348      * An array of "dates" to disable, as strings. These strings will be used to build a dynamic regular
38349      * expression so they are very powerful. Some examples:
38350      * <ul>
38351      * <li>["03/08/2003", "09/16/2003"] would disable those exact dates</li>
38352      * <li>["03/08", "09/16"] would disable those days for every year</li>
38353      * <li>["^03/08"] would only match the beginning (useful if you are using short years)</li>
38354      * <li>["03/../2006"] would disable every day in March 2006</li>
38355      * <li>["^03"] would disable every day in every March</li>
38356      * </ul>
38357      * In order to support regular expressions, if you are using a date format that has "." in it, you will have to
38358      * escape the dot when restricting dates. For example: ["03\\.08\\.03"].
38359      */
38360     disabledDates : null,
38361     /**
38362      * @cfg {String} disabledDatesText
38363      * The tooltip text to display when the date falls on a disabled date (defaults to 'Disabled')
38364      */
38365     disabledDatesText : "Disabled",
38366     /**
38367      * @cfg {Date/String} minValue
38368      * The minimum allowed date. Can be either a Javascript date object or a string date in a
38369      * valid format (defaults to null).
38370      */
38371     minValue : null,
38372     /**
38373      * @cfg {Date/String} maxValue
38374      * The maximum allowed date. Can be either a Javascript date object or a string date in a
38375      * valid format (defaults to null).
38376      */
38377     maxValue : null,
38378     /**
38379      * @cfg {String} minText
38380      * The error text to display when the date in the cell is before minValue (defaults to
38381      * 'The date in this field must be after {minValue}').
38382      */
38383     minText : "The date in this field must be equal to or after {0}",
38384     /**
38385      * @cfg {String} maxText
38386      * The error text to display when the date in the cell is after maxValue (defaults to
38387      * 'The date in this field must be before {maxValue}').
38388      */
38389     maxText : "The date in this field must be equal to or before {0}",
38390     /**
38391      * @cfg {String} invalidText
38392      * The error text to display when the date in the field is invalid (defaults to
38393      * '{value} is not a valid date - it must be in the format {format}').
38394      */
38395     invalidText : "{0} is not a valid date - it must be in the format {1}",
38396     /**
38397      * @cfg {String} triggerClass
38398      * An additional CSS class used to style the trigger button.  The trigger will always get the
38399      * class 'x-form-trigger' and triggerClass will be <b>appended</b> if specified (defaults to 'x-form-date-trigger'
38400      * which displays a calendar icon).
38401      */
38402     triggerClass : 'x-form-date-trigger',
38403     
38404
38405     /**
38406      * @cfg {Boolean} useIso
38407      * if enabled, then the date field will use a hidden field to store the 
38408      * real value as iso formated date. default (false)
38409      */ 
38410     useIso : false,
38411     /**
38412      * @cfg {String/Object} autoCreate
38413      * A DomHelper element spec, or true for a default element spec (defaults to
38414      * {tag: "input", type: "text", size: "10", autocomplete: "off"})
38415      */ 
38416     // private
38417     defaultAutoCreate : {tag: "input", type: "text", size: "10", autocomplete: "off"},
38418     
38419     // private
38420     hiddenField: false,
38421     
38422     onRender : function(ct, position)
38423     {
38424         Roo.form.DateField.superclass.onRender.call(this, ct, position);
38425         if (this.useIso) {
38426             //this.el.dom.removeAttribute('name'); 
38427             Roo.log("Changing name?");
38428             this.el.dom.setAttribute('name', this.name + '____hidden___' ); 
38429             this.hiddenField = this.el.insertSibling({ tag:'input', type:'hidden', name: this.name },
38430                     'before', true);
38431             this.hiddenField.value = this.value ? this.formatDate(this.value, 'Y-m-d') : '';
38432             // prevent input submission
38433             this.hiddenName = this.name;
38434         }
38435             
38436             
38437     },
38438     
38439     // private
38440     validateValue : function(value)
38441     {
38442         value = this.formatDate(value);
38443         if(!Roo.form.DateField.superclass.validateValue.call(this, value)){
38444             Roo.log('super failed');
38445             return false;
38446         }
38447         if(value.length < 1){ // if it's blank and textfield didn't flag it then it's valid
38448              return true;
38449         }
38450         var svalue = value;
38451         value = this.parseDate(value);
38452         if(!value){
38453             Roo.log('parse date failed' + svalue);
38454             this.markInvalid(String.format(this.invalidText, svalue, this.format));
38455             return false;
38456         }
38457         var time = value.getTime();
38458         if(this.minValue && time < this.minValue.getTime()){
38459             this.markInvalid(String.format(this.minText, this.formatDate(this.minValue)));
38460             return false;
38461         }
38462         if(this.maxValue && time > this.maxValue.getTime()){
38463             this.markInvalid(String.format(this.maxText, this.formatDate(this.maxValue)));
38464             return false;
38465         }
38466         if(this.disabledDays){
38467             var day = value.getDay();
38468             for(var i = 0; i < this.disabledDays.length; i++) {
38469                 if(day === this.disabledDays[i]){
38470                     this.markInvalid(this.disabledDaysText);
38471                     return false;
38472                 }
38473             }
38474         }
38475         var fvalue = this.formatDate(value);
38476         if(this.ddMatch && this.ddMatch.test(fvalue)){
38477             this.markInvalid(String.format(this.disabledDatesText, fvalue));
38478             return false;
38479         }
38480         return true;
38481     },
38482
38483     // private
38484     // Provides logic to override the default TriggerField.validateBlur which just returns true
38485     validateBlur : function(){
38486         return !this.menu || !this.menu.isVisible();
38487     },
38488     
38489     getName: function()
38490     {
38491         // returns hidden if it's set..
38492         if (!this.rendered) {return ''};
38493         return !this.hiddenName && this.el.dom.name  ? this.el.dom.name : (this.hiddenName || '');
38494         
38495     },
38496
38497     /**
38498      * Returns the current date value of the date field.
38499      * @return {Date} The date value
38500      */
38501     getValue : function(){
38502         
38503         return  this.hiddenField ?
38504                 this.hiddenField.value :
38505                 this.parseDate(Roo.form.DateField.superclass.getValue.call(this)) || "";
38506     },
38507
38508     /**
38509      * Sets the value of the date field.  You can pass a date object or any string that can be parsed into a valid
38510      * date, using DateField.format as the date format, according to the same rules as {@link Date#parseDate}
38511      * (the default format used is "m/d/y").
38512      * <br />Usage:
38513      * <pre><code>
38514 //All of these calls set the same date value (May 4, 2006)
38515
38516 //Pass a date object:
38517 var dt = new Date('5/4/06');
38518 dateField.setValue(dt);
38519
38520 //Pass a date string (default format):
38521 dateField.setValue('5/4/06');
38522
38523 //Pass a date string (custom format):
38524 dateField.format = 'Y-m-d';
38525 dateField.setValue('2006-5-4');
38526 </code></pre>
38527      * @param {String/Date} date The date or valid date string
38528      */
38529     setValue : function(date){
38530         if (this.hiddenField) {
38531             this.hiddenField.value = this.formatDate(this.parseDate(date), 'Y-m-d');
38532         }
38533         Roo.form.DateField.superclass.setValue.call(this, this.formatDate(this.parseDate(date)));
38534         // make sure the value field is always stored as a date..
38535         this.value = this.parseDate(date);
38536         
38537         
38538     },
38539
38540     // private
38541     parseDate : function(value){
38542         if(!value || value instanceof Date){
38543             return value;
38544         }
38545         var v = Date.parseDate(value, this.format);
38546          if (!v && this.useIso) {
38547             v = Date.parseDate(value, 'Y-m-d');
38548         }
38549         if(!v && this.altFormats){
38550             if(!this.altFormatsArray){
38551                 this.altFormatsArray = this.altFormats.split("|");
38552             }
38553             for(var i = 0, len = this.altFormatsArray.length; i < len && !v; i++){
38554                 v = Date.parseDate(value, this.altFormatsArray[i]);
38555             }
38556         }
38557         return v;
38558     },
38559
38560     // private
38561     formatDate : function(date, fmt){
38562         return (!date || !(date instanceof Date)) ?
38563                date : date.dateFormat(fmt || this.format);
38564     },
38565
38566     // private
38567     menuListeners : {
38568         select: function(m, d){
38569             
38570             this.setValue(d);
38571             this.fireEvent('select', this, d);
38572         },
38573         show : function(){ // retain focus styling
38574             this.onFocus();
38575         },
38576         hide : function(){
38577             this.focus.defer(10, this);
38578             var ml = this.menuListeners;
38579             this.menu.un("select", ml.select,  this);
38580             this.menu.un("show", ml.show,  this);
38581             this.menu.un("hide", ml.hide,  this);
38582         }
38583     },
38584
38585     // private
38586     // Implements the default empty TriggerField.onTriggerClick function to display the DatePicker
38587     onTriggerClick : function(){
38588         if(this.disabled){
38589             return;
38590         }
38591         if(this.menu == null){
38592             this.menu = new Roo.menu.DateMenu();
38593         }
38594         Roo.apply(this.menu.picker,  {
38595             showClear: this.allowBlank,
38596             minDate : this.minValue,
38597             maxDate : this.maxValue,
38598             disabledDatesRE : this.ddMatch,
38599             disabledDatesText : this.disabledDatesText,
38600             disabledDays : this.disabledDays,
38601             disabledDaysText : this.disabledDaysText,
38602             format : this.useIso ? 'Y-m-d' : this.format,
38603             minText : String.format(this.minText, this.formatDate(this.minValue)),
38604             maxText : String.format(this.maxText, this.formatDate(this.maxValue))
38605         });
38606         this.menu.on(Roo.apply({}, this.menuListeners, {
38607             scope:this
38608         }));
38609         this.menu.picker.setValue(this.getValue() || new Date());
38610         this.menu.show(this.el, "tl-bl?");
38611     },
38612
38613     beforeBlur : function(){
38614         var v = this.parseDate(this.getRawValue());
38615         if(v){
38616             this.setValue(v);
38617         }
38618     },
38619
38620     /*@
38621      * overide
38622      * 
38623      */
38624     isDirty : function() {
38625         if(this.disabled) {
38626             return false;
38627         }
38628         
38629         if(typeof(this.startValue) === 'undefined'){
38630             return false;
38631         }
38632         
38633         return String(this.getValue()) !== String(this.startValue);
38634         
38635     }
38636 });/*
38637  * Based on:
38638  * Ext JS Library 1.1.1
38639  * Copyright(c) 2006-2007, Ext JS, LLC.
38640  *
38641  * Originally Released Under LGPL - original licence link has changed is not relivant.
38642  *
38643  * Fork - LGPL
38644  * <script type="text/javascript">
38645  */
38646  
38647 /**
38648  * @class Roo.form.MonthField
38649  * @extends Roo.form.TriggerField
38650  * Provides a date input field with a {@link Roo.DatePicker} dropdown and automatic date validation.
38651 * @constructor
38652 * Create a new MonthField
38653 * @param {Object} config
38654  */
38655 Roo.form.MonthField = function(config){
38656     
38657     Roo.form.MonthField.superclass.constructor.call(this, config);
38658     
38659       this.addEvents({
38660          
38661         /**
38662          * @event select
38663          * Fires when a date is selected
38664              * @param {Roo.form.MonthFieeld} combo This combo box
38665              * @param {Date} date The date selected
38666              */
38667         'select' : true
38668          
38669     });
38670     
38671     
38672     if(typeof this.minValue == "string") this.minValue = this.parseDate(this.minValue);
38673     if(typeof this.maxValue == "string") this.maxValue = this.parseDate(this.maxValue);
38674     this.ddMatch = null;
38675     if(this.disabledDates){
38676         var dd = this.disabledDates;
38677         var re = "(?:";
38678         for(var i = 0; i < dd.length; i++){
38679             re += dd[i];
38680             if(i != dd.length-1) re += "|";
38681         }
38682         this.ddMatch = new RegExp(re + ")");
38683     }
38684 };
38685
38686 Roo.extend(Roo.form.MonthField, Roo.form.TriggerField,  {
38687     /**
38688      * @cfg {String} format
38689      * The default date format string which can be overriden for localization support.  The format must be
38690      * valid according to {@link Date#parseDate} (defaults to 'm/d/y').
38691      */
38692     format : "M Y",
38693     /**
38694      * @cfg {String} altFormats
38695      * Multiple date formats separated by "|" to try when parsing a user input value and it doesn't match the defined
38696      * format (defaults to 'm/d/Y|m-d-y|m-d-Y|m/d|m-d|d').
38697      */
38698     altFormats : "M Y|m/Y|m-y|m-Y|my|mY",
38699     /**
38700      * @cfg {Array} disabledDays
38701      * An array of days to disable, 0 based. For example, [0, 6] disables Sunday and Saturday (defaults to null).
38702      */
38703     disabledDays : [0,1,2,3,4,5,6],
38704     /**
38705      * @cfg {String} disabledDaysText
38706      * The tooltip to display when the date falls on a disabled day (defaults to 'Disabled')
38707      */
38708     disabledDaysText : "Disabled",
38709     /**
38710      * @cfg {Array} disabledDates
38711      * An array of "dates" to disable, as strings. These strings will be used to build a dynamic regular
38712      * expression so they are very powerful. Some examples:
38713      * <ul>
38714      * <li>["03/08/2003", "09/16/2003"] would disable those exact dates</li>
38715      * <li>["03/08", "09/16"] would disable those days for every year</li>
38716      * <li>["^03/08"] would only match the beginning (useful if you are using short years)</li>
38717      * <li>["03/../2006"] would disable every day in March 2006</li>
38718      * <li>["^03"] would disable every day in every March</li>
38719      * </ul>
38720      * In order to support regular expressions, if you are using a date format that has "." in it, you will have to
38721      * escape the dot when restricting dates. For example: ["03\\.08\\.03"].
38722      */
38723     disabledDates : null,
38724     /**
38725      * @cfg {String} disabledDatesText
38726      * The tooltip text to display when the date falls on a disabled date (defaults to 'Disabled')
38727      */
38728     disabledDatesText : "Disabled",
38729     /**
38730      * @cfg {Date/String} minValue
38731      * The minimum allowed date. Can be either a Javascript date object or a string date in a
38732      * valid format (defaults to null).
38733      */
38734     minValue : null,
38735     /**
38736      * @cfg {Date/String} maxValue
38737      * The maximum allowed date. Can be either a Javascript date object or a string date in a
38738      * valid format (defaults to null).
38739      */
38740     maxValue : null,
38741     /**
38742      * @cfg {String} minText
38743      * The error text to display when the date in the cell is before minValue (defaults to
38744      * 'The date in this field must be after {minValue}').
38745      */
38746     minText : "The date in this field must be equal to or after {0}",
38747     /**
38748      * @cfg {String} maxTextf
38749      * The error text to display when the date in the cell is after maxValue (defaults to
38750      * 'The date in this field must be before {maxValue}').
38751      */
38752     maxText : "The date in this field must be equal to or before {0}",
38753     /**
38754      * @cfg {String} invalidText
38755      * The error text to display when the date in the field is invalid (defaults to
38756      * '{value} is not a valid date - it must be in the format {format}').
38757      */
38758     invalidText : "{0} is not a valid date - it must be in the format {1}",
38759     /**
38760      * @cfg {String} triggerClass
38761      * An additional CSS class used to style the trigger button.  The trigger will always get the
38762      * class 'x-form-trigger' and triggerClass will be <b>appended</b> if specified (defaults to 'x-form-date-trigger'
38763      * which displays a calendar icon).
38764      */
38765     triggerClass : 'x-form-date-trigger',
38766     
38767
38768     /**
38769      * @cfg {Boolean} useIso
38770      * if enabled, then the date field will use a hidden field to store the 
38771      * real value as iso formated date. default (true)
38772      */ 
38773     useIso : true,
38774     /**
38775      * @cfg {String/Object} autoCreate
38776      * A DomHelper element spec, or true for a default element spec (defaults to
38777      * {tag: "input", type: "text", size: "10", autocomplete: "off"})
38778      */ 
38779     // private
38780     defaultAutoCreate : {tag: "input", type: "text", size: "10", autocomplete: "off"},
38781     
38782     // private
38783     hiddenField: false,
38784     
38785     hideMonthPicker : false,
38786     
38787     onRender : function(ct, position)
38788     {
38789         Roo.form.MonthField.superclass.onRender.call(this, ct, position);
38790         if (this.useIso) {
38791             this.el.dom.removeAttribute('name'); 
38792             this.hiddenField = this.el.insertSibling({ tag:'input', type:'hidden', name: this.name },
38793                     'before', true);
38794             this.hiddenField.value = this.value ? this.formatDate(this.value, 'Y-m-d') : '';
38795             // prevent input submission
38796             this.hiddenName = this.name;
38797         }
38798             
38799             
38800     },
38801     
38802     // private
38803     validateValue : function(value)
38804     {
38805         value = this.formatDate(value);
38806         if(!Roo.form.MonthField.superclass.validateValue.call(this, value)){
38807             return false;
38808         }
38809         if(value.length < 1){ // if it's blank and textfield didn't flag it then it's valid
38810              return true;
38811         }
38812         var svalue = value;
38813         value = this.parseDate(value);
38814         if(!value){
38815             this.markInvalid(String.format(this.invalidText, svalue, this.format));
38816             return false;
38817         }
38818         var time = value.getTime();
38819         if(this.minValue && time < this.minValue.getTime()){
38820             this.markInvalid(String.format(this.minText, this.formatDate(this.minValue)));
38821             return false;
38822         }
38823         if(this.maxValue && time > this.maxValue.getTime()){
38824             this.markInvalid(String.format(this.maxText, this.formatDate(this.maxValue)));
38825             return false;
38826         }
38827         /*if(this.disabledDays){
38828             var day = value.getDay();
38829             for(var i = 0; i < this.disabledDays.length; i++) {
38830                 if(day === this.disabledDays[i]){
38831                     this.markInvalid(this.disabledDaysText);
38832                     return false;
38833                 }
38834             }
38835         }
38836         */
38837         var fvalue = this.formatDate(value);
38838         /*if(this.ddMatch && this.ddMatch.test(fvalue)){
38839             this.markInvalid(String.format(this.disabledDatesText, fvalue));
38840             return false;
38841         }
38842         */
38843         return true;
38844     },
38845
38846     // private
38847     // Provides logic to override the default TriggerField.validateBlur which just returns true
38848     validateBlur : function(){
38849         return !this.menu || !this.menu.isVisible();
38850     },
38851
38852     /**
38853      * Returns the current date value of the date field.
38854      * @return {Date} The date value
38855      */
38856     getValue : function(){
38857         
38858         
38859         
38860         return  this.hiddenField ?
38861                 this.hiddenField.value :
38862                 this.parseDate(Roo.form.MonthField.superclass.getValue.call(this)) || "";
38863     },
38864
38865     /**
38866      * Sets the value of the date field.  You can pass a date object or any string that can be parsed into a valid
38867      * date, using MonthField.format as the date format, according to the same rules as {@link Date#parseDate}
38868      * (the default format used is "m/d/y").
38869      * <br />Usage:
38870      * <pre><code>
38871 //All of these calls set the same date value (May 4, 2006)
38872
38873 //Pass a date object:
38874 var dt = new Date('5/4/06');
38875 monthField.setValue(dt);
38876
38877 //Pass a date string (default format):
38878 monthField.setValue('5/4/06');
38879
38880 //Pass a date string (custom format):
38881 monthField.format = 'Y-m-d';
38882 monthField.setValue('2006-5-4');
38883 </code></pre>
38884      * @param {String/Date} date The date or valid date string
38885      */
38886     setValue : function(date){
38887         Roo.log('month setValue' + date);
38888         // can only be first of month..
38889         
38890         var val = this.parseDate(date);
38891         
38892         if (this.hiddenField) {
38893             this.hiddenField.value = this.formatDate(this.parseDate(date), 'Y-m-d');
38894         }
38895         Roo.form.MonthField.superclass.setValue.call(this, this.formatDate(this.parseDate(date)));
38896         this.value = this.parseDate(date);
38897     },
38898
38899     // private
38900     parseDate : function(value){
38901         if(!value || value instanceof Date){
38902             value = value ? Date.parseDate(value.format('Y-m') + '-01', 'Y-m-d') : null;
38903             return value;
38904         }
38905         var v = Date.parseDate(value, this.format);
38906         if (!v && this.useIso) {
38907             v = Date.parseDate(value, 'Y-m-d');
38908         }
38909         if (v) {
38910             // 
38911             v = Date.parseDate(v.format('Y-m') +'-01', 'Y-m-d');
38912         }
38913         
38914         
38915         if(!v && this.altFormats){
38916             if(!this.altFormatsArray){
38917                 this.altFormatsArray = this.altFormats.split("|");
38918             }
38919             for(var i = 0, len = this.altFormatsArray.length; i < len && !v; i++){
38920                 v = Date.parseDate(value, this.altFormatsArray[i]);
38921             }
38922         }
38923         return v;
38924     },
38925
38926     // private
38927     formatDate : function(date, fmt){
38928         return (!date || !(date instanceof Date)) ?
38929                date : date.dateFormat(fmt || this.format);
38930     },
38931
38932     // private
38933     menuListeners : {
38934         select: function(m, d){
38935             this.setValue(d);
38936             this.fireEvent('select', this, d);
38937         },
38938         show : function(){ // retain focus styling
38939             this.onFocus();
38940         },
38941         hide : function(){
38942             this.focus.defer(10, this);
38943             var ml = this.menuListeners;
38944             this.menu.un("select", ml.select,  this);
38945             this.menu.un("show", ml.show,  this);
38946             this.menu.un("hide", ml.hide,  this);
38947         }
38948     },
38949     // private
38950     // Implements the default empty TriggerField.onTriggerClick function to display the DatePicker
38951     onTriggerClick : function(){
38952         if(this.disabled){
38953             return;
38954         }
38955         if(this.menu == null){
38956             this.menu = new Roo.menu.DateMenu();
38957            
38958         }
38959         
38960         Roo.apply(this.menu.picker,  {
38961             
38962             showClear: this.allowBlank,
38963             minDate : this.minValue,
38964             maxDate : this.maxValue,
38965             disabledDatesRE : this.ddMatch,
38966             disabledDatesText : this.disabledDatesText,
38967             
38968             format : this.useIso ? 'Y-m-d' : this.format,
38969             minText : String.format(this.minText, this.formatDate(this.minValue)),
38970             maxText : String.format(this.maxText, this.formatDate(this.maxValue))
38971             
38972         });
38973          this.menu.on(Roo.apply({}, this.menuListeners, {
38974             scope:this
38975         }));
38976        
38977         
38978         var m = this.menu;
38979         var p = m.picker;
38980         
38981         // hide month picker get's called when we called by 'before hide';
38982         
38983         var ignorehide = true;
38984         p.hideMonthPicker  = function(disableAnim){
38985             if (ignorehide) {
38986                 return;
38987             }
38988              if(this.monthPicker){
38989                 Roo.log("hideMonthPicker called");
38990                 if(disableAnim === true){
38991                     this.monthPicker.hide();
38992                 }else{
38993                     this.monthPicker.slideOut('t', {duration:.2});
38994                     p.setValue(new Date(m.picker.mpSelYear, m.picker.mpSelMonth, 1));
38995                     p.fireEvent("select", this, this.value);
38996                     m.hide();
38997                 }
38998             }
38999         }
39000         
39001         Roo.log('picker set value');
39002         Roo.log(this.getValue());
39003         p.setValue(this.getValue() ? this.parseDate(this.getValue()) : new Date());
39004         m.show(this.el, 'tl-bl?');
39005         ignorehide  = false;
39006         // this will trigger hideMonthPicker..
39007         
39008         
39009         // hidden the day picker
39010         Roo.select('.x-date-picker table', true).first().dom.style.visibility = "hidden";
39011         
39012         
39013         
39014       
39015         
39016         p.showMonthPicker.defer(100, p);
39017     
39018         
39019        
39020     },
39021
39022     beforeBlur : function(){
39023         var v = this.parseDate(this.getRawValue());
39024         if(v){
39025             this.setValue(v);
39026         }
39027     }
39028
39029     /** @cfg {Boolean} grow @hide */
39030     /** @cfg {Number} growMin @hide */
39031     /** @cfg {Number} growMax @hide */
39032     /**
39033      * @hide
39034      * @method autoSize
39035      */
39036 });/*
39037  * Based on:
39038  * Ext JS Library 1.1.1
39039  * Copyright(c) 2006-2007, Ext JS, LLC.
39040  *
39041  * Originally Released Under LGPL - original licence link has changed is not relivant.
39042  *
39043  * Fork - LGPL
39044  * <script type="text/javascript">
39045  */
39046  
39047
39048 /**
39049  * @class Roo.form.ComboBox
39050  * @extends Roo.form.TriggerField
39051  * A combobox control with support for autocomplete, remote-loading, paging and many other features.
39052  * @constructor
39053  * Create a new ComboBox.
39054  * @param {Object} config Configuration options
39055  */
39056 Roo.form.ComboBox = function(config){
39057     Roo.form.ComboBox.superclass.constructor.call(this, config);
39058     this.addEvents({
39059         /**
39060          * @event expand
39061          * Fires when the dropdown list is expanded
39062              * @param {Roo.form.ComboBox} combo This combo box
39063              */
39064         'expand' : true,
39065         /**
39066          * @event collapse
39067          * Fires when the dropdown list is collapsed
39068              * @param {Roo.form.ComboBox} combo This combo box
39069              */
39070         'collapse' : true,
39071         /**
39072          * @event beforeselect
39073          * Fires before a list item is selected. Return false to cancel the selection.
39074              * @param {Roo.form.ComboBox} combo This combo box
39075              * @param {Roo.data.Record} record The data record returned from the underlying store
39076              * @param {Number} index The index of the selected item in the dropdown list
39077              */
39078         'beforeselect' : true,
39079         /**
39080          * @event select
39081          * Fires when a list item is selected
39082              * @param {Roo.form.ComboBox} combo This combo box
39083              * @param {Roo.data.Record} record The data record returned from the underlying store (or false on clear)
39084              * @param {Number} index The index of the selected item in the dropdown list
39085              */
39086         'select' : true,
39087         /**
39088          * @event beforequery
39089          * Fires before all queries are processed. Return false to cancel the query or set cancel to true.
39090          * The event object passed has these properties:
39091              * @param {Roo.form.ComboBox} combo This combo box
39092              * @param {String} query The query
39093              * @param {Boolean} forceAll true to force "all" query
39094              * @param {Boolean} cancel true to cancel the query
39095              * @param {Object} e The query event object
39096              */
39097         'beforequery': true,
39098          /**
39099          * @event add
39100          * Fires when the 'add' icon is pressed (add a listener to enable add button)
39101              * @param {Roo.form.ComboBox} combo This combo box
39102              */
39103         'add' : true,
39104         /**
39105          * @event edit
39106          * Fires when the 'edit' icon is pressed (add a listener to enable add button)
39107              * @param {Roo.form.ComboBox} combo This combo box
39108              * @param {Roo.data.Record|false} record The data record returned from the underlying store (or false on nothing selected)
39109              */
39110         'edit' : true
39111         
39112         
39113     });
39114     if(this.transform){
39115         this.allowDomMove = false;
39116         var s = Roo.getDom(this.transform);
39117         if(!this.hiddenName){
39118             this.hiddenName = s.name;
39119         }
39120         if(!this.store){
39121             this.mode = 'local';
39122             var d = [], opts = s.options;
39123             for(var i = 0, len = opts.length;i < len; i++){
39124                 var o = opts[i];
39125                 var value = (Roo.isIE ? o.getAttributeNode('value').specified : o.hasAttribute('value')) ? o.value : o.text;
39126                 if(o.selected) {
39127                     this.value = value;
39128                 }
39129                 d.push([value, o.text]);
39130             }
39131             this.store = new Roo.data.SimpleStore({
39132                 'id': 0,
39133                 fields: ['value', 'text'],
39134                 data : d
39135             });
39136             this.valueField = 'value';
39137             this.displayField = 'text';
39138         }
39139         s.name = Roo.id(); // wipe out the name in case somewhere else they have a reference
39140         if(!this.lazyRender){
39141             this.target = true;
39142             this.el = Roo.DomHelper.insertBefore(s, this.autoCreate || this.defaultAutoCreate);
39143             s.parentNode.removeChild(s); // remove it
39144             this.render(this.el.parentNode);
39145         }else{
39146             s.parentNode.removeChild(s); // remove it
39147         }
39148
39149     }
39150     if (this.store) {
39151         this.store = Roo.factory(this.store, Roo.data);
39152     }
39153     
39154     this.selectedIndex = -1;
39155     if(this.mode == 'local'){
39156         if(config.queryDelay === undefined){
39157             this.queryDelay = 10;
39158         }
39159         if(config.minChars === undefined){
39160             this.minChars = 0;
39161         }
39162     }
39163 };
39164
39165 Roo.extend(Roo.form.ComboBox, Roo.form.TriggerField, {
39166     /**
39167      * @cfg {String/HTMLElement/Element} transform The id, DOM node or element of an existing select to convert to a ComboBox
39168      */
39169     /**
39170      * @cfg {Boolean} lazyRender True to prevent the ComboBox from rendering until requested (should always be used when
39171      * rendering into an Roo.Editor, defaults to false)
39172      */
39173     /**
39174      * @cfg {Boolean/Object} autoCreate A DomHelper element spec, or true for a default element spec (defaults to:
39175      * {tag: "input", type: "text", size: "24", autocomplete: "off"})
39176      */
39177     /**
39178      * @cfg {Roo.data.Store} store The data store to which this combo is bound (defaults to undefined)
39179      */
39180     /**
39181      * @cfg {String} title If supplied, a header element is created containing this text and added into the top of
39182      * the dropdown list (defaults to undefined, with no header element)
39183      */
39184
39185      /**
39186      * @cfg {String/Roo.Template} tpl The template to use to render the output
39187      */
39188      
39189     // private
39190     defaultAutoCreate : {tag: "input", type: "text", size: "24", autocomplete: "off"},
39191     /**
39192      * @cfg {Number} listWidth The width in pixels of the dropdown list (defaults to the width of the ComboBox field)
39193      */
39194     listWidth: undefined,
39195     /**
39196      * @cfg {String} displayField The underlying data field name to bind to this CombBox (defaults to undefined if
39197      * mode = 'remote' or 'text' if mode = 'local')
39198      */
39199     displayField: undefined,
39200     /**
39201      * @cfg {String} valueField The underlying data value name to bind to this CombBox (defaults to undefined if
39202      * mode = 'remote' or 'value' if mode = 'local'). 
39203      * Note: use of a valueField requires the user make a selection
39204      * in order for a value to be mapped.
39205      */
39206     valueField: undefined,
39207     
39208     
39209     /**
39210      * @cfg {String} hiddenName If specified, a hidden form field with this name is dynamically generated to store the
39211      * field's data value (defaults to the underlying DOM element's name)
39212      */
39213     hiddenName: undefined,
39214     /**
39215      * @cfg {String} listClass CSS class to apply to the dropdown list element (defaults to '')
39216      */
39217     listClass: '',
39218     /**
39219      * @cfg {String} selectedClass CSS class to apply to the selected item in the dropdown list (defaults to 'x-combo-selected')
39220      */
39221     selectedClass: 'x-combo-selected',
39222     /**
39223      * @cfg {String} triggerClass An additional CSS class used to style the trigger button.  The trigger will always get the
39224      * class 'x-form-trigger' and triggerClass will be <b>appended</b> if specified (defaults to 'x-form-arrow-trigger'
39225      * which displays a downward arrow icon).
39226      */
39227     triggerClass : 'x-form-arrow-trigger',
39228     /**
39229      * @cfg {Boolean/String} shadow True or "sides" for the default effect, "frame" for 4-way shadow, and "drop" for bottom-right
39230      */
39231     shadow:'sides',
39232     /**
39233      * @cfg {String} listAlign A valid anchor position value. See {@link Roo.Element#alignTo} for details on supported
39234      * anchor positions (defaults to 'tl-bl')
39235      */
39236     listAlign: 'tl-bl?',
39237     /**
39238      * @cfg {Number} maxHeight The maximum height in pixels of the dropdown list before scrollbars are shown (defaults to 300)
39239      */
39240     maxHeight: 300,
39241     /**
39242      * @cfg {String} triggerAction The action to execute when the trigger field is activated.  Use 'all' to run the
39243      * query specified by the allQuery config option (defaults to 'query')
39244      */
39245     triggerAction: 'query',
39246     /**
39247      * @cfg {Number} minChars The minimum number of characters the user must type before autocomplete and typeahead activate
39248      * (defaults to 4, does not apply if editable = false)
39249      */
39250     minChars : 4,
39251     /**
39252      * @cfg {Boolean} typeAhead True to populate and autoselect the remainder of the text being typed after a configurable
39253      * delay (typeAheadDelay) if it matches a known value (defaults to false)
39254      */
39255     typeAhead: false,
39256     /**
39257      * @cfg {Number} queryDelay The length of time in milliseconds to delay between the start of typing and sending the
39258      * query to filter the dropdown list (defaults to 500 if mode = 'remote' or 10 if mode = 'local')
39259      */
39260     queryDelay: 500,
39261     /**
39262      * @cfg {Number} pageSize If greater than 0, a paging toolbar is displayed in the footer of the dropdown list and the
39263      * filter queries will execute with page start and limit parameters.  Only applies when mode = 'remote' (defaults to 0)
39264      */
39265     pageSize: 0,
39266     /**
39267      * @cfg {Boolean} selectOnFocus True to select any existing text in the field immediately on focus.  Only applies
39268      * when editable = true (defaults to false)
39269      */
39270     selectOnFocus:false,
39271     /**
39272      * @cfg {String} queryParam Name of the query as it will be passed on the querystring (defaults to 'query')
39273      */
39274     queryParam: 'query',
39275     /**
39276      * @cfg {String} loadingText The text to display in the dropdown list while data is loading.  Only applies
39277      * when mode = 'remote' (defaults to 'Loading...')
39278      */
39279     loadingText: 'Loading...',
39280     /**
39281      * @cfg {Boolean} resizable True to add a resize handle to the bottom of the dropdown list (defaults to false)
39282      */
39283     resizable: false,
39284     /**
39285      * @cfg {Number} handleHeight The height in pixels of the dropdown list resize handle if resizable = true (defaults to 8)
39286      */
39287     handleHeight : 8,
39288     /**
39289      * @cfg {Boolean} editable False to prevent the user from typing text directly into the field, just like a
39290      * traditional select (defaults to true)
39291      */
39292     editable: true,
39293     /**
39294      * @cfg {String} allQuery The text query to send to the server to return all records for the list with no filtering (defaults to '')
39295      */
39296     allQuery: '',
39297     /**
39298      * @cfg {String} mode Set to 'local' if the ComboBox loads local data (defaults to 'remote' which loads from the server)
39299      */
39300     mode: 'remote',
39301     /**
39302      * @cfg {Number} minListWidth The minimum width of the dropdown list in pixels (defaults to 70, will be ignored if
39303      * listWidth has a higher value)
39304      */
39305     minListWidth : 70,
39306     /**
39307      * @cfg {Boolean} forceSelection True to restrict the selected value to one of the values in the list, false to
39308      * allow the user to set arbitrary text into the field (defaults to false)
39309      */
39310     forceSelection:false,
39311     /**
39312      * @cfg {Number} typeAheadDelay The length of time in milliseconds to wait until the typeahead text is displayed
39313      * if typeAhead = true (defaults to 250)
39314      */
39315     typeAheadDelay : 250,
39316     /**
39317      * @cfg {String} valueNotFoundText When using a name/value combo, if the value passed to setValue is not found in
39318      * the store, valueNotFoundText will be displayed as the field text if defined (defaults to undefined)
39319      */
39320     valueNotFoundText : undefined,
39321     /**
39322      * @cfg {Boolean} blockFocus Prevents all focus calls, so it can work with things like HTML edtor bar
39323      */
39324     blockFocus : false,
39325     
39326     /**
39327      * @cfg {Boolean} disableClear Disable showing of clear button.
39328      */
39329     disableClear : false,
39330     /**
39331      * @cfg {Boolean} alwaysQuery  Disable caching of results, and always send query
39332      */
39333     alwaysQuery : false,
39334     
39335     //private
39336     addicon : false,
39337     editicon: false,
39338     
39339     // element that contains real text value.. (when hidden is used..)
39340      
39341     // private
39342     onRender : function(ct, position){
39343         Roo.form.ComboBox.superclass.onRender.call(this, ct, position);
39344         if(this.hiddenName){
39345             this.hiddenField = this.el.insertSibling({tag:'input', type:'hidden', name: this.hiddenName, id:  (this.hiddenId||this.hiddenName)},
39346                     'before', true);
39347             this.hiddenField.value =
39348                 this.hiddenValue !== undefined ? this.hiddenValue :
39349                 this.value !== undefined ? this.value : '';
39350
39351             // prevent input submission
39352             this.el.dom.removeAttribute('name');
39353              
39354              
39355         }
39356         if(Roo.isGecko){
39357             this.el.dom.setAttribute('autocomplete', 'off');
39358         }
39359
39360         var cls = 'x-combo-list';
39361
39362         this.list = new Roo.Layer({
39363             shadow: this.shadow, cls: [cls, this.listClass].join(' '), constrain:false
39364         });
39365
39366         var lw = this.listWidth || Math.max(this.wrap.getWidth(), this.minListWidth);
39367         this.list.setWidth(lw);
39368         this.list.swallowEvent('mousewheel');
39369         this.assetHeight = 0;
39370
39371         if(this.title){
39372             this.header = this.list.createChild({cls:cls+'-hd', html: this.title});
39373             this.assetHeight += this.header.getHeight();
39374         }
39375
39376         this.innerList = this.list.createChild({cls:cls+'-inner'});
39377         this.innerList.on('mouseover', this.onViewOver, this);
39378         this.innerList.on('mousemove', this.onViewMove, this);
39379         this.innerList.setWidth(lw - this.list.getFrameWidth('lr'));
39380         
39381         if(this.allowBlank && !this.pageSize && !this.disableClear){
39382             this.footer = this.list.createChild({cls:cls+'-ft'});
39383             this.pageTb = new Roo.Toolbar(this.footer);
39384            
39385         }
39386         if(this.pageSize){
39387             this.footer = this.list.createChild({cls:cls+'-ft'});
39388             this.pageTb = new Roo.PagingToolbar(this.footer, this.store,
39389                     {pageSize: this.pageSize});
39390             
39391         }
39392         
39393         if (this.pageTb && this.allowBlank && !this.disableClear) {
39394             var _this = this;
39395             this.pageTb.add(new Roo.Toolbar.Fill(), {
39396                 cls: 'x-btn-icon x-btn-clear',
39397                 text: '&#160;',
39398                 handler: function()
39399                 {
39400                     _this.collapse();
39401                     _this.clearValue();
39402                     _this.onSelect(false, -1);
39403                 }
39404             });
39405         }
39406         if (this.footer) {
39407             this.assetHeight += this.footer.getHeight();
39408         }
39409         
39410
39411         if(!this.tpl){
39412             this.tpl = '<div class="'+cls+'-item">{' + this.displayField + '}</div>';
39413         }
39414
39415         this.view = new Roo.View(this.innerList, this.tpl, {
39416             singleSelect:true, store: this.store, selectedClass: this.selectedClass
39417         });
39418
39419         this.view.on('click', this.onViewClick, this);
39420
39421         this.store.on('beforeload', this.onBeforeLoad, this);
39422         this.store.on('load', this.onLoad, this);
39423         this.store.on('loadexception', this.onLoadException, this);
39424
39425         if(this.resizable){
39426             this.resizer = new Roo.Resizable(this.list,  {
39427                pinned:true, handles:'se'
39428             });
39429             this.resizer.on('resize', function(r, w, h){
39430                 this.maxHeight = h-this.handleHeight-this.list.getFrameWidth('tb')-this.assetHeight;
39431                 this.listWidth = w;
39432                 this.innerList.setWidth(w - this.list.getFrameWidth('lr'));
39433                 this.restrictHeight();
39434             }, this);
39435             this[this.pageSize?'footer':'innerList'].setStyle('margin-bottom', this.handleHeight+'px');
39436         }
39437         if(!this.editable){
39438             this.editable = true;
39439             this.setEditable(false);
39440         }  
39441         
39442         
39443         if (typeof(this.events.add.listeners) != 'undefined') {
39444             
39445             this.addicon = this.wrap.createChild(
39446                 {tag: 'img', src: Roo.BLANK_IMAGE_URL, cls: 'x-form-combo-add' });  
39447        
39448             this.addicon.on('click', function(e) {
39449                 this.fireEvent('add', this);
39450             }, this);
39451         }
39452         if (typeof(this.events.edit.listeners) != 'undefined') {
39453             
39454             this.editicon = this.wrap.createChild(
39455                 {tag: 'img', src: Roo.BLANK_IMAGE_URL, cls: 'x-form-combo-edit' });  
39456             if (this.addicon) {
39457                 this.editicon.setStyle('margin-left', '40px');
39458             }
39459             this.editicon.on('click', function(e) {
39460                 
39461                 // we fire even  if inothing is selected..
39462                 this.fireEvent('edit', this, this.lastData );
39463                 
39464             }, this);
39465         }
39466         
39467         
39468         
39469     },
39470
39471     // private
39472     initEvents : function(){
39473         Roo.form.ComboBox.superclass.initEvents.call(this);
39474
39475         this.keyNav = new Roo.KeyNav(this.el, {
39476             "up" : function(e){
39477                 this.inKeyMode = true;
39478                 this.selectPrev();
39479             },
39480
39481             "down" : function(e){
39482                 if(!this.isExpanded()){
39483                     this.onTriggerClick();
39484                 }else{
39485                     this.inKeyMode = true;
39486                     this.selectNext();
39487                 }
39488             },
39489
39490             "enter" : function(e){
39491                 this.onViewClick();
39492                 //return true;
39493             },
39494
39495             "esc" : function(e){
39496                 this.collapse();
39497             },
39498
39499             "tab" : function(e){
39500                 this.onViewClick(false);
39501                 this.fireEvent("specialkey", this, e);
39502                 return true;
39503             },
39504
39505             scope : this,
39506
39507             doRelay : function(foo, bar, hname){
39508                 if(hname == 'down' || this.scope.isExpanded()){
39509                    return Roo.KeyNav.prototype.doRelay.apply(this, arguments);
39510                 }
39511                 return true;
39512             },
39513
39514             forceKeyDown: true
39515         });
39516         this.queryDelay = Math.max(this.queryDelay || 10,
39517                 this.mode == 'local' ? 10 : 250);
39518         this.dqTask = new Roo.util.DelayedTask(this.initQuery, this);
39519         if(this.typeAhead){
39520             this.taTask = new Roo.util.DelayedTask(this.onTypeAhead, this);
39521         }
39522         if(this.editable !== false){
39523             this.el.on("keyup", this.onKeyUp, this);
39524         }
39525         if(this.forceSelection){
39526             this.on('blur', this.doForce, this);
39527         }
39528     },
39529
39530     onDestroy : function(){
39531         if(this.view){
39532             this.view.setStore(null);
39533             this.view.el.removeAllListeners();
39534             this.view.el.remove();
39535             this.view.purgeListeners();
39536         }
39537         if(this.list){
39538             this.list.destroy();
39539         }
39540         if(this.store){
39541             this.store.un('beforeload', this.onBeforeLoad, this);
39542             this.store.un('load', this.onLoad, this);
39543             this.store.un('loadexception', this.onLoadException, this);
39544         }
39545         Roo.form.ComboBox.superclass.onDestroy.call(this);
39546     },
39547
39548     // private
39549     fireKey : function(e){
39550         if(e.isNavKeyPress() && !this.list.isVisible()){
39551             this.fireEvent("specialkey", this, e);
39552         }
39553     },
39554
39555     // private
39556     onResize: function(w, h){
39557         Roo.form.ComboBox.superclass.onResize.apply(this, arguments);
39558         
39559         if(typeof w != 'number'){
39560             // we do not handle it!?!?
39561             return;
39562         }
39563         var tw = this.trigger.getWidth();
39564         tw += this.addicon ? this.addicon.getWidth() : 0;
39565         tw += this.editicon ? this.editicon.getWidth() : 0;
39566         var x = w - tw;
39567         this.el.setWidth( this.adjustWidth('input', x));
39568             
39569         this.trigger.setStyle('left', x+'px');
39570         
39571         if(this.list && this.listWidth === undefined){
39572             var lw = Math.max(x + this.trigger.getWidth(), this.minListWidth);
39573             this.list.setWidth(lw);
39574             this.innerList.setWidth(lw - this.list.getFrameWidth('lr'));
39575         }
39576         
39577     
39578         
39579     },
39580
39581     /**
39582      * Allow or prevent the user from directly editing the field text.  If false is passed,
39583      * the user will only be able to select from the items defined in the dropdown list.  This method
39584      * is the runtime equivalent of setting the 'editable' config option at config time.
39585      * @param {Boolean} value True to allow the user to directly edit the field text
39586      */
39587     setEditable : function(value){
39588         if(value == this.editable){
39589             return;
39590         }
39591         this.editable = value;
39592         if(!value){
39593             this.el.dom.setAttribute('readOnly', true);
39594             this.el.on('mousedown', this.onTriggerClick,  this);
39595             this.el.addClass('x-combo-noedit');
39596         }else{
39597             this.el.dom.setAttribute('readOnly', false);
39598             this.el.un('mousedown', this.onTriggerClick,  this);
39599             this.el.removeClass('x-combo-noedit');
39600         }
39601     },
39602
39603     // private
39604     onBeforeLoad : function(){
39605         if(!this.hasFocus){
39606             return;
39607         }
39608         this.innerList.update(this.loadingText ?
39609                '<div class="loading-indicator">'+this.loadingText+'</div>' : '');
39610         this.restrictHeight();
39611         this.selectedIndex = -1;
39612     },
39613
39614     // private
39615     onLoad : function(){
39616         if(!this.hasFocus){
39617             return;
39618         }
39619         if(this.store.getCount() > 0){
39620             this.expand();
39621             this.restrictHeight();
39622             if(this.lastQuery == this.allQuery){
39623                 if(this.editable){
39624                     this.el.dom.select();
39625                 }
39626                 if(!this.selectByValue(this.value, true)){
39627                     this.select(0, true);
39628                 }
39629             }else{
39630                 this.selectNext();
39631                 if(this.typeAhead && this.lastKey != Roo.EventObject.BACKSPACE && this.lastKey != Roo.EventObject.DELETE){
39632                     this.taTask.delay(this.typeAheadDelay);
39633                 }
39634             }
39635         }else{
39636             this.onEmptyResults();
39637         }
39638         //this.el.focus();
39639     },
39640     // private
39641     onLoadException : function()
39642     {
39643         this.collapse();
39644         Roo.log(this.store.reader.jsonData);
39645         if (this.store && typeof(this.store.reader.jsonData.errorMsg) != 'undefined') {
39646             Roo.MessageBox.alert("Error loading",this.store.reader.jsonData.errorMsg);
39647         }
39648         
39649         
39650     },
39651     // private
39652     onTypeAhead : function(){
39653         if(this.store.getCount() > 0){
39654             var r = this.store.getAt(0);
39655             var newValue = r.data[this.displayField];
39656             var len = newValue.length;
39657             var selStart = this.getRawValue().length;
39658             if(selStart != len){
39659                 this.setRawValue(newValue);
39660                 this.selectText(selStart, newValue.length);
39661             }
39662         }
39663     },
39664
39665     // private
39666     onSelect : function(record, index){
39667         if(this.fireEvent('beforeselect', this, record, index) !== false){
39668             this.setFromData(index > -1 ? record.data : false);
39669             this.collapse();
39670             this.fireEvent('select', this, record, index);
39671         }
39672     },
39673
39674     /**
39675      * Returns the currently selected field value or empty string if no value is set.
39676      * @return {String} value The selected value
39677      */
39678     getValue : function(){
39679         if(this.valueField){
39680             return typeof this.value != 'undefined' ? this.value : '';
39681         }else{
39682             return Roo.form.ComboBox.superclass.getValue.call(this);
39683         }
39684     },
39685
39686     /**
39687      * Clears any text/value currently set in the field
39688      */
39689     clearValue : function(){
39690         if(this.hiddenField){
39691             this.hiddenField.value = '';
39692         }
39693         this.value = '';
39694         this.setRawValue('');
39695         this.lastSelectionText = '';
39696         
39697     },
39698
39699     /**
39700      * Sets the specified value into the field.  If the value finds a match, the corresponding record text
39701      * will be displayed in the field.  If the value does not match the data value of an existing item,
39702      * and the valueNotFoundText config option is defined, it will be displayed as the default field text.
39703      * Otherwise the field will be blank (although the value will still be set).
39704      * @param {String} value The value to match
39705      */
39706     setValue : function(v){
39707         var text = v;
39708         if(this.valueField){
39709             var r = this.findRecord(this.valueField, v);
39710             if(r){
39711                 text = r.data[this.displayField];
39712             }else if(this.valueNotFoundText !== undefined){
39713                 text = this.valueNotFoundText;
39714             }
39715         }
39716         this.lastSelectionText = text;
39717         if(this.hiddenField){
39718             this.hiddenField.value = v;
39719         }
39720         Roo.form.ComboBox.superclass.setValue.call(this, text);
39721         this.value = v;
39722     },
39723     /**
39724      * @property {Object} the last set data for the element
39725      */
39726     
39727     lastData : false,
39728     /**
39729      * Sets the value of the field based on a object which is related to the record format for the store.
39730      * @param {Object} value the value to set as. or false on reset?
39731      */
39732     setFromData : function(o){
39733         var dv = ''; // display value
39734         var vv = ''; // value value..
39735         this.lastData = o;
39736         if (this.displayField) {
39737             dv = !o || typeof(o[this.displayField]) == 'undefined' ? '' : o[this.displayField];
39738         } else {
39739             // this is an error condition!!!
39740             Roo.log('no  displayField value set for '+ (this.name ? this.name : this.id));
39741         }
39742         
39743         if(this.valueField){
39744             vv = !o || typeof(o[this.valueField]) == 'undefined' ? dv : o[this.valueField];
39745         }
39746         if(this.hiddenField){
39747             this.hiddenField.value = vv;
39748             
39749             this.lastSelectionText = dv;
39750             Roo.form.ComboBox.superclass.setValue.call(this, dv);
39751             this.value = vv;
39752             return;
39753         }
39754         // no hidden field.. - we store the value in 'value', but still display
39755         // display field!!!!
39756         this.lastSelectionText = dv;
39757         Roo.form.ComboBox.superclass.setValue.call(this, dv);
39758         this.value = vv;
39759         
39760         
39761     },
39762     // private
39763     reset : function(){
39764         // overridden so that last data is reset..
39765         this.setValue(this.resetValue);
39766         this.clearInvalid();
39767         this.lastData = false;
39768         if (this.view) {
39769             this.view.clearSelections();
39770         }
39771     },
39772     // private
39773     findRecord : function(prop, value){
39774         var record;
39775         if(this.store.getCount() > 0){
39776             this.store.each(function(r){
39777                 if(r.data[prop] == value){
39778                     record = r;
39779                     return false;
39780                 }
39781                 return true;
39782             });
39783         }
39784         return record;
39785     },
39786     
39787     getName: function()
39788     {
39789         // returns hidden if it's set..
39790         if (!this.rendered) {return ''};
39791         return !this.hiddenName && this.el.dom.name  ? this.el.dom.name : (this.hiddenName || '');
39792         
39793     },
39794     // private
39795     onViewMove : function(e, t){
39796         this.inKeyMode = false;
39797     },
39798
39799     // private
39800     onViewOver : function(e, t){
39801         if(this.inKeyMode){ // prevent key nav and mouse over conflicts
39802             return;
39803         }
39804         var item = this.view.findItemFromChild(t);
39805         if(item){
39806             var index = this.view.indexOf(item);
39807             this.select(index, false);
39808         }
39809     },
39810
39811     // private
39812     onViewClick : function(doFocus)
39813     {
39814         var index = this.view.getSelectedIndexes()[0];
39815         var r = this.store.getAt(index);
39816         if(r){
39817             this.onSelect(r, index);
39818         }
39819         if(doFocus !== false && !this.blockFocus){
39820             this.el.focus();
39821         }
39822     },
39823
39824     // private
39825     restrictHeight : function(){
39826         this.innerList.dom.style.height = '';
39827         var inner = this.innerList.dom;
39828         var h = Math.max(inner.clientHeight, inner.offsetHeight, inner.scrollHeight);
39829         this.innerList.setHeight(h < this.maxHeight ? 'auto' : this.maxHeight);
39830         this.list.beginUpdate();
39831         this.list.setHeight(this.innerList.getHeight()+this.list.getFrameWidth('tb')+(this.resizable?this.handleHeight:0)+this.assetHeight);
39832         this.list.alignTo(this.el, this.listAlign);
39833         this.list.endUpdate();
39834     },
39835
39836     // private
39837     onEmptyResults : function(){
39838         this.collapse();
39839     },
39840
39841     /**
39842      * Returns true if the dropdown list is expanded, else false.
39843      */
39844     isExpanded : function(){
39845         return this.list.isVisible();
39846     },
39847
39848     /**
39849      * Select an item in the dropdown list by its data value. This function does NOT cause the select event to fire.
39850      * The store must be loaded and the list expanded for this function to work, otherwise use setValue.
39851      * @param {String} value The data value of the item to select
39852      * @param {Boolean} scrollIntoView False to prevent the dropdown list from autoscrolling to display the
39853      * selected item if it is not currently in view (defaults to true)
39854      * @return {Boolean} True if the value matched an item in the list, else false
39855      */
39856     selectByValue : function(v, scrollIntoView){
39857         if(v !== undefined && v !== null){
39858             var r = this.findRecord(this.valueField || this.displayField, v);
39859             if(r){
39860                 this.select(this.store.indexOf(r), scrollIntoView);
39861                 return true;
39862             }
39863         }
39864         return false;
39865     },
39866
39867     /**
39868      * Select an item in the dropdown list by its numeric index in the list. This function does NOT cause the select event to fire.
39869      * The store must be loaded and the list expanded for this function to work, otherwise use setValue.
39870      * @param {Number} index The zero-based index of the list item to select
39871      * @param {Boolean} scrollIntoView False to prevent the dropdown list from autoscrolling to display the
39872      * selected item if it is not currently in view (defaults to true)
39873      */
39874     select : function(index, scrollIntoView){
39875         this.selectedIndex = index;
39876         this.view.select(index);
39877         if(scrollIntoView !== false){
39878             var el = this.view.getNode(index);
39879             if(el){
39880                 this.innerList.scrollChildIntoView(el, false);
39881             }
39882         }
39883     },
39884
39885     // private
39886     selectNext : function(){
39887         var ct = this.store.getCount();
39888         if(ct > 0){
39889             if(this.selectedIndex == -1){
39890                 this.select(0);
39891             }else if(this.selectedIndex < ct-1){
39892                 this.select(this.selectedIndex+1);
39893             }
39894         }
39895     },
39896
39897     // private
39898     selectPrev : function(){
39899         var ct = this.store.getCount();
39900         if(ct > 0){
39901             if(this.selectedIndex == -1){
39902                 this.select(0);
39903             }else if(this.selectedIndex != 0){
39904                 this.select(this.selectedIndex-1);
39905             }
39906         }
39907     },
39908
39909     // private
39910     onKeyUp : function(e){
39911         if(this.editable !== false && !e.isSpecialKey()){
39912             this.lastKey = e.getKey();
39913             this.dqTask.delay(this.queryDelay);
39914         }
39915     },
39916
39917     // private
39918     validateBlur : function(){
39919         return !this.list || !this.list.isVisible();   
39920     },
39921
39922     // private
39923     initQuery : function(){
39924         this.doQuery(this.getRawValue());
39925     },
39926
39927     // private
39928     doForce : function(){
39929         if(this.el.dom.value.length > 0){
39930             this.el.dom.value =
39931                 this.lastSelectionText === undefined ? '' : this.lastSelectionText;
39932              
39933         }
39934     },
39935
39936     /**
39937      * Execute a query to filter the dropdown list.  Fires the beforequery event prior to performing the
39938      * query allowing the query action to be canceled if needed.
39939      * @param {String} query The SQL query to execute
39940      * @param {Boolean} forceAll True to force the query to execute even if there are currently fewer characters
39941      * in the field than the minimum specified by the minChars config option.  It also clears any filter previously
39942      * saved in the current store (defaults to false)
39943      */
39944     doQuery : function(q, forceAll){
39945         if(q === undefined || q === null){
39946             q = '';
39947         }
39948         var qe = {
39949             query: q,
39950             forceAll: forceAll,
39951             combo: this,
39952             cancel:false
39953         };
39954         if(this.fireEvent('beforequery', qe)===false || qe.cancel){
39955             return false;
39956         }
39957         q = qe.query;
39958         forceAll = qe.forceAll;
39959         if(forceAll === true || (q.length >= this.minChars)){
39960             if(this.lastQuery != q || this.alwaysQuery){
39961                 this.lastQuery = q;
39962                 if(this.mode == 'local'){
39963                     this.selectedIndex = -1;
39964                     if(forceAll){
39965                         this.store.clearFilter();
39966                     }else{
39967                         this.store.filter(this.displayField, q);
39968                     }
39969                     this.onLoad();
39970                 }else{
39971                     this.store.baseParams[this.queryParam] = q;
39972                     this.store.load({
39973                         params: this.getParams(q)
39974                     });
39975                     this.expand();
39976                 }
39977             }else{
39978                 this.selectedIndex = -1;
39979                 this.onLoad();   
39980             }
39981         }
39982     },
39983
39984     // private
39985     getParams : function(q){
39986         var p = {};
39987         //p[this.queryParam] = q;
39988         if(this.pageSize){
39989             p.start = 0;
39990             p.limit = this.pageSize;
39991         }
39992         return p;
39993     },
39994
39995     /**
39996      * Hides the dropdown list if it is currently expanded. Fires the 'collapse' event on completion.
39997      */
39998     collapse : function(){
39999         if(!this.isExpanded()){
40000             return;
40001         }
40002         this.list.hide();
40003         Roo.get(document).un('mousedown', this.collapseIf, this);
40004         Roo.get(document).un('mousewheel', this.collapseIf, this);
40005         if (!this.editable) {
40006             Roo.get(document).un('keydown', this.listKeyPress, this);
40007         }
40008         this.fireEvent('collapse', this);
40009     },
40010
40011     // private
40012     collapseIf : function(e){
40013         if(!e.within(this.wrap) && !e.within(this.list)){
40014             this.collapse();
40015         }
40016     },
40017
40018     /**
40019      * Expands the dropdown list if it is currently hidden. Fires the 'expand' event on completion.
40020      */
40021     expand : function(){
40022         if(this.isExpanded() || !this.hasFocus){
40023             return;
40024         }
40025         this.list.alignTo(this.el, this.listAlign);
40026         this.list.show();
40027         Roo.get(document).on('mousedown', this.collapseIf, this);
40028         Roo.get(document).on('mousewheel', this.collapseIf, this);
40029         if (!this.editable) {
40030             Roo.get(document).on('keydown', this.listKeyPress, this);
40031         }
40032         
40033         this.fireEvent('expand', this);
40034     },
40035
40036     // private
40037     // Implements the default empty TriggerField.onTriggerClick function
40038     onTriggerClick : function(){
40039         if(this.disabled){
40040             return;
40041         }
40042         if(this.isExpanded()){
40043             this.collapse();
40044             if (!this.blockFocus) {
40045                 this.el.focus();
40046             }
40047             
40048         }else {
40049             this.hasFocus = true;
40050             if(this.triggerAction == 'all') {
40051                 this.doQuery(this.allQuery, true);
40052             } else {
40053                 this.doQuery(this.getRawValue());
40054             }
40055             if (!this.blockFocus) {
40056                 this.el.focus();
40057             }
40058         }
40059     },
40060     listKeyPress : function(e)
40061     {
40062         //Roo.log('listkeypress');
40063         // scroll to first matching element based on key pres..
40064         if (e.isSpecialKey()) {
40065             return false;
40066         }
40067         var k = String.fromCharCode(e.getKey()).toUpperCase();
40068         //Roo.log(k);
40069         var match  = false;
40070         var csel = this.view.getSelectedNodes();
40071         var cselitem = false;
40072         if (csel.length) {
40073             var ix = this.view.indexOf(csel[0]);
40074             cselitem  = this.store.getAt(ix);
40075             if (!cselitem.get(this.displayField) || cselitem.get(this.displayField).substring(0,1).toUpperCase() != k) {
40076                 cselitem = false;
40077             }
40078             
40079         }
40080         
40081         this.store.each(function(v) { 
40082             if (cselitem) {
40083                 // start at existing selection.
40084                 if (cselitem.id == v.id) {
40085                     cselitem = false;
40086                 }
40087                 return;
40088             }
40089                 
40090             if (v.get(this.displayField) && v.get(this.displayField).substring(0,1).toUpperCase() == k) {
40091                 match = this.store.indexOf(v);
40092                 return false;
40093             }
40094         }, this);
40095         
40096         if (match === false) {
40097             return true; // no more action?
40098         }
40099         // scroll to?
40100         this.view.select(match);
40101         var sn = Roo.get(this.view.getSelectedNodes()[0])
40102         sn.scrollIntoView(sn.dom.parentNode, false);
40103     }
40104
40105     /** 
40106     * @cfg {Boolean} grow 
40107     * @hide 
40108     */
40109     /** 
40110     * @cfg {Number} growMin 
40111     * @hide 
40112     */
40113     /** 
40114     * @cfg {Number} growMax 
40115     * @hide 
40116     */
40117     /**
40118      * @hide
40119      * @method autoSize
40120      */
40121 });/*
40122  * Copyright(c) 2010-2012, Roo J Solutions Limited
40123  *
40124  * Licence LGPL
40125  *
40126  */
40127
40128 /**
40129  * @class Roo.form.ComboBoxArray
40130  * @extends Roo.form.TextField
40131  * A facebook style adder... for lists of email / people / countries  etc...
40132  * pick multiple items from a combo box, and shows each one.
40133  *
40134  *  Fred [x]  Brian [x]  [Pick another |v]
40135  *
40136  *
40137  *  For this to work: it needs various extra information
40138  *    - normal combo problay has
40139  *      name, hiddenName
40140  *    + displayField, valueField
40141  *
40142  *    For our purpose...
40143  *
40144  *
40145  *   If we change from 'extends' to wrapping...
40146  *   
40147  *  
40148  *
40149  
40150  
40151  * @constructor
40152  * Create a new ComboBoxArray.
40153  * @param {Object} config Configuration options
40154  */
40155  
40156
40157 Roo.form.ComboBoxArray = function(config)
40158 {
40159     
40160     Roo.form.ComboBoxArray.superclass.constructor.call(this, config);
40161     
40162     this.items = new Roo.util.MixedCollection(false);
40163     
40164     // construct the child combo...
40165     
40166     
40167     
40168     
40169    
40170     
40171 }
40172
40173  
40174 Roo.extend(Roo.form.ComboBoxArray, Roo.form.TextField,
40175
40176     /**
40177      * @cfg {Roo.form.Combo} combo The combo box that is wrapped
40178      */
40179     
40180     lastData : false,
40181     
40182     // behavies liek a hiddne field
40183     inputType:      'hidden',
40184     /**
40185      * @cfg {Number} width The width of the box that displays the selected element
40186      */ 
40187     width:          300,
40188
40189     
40190     
40191     /**
40192      * @cfg {String} name    The name of the visable items on this form (eg. titles not ids)
40193      */
40194     name : false,
40195     /**
40196      * @cfg {String} hiddenName    The hidden name of the field, often contains an comma seperated list of names
40197      */
40198     hiddenName : false,
40199     
40200     
40201     // private the array of items that are displayed..
40202     items  : false,
40203     // private - the hidden field el.
40204     hiddenEl : false,
40205     // private - the filed el..
40206     el : false,
40207     
40208     //validateValue : function() { return true; }, // all values are ok!
40209     //onAddClick: function() { },
40210     
40211     onRender : function(ct, position) 
40212     {
40213         
40214         // create the standard hidden element
40215         //Roo.form.ComboBoxArray.superclass.onRender.call(this, ct, position);
40216         
40217         
40218         // give fake names to child combo;
40219         this.combo.hiddenName = this.hiddenName ? (this.hiddenName+'-subcombo') : this.hiddenName;
40220         this.combo.name = this.name? (this.name+'-subcombo') : this.name;
40221         
40222         this.combo = Roo.factory(this.combo, Roo.form);
40223         this.combo.onRender(ct, position);
40224         if (typeof(this.combo.width) != 'undefined') {
40225             this.combo.onResize(this.combo.width,0);
40226         }
40227         
40228         this.combo.initEvents();
40229         
40230         // assigned so form know we need to do this..
40231         this.store          = this.combo.store;
40232         this.valueField     = this.combo.valueField;
40233         this.displayField   = this.combo.displayField ;
40234         
40235         
40236         this.combo.wrap.addClass('x-cbarray-grp');
40237         
40238         var cbwrap = this.combo.wrap.createChild(
40239             {tag: 'div', cls: 'x-cbarray-cb'},
40240             this.combo.el.dom
40241         );
40242         
40243              
40244         this.hiddenEl = this.combo.wrap.createChild({
40245             tag: 'input',  type:'hidden' , name: this.hiddenName, value : ''
40246         });
40247         this.el = this.combo.wrap.createChild({
40248             tag: 'input',  type:'hidden' , name: this.name, value : ''
40249         });
40250          //   this.el.dom.removeAttribute("name");
40251         
40252         
40253         this.outerWrap = this.combo.wrap;
40254         this.wrap = cbwrap;
40255         
40256         this.outerWrap.setWidth(this.width);
40257         this.outerWrap.dom.removeChild(this.el.dom);
40258         
40259         this.wrap.dom.appendChild(this.el.dom);
40260         this.outerWrap.dom.removeChild(this.combo.trigger.dom);
40261         this.combo.wrap.dom.appendChild(this.combo.trigger.dom);
40262         
40263         this.combo.trigger.setStyle('position','relative');
40264         this.combo.trigger.setStyle('left', '0px');
40265         this.combo.trigger.setStyle('top', '2px');
40266         
40267         this.combo.el.setStyle('vertical-align', 'text-bottom');
40268         
40269         //this.trigger.setStyle('vertical-align', 'top');
40270         
40271         // this should use the code from combo really... on('add' ....)
40272         if (this.adder) {
40273             
40274         
40275             this.adder = this.outerWrap.createChild(
40276                 {tag: 'img', src: Roo.BLANK_IMAGE_URL, cls: 'x-form-adder', style: 'margin-left:2px'});  
40277             var _t = this;
40278             this.adder.on('click', function(e) {
40279                 _t.fireEvent('adderclick', this, e);
40280             }, _t);
40281         }
40282         //var _t = this;
40283         //this.adder.on('click', this.onAddClick, _t);
40284         
40285         
40286         this.combo.on('select', function(cb, rec, ix) {
40287             this.addItem(rec.data);
40288             
40289             cb.setValue('');
40290             cb.el.dom.value = '';
40291             //cb.lastData = rec.data;
40292             // add to list
40293             
40294         }, this);
40295         
40296         
40297     },
40298     
40299     
40300     getName: function()
40301     {
40302         // returns hidden if it's set..
40303         if (!this.rendered) {return ''};
40304         return  this.hiddenName ? this.hiddenName : this.name;
40305         
40306     },
40307     
40308     
40309     onResize: function(w, h){
40310         
40311         return;
40312         // not sure if this is needed..
40313         //this.combo.onResize(w,h);
40314         
40315         if(typeof w != 'number'){
40316             // we do not handle it!?!?
40317             return;
40318         }
40319         var tw = this.combo.trigger.getWidth();
40320         tw += this.addicon ? this.addicon.getWidth() : 0;
40321         tw += this.editicon ? this.editicon.getWidth() : 0;
40322         var x = w - tw;
40323         this.combo.el.setWidth( this.combo.adjustWidth('input', x));
40324             
40325         this.combo.trigger.setStyle('left', '0px');
40326         
40327         if(this.list && this.listWidth === undefined){
40328             var lw = Math.max(x + this.combo.trigger.getWidth(), this.combo.minListWidth);
40329             this.list.setWidth(lw);
40330             this.innerList.setWidth(lw - this.list.getFrameWidth('lr'));
40331         }
40332         
40333     
40334         
40335     },
40336     
40337     addItem: function(rec)
40338     {
40339         var valueField = this.combo.valueField;
40340         var displayField = this.combo.displayField;
40341         if (this.items.indexOfKey(rec[valueField]) > -1) {
40342             //console.log("GOT " + rec.data.id);
40343             return;
40344         }
40345         
40346         var x = new Roo.form.ComboBoxArray.Item({
40347             //id : rec[this.idField],
40348             data : rec,
40349             displayField : displayField ,
40350             tipField : displayField ,
40351             cb : this
40352         });
40353         // use the 
40354         this.items.add(rec[valueField],x);
40355         // add it before the element..
40356         this.updateHiddenEl();
40357         x.render(this.outerWrap, this.wrap.dom);
40358         // add the image handler..
40359     },
40360     
40361     updateHiddenEl : function()
40362     {
40363         this.validate();
40364         if (!this.hiddenEl) {
40365             return;
40366         }
40367         var ar = [];
40368         var idField = this.combo.valueField;
40369         
40370         this.items.each(function(f) {
40371             ar.push(f.data[idField]);
40372            
40373         });
40374         this.hiddenEl.dom.value = ar.join(',');
40375         this.validate();
40376     },
40377     
40378     reset : function()
40379     {
40380         //Roo.form.ComboBoxArray.superclass.reset.call(this); 
40381         this.items.each(function(f) {
40382            f.remove(); 
40383         });
40384         this.el.dom.value = '';
40385         if (this.hiddenEl) {
40386             this.hiddenEl.dom.value = '';
40387         }
40388         
40389     },
40390     getValue: function()
40391     {
40392         return this.hiddenEl ? this.hiddenEl.dom.value : '';
40393     },
40394     setValue: function(v) // not a valid action - must use addItems..
40395     {
40396          
40397         this.reset();
40398         
40399         
40400         
40401         if (this.store.isLocal && (typeof(v) == 'string')) {
40402             // then we can use the store to find the values..
40403             // comma seperated at present.. this needs to allow JSON based encoding..
40404             this.hiddenEl.value  = v;
40405             var v_ar = [];
40406             Roo.each(v.split(','), function(k) {
40407                 Roo.log("CHECK " + this.valueField + ',' + k);
40408                 var li = this.store.query(this.valueField, k);
40409                 if (!li.length) {
40410                     return;
40411                 }
40412                 var add = {};
40413                 add[this.valueField] = k;
40414                 add[this.displayField] = li.item(0).data[this.displayField];
40415                 
40416                 this.addItem(add);
40417             }, this) 
40418              
40419         }
40420         if (typeof(v) == 'object') {
40421             // then let's assume it's an array of objects..
40422             Roo.each(v, function(l) {
40423                 this.addItem(l);
40424             }, this);
40425              
40426         }
40427         
40428         
40429     },
40430     setFromData: function(v)
40431     {
40432         // this recieves an object, if setValues is called.
40433         this.reset();
40434         this.el.dom.value = v[this.displayField];
40435         this.hiddenEl.dom.value = v[this.valueField];
40436         if (typeof(v[this.valueField]) != 'string' || !v[this.valueField].length) {
40437             return;
40438         }
40439         var kv = v[this.valueField];
40440         var dv = v[this.displayField];
40441         kv = typeof(kv) != 'string' ? '' : kv;
40442         dv = typeof(dv) != 'string' ? '' : dv;
40443         
40444         
40445         var keys = kv.split(',');
40446         var display = dv.split(',');
40447         for (var i = 0 ; i < keys.length; i++) {
40448             
40449             add = {};
40450             add[this.valueField] = keys[i];
40451             add[this.displayField] = display[i];
40452             this.addItem(add);
40453         }
40454       
40455         
40456     },
40457     
40458     /**
40459      * Validates the combox array value
40460      * @return {Boolean} True if the value is valid, else false
40461      */
40462     validate : function(){
40463         if(this.disabled || this.validateValue(this.processValue(this.getValue()))){
40464             this.clearInvalid();
40465             return true;
40466         }
40467         return false;
40468     },
40469     
40470     validateValue : function(value){
40471         return Roo.form.ComboBoxArray.superclass.validateValue.call(this, this.getValue());
40472         
40473     },
40474     
40475     /*@
40476      * overide
40477      * 
40478      */
40479     isDirty : function() {
40480         if(this.disabled) {
40481             return false;
40482         }
40483         
40484         try {
40485             var d = Roo.decode(String(this.originalValue));
40486         } catch (e) {
40487             return String(this.getValue()) !== String(this.originalValue);
40488         }
40489         
40490         var originalValue = [];
40491         
40492         for (var i = 0; i < d.length; i++){
40493             originalValue.push(d[i][this.valueField]);
40494         }
40495         
40496         return String(this.getValue()) !== String(originalValue.join(','));
40497         
40498     }
40499     
40500 });
40501
40502
40503
40504 /**
40505  * @class Roo.form.ComboBoxArray.Item
40506  * @extends Roo.BoxComponent
40507  * A selected item in the list
40508  *  Fred [x]  Brian [x]  [Pick another |v]
40509  * 
40510  * @constructor
40511  * Create a new item.
40512  * @param {Object} config Configuration options
40513  */
40514  
40515 Roo.form.ComboBoxArray.Item = function(config) {
40516     config.id = Roo.id();
40517     Roo.form.ComboBoxArray.Item.superclass.constructor.call(this, config);
40518 }
40519
40520 Roo.extend(Roo.form.ComboBoxArray.Item, Roo.BoxComponent, {
40521     data : {},
40522     cb: false,
40523     displayField : false,
40524     tipField : false,
40525     
40526     
40527     defaultAutoCreate : {
40528         tag: 'div',
40529         cls: 'x-cbarray-item',
40530         cn : [ 
40531             { tag: 'div' },
40532             {
40533                 tag: 'img',
40534                 width:16,
40535                 height : 16,
40536                 src : Roo.BLANK_IMAGE_URL ,
40537                 align: 'center'
40538             }
40539         ]
40540         
40541     },
40542     
40543  
40544     onRender : function(ct, position)
40545     {
40546         Roo.form.Field.superclass.onRender.call(this, ct, position);
40547         
40548         if(!this.el){
40549             var cfg = this.getAutoCreate();
40550             this.el = ct.createChild(cfg, position);
40551         }
40552         
40553         this.el.child('img').dom.setAttribute('src', Roo.BLANK_IMAGE_URL);
40554         
40555         this.el.child('div').dom.innerHTML = this.cb.renderer ? 
40556             this.cb.renderer(this.data) :
40557             String.format('{0}',this.data[this.displayField]);
40558         
40559             
40560         this.el.child('div').dom.setAttribute('qtip',
40561                         String.format('{0}',this.data[this.tipField])
40562         );
40563         
40564         this.el.child('img').on('click', this.remove, this);
40565         
40566     },
40567    
40568     remove : function()
40569     {
40570         
40571         this.cb.items.remove(this);
40572         this.el.child('img').un('click', this.remove, this);
40573         this.el.remove();
40574         this.cb.updateHiddenEl();
40575     }
40576 });/*
40577  * Based on:
40578  * Ext JS Library 1.1.1
40579  * Copyright(c) 2006-2007, Ext JS, LLC.
40580  *
40581  * Originally Released Under LGPL - original licence link has changed is not relivant.
40582  *
40583  * Fork - LGPL
40584  * <script type="text/javascript">
40585  */
40586 /**
40587  * @class Roo.form.Checkbox
40588  * @extends Roo.form.Field
40589  * Single checkbox field.  Can be used as a direct replacement for traditional checkbox fields.
40590  * @constructor
40591  * Creates a new Checkbox
40592  * @param {Object} config Configuration options
40593  */
40594 Roo.form.Checkbox = function(config){
40595     Roo.form.Checkbox.superclass.constructor.call(this, config);
40596     this.addEvents({
40597         /**
40598          * @event check
40599          * Fires when the checkbox is checked or unchecked.
40600              * @param {Roo.form.Checkbox} this This checkbox
40601              * @param {Boolean} checked The new checked value
40602              */
40603         check : true
40604     });
40605 };
40606
40607 Roo.extend(Roo.form.Checkbox, Roo.form.Field,  {
40608     /**
40609      * @cfg {String} focusClass The CSS class to use when the checkbox receives focus (defaults to undefined)
40610      */
40611     focusClass : undefined,
40612     /**
40613      * @cfg {String} fieldClass The default CSS class for the checkbox (defaults to "x-form-field")
40614      */
40615     fieldClass: "x-form-field",
40616     /**
40617      * @cfg {Boolean} checked True if the the checkbox should render already checked (defaults to false)
40618      */
40619     checked: false,
40620     /**
40621      * @cfg {String/Object} autoCreate A DomHelper element spec, or true for a default element spec (defaults to
40622      * {tag: "input", type: "checkbox", autocomplete: "off"})
40623      */
40624     defaultAutoCreate : { tag: "input", type: 'hidden', autocomplete: "off"},
40625     /**
40626      * @cfg {String} boxLabel The text that appears beside the checkbox
40627      */
40628     boxLabel : "",
40629     /**
40630      * @cfg {String} inputValue The value that should go into the generated input element's value attribute
40631      */  
40632     inputValue : '1',
40633     /**
40634      * @cfg {String} valueOff The value that should go into the generated input element's value when unchecked.
40635      */
40636      valueOff: '0', // value when not checked..
40637
40638     actionMode : 'viewEl', 
40639     //
40640     // private
40641     itemCls : 'x-menu-check-item x-form-item',
40642     groupClass : 'x-menu-group-item',
40643     inputType : 'hidden',
40644     
40645     
40646     inSetChecked: false, // check that we are not calling self...
40647     
40648     inputElement: false, // real input element?
40649     basedOn: false, // ????
40650     
40651     isFormField: true, // not sure where this is needed!!!!
40652
40653     onResize : function(){
40654         Roo.form.Checkbox.superclass.onResize.apply(this, arguments);
40655         if(!this.boxLabel){
40656             this.el.alignTo(this.wrap, 'c-c');
40657         }
40658     },
40659
40660     initEvents : function(){
40661         Roo.form.Checkbox.superclass.initEvents.call(this);
40662         this.el.on("click", this.onClick,  this);
40663         this.el.on("change", this.onClick,  this);
40664     },
40665
40666
40667     getResizeEl : function(){
40668         return this.wrap;
40669     },
40670
40671     getPositionEl : function(){
40672         return this.wrap;
40673     },
40674
40675     // private
40676     onRender : function(ct, position){
40677         Roo.form.Checkbox.superclass.onRender.call(this, ct, position);
40678         /*
40679         if(this.inputValue !== undefined){
40680             this.el.dom.value = this.inputValue;
40681         }
40682         */
40683         //this.wrap = this.el.wrap({cls: "x-form-check-wrap"});
40684         this.wrap = this.el.wrap({cls: 'x-menu-check-item '});
40685         var viewEl = this.wrap.createChild({ 
40686             tag: 'img', cls: 'x-menu-item-icon', style: 'margin: 0px;' ,src : Roo.BLANK_IMAGE_URL });
40687         this.viewEl = viewEl;   
40688         this.wrap.on('click', this.onClick,  this); 
40689         
40690         this.el.on('DOMAttrModified', this.setFromHidden,  this); //ff
40691         this.el.on('propertychange', this.setFromHidden,  this);  //ie
40692         
40693         
40694         
40695         if(this.boxLabel){
40696             this.wrap.createChild({tag: 'label', htmlFor: this.el.id, cls: 'x-form-cb-label', html: this.boxLabel});
40697         //    viewEl.on('click', this.onClick,  this); 
40698         }
40699         //if(this.checked){
40700             this.setChecked(this.checked);
40701         //}else{
40702             //this.checked = this.el.dom;
40703         //}
40704
40705     },
40706
40707     // private
40708     initValue : Roo.emptyFn,
40709
40710     /**
40711      * Returns the checked state of the checkbox.
40712      * @return {Boolean} True if checked, else false
40713      */
40714     getValue : function(){
40715         if(this.el){
40716             return String(this.el.dom.value) == String(this.inputValue ) ? this.inputValue : this.valueOff;
40717         }
40718         return this.valueOff;
40719         
40720     },
40721
40722         // private
40723     onClick : function(){ 
40724         this.setChecked(!this.checked);
40725
40726         //if(this.el.dom.checked != this.checked){
40727         //    this.setValue(this.el.dom.checked);
40728        // }
40729     },
40730
40731     /**
40732      * Sets the checked state of the checkbox.
40733      * On is always based on a string comparison between inputValue and the param.
40734      * @param {Boolean/String} value - the value to set 
40735      * @param {Boolean/String} suppressEvent - whether to suppress the checkchange event.
40736      */
40737     setValue : function(v,suppressEvent){
40738         
40739         
40740         //this.checked = (v === true || v === 'true' || v == '1' || String(v).toLowerCase() == 'on');
40741         //if(this.el && this.el.dom){
40742         //    this.el.dom.checked = this.checked;
40743         //    this.el.dom.defaultChecked = this.checked;
40744         //}
40745         this.setChecked(String(v) === String(this.inputValue), suppressEvent);
40746         //this.fireEvent("check", this, this.checked);
40747     },
40748     // private..
40749     setChecked : function(state,suppressEvent)
40750     {
40751         if (this.inSetChecked) {
40752             this.checked = state;
40753             return;
40754         }
40755         
40756     
40757         if(this.wrap){
40758             this.wrap[state ? 'addClass' : 'removeClass']('x-menu-item-checked');
40759         }
40760         this.checked = state;
40761         if(suppressEvent !== true){
40762             this.fireEvent('check', this, state);
40763         }
40764         this.inSetChecked = true;
40765         this.el.dom.value = state ? this.inputValue : this.valueOff;
40766         this.inSetChecked = false;
40767         
40768     },
40769     // handle setting of hidden value by some other method!!?!?
40770     setFromHidden: function()
40771     {
40772         if(!this.el){
40773             return;
40774         }
40775         //console.log("SET FROM HIDDEN");
40776         //alert('setFrom hidden');
40777         this.setValue(this.el.dom.value);
40778     },
40779     
40780     onDestroy : function()
40781     {
40782         if(this.viewEl){
40783             Roo.get(this.viewEl).remove();
40784         }
40785          
40786         Roo.form.Checkbox.superclass.onDestroy.call(this);
40787     }
40788
40789 });/*
40790  * Based on:
40791  * Ext JS Library 1.1.1
40792  * Copyright(c) 2006-2007, Ext JS, LLC.
40793  *
40794  * Originally Released Under LGPL - original licence link has changed is not relivant.
40795  *
40796  * Fork - LGPL
40797  * <script type="text/javascript">
40798  */
40799  
40800 /**
40801  * @class Roo.form.Radio
40802  * @extends Roo.form.Checkbox
40803  * Single radio field.  Same as Checkbox, but provided as a convenience for automatically setting the input type.
40804  * Radio grouping is handled automatically by the browser if you give each radio in a group the same name.
40805  * @constructor
40806  * Creates a new Radio
40807  * @param {Object} config Configuration options
40808  */
40809 Roo.form.Radio = function(){
40810     Roo.form.Radio.superclass.constructor.apply(this, arguments);
40811 };
40812 Roo.extend(Roo.form.Radio, Roo.form.Checkbox, {
40813     inputType: 'radio',
40814
40815     /**
40816      * If this radio is part of a group, it will return the selected value
40817      * @return {String}
40818      */
40819     getGroupValue : function(){
40820         return this.el.up('form').child('input[name='+this.el.dom.name+']:checked', true).value;
40821     },
40822     
40823     
40824     onRender : function(ct, position){
40825         Roo.form.Checkbox.superclass.onRender.call(this, ct, position);
40826         
40827         if(this.inputValue !== undefined){
40828             this.el.dom.value = this.inputValue;
40829         }
40830          
40831         this.wrap = this.el.wrap({cls: "x-form-check-wrap"});
40832         //this.wrap = this.el.wrap({cls: 'x-menu-check-item '});
40833         //var viewEl = this.wrap.createChild({ 
40834         //    tag: 'img', cls: 'x-menu-item-icon', style: 'margin: 0px;' ,src : Roo.BLANK_IMAGE_URL });
40835         //this.viewEl = viewEl;   
40836         //this.wrap.on('click', this.onClick,  this); 
40837         
40838         //this.el.on('DOMAttrModified', this.setFromHidden,  this); //ff
40839         //this.el.on('propertychange', this.setFromHidden,  this);  //ie
40840         
40841         
40842         
40843         if(this.boxLabel){
40844             this.wrap.createChild({tag: 'label', htmlFor: this.el.id, cls: 'x-form-cb-label', html: this.boxLabel});
40845         //    viewEl.on('click', this.onClick,  this); 
40846         }
40847          if(this.checked){
40848             this.el.dom.checked =   'checked' ;
40849         }
40850          
40851     } 
40852     
40853     
40854 });//<script type="text/javascript">
40855
40856 /*
40857  * Based  Ext JS Library 1.1.1
40858  * Copyright(c) 2006-2007, Ext JS, LLC.
40859  * LGPL
40860  *
40861  */
40862  
40863 /**
40864  * @class Roo.HtmlEditorCore
40865  * @extends Roo.Component
40866  * Provides a the editing component for the HTML editors in Roo. (bootstrap and Roo.form)
40867  *
40868  * any element that has display set to 'none' can cause problems in Safari and Firefox.<br/><br/>
40869  */
40870
40871 Roo.HtmlEditorCore = function(config){
40872     
40873     
40874     Roo.HtmlEditorCore.superclass.constructor.call(this, config);
40875     this.addEvents({
40876         /**
40877          * @event initialize
40878          * Fires when the editor is fully initialized (including the iframe)
40879          * @param {Roo.HtmlEditorCore} this
40880          */
40881         initialize: true,
40882         /**
40883          * @event activate
40884          * Fires when the editor is first receives the focus. Any insertion must wait
40885          * until after this event.
40886          * @param {Roo.HtmlEditorCore} this
40887          */
40888         activate: true,
40889          /**
40890          * @event beforesync
40891          * Fires before the textarea is updated with content from the editor iframe. Return false
40892          * to cancel the sync.
40893          * @param {Roo.HtmlEditorCore} this
40894          * @param {String} html
40895          */
40896         beforesync: true,
40897          /**
40898          * @event beforepush
40899          * Fires before the iframe editor is updated with content from the textarea. Return false
40900          * to cancel the push.
40901          * @param {Roo.HtmlEditorCore} this
40902          * @param {String} html
40903          */
40904         beforepush: true,
40905          /**
40906          * @event sync
40907          * Fires when the textarea is updated with content from the editor iframe.
40908          * @param {Roo.HtmlEditorCore} this
40909          * @param {String} html
40910          */
40911         sync: true,
40912          /**
40913          * @event push
40914          * Fires when the iframe editor is updated with content from the textarea.
40915          * @param {Roo.HtmlEditorCore} this
40916          * @param {String} html
40917          */
40918         push: true,
40919         
40920         /**
40921          * @event editorevent
40922          * Fires when on any editor (mouse up/down cursor movement etc.) - used for toolbar hooks.
40923          * @param {Roo.HtmlEditorCore} this
40924          */
40925         editorevent: true
40926     });
40927      
40928 };
40929
40930
40931 Roo.extend(Roo.HtmlEditorCore, Roo.Component,  {
40932
40933
40934      /**
40935      * @cfg {Roo.form.HtmlEditor|Roo.bootstrap.HtmlEditor} the owner field 
40936      */
40937     
40938     owner : false,
40939     
40940      /**
40941      * @cfg {String} resizable  's' or 'se' or 'e' - wrapps the element in a
40942      *                        Roo.resizable.
40943      */
40944     resizable : false,
40945      /**
40946      * @cfg {Number} height (in pixels)
40947      */   
40948     height: 300,
40949    /**
40950      * @cfg {Number} width (in pixels)
40951      */   
40952     width: 500,
40953     
40954     /**
40955      * @cfg {Array} stylesheets url of stylesheets. set to [] to disable stylesheets.
40956      * 
40957      */
40958     stylesheets: false,
40959     
40960     // id of frame..
40961     frameId: false,
40962     
40963     // private properties
40964     validationEvent : false,
40965     deferHeight: true,
40966     initialized : false,
40967     activated : false,
40968     sourceEditMode : false,
40969     onFocus : Roo.emptyFn,
40970     iframePad:3,
40971     hideMode:'offsets',
40972     
40973      
40974     
40975
40976     /**
40977      * Protected method that will not generally be called directly. It
40978      * is called when the editor initializes the iframe with HTML contents. Override this method if you
40979      * want to change the initialization markup of the iframe (e.g. to add stylesheets).
40980      */
40981     getDocMarkup : function(){
40982         // body styles..
40983         var st = '';
40984         Roo.log(this.stylesheets);
40985         
40986         // inherit styels from page...?? 
40987         if (this.stylesheets === false) {
40988             
40989             Roo.get(document.head).select('style').each(function(node) {
40990                 st += node.dom.outerHTML || new XMLSerializer().serializeToString(node.dom);
40991             });
40992             
40993             Roo.get(document.head).select('link').each(function(node) { 
40994                 st += node.dom.outerHTML || new XMLSerializer().serializeToString(node.dom);
40995             });
40996             
40997         } else if (!this.stylesheets.length) {
40998                 // simple..
40999                 st = '<style type="text/css">' +
41000                     'body{border:0;margin:0;padding:3px;height:98%;cursor:text;}' +
41001                    '</style>';
41002         } else {
41003             Roo.each(this.stylesheets, function(s) {
41004                 st += '<link rel="stylesheet" type="text/css" href="' + s +'" />'
41005             });
41006             
41007         }
41008         
41009         st +=  '<style type="text/css">' +
41010             'IMG { cursor: pointer } ' +
41011         '</style>';
41012
41013         
41014         return '<html><head>' + st  +
41015             //<style type="text/css">' +
41016             //'body{border:0;margin:0;padding:3px;height:98%;cursor:text;}' +
41017             //'</style>' +
41018             ' </head><body class="roo-htmleditor-body"></body></html>';
41019     },
41020
41021     // private
41022     onRender : function(ct, position)
41023     {
41024         var _t = this;
41025         //Roo.HtmlEditorCore.superclass.onRender.call(this, ct, position);
41026         this.el = this.owner.el;
41027         
41028         
41029         this.el.dom.style.border = '0 none';
41030         this.el.dom.setAttribute('tabIndex', -1);
41031         this.el.addClass('x-hidden');
41032         
41033         
41034         
41035         if(Roo.isIE){ // fix IE 1px bogus margin
41036             this.el.applyStyles('margin-top:-1px;margin-bottom:-1px;')
41037         }
41038        
41039         
41040         this.frameId = Roo.id();
41041         
41042          
41043         
41044         var iframe = this.owner.wrap.createChild({
41045             tag: 'iframe',
41046             id: this.frameId,
41047             name: this.frameId,
41048             frameBorder : 'no',
41049             'src' : Roo.SSL_SECURE_URL ? Roo.SSL_SECURE_URL  :  "javascript:false"
41050         }, this.el
41051         );
41052         
41053         
41054         this.iframe = iframe.dom;
41055
41056          this.assignDocWin();
41057         
41058         this.doc.designMode = 'on';
41059        
41060         this.doc.open();
41061         this.doc.write(this.getDocMarkup());
41062         this.doc.close();
41063
41064         
41065         var task = { // must defer to wait for browser to be ready
41066             run : function(){
41067                 //console.log("run task?" + this.doc.readyState);
41068                 this.assignDocWin();
41069                 if(this.doc.body || this.doc.readyState == 'complete'){
41070                     try {
41071                         this.doc.designMode="on";
41072                     } catch (e) {
41073                         return;
41074                     }
41075                     Roo.TaskMgr.stop(task);
41076                     this.initEditor.defer(10, this);
41077                 }
41078             },
41079             interval : 10,
41080             duration: 10000,
41081             scope: this
41082         };
41083         Roo.TaskMgr.start(task);
41084
41085         
41086          
41087     },
41088
41089     // private
41090     onResize : function(w, h)
41091     {
41092         //Roo.log('resize: ' +w + ',' + h );
41093         //Roo.HtmlEditorCore.superclass.onResize.apply(this, arguments);
41094         if(!this.iframe){
41095             return;
41096         }
41097         if(typeof w == 'number'){
41098             
41099             this.iframe.style.width = w + 'px';
41100         }
41101         if(typeof h == 'number'){
41102             
41103             this.iframe.style.height = h + 'px';
41104             if(this.doc){
41105                 (this.doc.body || this.doc.documentElement).style.height = (h - (this.iframePad*2)) + 'px';
41106             }
41107         }
41108         
41109     },
41110
41111     /**
41112      * Toggles the editor between standard and source edit mode.
41113      * @param {Boolean} sourceEdit (optional) True for source edit, false for standard
41114      */
41115     toggleSourceEdit : function(sourceEditMode){
41116         
41117         this.sourceEditMode = sourceEditMode === true;
41118         
41119         if(this.sourceEditMode){
41120  
41121             this.iframe.className = 'x-hidden';     //FIXME - what's the BS styles for these
41122             
41123         }else{
41124  
41125             this.iframe.className = '';
41126             this.deferFocus();
41127         }
41128         //this.setSize(this.owner.wrap.getSize());
41129         //this.fireEvent('editmodechange', this, this.sourceEditMode);
41130     },
41131
41132     
41133   
41134
41135     /**
41136      * Protected method that will not generally be called directly. If you need/want
41137      * custom HTML cleanup, this is the method you should override.
41138      * @param {String} html The HTML to be cleaned
41139      * return {String} The cleaned HTML
41140      */
41141     cleanHtml : function(html){
41142         html = String(html);
41143         if(html.length > 5){
41144             if(Roo.isSafari){ // strip safari nonsense
41145                 html = html.replace(/\sclass="(?:Apple-style-span|khtml-block-placeholder)"/gi, '');
41146             }
41147         }
41148         if(html == '&nbsp;'){
41149             html = '';
41150         }
41151         return html;
41152     },
41153
41154     /**
41155      * HTML Editor -> Textarea
41156      * Protected method that will not generally be called directly. Syncs the contents
41157      * of the editor iframe with the textarea.
41158      */
41159     syncValue : function(){
41160         if(this.initialized){
41161             var bd = (this.doc.body || this.doc.documentElement);
41162             //this.cleanUpPaste(); -- this is done else where and causes havoc..
41163             var html = bd.innerHTML;
41164             if(Roo.isSafari){
41165                 var bs = bd.getAttribute('style'); // Safari puts text-align styles on the body element!
41166                 var m = bs ? bs.match(/text-align:(.*?);/i) : false;
41167                 if(m && m[1]){
41168                     html = '<div style="'+m[0]+'">' + html + '</div>';
41169                 }
41170             }
41171             html = this.cleanHtml(html);
41172             // fix up the special chars.. normaly like back quotes in word...
41173             // however we do not want to do this with chinese..
41174             html = html.replace(/([\x80-\uffff])/g, function (a, b) {
41175                 var cc = b.charCodeAt();
41176                 if (
41177                     (cc >= 0x4E00 && cc < 0xA000 ) ||
41178                     (cc >= 0x3400 && cc < 0x4E00 ) ||
41179                     (cc >= 0xf900 && cc < 0xfb00 )
41180                 ) {
41181                         return b;
41182                 }
41183                 return "&#"+cc+";" 
41184             });
41185             if(this.owner.fireEvent('beforesync', this, html) !== false){
41186                 this.el.dom.value = html;
41187                 this.owner.fireEvent('sync', this, html);
41188             }
41189         }
41190     },
41191
41192     /**
41193      * Protected method that will not generally be called directly. Pushes the value of the textarea
41194      * into the iframe editor.
41195      */
41196     pushValue : function(){
41197         if(this.initialized){
41198             var v = this.el.dom.value;
41199             
41200             if(v.length < 1){
41201                 v = '&#160;';
41202             }
41203             
41204             if(this.owner.fireEvent('beforepush', this, v) !== false){
41205                 var d = (this.doc.body || this.doc.documentElement);
41206                 d.innerHTML = v;
41207                 this.cleanUpPaste();
41208                 this.el.dom.value = d.innerHTML;
41209                 this.owner.fireEvent('push', this, v);
41210             }
41211         }
41212     },
41213
41214     // private
41215     deferFocus : function(){
41216         this.focus.defer(10, this);
41217     },
41218
41219     // doc'ed in Field
41220     focus : function(){
41221         if(this.win && !this.sourceEditMode){
41222             this.win.focus();
41223         }else{
41224             this.el.focus();
41225         }
41226     },
41227     
41228     assignDocWin: function()
41229     {
41230         var iframe = this.iframe;
41231         
41232          if(Roo.isIE){
41233             this.doc = iframe.contentWindow.document;
41234             this.win = iframe.contentWindow;
41235         } else {
41236             if (!Roo.get(this.frameId)) {
41237                 return;
41238             }
41239             this.doc = (iframe.contentDocument || Roo.get(this.frameId).dom.document);
41240             this.win = Roo.get(this.frameId).dom.contentWindow;
41241         }
41242     },
41243     
41244     // private
41245     initEditor : function(){
41246         //console.log("INIT EDITOR");
41247         this.assignDocWin();
41248         
41249         
41250         
41251         this.doc.designMode="on";
41252         this.doc.open();
41253         this.doc.write(this.getDocMarkup());
41254         this.doc.close();
41255         
41256         var dbody = (this.doc.body || this.doc.documentElement);
41257         //var ss = this.el.getStyles('font-size', 'font-family', 'background-image', 'background-repeat');
41258         // this copies styles from the containing element into thsi one..
41259         // not sure why we need all of this..
41260         var ss = this.el.getStyles('font-size', 'background-image', 'background-repeat');
41261         ss['background-attachment'] = 'fixed'; // w3c
41262         dbody.bgProperties = 'fixed'; // ie
41263         Roo.DomHelper.applyStyles(dbody, ss);
41264         Roo.EventManager.on(this.doc, {
41265             //'mousedown': this.onEditorEvent,
41266             'mouseup': this.onEditorEvent,
41267             'dblclick': this.onEditorEvent,
41268             'click': this.onEditorEvent,
41269             'keyup': this.onEditorEvent,
41270             buffer:100,
41271             scope: this
41272         });
41273         if(Roo.isGecko){
41274             Roo.EventManager.on(this.doc, 'keypress', this.mozKeyPress, this);
41275         }
41276         if(Roo.isIE || Roo.isSafari || Roo.isOpera){
41277             Roo.EventManager.on(this.doc, 'keydown', this.fixKeys, this);
41278         }
41279         this.initialized = true;
41280
41281         this.owner.fireEvent('initialize', this);
41282         this.pushValue();
41283     },
41284
41285     // private
41286     onDestroy : function(){
41287         
41288         
41289         
41290         if(this.rendered){
41291             
41292             //for (var i =0; i < this.toolbars.length;i++) {
41293             //    // fixme - ask toolbars for heights?
41294             //    this.toolbars[i].onDestroy();
41295            // }
41296             
41297             //this.wrap.dom.innerHTML = '';
41298             //this.wrap.remove();
41299         }
41300     },
41301
41302     // private
41303     onFirstFocus : function(){
41304         
41305         this.assignDocWin();
41306         
41307         
41308         this.activated = true;
41309          
41310     
41311         if(Roo.isGecko){ // prevent silly gecko errors
41312             this.win.focus();
41313             var s = this.win.getSelection();
41314             if(!s.focusNode || s.focusNode.nodeType != 3){
41315                 var r = s.getRangeAt(0);
41316                 r.selectNodeContents((this.doc.body || this.doc.documentElement));
41317                 r.collapse(true);
41318                 this.deferFocus();
41319             }
41320             try{
41321                 this.execCmd('useCSS', true);
41322                 this.execCmd('styleWithCSS', false);
41323             }catch(e){}
41324         }
41325         this.owner.fireEvent('activate', this);
41326     },
41327
41328     // private
41329     adjustFont: function(btn){
41330         var adjust = btn.cmd == 'increasefontsize' ? 1 : -1;
41331         //if(Roo.isSafari){ // safari
41332         //    adjust *= 2;
41333        // }
41334         var v = parseInt(this.doc.queryCommandValue('FontSize')|| 3, 10);
41335         if(Roo.isSafari){ // safari
41336             var sm = { 10 : 1, 13: 2, 16:3, 18:4, 24: 5, 32:6, 48: 7 };
41337             v =  (v < 10) ? 10 : v;
41338             v =  (v > 48) ? 48 : v;
41339             v = typeof(sm[v]) == 'undefined' ? 1 : sm[v];
41340             
41341         }
41342         
41343         
41344         v = Math.max(1, v+adjust);
41345         
41346         this.execCmd('FontSize', v  );
41347     },
41348
41349     onEditorEvent : function(e){
41350         this.owner.fireEvent('editorevent', this, e);
41351       //  this.updateToolbar();
41352         this.syncValue(); //we can not sync so often.. sync cleans, so this breaks stuff
41353     },
41354
41355     insertTag : function(tg)
41356     {
41357         // could be a bit smarter... -> wrap the current selected tRoo..
41358         if (tg.toLowerCase() == 'span' || tg.toLowerCase() == 'code') {
41359             
41360             range = this.createRange(this.getSelection());
41361             var wrappingNode = this.doc.createElement(tg.toLowerCase());
41362             wrappingNode.appendChild(range.extractContents());
41363             range.insertNode(wrappingNode);
41364
41365             return;
41366             
41367             
41368             
41369         }
41370         this.execCmd("formatblock",   tg);
41371         
41372     },
41373     
41374     insertText : function(txt)
41375     {
41376         
41377         
41378         var range = this.createRange();
41379         range.deleteContents();
41380                //alert(Sender.getAttribute('label'));
41381                
41382         range.insertNode(this.doc.createTextNode(txt));
41383     } ,
41384     
41385      
41386
41387     /**
41388      * Executes a Midas editor command on the editor document and performs necessary focus and
41389      * toolbar updates. <b>This should only be called after the editor is initialized.</b>
41390      * @param {String} cmd The Midas command
41391      * @param {String/Boolean} value (optional) The value to pass to the command (defaults to null)
41392      */
41393     relayCmd : function(cmd, value){
41394         this.win.focus();
41395         this.execCmd(cmd, value);
41396         this.owner.fireEvent('editorevent', this);
41397         //this.updateToolbar();
41398         this.owner.deferFocus();
41399     },
41400
41401     /**
41402      * Executes a Midas editor command directly on the editor document.
41403      * For visual commands, you should use {@link #relayCmd} instead.
41404      * <b>This should only be called after the editor is initialized.</b>
41405      * @param {String} cmd The Midas command
41406      * @param {String/Boolean} value (optional) The value to pass to the command (defaults to null)
41407      */
41408     execCmd : function(cmd, value){
41409         this.doc.execCommand(cmd, false, value === undefined ? null : value);
41410         this.syncValue();
41411     },
41412  
41413  
41414    
41415     /**
41416      * Inserts the passed text at the current cursor position. Note: the editor must be initialized and activated
41417      * to insert tRoo.
41418      * @param {String} text | dom node.. 
41419      */
41420     insertAtCursor : function(text)
41421     {
41422         
41423         
41424         
41425         if(!this.activated){
41426             return;
41427         }
41428         /*
41429         if(Roo.isIE){
41430             this.win.focus();
41431             var r = this.doc.selection.createRange();
41432             if(r){
41433                 r.collapse(true);
41434                 r.pasteHTML(text);
41435                 this.syncValue();
41436                 this.deferFocus();
41437             
41438             }
41439             return;
41440         }
41441         */
41442         if(Roo.isGecko || Roo.isOpera || Roo.isSafari){
41443             this.win.focus();
41444             
41445             
41446             // from jquery ui (MIT licenced)
41447             var range, node;
41448             var win = this.win;
41449             
41450             if (win.getSelection && win.getSelection().getRangeAt) {
41451                 range = win.getSelection().getRangeAt(0);
41452                 node = typeof(text) == 'string' ? range.createContextualFragment(text) : text;
41453                 range.insertNode(node);
41454             } else if (win.document.selection && win.document.selection.createRange) {
41455                 // no firefox support
41456                 var txt = typeof(text) == 'string' ? text : text.outerHTML;
41457                 win.document.selection.createRange().pasteHTML(txt);
41458             } else {
41459                 // no firefox support
41460                 var txt = typeof(text) == 'string' ? text : text.outerHTML;
41461                 this.execCmd('InsertHTML', txt);
41462             } 
41463             
41464             this.syncValue();
41465             
41466             this.deferFocus();
41467         }
41468     },
41469  // private
41470     mozKeyPress : function(e){
41471         if(e.ctrlKey){
41472             var c = e.getCharCode(), cmd;
41473           
41474             if(c > 0){
41475                 c = String.fromCharCode(c).toLowerCase();
41476                 switch(c){
41477                     case 'b':
41478                         cmd = 'bold';
41479                         break;
41480                     case 'i':
41481                         cmd = 'italic';
41482                         break;
41483                     
41484                     case 'u':
41485                         cmd = 'underline';
41486                         break;
41487                     
41488                     case 'v':
41489                         this.cleanUpPaste.defer(100, this);
41490                         return;
41491                         
41492                 }
41493                 if(cmd){
41494                     this.win.focus();
41495                     this.execCmd(cmd);
41496                     this.deferFocus();
41497                     e.preventDefault();
41498                 }
41499                 
41500             }
41501         }
41502     },
41503
41504     // private
41505     fixKeys : function(){ // load time branching for fastest keydown performance
41506         if(Roo.isIE){
41507             return function(e){
41508                 var k = e.getKey(), r;
41509                 if(k == e.TAB){
41510                     e.stopEvent();
41511                     r = this.doc.selection.createRange();
41512                     if(r){
41513                         r.collapse(true);
41514                         r.pasteHTML('&#160;&#160;&#160;&#160;');
41515                         this.deferFocus();
41516                     }
41517                     return;
41518                 }
41519                 
41520                 if(k == e.ENTER){
41521                     r = this.doc.selection.createRange();
41522                     if(r){
41523                         var target = r.parentElement();
41524                         if(!target || target.tagName.toLowerCase() != 'li'){
41525                             e.stopEvent();
41526                             r.pasteHTML('<br />');
41527                             r.collapse(false);
41528                             r.select();
41529                         }
41530                     }
41531                 }
41532                 if (String.fromCharCode(k).toLowerCase() == 'v') { // paste
41533                     this.cleanUpPaste.defer(100, this);
41534                     return;
41535                 }
41536                 
41537                 
41538             };
41539         }else if(Roo.isOpera){
41540             return function(e){
41541                 var k = e.getKey();
41542                 if(k == e.TAB){
41543                     e.stopEvent();
41544                     this.win.focus();
41545                     this.execCmd('InsertHTML','&#160;&#160;&#160;&#160;');
41546                     this.deferFocus();
41547                 }
41548                 if (String.fromCharCode(k).toLowerCase() == 'v') { // paste
41549                     this.cleanUpPaste.defer(100, this);
41550                     return;
41551                 }
41552                 
41553             };
41554         }else if(Roo.isSafari){
41555             return function(e){
41556                 var k = e.getKey();
41557                 
41558                 if(k == e.TAB){
41559                     e.stopEvent();
41560                     this.execCmd('InsertText','\t');
41561                     this.deferFocus();
41562                     return;
41563                 }
41564                if (String.fromCharCode(k).toLowerCase() == 'v') { // paste
41565                     this.cleanUpPaste.defer(100, this);
41566                     return;
41567                 }
41568                 
41569              };
41570         }
41571     }(),
41572     
41573     getAllAncestors: function()
41574     {
41575         var p = this.getSelectedNode();
41576         var a = [];
41577         if (!p) {
41578             a.push(p); // push blank onto stack..
41579             p = this.getParentElement();
41580         }
41581         
41582         
41583         while (p && (p.nodeType == 1) && (p.tagName.toLowerCase() != 'body')) {
41584             a.push(p);
41585             p = p.parentNode;
41586         }
41587         a.push(this.doc.body);
41588         return a;
41589     },
41590     lastSel : false,
41591     lastSelNode : false,
41592     
41593     
41594     getSelection : function() 
41595     {
41596         this.assignDocWin();
41597         return Roo.isIE ? this.doc.selection : this.win.getSelection();
41598     },
41599     
41600     getSelectedNode: function() 
41601     {
41602         // this may only work on Gecko!!!
41603         
41604         // should we cache this!!!!
41605         
41606         
41607         
41608          
41609         var range = this.createRange(this.getSelection()).cloneRange();
41610         
41611         if (Roo.isIE) {
41612             var parent = range.parentElement();
41613             while (true) {
41614                 var testRange = range.duplicate();
41615                 testRange.moveToElementText(parent);
41616                 if (testRange.inRange(range)) {
41617                     break;
41618                 }
41619                 if ((parent.nodeType != 1) || (parent.tagName.toLowerCase() == 'body')) {
41620                     break;
41621                 }
41622                 parent = parent.parentElement;
41623             }
41624             return parent;
41625         }
41626         
41627         // is ancestor a text element.
41628         var ac =  range.commonAncestorContainer;
41629         if (ac.nodeType == 3) {
41630             ac = ac.parentNode;
41631         }
41632         
41633         var ar = ac.childNodes;
41634          
41635         var nodes = [];
41636         var other_nodes = [];
41637         var has_other_nodes = false;
41638         for (var i=0;i<ar.length;i++) {
41639             if ((ar[i].nodeType == 3) && (!ar[i].data.length)) { // empty text ? 
41640                 continue;
41641             }
41642             // fullly contained node.
41643             
41644             if (this.rangeIntersectsNode(range,ar[i]) && this.rangeCompareNode(range,ar[i]) == 3) {
41645                 nodes.push(ar[i]);
41646                 continue;
41647             }
41648             
41649             // probably selected..
41650             if ((ar[i].nodeType == 1) && this.rangeIntersectsNode(range,ar[i]) && (this.rangeCompareNode(range,ar[i]) > 0)) {
41651                 other_nodes.push(ar[i]);
41652                 continue;
41653             }
41654             // outer..
41655             if (!this.rangeIntersectsNode(range,ar[i])|| (this.rangeCompareNode(range,ar[i]) == 0))  {
41656                 continue;
41657             }
41658             
41659             
41660             has_other_nodes = true;
41661         }
41662         if (!nodes.length && other_nodes.length) {
41663             nodes= other_nodes;
41664         }
41665         if (has_other_nodes || !nodes.length || (nodes.length > 1)) {
41666             return false;
41667         }
41668         
41669         return nodes[0];
41670     },
41671     createRange: function(sel)
41672     {
41673         // this has strange effects when using with 
41674         // top toolbar - not sure if it's a great idea.
41675         //this.editor.contentWindow.focus();
41676         if (typeof sel != "undefined") {
41677             try {
41678                 return sel.getRangeAt ? sel.getRangeAt(0) : sel.createRange();
41679             } catch(e) {
41680                 return this.doc.createRange();
41681             }
41682         } else {
41683             return this.doc.createRange();
41684         }
41685     },
41686     getParentElement: function()
41687     {
41688         
41689         this.assignDocWin();
41690         var sel = Roo.isIE ? this.doc.selection : this.win.getSelection();
41691         
41692         var range = this.createRange(sel);
41693          
41694         try {
41695             var p = range.commonAncestorContainer;
41696             while (p.nodeType == 3) { // text node
41697                 p = p.parentNode;
41698             }
41699             return p;
41700         } catch (e) {
41701             return null;
41702         }
41703     
41704     },
41705     /***
41706      *
41707      * Range intersection.. the hard stuff...
41708      *  '-1' = before
41709      *  '0' = hits..
41710      *  '1' = after.
41711      *         [ -- selected range --- ]
41712      *   [fail]                        [fail]
41713      *
41714      *    basically..
41715      *      if end is before start or  hits it. fail.
41716      *      if start is after end or hits it fail.
41717      *
41718      *   if either hits (but other is outside. - then it's not 
41719      *   
41720      *    
41721      **/
41722     
41723     
41724     // @see http://www.thismuchiknow.co.uk/?p=64.
41725     rangeIntersectsNode : function(range, node)
41726     {
41727         var nodeRange = node.ownerDocument.createRange();
41728         try {
41729             nodeRange.selectNode(node);
41730         } catch (e) {
41731             nodeRange.selectNodeContents(node);
41732         }
41733     
41734         var rangeStartRange = range.cloneRange();
41735         rangeStartRange.collapse(true);
41736     
41737         var rangeEndRange = range.cloneRange();
41738         rangeEndRange.collapse(false);
41739     
41740         var nodeStartRange = nodeRange.cloneRange();
41741         nodeStartRange.collapse(true);
41742     
41743         var nodeEndRange = nodeRange.cloneRange();
41744         nodeEndRange.collapse(false);
41745     
41746         return rangeStartRange.compareBoundaryPoints(
41747                  Range.START_TO_START, nodeEndRange) == -1 &&
41748                rangeEndRange.compareBoundaryPoints(
41749                  Range.START_TO_START, nodeStartRange) == 1;
41750         
41751          
41752     },
41753     rangeCompareNode : function(range, node)
41754     {
41755         var nodeRange = node.ownerDocument.createRange();
41756         try {
41757             nodeRange.selectNode(node);
41758         } catch (e) {
41759             nodeRange.selectNodeContents(node);
41760         }
41761         
41762         
41763         range.collapse(true);
41764     
41765         nodeRange.collapse(true);
41766      
41767         var ss = range.compareBoundaryPoints( Range.START_TO_START, nodeRange);
41768         var ee = range.compareBoundaryPoints(  Range.END_TO_END, nodeRange);
41769          
41770         //Roo.log(node.tagName + ': ss='+ss +', ee='+ee)
41771         
41772         var nodeIsBefore   =  ss == 1;
41773         var nodeIsAfter    = ee == -1;
41774         
41775         if (nodeIsBefore && nodeIsAfter)
41776             return 0; // outer
41777         if (!nodeIsBefore && nodeIsAfter)
41778             return 1; //right trailed.
41779         
41780         if (nodeIsBefore && !nodeIsAfter)
41781             return 2;  // left trailed.
41782         // fully contined.
41783         return 3;
41784     },
41785
41786     // private? - in a new class?
41787     cleanUpPaste :  function()
41788     {
41789         // cleans up the whole document..
41790          Roo.log('cleanuppaste');
41791         this.cleanUpChildren(this.doc.body);
41792         var clean = this.cleanWordChars(this.doc.body.innerHTML);
41793         if (clean != this.doc.body.innerHTML) {
41794             this.doc.body.innerHTML = clean;
41795         }
41796         
41797     },
41798     
41799     cleanWordChars : function(input) {// change the chars to hex code
41800         var he = Roo.HtmlEditorCore;
41801         
41802         var output = input;
41803         Roo.each(he.swapCodes, function(sw) { 
41804             var swapper = new RegExp("\\u" + sw[0].toString(16), "g"); // hex codes
41805             
41806             output = output.replace(swapper, sw[1]);
41807         });
41808         
41809         return output;
41810     },
41811     
41812     
41813     cleanUpChildren : function (n)
41814     {
41815         if (!n.childNodes.length) {
41816             return;
41817         }
41818         for (var i = n.childNodes.length-1; i > -1 ; i--) {
41819            this.cleanUpChild(n.childNodes[i]);
41820         }
41821     },
41822     
41823     
41824         
41825     
41826     cleanUpChild : function (node)
41827     {
41828         var ed = this;
41829         //console.log(node);
41830         if (node.nodeName == "#text") {
41831             // clean up silly Windows -- stuff?
41832             return; 
41833         }
41834         if (node.nodeName == "#comment") {
41835             node.parentNode.removeChild(node);
41836             // clean up silly Windows -- stuff?
41837             return; 
41838         }
41839         
41840         if (Roo.HtmlEditorCore.black.indexOf(node.tagName.toLowerCase()) > -1) {
41841             // remove node.
41842             node.parentNode.removeChild(node);
41843             return;
41844             
41845         }
41846         
41847         var remove_keep_children= Roo.HtmlEditorCore.remove.indexOf(node.tagName.toLowerCase()) > -1;
41848         
41849         // remove <a name=....> as rendering on yahoo mailer is borked with this.
41850         // this will have to be flaged elsewhere - perhaps ablack=name... on the mailer..
41851         
41852         //if (node.tagName.toLowerCase() == 'a' && !node.hasAttribute('href')) {
41853         //    remove_keep_children = true;
41854         //}
41855         
41856         if (remove_keep_children) {
41857             this.cleanUpChildren(node);
41858             // inserts everything just before this node...
41859             while (node.childNodes.length) {
41860                 var cn = node.childNodes[0];
41861                 node.removeChild(cn);
41862                 node.parentNode.insertBefore(cn, node);
41863             }
41864             node.parentNode.removeChild(node);
41865             return;
41866         }
41867         
41868         if (!node.attributes || !node.attributes.length) {
41869             this.cleanUpChildren(node);
41870             return;
41871         }
41872         
41873         function cleanAttr(n,v)
41874         {
41875             
41876             if (v.match(/^\./) || v.match(/^\//)) {
41877                 return;
41878             }
41879             if (v.match(/^(http|https):\/\//) || v.match(/^mailto:/)) {
41880                 return;
41881             }
41882             if (v.match(/^#/)) {
41883                 return;
41884             }
41885 //            Roo.log("(REMOVE TAG)"+ node.tagName +'.' + n + '=' + v);
41886             node.removeAttribute(n);
41887             
41888         }
41889         
41890         function cleanStyle(n,v)
41891         {
41892             if (v.match(/expression/)) { //XSS?? should we even bother..
41893                 node.removeAttribute(n);
41894                 return;
41895             }
41896             var cwhite = typeof(ed.cwhite) == 'undefined' ? Roo.HtmlEditorCore.cwhite : ed.cwhite;
41897             var cblack = typeof(ed.cblack) == 'undefined' ? Roo.HtmlEditorCore.cblack : ed.cblack;
41898             
41899             
41900             var parts = v.split(/;/);
41901             var clean = [];
41902             
41903             Roo.each(parts, function(p) {
41904                 p = p.replace(/^\s+/g,'').replace(/\s+$/g,'');
41905                 if (!p.length) {
41906                     return true;
41907                 }
41908                 var l = p.split(':').shift().replace(/\s+/g,'');
41909                 l = l.replace(/^\s+/g,'').replace(/\s+$/g,'');
41910                 
41911                 
41912                 if ( cblack.indexOf(l) > -1) {
41913 //                    Roo.log('(REMOVE CSS)' + node.tagName +'.' + n + ':'+l + '=' + v);
41914                     //node.removeAttribute(n);
41915                     return true;
41916                 }
41917                 //Roo.log()
41918                 // only allow 'c whitelisted system attributes'
41919                 if ( cwhite.length &&  cwhite.indexOf(l) < 0) {
41920 //                    Roo.log('(REMOVE CSS)' + node.tagName +'.' + n + ':'+l + '=' + v);
41921                     //node.removeAttribute(n);
41922                     return true;
41923                 }
41924                 
41925                 
41926                  
41927                 
41928                 clean.push(p);
41929                 return true;
41930             });
41931             if (clean.length) { 
41932                 node.setAttribute(n, clean.join(';'));
41933             } else {
41934                 node.removeAttribute(n);
41935             }
41936             
41937         }
41938         
41939         
41940         for (var i = node.attributes.length-1; i > -1 ; i--) {
41941             var a = node.attributes[i];
41942             //console.log(a);
41943             
41944             if (a.name.toLowerCase().substr(0,2)=='on')  {
41945                 node.removeAttribute(a.name);
41946                 continue;
41947             }
41948             if (Roo.HtmlEditorCore.ablack.indexOf(a.name.toLowerCase()) > -1) {
41949                 node.removeAttribute(a.name);
41950                 continue;
41951             }
41952             if (Roo.HtmlEditorCore.aclean.indexOf(a.name.toLowerCase()) > -1) {
41953                 cleanAttr(a.name,a.value); // fixme..
41954                 continue;
41955             }
41956             if (a.name == 'style') {
41957                 cleanStyle(a.name,a.value);
41958                 continue;
41959             }
41960             /// clean up MS crap..
41961             // tecnically this should be a list of valid class'es..
41962             
41963             
41964             if (a.name == 'class') {
41965                 if (a.value.match(/^Mso/)) {
41966                     node.className = '';
41967                 }
41968                 
41969                 if (a.value.match(/body/)) {
41970                     node.className = '';
41971                 }
41972                 continue;
41973             }
41974             
41975             // style cleanup!?
41976             // class cleanup?
41977             
41978         }
41979         
41980         
41981         this.cleanUpChildren(node);
41982         
41983         
41984     }
41985     
41986     
41987     // hide stuff that is not compatible
41988     /**
41989      * @event blur
41990      * @hide
41991      */
41992     /**
41993      * @event change
41994      * @hide
41995      */
41996     /**
41997      * @event focus
41998      * @hide
41999      */
42000     /**
42001      * @event specialkey
42002      * @hide
42003      */
42004     /**
42005      * @cfg {String} fieldClass @hide
42006      */
42007     /**
42008      * @cfg {String} focusClass @hide
42009      */
42010     /**
42011      * @cfg {String} autoCreate @hide
42012      */
42013     /**
42014      * @cfg {String} inputType @hide
42015      */
42016     /**
42017      * @cfg {String} invalidClass @hide
42018      */
42019     /**
42020      * @cfg {String} invalidText @hide
42021      */
42022     /**
42023      * @cfg {String} msgFx @hide
42024      */
42025     /**
42026      * @cfg {String} validateOnBlur @hide
42027      */
42028 });
42029
42030 Roo.HtmlEditorCore.white = [
42031         'area', 'br', 'img', 'input', 'hr', 'wbr',
42032         
42033        'address', 'blockquote', 'center', 'dd',      'dir',       'div', 
42034        'dl',      'dt',         'h1',     'h2',      'h3',        'h4', 
42035        'h5',      'h6',         'hr',     'isindex', 'listing',   'marquee', 
42036        'menu',    'multicol',   'ol',     'p',       'plaintext', 'pre', 
42037        'table',   'ul',         'xmp', 
42038        
42039        'caption', 'col', 'colgroup', 'tbody', 'td', 'tfoot', 'th', 
42040       'thead',   'tr', 
42041      
42042       'dir', 'menu', 'ol', 'ul', 'dl',
42043        
42044       'embed',  'object'
42045 ];
42046
42047
42048 Roo.HtmlEditorCore.black = [
42049     //    'embed',  'object', // enable - backend responsiblity to clean thiese
42050         'applet', // 
42051         'base',   'basefont', 'bgsound', 'blink',  'body', 
42052         'frame',  'frameset', 'head',    'html',   'ilayer', 
42053         'iframe', 'layer',  'link',     'meta',    'object',   
42054         'script', 'style' ,'title',  'xml' // clean later..
42055 ];
42056 Roo.HtmlEditorCore.clean = [
42057     'script', 'style', 'title', 'xml'
42058 ];
42059 Roo.HtmlEditorCore.remove = [
42060     'font'
42061 ];
42062 // attributes..
42063
42064 Roo.HtmlEditorCore.ablack = [
42065     'on'
42066 ];
42067     
42068 Roo.HtmlEditorCore.aclean = [ 
42069     'action', 'background', 'codebase', 'dynsrc', 'href', 'lowsrc' 
42070 ];
42071
42072 // protocols..
42073 Roo.HtmlEditorCore.pwhite= [
42074         'http',  'https',  'mailto'
42075 ];
42076
42077 // white listed style attributes.
42078 Roo.HtmlEditorCore.cwhite= [
42079       //  'text-align', /// default is to allow most things..
42080       
42081          
42082 //        'font-size'//??
42083 ];
42084
42085 // black listed style attributes.
42086 Roo.HtmlEditorCore.cblack= [
42087       //  'font-size' -- this can be set by the project 
42088 ];
42089
42090
42091 Roo.HtmlEditorCore.swapCodes   =[ 
42092     [    8211, "--" ], 
42093     [    8212, "--" ], 
42094     [    8216,  "'" ],  
42095     [    8217, "'" ],  
42096     [    8220, '"' ],  
42097     [    8221, '"' ],  
42098     [    8226, "*" ],  
42099     [    8230, "..." ]
42100 ]; 
42101
42102     //<script type="text/javascript">
42103
42104 /*
42105  * Ext JS Library 1.1.1
42106  * Copyright(c) 2006-2007, Ext JS, LLC.
42107  * Licence LGPL
42108  * 
42109  */
42110  
42111  
42112 Roo.form.HtmlEditor = function(config){
42113     
42114     
42115     
42116     Roo.form.HtmlEditor.superclass.constructor.call(this, config);
42117     
42118     if (!this.toolbars) {
42119         this.toolbars = [];
42120     }
42121     this.editorcore = new Roo.HtmlEditorCore(Roo.apply({ owner : this} , config));
42122     
42123     
42124 };
42125
42126 /**
42127  * @class Roo.form.HtmlEditor
42128  * @extends Roo.form.Field
42129  * Provides a lightweight HTML Editor component.
42130  *
42131  * This has been tested on Fireforx / Chrome.. IE may not be so great..
42132  * 
42133  * <br><br><b>Note: The focus/blur and validation marking functionality inherited from Ext.form.Field is NOT
42134  * supported by this editor.</b><br/><br/>
42135  * An Editor is a sensitive component that can't be used in all spots standard fields can be used. Putting an Editor within
42136  * any element that has display set to 'none' can cause problems in Safari and Firefox.<br/><br/>
42137  */
42138 Roo.extend(Roo.form.HtmlEditor, Roo.form.Field, {
42139       /**
42140      * @cfg {Array} toolbars Array of toolbars. - defaults to just the Standard one
42141      */
42142     toolbars : false,
42143    
42144      /**
42145      * @cfg {String} resizable  's' or 'se' or 'e' - wrapps the element in a
42146      *                        Roo.resizable.
42147      */
42148     resizable : false,
42149      /**
42150      * @cfg {Number} height (in pixels)
42151      */   
42152     height: 300,
42153    /**
42154      * @cfg {Number} width (in pixels)
42155      */   
42156     width: 500,
42157     
42158     /**
42159      * @cfg {Array} stylesheets url of stylesheets. set to [] to disable stylesheets.
42160      * 
42161      */
42162     stylesheets: false,
42163     
42164     // id of frame..
42165     frameId: false,
42166     
42167     // private properties
42168     validationEvent : false,
42169     deferHeight: true,
42170     initialized : false,
42171     activated : false,
42172     
42173     onFocus : Roo.emptyFn,
42174     iframePad:3,
42175     hideMode:'offsets',
42176     
42177     defaultAutoCreate : { // modified by initCompnoent..
42178         tag: "textarea",
42179         style:"width:500px;height:300px;",
42180         autocomplete: "off"
42181     },
42182
42183     // private
42184     initComponent : function(){
42185         this.addEvents({
42186             /**
42187              * @event initialize
42188              * Fires when the editor is fully initialized (including the iframe)
42189              * @param {HtmlEditor} this
42190              */
42191             initialize: true,
42192             /**
42193              * @event activate
42194              * Fires when the editor is first receives the focus. Any insertion must wait
42195              * until after this event.
42196              * @param {HtmlEditor} this
42197              */
42198             activate: true,
42199              /**
42200              * @event beforesync
42201              * Fires before the textarea is updated with content from the editor iframe. Return false
42202              * to cancel the sync.
42203              * @param {HtmlEditor} this
42204              * @param {String} html
42205              */
42206             beforesync: true,
42207              /**
42208              * @event beforepush
42209              * Fires before the iframe editor is updated with content from the textarea. Return false
42210              * to cancel the push.
42211              * @param {HtmlEditor} this
42212              * @param {String} html
42213              */
42214             beforepush: true,
42215              /**
42216              * @event sync
42217              * Fires when the textarea is updated with content from the editor iframe.
42218              * @param {HtmlEditor} this
42219              * @param {String} html
42220              */
42221             sync: true,
42222              /**
42223              * @event push
42224              * Fires when the iframe editor is updated with content from the textarea.
42225              * @param {HtmlEditor} this
42226              * @param {String} html
42227              */
42228             push: true,
42229              /**
42230              * @event editmodechange
42231              * Fires when the editor switches edit modes
42232              * @param {HtmlEditor} this
42233              * @param {Boolean} sourceEdit True if source edit, false if standard editing.
42234              */
42235             editmodechange: true,
42236             /**
42237              * @event editorevent
42238              * Fires when on any editor (mouse up/down cursor movement etc.) - used for toolbar hooks.
42239              * @param {HtmlEditor} this
42240              */
42241             editorevent: true,
42242             /**
42243              * @event firstfocus
42244              * Fires when on first focus - needed by toolbars..
42245              * @param {HtmlEditor} this
42246              */
42247             firstfocus: true,
42248             /**
42249              * @event autosave
42250              * Auto save the htmlEditor value as a file into Events
42251              * @param {HtmlEditor} this
42252              */
42253             autosave: true,
42254             /**
42255              * @event savedpreview
42256              * preview the saved version of htmlEditor
42257              * @param {HtmlEditor} this
42258              */
42259             savedpreview: true
42260         });
42261         this.defaultAutoCreate =  {
42262             tag: "textarea",
42263             style:'width: ' + this.width + 'px;height: ' + this.height + 'px;',
42264             autocomplete: "off"
42265         };
42266     },
42267
42268     /**
42269      * Protected method that will not generally be called directly. It
42270      * is called when the editor creates its toolbar. Override this method if you need to
42271      * add custom toolbar buttons.
42272      * @param {HtmlEditor} editor
42273      */
42274     createToolbar : function(editor){
42275         Roo.log("create toolbars");
42276         if (!editor.toolbars || !editor.toolbars.length) {
42277             editor.toolbars = [ new Roo.form.HtmlEditor.ToolbarStandard() ]; // can be empty?
42278         }
42279         
42280         for (var i =0 ; i < editor.toolbars.length;i++) {
42281             editor.toolbars[i] = Roo.factory(
42282                     typeof(editor.toolbars[i]) == 'string' ?
42283                         { xtype: editor.toolbars[i]} : editor.toolbars[i],
42284                 Roo.form.HtmlEditor);
42285             editor.toolbars[i].init(editor);
42286         }
42287          
42288         
42289     },
42290
42291      
42292     // private
42293     onRender : function(ct, position)
42294     {
42295         var _t = this;
42296         Roo.form.HtmlEditor.superclass.onRender.call(this, ct, position);
42297         
42298         this.wrap = this.el.wrap({
42299             cls:'x-html-editor-wrap', cn:{cls:'x-html-editor-tb'}
42300         });
42301         
42302         this.editorcore.onRender(ct, position);
42303          
42304         if (this.resizable) {
42305             this.resizeEl = new Roo.Resizable(this.wrap, {
42306                 pinned : true,
42307                 wrap: true,
42308                 dynamic : true,
42309                 minHeight : this.height,
42310                 height: this.height,
42311                 handles : this.resizable,
42312                 width: this.width,
42313                 listeners : {
42314                     resize : function(r, w, h) {
42315                         _t.onResize(w,h); // -something
42316                     }
42317                 }
42318             });
42319             
42320         }
42321         this.createToolbar(this);
42322        
42323         
42324         if(!this.width){
42325             this.setSize(this.wrap.getSize());
42326         }
42327         if (this.resizeEl) {
42328             this.resizeEl.resizeTo.defer(100, this.resizeEl,[ this.width,this.height ] );
42329             // should trigger onReize..
42330         }
42331         
42332 //        if(this.autosave && this.w){
42333 //            this.autoSaveFn = setInterval(this.autosave, 1000);
42334 //        }
42335     },
42336
42337     // private
42338     onResize : function(w, h)
42339     {
42340         //Roo.log('resize: ' +w + ',' + h );
42341         Roo.form.HtmlEditor.superclass.onResize.apply(this, arguments);
42342         var ew = false;
42343         var eh = false;
42344         
42345         if(this.el ){
42346             if(typeof w == 'number'){
42347                 var aw = w - this.wrap.getFrameWidth('lr');
42348                 this.el.setWidth(this.adjustWidth('textarea', aw));
42349                 ew = aw;
42350             }
42351             if(typeof h == 'number'){
42352                 var tbh = 0;
42353                 for (var i =0; i < this.toolbars.length;i++) {
42354                     // fixme - ask toolbars for heights?
42355                     tbh += this.toolbars[i].tb.el.getHeight();
42356                     if (this.toolbars[i].footer) {
42357                         tbh += this.toolbars[i].footer.el.getHeight();
42358                     }
42359                 }
42360                 
42361                 
42362                 
42363                 
42364                 var ah = h - this.wrap.getFrameWidth('tb') - tbh;// this.tb.el.getHeight();
42365                 ah -= 5; // knock a few pixes off for look..
42366                 this.el.setHeight(this.adjustWidth('textarea', ah));
42367                 var eh = ah;
42368             }
42369         }
42370         Roo.log('onResize:' + [w,h,ew,eh].join(',') );
42371         this.editorcore.onResize(ew,eh);
42372         
42373     },
42374
42375     /**
42376      * Toggles the editor between standard and source edit mode.
42377      * @param {Boolean} sourceEdit (optional) True for source edit, false for standard
42378      */
42379     toggleSourceEdit : function(sourceEditMode)
42380     {
42381         this.editorcore.toggleSourceEdit(sourceEditMode);
42382         
42383         if(this.editorcore.sourceEditMode){
42384             Roo.log('editor - showing textarea');
42385             
42386 //            Roo.log('in');
42387 //            Roo.log(this.syncValue());
42388             this.editorcore.syncValue();
42389             this.el.removeClass('x-hidden');
42390             this.el.dom.removeAttribute('tabIndex');
42391             this.el.focus();
42392         }else{
42393             Roo.log('editor - hiding textarea');
42394 //            Roo.log('out')
42395 //            Roo.log(this.pushValue()); 
42396             this.editorcore.pushValue();
42397             
42398             this.el.addClass('x-hidden');
42399             this.el.dom.setAttribute('tabIndex', -1);
42400             //this.deferFocus();
42401         }
42402          
42403         this.setSize(this.wrap.getSize());
42404         this.fireEvent('editmodechange', this, this.editorcore.sourceEditMode);
42405     },
42406  
42407     // private (for BoxComponent)
42408     adjustSize : Roo.BoxComponent.prototype.adjustSize,
42409
42410     // private (for BoxComponent)
42411     getResizeEl : function(){
42412         return this.wrap;
42413     },
42414
42415     // private (for BoxComponent)
42416     getPositionEl : function(){
42417         return this.wrap;
42418     },
42419
42420     // private
42421     initEvents : function(){
42422         this.originalValue = this.getValue();
42423     },
42424
42425     /**
42426      * Overridden and disabled. The editor element does not support standard valid/invalid marking. @hide
42427      * @method
42428      */
42429     markInvalid : Roo.emptyFn,
42430     /**
42431      * Overridden and disabled. The editor element does not support standard valid/invalid marking. @hide
42432      * @method
42433      */
42434     clearInvalid : Roo.emptyFn,
42435
42436     setValue : function(v){
42437         Roo.form.HtmlEditor.superclass.setValue.call(this, v);
42438         this.editorcore.pushValue();
42439     },
42440
42441      
42442     // private
42443     deferFocus : function(){
42444         this.focus.defer(10, this);
42445     },
42446
42447     // doc'ed in Field
42448     focus : function(){
42449         this.editorcore.focus();
42450         
42451     },
42452       
42453
42454     // private
42455     onDestroy : function(){
42456         
42457         
42458         
42459         if(this.rendered){
42460             
42461             for (var i =0; i < this.toolbars.length;i++) {
42462                 // fixme - ask toolbars for heights?
42463                 this.toolbars[i].onDestroy();
42464             }
42465             
42466             this.wrap.dom.innerHTML = '';
42467             this.wrap.remove();
42468         }
42469     },
42470
42471     // private
42472     onFirstFocus : function(){
42473         //Roo.log("onFirstFocus");
42474         this.editorcore.onFirstFocus();
42475          for (var i =0; i < this.toolbars.length;i++) {
42476             this.toolbars[i].onFirstFocus();
42477         }
42478         
42479     },
42480     
42481     // private
42482     syncValue : function()
42483     {
42484         this.editorcore.syncValue();
42485     }
42486      
42487     
42488     // hide stuff that is not compatible
42489     /**
42490      * @event blur
42491      * @hide
42492      */
42493     /**
42494      * @event change
42495      * @hide
42496      */
42497     /**
42498      * @event focus
42499      * @hide
42500      */
42501     /**
42502      * @event specialkey
42503      * @hide
42504      */
42505     /**
42506      * @cfg {String} fieldClass @hide
42507      */
42508     /**
42509      * @cfg {String} focusClass @hide
42510      */
42511     /**
42512      * @cfg {String} autoCreate @hide
42513      */
42514     /**
42515      * @cfg {String} inputType @hide
42516      */
42517     /**
42518      * @cfg {String} invalidClass @hide
42519      */
42520     /**
42521      * @cfg {String} invalidText @hide
42522      */
42523     /**
42524      * @cfg {String} msgFx @hide
42525      */
42526     /**
42527      * @cfg {String} validateOnBlur @hide
42528      */
42529 });
42530  
42531     // <script type="text/javascript">
42532 /*
42533  * Based on
42534  * Ext JS Library 1.1.1
42535  * Copyright(c) 2006-2007, Ext JS, LLC.
42536  *  
42537  
42538  */
42539
42540 /**
42541  * @class Roo.form.HtmlEditorToolbar1
42542  * Basic Toolbar
42543  * 
42544  * Usage:
42545  *
42546  new Roo.form.HtmlEditor({
42547     ....
42548     toolbars : [
42549         new Roo.form.HtmlEditorToolbar1({
42550             disable : { fonts: 1 , format: 1, ..., ... , ...],
42551             btns : [ .... ]
42552         })
42553     }
42554      
42555  * 
42556  * @cfg {Object} disable List of elements to disable..
42557  * @cfg {Array} btns List of additional buttons.
42558  * 
42559  * 
42560  * NEEDS Extra CSS? 
42561  * .x-html-editor-tb .x-edit-none .x-btn-text { background: none; }
42562  */
42563  
42564 Roo.form.HtmlEditor.ToolbarStandard = function(config)
42565 {
42566     
42567     Roo.apply(this, config);
42568     
42569     // default disabled, based on 'good practice'..
42570     this.disable = this.disable || {};
42571     Roo.applyIf(this.disable, {
42572         fontSize : true,
42573         colors : true,
42574         specialElements : true
42575     });
42576     
42577     
42578     //Roo.form.HtmlEditorToolbar1.superclass.constructor.call(this, editor.wrap.dom.firstChild, [], config);
42579     // dont call parent... till later.
42580 }
42581
42582 Roo.apply(Roo.form.HtmlEditor.ToolbarStandard.prototype,  {
42583     
42584     tb: false,
42585     
42586     rendered: false,
42587     
42588     editor : false,
42589     editorcore : false,
42590     /**
42591      * @cfg {Object} disable  List of toolbar elements to disable
42592          
42593      */
42594     disable : false,
42595     
42596     
42597      /**
42598      * @cfg {String} createLinkText The default text for the create link prompt
42599      */
42600     createLinkText : 'Please enter the URL for the link:',
42601     /**
42602      * @cfg {String} defaultLinkValue The default value for the create link prompt (defaults to http:/ /)
42603      */
42604     defaultLinkValue : 'http:/'+'/',
42605    
42606     
42607       /**
42608      * @cfg {Array} fontFamilies An array of available font families
42609      */
42610     fontFamilies : [
42611         'Arial',
42612         'Courier New',
42613         'Tahoma',
42614         'Times New Roman',
42615         'Verdana'
42616     ],
42617     
42618     specialChars : [
42619            "&#169;",
42620           "&#174;",     
42621           "&#8482;",    
42622           "&#163;" ,    
42623          // "&#8212;",    
42624           "&#8230;",    
42625           "&#247;" ,    
42626         //  "&#225;" ,     ?? a acute?
42627            "&#8364;"    , //Euro
42628        //   "&#8220;"    ,
42629         //  "&#8221;"    ,
42630         //  "&#8226;"    ,
42631           "&#176;"  //   , // degrees
42632
42633          // "&#233;"     , // e ecute
42634          // "&#250;"     , // u ecute?
42635     ],
42636     
42637     specialElements : [
42638         {
42639             text: "Insert Table",
42640             xtype: 'MenuItem',
42641             xns : Roo.Menu,
42642             ihtml :  '<table><tr><td>Cell</td></tr></table>' 
42643                 
42644         },
42645         {    
42646             text: "Insert Image",
42647             xtype: 'MenuItem',
42648             xns : Roo.Menu,
42649             ihtml : '<img src="about:blank"/>'
42650             
42651         }
42652         
42653          
42654     ],
42655     
42656     
42657     inputElements : [ 
42658             "form", "input:text", "input:hidden", "input:checkbox", "input:radio", "input:password", 
42659             "input:submit", "input:button", "select", "textarea", "label" ],
42660     formats : [
42661         ["p"] ,  
42662         ["h1"],["h2"],["h3"],["h4"],["h5"],["h6"], 
42663         ["pre"],[ "code"], 
42664         ["abbr"],[ "acronym"],[ "address"],[ "cite"],[ "samp"],[ "var"],
42665         ['div'],['span']
42666     ],
42667     
42668     cleanStyles : [
42669         "font-size"
42670     ],
42671      /**
42672      * @cfg {String} defaultFont default font to use.
42673      */
42674     defaultFont: 'tahoma',
42675    
42676     fontSelect : false,
42677     
42678     
42679     formatCombo : false,
42680     
42681     init : function(editor)
42682     {
42683         this.editor = editor;
42684         this.editorcore = editor.editorcore ? editor.editorcore : editor;
42685         var editorcore = this.editorcore;
42686         
42687         var _t = this;
42688         
42689         var fid = editorcore.frameId;
42690         var etb = this;
42691         function btn(id, toggle, handler){
42692             var xid = fid + '-'+ id ;
42693             return {
42694                 id : xid,
42695                 cmd : id,
42696                 cls : 'x-btn-icon x-edit-'+id,
42697                 enableToggle:toggle !== false,
42698                 scope: _t, // was editor...
42699                 handler:handler||_t.relayBtnCmd,
42700                 clickEvent:'mousedown',
42701                 tooltip: etb.buttonTips[id] || undefined, ///tips ???
42702                 tabIndex:-1
42703             };
42704         }
42705         
42706         
42707         
42708         var tb = new Roo.Toolbar(editor.wrap.dom.firstChild);
42709         this.tb = tb;
42710          // stop form submits
42711         tb.el.on('click', function(e){
42712             e.preventDefault(); // what does this do?
42713         });
42714
42715         if(!this.disable.font) { // && !Roo.isSafari){
42716             /* why no safari for fonts 
42717             editor.fontSelect = tb.el.createChild({
42718                 tag:'select',
42719                 tabIndex: -1,
42720                 cls:'x-font-select',
42721                 html: this.createFontOptions()
42722             });
42723             
42724             editor.fontSelect.on('change', function(){
42725                 var font = editor.fontSelect.dom.value;
42726                 editor.relayCmd('fontname', font);
42727                 editor.deferFocus();
42728             }, editor);
42729             
42730             tb.add(
42731                 editor.fontSelect.dom,
42732                 '-'
42733             );
42734             */
42735             
42736         };
42737         if(!this.disable.formats){
42738             this.formatCombo = new Roo.form.ComboBox({
42739                 store: new Roo.data.SimpleStore({
42740                     id : 'tag',
42741                     fields: ['tag'],
42742                     data : this.formats // from states.js
42743                 }),
42744                 blockFocus : true,
42745                 name : '',
42746                 //autoCreate : {tag: "div",  size: "20"},
42747                 displayField:'tag',
42748                 typeAhead: false,
42749                 mode: 'local',
42750                 editable : false,
42751                 triggerAction: 'all',
42752                 emptyText:'Add tag',
42753                 selectOnFocus:true,
42754                 width:135,
42755                 listeners : {
42756                     'select': function(c, r, i) {
42757                         editorcore.insertTag(r.get('tag'));
42758                         editor.focus();
42759                     }
42760                 }
42761
42762             });
42763             tb.addField(this.formatCombo);
42764             
42765         }
42766         
42767         if(!this.disable.format){
42768             tb.add(
42769                 btn('bold'),
42770                 btn('italic'),
42771                 btn('underline')
42772             );
42773         };
42774         if(!this.disable.fontSize){
42775             tb.add(
42776                 '-',
42777                 
42778                 
42779                 btn('increasefontsize', false, editorcore.adjustFont),
42780                 btn('decreasefontsize', false, editorcore.adjustFont)
42781             );
42782         };
42783         
42784         
42785         if(!this.disable.colors){
42786             tb.add(
42787                 '-', {
42788                     id:editorcore.frameId +'-forecolor',
42789                     cls:'x-btn-icon x-edit-forecolor',
42790                     clickEvent:'mousedown',
42791                     tooltip: this.buttonTips['forecolor'] || undefined,
42792                     tabIndex:-1,
42793                     menu : new Roo.menu.ColorMenu({
42794                         allowReselect: true,
42795                         focus: Roo.emptyFn,
42796                         value:'000000',
42797                         plain:true,
42798                         selectHandler: function(cp, color){
42799                             editorcore.execCmd('forecolor', Roo.isSafari || Roo.isIE ? '#'+color : color);
42800                             editor.deferFocus();
42801                         },
42802                         scope: editorcore,
42803                         clickEvent:'mousedown'
42804                     })
42805                 }, {
42806                     id:editorcore.frameId +'backcolor',
42807                     cls:'x-btn-icon x-edit-backcolor',
42808                     clickEvent:'mousedown',
42809                     tooltip: this.buttonTips['backcolor'] || undefined,
42810                     tabIndex:-1,
42811                     menu : new Roo.menu.ColorMenu({
42812                         focus: Roo.emptyFn,
42813                         value:'FFFFFF',
42814                         plain:true,
42815                         allowReselect: true,
42816                         selectHandler: function(cp, color){
42817                             if(Roo.isGecko){
42818                                 editorcore.execCmd('useCSS', false);
42819                                 editorcore.execCmd('hilitecolor', color);
42820                                 editorcore.execCmd('useCSS', true);
42821                                 editor.deferFocus();
42822                             }else{
42823                                 editorcore.execCmd(Roo.isOpera ? 'hilitecolor' : 'backcolor', 
42824                                     Roo.isSafari || Roo.isIE ? '#'+color : color);
42825                                 editor.deferFocus();
42826                             }
42827                         },
42828                         scope:editorcore,
42829                         clickEvent:'mousedown'
42830                     })
42831                 }
42832             );
42833         };
42834         // now add all the items...
42835         
42836
42837         if(!this.disable.alignments){
42838             tb.add(
42839                 '-',
42840                 btn('justifyleft'),
42841                 btn('justifycenter'),
42842                 btn('justifyright')
42843             );
42844         };
42845
42846         //if(!Roo.isSafari){
42847             if(!this.disable.links){
42848                 tb.add(
42849                     '-',
42850                     btn('createlink', false, this.createLink)    /// MOVE TO HERE?!!?!?!?!
42851                 );
42852             };
42853
42854             if(!this.disable.lists){
42855                 tb.add(
42856                     '-',
42857                     btn('insertorderedlist'),
42858                     btn('insertunorderedlist')
42859                 );
42860             }
42861             if(!this.disable.sourceEdit){
42862                 tb.add(
42863                     '-',
42864                     btn('sourceedit', true, function(btn){
42865                         Roo.log(this);
42866                         this.toggleSourceEdit(btn.pressed);
42867                     })
42868                 );
42869             }
42870         //}
42871         
42872         var smenu = { };
42873         // special menu.. - needs to be tidied up..
42874         if (!this.disable.special) {
42875             smenu = {
42876                 text: "&#169;",
42877                 cls: 'x-edit-none',
42878                 
42879                 menu : {
42880                     items : []
42881                 }
42882             };
42883             for (var i =0; i < this.specialChars.length; i++) {
42884                 smenu.menu.items.push({
42885                     
42886                     html: this.specialChars[i],
42887                     handler: function(a,b) {
42888                         editorcore.insertAtCursor(String.fromCharCode(a.html.replace('&#','').replace(';', '')));
42889                         //editor.insertAtCursor(a.html);
42890                         
42891                     },
42892                     tabIndex:-1
42893                 });
42894             }
42895             
42896             
42897             tb.add(smenu);
42898             
42899             
42900         }
42901         
42902         var cmenu = { };
42903         if (!this.disable.cleanStyles) {
42904             cmenu = {
42905                 cls: 'x-btn-icon x-btn-clear',
42906                 
42907                 menu : {
42908                     items : []
42909                 }
42910             };
42911             for (var i =0; i < this.cleanStyles.length; i++) {
42912                 cmenu.menu.items.push({
42913                     actiontype : this.cleanStyles[i],
42914                     html: 'Remove ' + this.cleanStyles[i],
42915                     handler: function(a,b) {
42916                         Roo.log(a);
42917                         Roo.log(b);
42918                         var c = Roo.get(editorcore.doc.body);
42919                         c.select('[style]').each(function(s) {
42920                             s.dom.style.removeProperty(a.actiontype);
42921                         });
42922                         
42923                     },
42924                     tabIndex:-1
42925                 });
42926             }
42927             
42928             tb.add(cmenu);
42929         }
42930          
42931         if (!this.disable.specialElements) {
42932             var semenu = {
42933                 text: "Other;",
42934                 cls: 'x-edit-none',
42935                 menu : {
42936                     items : []
42937                 }
42938             };
42939             for (var i =0; i < this.specialElements.length; i++) {
42940                 semenu.menu.items.push(
42941                     Roo.apply({ 
42942                         handler: function(a,b) {
42943                             editor.insertAtCursor(this.ihtml);
42944                         }
42945                     }, this.specialElements[i])
42946                 );
42947                     
42948             }
42949             
42950             tb.add(semenu);
42951             
42952             
42953         }
42954          
42955         
42956         if (this.btns) {
42957             for(var i =0; i< this.btns.length;i++) {
42958                 var b = Roo.factory(this.btns[i],Roo.form);
42959                 b.cls =  'x-edit-none';
42960                 b.scope = editorcore;
42961                 tb.add(b);
42962             }
42963         
42964         }
42965         
42966         
42967         
42968         // disable everything...
42969         
42970         this.tb.items.each(function(item){
42971            if(item.id != editorcore.frameId+ '-sourceedit'){
42972                 item.disable();
42973             }
42974         });
42975         this.rendered = true;
42976         
42977         // the all the btns;
42978         editor.on('editorevent', this.updateToolbar, this);
42979         // other toolbars need to implement this..
42980         //editor.on('editmodechange', this.updateToolbar, this);
42981     },
42982     
42983     
42984     relayBtnCmd : function(btn) {
42985         this.editorcore.relayCmd(btn.cmd);
42986     },
42987     // private used internally
42988     createLink : function(){
42989         Roo.log("create link?");
42990         var url = prompt(this.createLinkText, this.defaultLinkValue);
42991         if(url && url != 'http:/'+'/'){
42992             this.editorcore.relayCmd('createlink', url);
42993         }
42994     },
42995
42996     
42997     /**
42998      * Protected method that will not generally be called directly. It triggers
42999      * a toolbar update by reading the markup state of the current selection in the editor.
43000      */
43001     updateToolbar: function(){
43002
43003         if(!this.editorcore.activated){
43004             this.editor.onFirstFocus();
43005             return;
43006         }
43007
43008         var btns = this.tb.items.map, 
43009             doc = this.editorcore.doc,
43010             frameId = this.editorcore.frameId;
43011
43012         if(!this.disable.font && !Roo.isSafari){
43013             /*
43014             var name = (doc.queryCommandValue('FontName')||this.editor.defaultFont).toLowerCase();
43015             if(name != this.fontSelect.dom.value){
43016                 this.fontSelect.dom.value = name;
43017             }
43018             */
43019         }
43020         if(!this.disable.format){
43021             btns[frameId + '-bold'].toggle(doc.queryCommandState('bold'));
43022             btns[frameId + '-italic'].toggle(doc.queryCommandState('italic'));
43023             btns[frameId + '-underline'].toggle(doc.queryCommandState('underline'));
43024         }
43025         if(!this.disable.alignments){
43026             btns[frameId + '-justifyleft'].toggle(doc.queryCommandState('justifyleft'));
43027             btns[frameId + '-justifycenter'].toggle(doc.queryCommandState('justifycenter'));
43028             btns[frameId + '-justifyright'].toggle(doc.queryCommandState('justifyright'));
43029         }
43030         if(!Roo.isSafari && !this.disable.lists){
43031             btns[frameId + '-insertorderedlist'].toggle(doc.queryCommandState('insertorderedlist'));
43032             btns[frameId + '-insertunorderedlist'].toggle(doc.queryCommandState('insertunorderedlist'));
43033         }
43034         
43035         var ans = this.editorcore.getAllAncestors();
43036         if (this.formatCombo) {
43037             
43038             
43039             var store = this.formatCombo.store;
43040             this.formatCombo.setValue("");
43041             for (var i =0; i < ans.length;i++) {
43042                 if (ans[i] && store.query('tag',ans[i].tagName.toLowerCase(), false).length) {
43043                     // select it..
43044                     this.formatCombo.setValue(ans[i].tagName.toLowerCase());
43045                     break;
43046                 }
43047             }
43048         }
43049         
43050         
43051         
43052         // hides menus... - so this cant be on a menu...
43053         Roo.menu.MenuMgr.hideAll();
43054
43055         //this.editorsyncValue();
43056     },
43057    
43058     
43059     createFontOptions : function(){
43060         var buf = [], fs = this.fontFamilies, ff, lc;
43061         
43062         
43063         
43064         for(var i = 0, len = fs.length; i< len; i++){
43065             ff = fs[i];
43066             lc = ff.toLowerCase();
43067             buf.push(
43068                 '<option value="',lc,'" style="font-family:',ff,';"',
43069                     (this.defaultFont == lc ? ' selected="true">' : '>'),
43070                     ff,
43071                 '</option>'
43072             );
43073         }
43074         return buf.join('');
43075     },
43076     
43077     toggleSourceEdit : function(sourceEditMode){
43078         
43079         Roo.log("toolbar toogle");
43080         if(sourceEditMode === undefined){
43081             sourceEditMode = !this.sourceEditMode;
43082         }
43083         this.sourceEditMode = sourceEditMode === true;
43084         var btn = this.tb.items.get(this.editorcore.frameId +'-sourceedit');
43085         // just toggle the button?
43086         if(btn.pressed !== this.sourceEditMode){
43087             btn.toggle(this.sourceEditMode);
43088             return;
43089         }
43090         
43091         if(sourceEditMode){
43092             Roo.log("disabling buttons");
43093             this.tb.items.each(function(item){
43094                 if(item.cmd != 'sourceedit'){
43095                     item.disable();
43096                 }
43097             });
43098           
43099         }else{
43100             Roo.log("enabling buttons");
43101             if(this.editorcore.initialized){
43102                 this.tb.items.each(function(item){
43103                     item.enable();
43104                 });
43105             }
43106             
43107         }
43108         Roo.log("calling toggole on editor");
43109         // tell the editor that it's been pressed..
43110         this.editor.toggleSourceEdit(sourceEditMode);
43111        
43112     },
43113      /**
43114      * Object collection of toolbar tooltips for the buttons in the editor. The key
43115      * is the command id associated with that button and the value is a valid QuickTips object.
43116      * For example:
43117 <pre><code>
43118 {
43119     bold : {
43120         title: 'Bold (Ctrl+B)',
43121         text: 'Make the selected text bold.',
43122         cls: 'x-html-editor-tip'
43123     },
43124     italic : {
43125         title: 'Italic (Ctrl+I)',
43126         text: 'Make the selected text italic.',
43127         cls: 'x-html-editor-tip'
43128     },
43129     ...
43130 </code></pre>
43131     * @type Object
43132      */
43133     buttonTips : {
43134         bold : {
43135             title: 'Bold (Ctrl+B)',
43136             text: 'Make the selected text bold.',
43137             cls: 'x-html-editor-tip'
43138         },
43139         italic : {
43140             title: 'Italic (Ctrl+I)',
43141             text: 'Make the selected text italic.',
43142             cls: 'x-html-editor-tip'
43143         },
43144         underline : {
43145             title: 'Underline (Ctrl+U)',
43146             text: 'Underline the selected text.',
43147             cls: 'x-html-editor-tip'
43148         },
43149         increasefontsize : {
43150             title: 'Grow Text',
43151             text: 'Increase the font size.',
43152             cls: 'x-html-editor-tip'
43153         },
43154         decreasefontsize : {
43155             title: 'Shrink Text',
43156             text: 'Decrease the font size.',
43157             cls: 'x-html-editor-tip'
43158         },
43159         backcolor : {
43160             title: 'Text Highlight Color',
43161             text: 'Change the background color of the selected text.',
43162             cls: 'x-html-editor-tip'
43163         },
43164         forecolor : {
43165             title: 'Font Color',
43166             text: 'Change the color of the selected text.',
43167             cls: 'x-html-editor-tip'
43168         },
43169         justifyleft : {
43170             title: 'Align Text Left',
43171             text: 'Align text to the left.',
43172             cls: 'x-html-editor-tip'
43173         },
43174         justifycenter : {
43175             title: 'Center Text',
43176             text: 'Center text in the editor.',
43177             cls: 'x-html-editor-tip'
43178         },
43179         justifyright : {
43180             title: 'Align Text Right',
43181             text: 'Align text to the right.',
43182             cls: 'x-html-editor-tip'
43183         },
43184         insertunorderedlist : {
43185             title: 'Bullet List',
43186             text: 'Start a bulleted list.',
43187             cls: 'x-html-editor-tip'
43188         },
43189         insertorderedlist : {
43190             title: 'Numbered List',
43191             text: 'Start a numbered list.',
43192             cls: 'x-html-editor-tip'
43193         },
43194         createlink : {
43195             title: 'Hyperlink',
43196             text: 'Make the selected text a hyperlink.',
43197             cls: 'x-html-editor-tip'
43198         },
43199         sourceedit : {
43200             title: 'Source Edit',
43201             text: 'Switch to source editing mode.',
43202             cls: 'x-html-editor-tip'
43203         }
43204     },
43205     // private
43206     onDestroy : function(){
43207         if(this.rendered){
43208             
43209             this.tb.items.each(function(item){
43210                 if(item.menu){
43211                     item.menu.removeAll();
43212                     if(item.menu.el){
43213                         item.menu.el.destroy();
43214                     }
43215                 }
43216                 item.destroy();
43217             });
43218              
43219         }
43220     },
43221     onFirstFocus: function() {
43222         this.tb.items.each(function(item){
43223            item.enable();
43224         });
43225     }
43226 });
43227
43228
43229
43230
43231 // <script type="text/javascript">
43232 /*
43233  * Based on
43234  * Ext JS Library 1.1.1
43235  * Copyright(c) 2006-2007, Ext JS, LLC.
43236  *  
43237  
43238  */
43239
43240  
43241 /**
43242  * @class Roo.form.HtmlEditor.ToolbarContext
43243  * Context Toolbar
43244  * 
43245  * Usage:
43246  *
43247  new Roo.form.HtmlEditor({
43248     ....
43249     toolbars : [
43250         { xtype: 'ToolbarStandard', styles : {} }
43251         { xtype: 'ToolbarContext', disable : {} }
43252     ]
43253 })
43254
43255      
43256  * 
43257  * @config : {Object} disable List of elements to disable.. (not done yet.)
43258  * @config : {Object} styles  Map of styles available.
43259  * 
43260  */
43261
43262 Roo.form.HtmlEditor.ToolbarContext = function(config)
43263 {
43264     
43265     Roo.apply(this, config);
43266     //Roo.form.HtmlEditorToolbar1.superclass.constructor.call(this, editor.wrap.dom.firstChild, [], config);
43267     // dont call parent... till later.
43268     this.styles = this.styles || {};
43269 }
43270
43271  
43272
43273 Roo.form.HtmlEditor.ToolbarContext.types = {
43274     'IMG' : {
43275         width : {
43276             title: "Width",
43277             width: 40
43278         },
43279         height:  {
43280             title: "Height",
43281             width: 40
43282         },
43283         align: {
43284             title: "Align",
43285             opts : [ [""],[ "left"],[ "right"],[ "center"],[ "top"]],
43286             width : 80
43287             
43288         },
43289         border: {
43290             title: "Border",
43291             width: 40
43292         },
43293         alt: {
43294             title: "Alt",
43295             width: 120
43296         },
43297         src : {
43298             title: "Src",
43299             width: 220
43300         }
43301         
43302     },
43303     'A' : {
43304         name : {
43305             title: "Name",
43306             width: 50
43307         },
43308         target:  {
43309             title: "Target",
43310             width: 120
43311         },
43312         href:  {
43313             title: "Href",
43314             width: 220
43315         } // border?
43316         
43317     },
43318     'TABLE' : {
43319         rows : {
43320             title: "Rows",
43321             width: 20
43322         },
43323         cols : {
43324             title: "Cols",
43325             width: 20
43326         },
43327         width : {
43328             title: "Width",
43329             width: 40
43330         },
43331         height : {
43332             title: "Height",
43333             width: 40
43334         },
43335         border : {
43336             title: "Border",
43337             width: 20
43338         }
43339     },
43340     'TD' : {
43341         width : {
43342             title: "Width",
43343             width: 40
43344         },
43345         height : {
43346             title: "Height",
43347             width: 40
43348         },   
43349         align: {
43350             title: "Align",
43351             opts : [[""],[ "left"],[ "center"],[ "right"],[ "justify"],[ "char"]],
43352             width: 80
43353         },
43354         valign: {
43355             title: "Valign",
43356             opts : [[""],[ "top"],[ "middle"],[ "bottom"],[ "baseline"]],
43357             width: 80
43358         },
43359         colspan: {
43360             title: "Colspan",
43361             width: 20
43362             
43363         },
43364          'font-family'  : {
43365             title : "Font",
43366             style : 'fontFamily',
43367             displayField: 'display',
43368             optname : 'font-family',
43369             width: 140
43370         }
43371     },
43372     'INPUT' : {
43373         name : {
43374             title: "name",
43375             width: 120
43376         },
43377         value : {
43378             title: "Value",
43379             width: 120
43380         },
43381         width : {
43382             title: "Width",
43383             width: 40
43384         }
43385     },
43386     'LABEL' : {
43387         'for' : {
43388             title: "For",
43389             width: 120
43390         }
43391     },
43392     'TEXTAREA' : {
43393           name : {
43394             title: "name",
43395             width: 120
43396         },
43397         rows : {
43398             title: "Rows",
43399             width: 20
43400         },
43401         cols : {
43402             title: "Cols",
43403             width: 20
43404         }
43405     },
43406     'SELECT' : {
43407         name : {
43408             title: "name",
43409             width: 120
43410         },
43411         selectoptions : {
43412             title: "Options",
43413             width: 200
43414         }
43415     },
43416     
43417     // should we really allow this??
43418     // should this just be 
43419     'BODY' : {
43420         title : {
43421             title: "Title",
43422             width: 200,
43423             disabled : true
43424         }
43425     },
43426     'SPAN' : {
43427         'font-family'  : {
43428             title : "Font",
43429             style : 'fontFamily',
43430             displayField: 'display',
43431             optname : 'font-family',
43432             width: 140
43433         }
43434     },
43435     'DIV' : {
43436         'font-family'  : {
43437             title : "Font",
43438             style : 'fontFamily',
43439             displayField: 'display',
43440             optname : 'font-family',
43441             width: 140
43442         }
43443     },
43444      'P' : {
43445         'font-family'  : {
43446             title : "Font",
43447             style : 'fontFamily',
43448             displayField: 'display',
43449             optname : 'font-family',
43450             width: 140
43451         }
43452     },
43453     
43454     '*' : {
43455         // empty..
43456     }
43457
43458 };
43459
43460 // this should be configurable.. - you can either set it up using stores, or modify options somehwere..
43461 Roo.form.HtmlEditor.ToolbarContext.stores = false;
43462
43463 Roo.form.HtmlEditor.ToolbarContext.options = {
43464         'font-family'  : [ 
43465                 [ 'Helvetica,Arial,sans-serif', 'Helvetica'],
43466                 [ 'Courier New', 'Courier New'],
43467                 [ 'Tahoma', 'Tahoma'],
43468                 [ 'Times New Roman,serif', 'Times'],
43469                 [ 'Verdana','Verdana' ]
43470         ]
43471 };
43472
43473 // fixme - these need to be configurable..
43474  
43475
43476 Roo.form.HtmlEditor.ToolbarContext.types
43477
43478
43479 Roo.apply(Roo.form.HtmlEditor.ToolbarContext.prototype,  {
43480     
43481     tb: false,
43482     
43483     rendered: false,
43484     
43485     editor : false,
43486     editorcore : false,
43487     /**
43488      * @cfg {Object} disable  List of toolbar elements to disable
43489          
43490      */
43491     disable : false,
43492     /**
43493      * @cfg {Object} styles List of styles 
43494      *    eg. { '*' : [ 'headline' ] , 'TD' : [ 'underline', 'double-underline' ] } 
43495      *
43496      * These must be defined in the page, so they get rendered correctly..
43497      * .headline { }
43498      * TD.underline { }
43499      * 
43500      */
43501     styles : false,
43502     
43503     options: false,
43504     
43505     toolbars : false,
43506     
43507     init : function(editor)
43508     {
43509         this.editor = editor;
43510         this.editorcore = editor.editorcore ? editor.editorcore : editor;
43511         var editorcore = this.editorcore;
43512         
43513         var fid = editorcore.frameId;
43514         var etb = this;
43515         function btn(id, toggle, handler){
43516             var xid = fid + '-'+ id ;
43517             return {
43518                 id : xid,
43519                 cmd : id,
43520                 cls : 'x-btn-icon x-edit-'+id,
43521                 enableToggle:toggle !== false,
43522                 scope: editorcore, // was editor...
43523                 handler:handler||editorcore.relayBtnCmd,
43524                 clickEvent:'mousedown',
43525                 tooltip: etb.buttonTips[id] || undefined, ///tips ???
43526                 tabIndex:-1
43527             };
43528         }
43529         // create a new element.
43530         var wdiv = editor.wrap.createChild({
43531                 tag: 'div'
43532             }, editor.wrap.dom.firstChild.nextSibling, true);
43533         
43534         // can we do this more than once??
43535         
43536          // stop form submits
43537       
43538  
43539         // disable everything...
43540         var ty= Roo.form.HtmlEditor.ToolbarContext.types;
43541         this.toolbars = {};
43542            
43543         for (var i in  ty) {
43544           
43545             this.toolbars[i] = this.buildToolbar(ty[i],i);
43546         }
43547         this.tb = this.toolbars.BODY;
43548         this.tb.el.show();
43549         this.buildFooter();
43550         this.footer.show();
43551         editor.on('hide', function( ) { this.footer.hide() }, this);
43552         editor.on('show', function( ) { this.footer.show() }, this);
43553         
43554          
43555         this.rendered = true;
43556         
43557         // the all the btns;
43558         editor.on('editorevent', this.updateToolbar, this);
43559         // other toolbars need to implement this..
43560         //editor.on('editmodechange', this.updateToolbar, this);
43561     },
43562     
43563     
43564     
43565     /**
43566      * Protected method that will not generally be called directly. It triggers
43567      * a toolbar update by reading the markup state of the current selection in the editor.
43568      */
43569     updateToolbar: function(editor,ev,sel){
43570
43571         //Roo.log(ev);
43572         // capture mouse up - this is handy for selecting images..
43573         // perhaps should go somewhere else...
43574         if(!this.editorcore.activated){
43575              this.editor.onFirstFocus();
43576             return;
43577         }
43578         
43579         // http://developer.yahoo.com/yui/docs/simple-editor.js.html
43580         // selectNode - might want to handle IE?
43581         if (ev &&
43582             (ev.type == 'mouseup' || ev.type == 'click' ) &&
43583             ev.target && ev.target.tagName == 'IMG') {
43584             // they have click on an image...
43585             // let's see if we can change the selection...
43586             sel = ev.target;
43587          
43588               var nodeRange = sel.ownerDocument.createRange();
43589             try {
43590                 nodeRange.selectNode(sel);
43591             } catch (e) {
43592                 nodeRange.selectNodeContents(sel);
43593             }
43594             //nodeRange.collapse(true);
43595             var s = this.editorcore.win.getSelection();
43596             s.removeAllRanges();
43597             s.addRange(nodeRange);
43598         }  
43599         
43600       
43601         var updateFooter = sel ? false : true;
43602         
43603         
43604         var ans = this.editorcore.getAllAncestors();
43605         
43606         // pick
43607         var ty= Roo.form.HtmlEditor.ToolbarContext.types;
43608         
43609         if (!sel) { 
43610             sel = ans.length ? (ans[0] ?  ans[0]  : ans[1]) : this.editorcore.doc.body;
43611             sel = sel ? sel : this.editorcore.doc.body;
43612             sel = sel.tagName.length ? sel : this.editorcore.doc.body;
43613             
43614         }
43615         // pick a menu that exists..
43616         var tn = sel.tagName.toUpperCase();
43617         //sel = typeof(ty[tn]) != 'undefined' ? sel : this.editor.doc.body;
43618         
43619         tn = sel.tagName.toUpperCase();
43620         
43621         var lastSel = this.tb.selectedNode
43622         
43623         this.tb.selectedNode = sel;
43624         
43625         // if current menu does not match..
43626         if ((this.tb.name != tn) || (lastSel != this.tb.selectedNode)) {
43627                 
43628             this.tb.el.hide();
43629             ///console.log("show: " + tn);
43630             this.tb =  typeof(ty[tn]) != 'undefined' ? this.toolbars[tn] : this.toolbars['*'];
43631             this.tb.el.show();
43632             // update name
43633             this.tb.items.first().el.innerHTML = tn + ':&nbsp;';
43634             
43635             
43636             // update attributes
43637             if (this.tb.fields) {
43638                 this.tb.fields.each(function(e) {
43639                     if (e.stylename) {
43640                         e.setValue(sel.style[e.stylename]);
43641                         return;
43642                     } 
43643                    e.setValue(sel.getAttribute(e.attrname));
43644                 });
43645             }
43646             
43647             var hasStyles = false;
43648             for(var i in this.styles) {
43649                 hasStyles = true;
43650                 break;
43651             }
43652             
43653             // update styles
43654             if (hasStyles) { 
43655                 var st = this.tb.fields.item(0);
43656                 
43657                 st.store.removeAll();
43658                
43659                 
43660                 var cn = sel.className.split(/\s+/);
43661                 
43662                 var avs = [];
43663                 if (this.styles['*']) {
43664                     
43665                     Roo.each(this.styles['*'], function(v) {
43666                         avs.push( [ v , cn.indexOf(v) > -1 ? 1 : 0 ] );         
43667                     });
43668                 }
43669                 if (this.styles[tn]) { 
43670                     Roo.each(this.styles[tn], function(v) {
43671                         avs.push( [ v , cn.indexOf(v) > -1 ? 1 : 0 ] );         
43672                     });
43673                 }
43674                 
43675                 st.store.loadData(avs);
43676                 st.collapse();
43677                 st.setValue(cn);
43678             }
43679             // flag our selected Node.
43680             this.tb.selectedNode = sel;
43681            
43682            
43683             Roo.menu.MenuMgr.hideAll();
43684
43685         }
43686         
43687         if (!updateFooter) {
43688             //this.footDisp.dom.innerHTML = ''; 
43689             return;
43690         }
43691         // update the footer
43692         //
43693         var html = '';
43694         
43695         this.footerEls = ans.reverse();
43696         Roo.each(this.footerEls, function(a,i) {
43697             if (!a) { return; }
43698             html += html.length ? ' &gt; '  :  '';
43699             
43700             html += '<span class="x-ed-loc-' + i + '">' + a.tagName + '</span>';
43701             
43702         });
43703        
43704         // 
43705         var sz = this.footDisp.up('td').getSize();
43706         this.footDisp.dom.style.width = (sz.width -10) + 'px';
43707         this.footDisp.dom.style.marginLeft = '5px';
43708         
43709         this.footDisp.dom.style.overflow = 'hidden';
43710         
43711         this.footDisp.dom.innerHTML = html;
43712             
43713         //this.editorsyncValue();
43714     },
43715      
43716     
43717    
43718        
43719     // private
43720     onDestroy : function(){
43721         if(this.rendered){
43722             
43723             this.tb.items.each(function(item){
43724                 if(item.menu){
43725                     item.menu.removeAll();
43726                     if(item.menu.el){
43727                         item.menu.el.destroy();
43728                     }
43729                 }
43730                 item.destroy();
43731             });
43732              
43733         }
43734     },
43735     onFirstFocus: function() {
43736         // need to do this for all the toolbars..
43737         this.tb.items.each(function(item){
43738            item.enable();
43739         });
43740     },
43741     buildToolbar: function(tlist, nm)
43742     {
43743         var editor = this.editor;
43744         var editorcore = this.editorcore;
43745          // create a new element.
43746         var wdiv = editor.wrap.createChild({
43747                 tag: 'div'
43748             }, editor.wrap.dom.firstChild.nextSibling, true);
43749         
43750        
43751         var tb = new Roo.Toolbar(wdiv);
43752         // add the name..
43753         
43754         tb.add(nm+ ":&nbsp;");
43755         
43756         var styles = [];
43757         for(var i in this.styles) {
43758             styles.push(i);
43759         }
43760         
43761         // styles...
43762         if (styles && styles.length) {
43763             
43764             // this needs a multi-select checkbox...
43765             tb.addField( new Roo.form.ComboBox({
43766                 store: new Roo.data.SimpleStore({
43767                     id : 'val',
43768                     fields: ['val', 'selected'],
43769                     data : [] 
43770                 }),
43771                 name : '-roo-edit-className',
43772                 attrname : 'className',
43773                 displayField: 'val',
43774                 typeAhead: false,
43775                 mode: 'local',
43776                 editable : false,
43777                 triggerAction: 'all',
43778                 emptyText:'Select Style',
43779                 selectOnFocus:true,
43780                 width: 130,
43781                 listeners : {
43782                     'select': function(c, r, i) {
43783                         // initial support only for on class per el..
43784                         tb.selectedNode.className =  r ? r.get('val') : '';
43785                         editorcore.syncValue();
43786                     }
43787                 }
43788     
43789             }));
43790         }
43791         
43792         var tbc = Roo.form.HtmlEditor.ToolbarContext;
43793         var tbops = tbc.options;
43794         
43795         for (var i in tlist) {
43796             
43797             var item = tlist[i];
43798             tb.add(item.title + ":&nbsp;");
43799             
43800             
43801             //optname == used so you can configure the options available..
43802             var opts = item.opts ? item.opts : false;
43803             if (item.optname) {
43804                 opts = tbops[item.optname];
43805            
43806             }
43807             
43808             if (opts) {
43809                 // opts == pulldown..
43810                 tb.addField( new Roo.form.ComboBox({
43811                     store:   typeof(tbc.stores[i]) != 'undefined' ?  Roo.factory(tbc.stores[i],Roo.data) : new Roo.data.SimpleStore({
43812                         id : 'val',
43813                         fields: ['val', 'display'],
43814                         data : opts  
43815                     }),
43816                     name : '-roo-edit-' + i,
43817                     attrname : i,
43818                     stylename : item.style ? item.style : false,
43819                     displayField: item.displayField ? item.displayField : 'val',
43820                     valueField :  'val',
43821                     typeAhead: false,
43822                     mode: typeof(tbc.stores[i]) != 'undefined'  ? 'remote' : 'local',
43823                     editable : false,
43824                     triggerAction: 'all',
43825                     emptyText:'Select',
43826                     selectOnFocus:true,
43827                     width: item.width ? item.width  : 130,
43828                     listeners : {
43829                         'select': function(c, r, i) {
43830                             if (c.stylename) {
43831                                 tb.selectedNode.style[c.stylename] =  r.get('val');
43832                                 return;
43833                             }
43834                             tb.selectedNode.setAttribute(c.attrname, r.get('val'));
43835                         }
43836                     }
43837
43838                 }));
43839                 continue;
43840                     
43841                  
43842                 
43843                 tb.addField( new Roo.form.TextField({
43844                     name: i,
43845                     width: 100,
43846                     //allowBlank:false,
43847                     value: ''
43848                 }));
43849                 continue;
43850             }
43851             tb.addField( new Roo.form.TextField({
43852                 name: '-roo-edit-' + i,
43853                 attrname : i,
43854                 
43855                 width: item.width,
43856                 //allowBlank:true,
43857                 value: '',
43858                 listeners: {
43859                     'change' : function(f, nv, ov) {
43860                         tb.selectedNode.setAttribute(f.attrname, nv);
43861                     }
43862                 }
43863             }));
43864              
43865         }
43866         tb.addFill();
43867         var _this = this;
43868         tb.addButton( {
43869             text: 'Remove Tag',
43870     
43871             listeners : {
43872                 click : function ()
43873                 {
43874                     // remove
43875                     // undo does not work.
43876                      
43877                     var sn = tb.selectedNode;
43878                     
43879                     var pn = sn.parentNode;
43880                     
43881                     var stn =  sn.childNodes[0];
43882                     var en = sn.childNodes[sn.childNodes.length - 1 ];
43883                     while (sn.childNodes.length) {
43884                         var node = sn.childNodes[0];
43885                         sn.removeChild(node);
43886                         //Roo.log(node);
43887                         pn.insertBefore(node, sn);
43888                         
43889                     }
43890                     pn.removeChild(sn);
43891                     var range = editorcore.createRange();
43892         
43893                     range.setStart(stn,0);
43894                     range.setEnd(en,0); //????
43895                     //range.selectNode(sel);
43896                     
43897                     
43898                     var selection = editorcore.getSelection();
43899                     selection.removeAllRanges();
43900                     selection.addRange(range);
43901                     
43902                     
43903                     
43904                     //_this.updateToolbar(null, null, pn);
43905                     _this.updateToolbar(null, null, null);
43906                     _this.footDisp.dom.innerHTML = ''; 
43907                 }
43908             }
43909             
43910                     
43911                 
43912             
43913         });
43914         
43915         
43916         tb.el.on('click', function(e){
43917             e.preventDefault(); // what does this do?
43918         });
43919         tb.el.setVisibilityMode( Roo.Element.DISPLAY);
43920         tb.el.hide();
43921         tb.name = nm;
43922         // dont need to disable them... as they will get hidden
43923         return tb;
43924          
43925         
43926     },
43927     buildFooter : function()
43928     {
43929         
43930         var fel = this.editor.wrap.createChild();
43931         this.footer = new Roo.Toolbar(fel);
43932         // toolbar has scrolly on left / right?
43933         var footDisp= new Roo.Toolbar.Fill();
43934         var _t = this;
43935         this.footer.add(
43936             {
43937                 text : '&lt;',
43938                 xtype: 'Button',
43939                 handler : function() {
43940                     _t.footDisp.scrollTo('left',0,true)
43941                 }
43942             }
43943         );
43944         this.footer.add( footDisp );
43945         this.footer.add( 
43946             {
43947                 text : '&gt;',
43948                 xtype: 'Button',
43949                 handler : function() {
43950                     // no animation..
43951                     _t.footDisp.select('span').last().scrollIntoView(_t.footDisp,true);
43952                 }
43953             }
43954         );
43955         var fel = Roo.get(footDisp.el);
43956         fel.addClass('x-editor-context');
43957         this.footDispWrap = fel; 
43958         this.footDispWrap.overflow  = 'hidden';
43959         
43960         this.footDisp = fel.createChild();
43961         this.footDispWrap.on('click', this.onContextClick, this)
43962         
43963         
43964     },
43965     onContextClick : function (ev,dom)
43966     {
43967         ev.preventDefault();
43968         var  cn = dom.className;
43969         //Roo.log(cn);
43970         if (!cn.match(/x-ed-loc-/)) {
43971             return;
43972         }
43973         var n = cn.split('-').pop();
43974         var ans = this.footerEls;
43975         var sel = ans[n];
43976         
43977          // pick
43978         var range = this.editorcore.createRange();
43979         
43980         range.selectNodeContents(sel);
43981         //range.selectNode(sel);
43982         
43983         
43984         var selection = this.editorcore.getSelection();
43985         selection.removeAllRanges();
43986         selection.addRange(range);
43987         
43988         
43989         
43990         this.updateToolbar(null, null, sel);
43991         
43992         
43993     }
43994     
43995     
43996     
43997     
43998     
43999 });
44000
44001
44002
44003
44004
44005 /*
44006  * Based on:
44007  * Ext JS Library 1.1.1
44008  * Copyright(c) 2006-2007, Ext JS, LLC.
44009  *
44010  * Originally Released Under LGPL - original licence link has changed is not relivant.
44011  *
44012  * Fork - LGPL
44013  * <script type="text/javascript">
44014  */
44015  
44016 /**
44017  * @class Roo.form.BasicForm
44018  * @extends Roo.util.Observable
44019  * Supplies the functionality to do "actions" on forms and initialize Roo.form.Field types on existing markup.
44020  * @constructor
44021  * @param {String/HTMLElement/Roo.Element} el The form element or its id
44022  * @param {Object} config Configuration options
44023  */
44024 Roo.form.BasicForm = function(el, config){
44025     this.allItems = [];
44026     this.childForms = [];
44027     Roo.apply(this, config);
44028     /*
44029      * The Roo.form.Field items in this form.
44030      * @type MixedCollection
44031      */
44032      
44033      
44034     this.items = new Roo.util.MixedCollection(false, function(o){
44035         return o.id || (o.id = Roo.id());
44036     });
44037     this.addEvents({
44038         /**
44039          * @event beforeaction
44040          * Fires before any action is performed. Return false to cancel the action.
44041          * @param {Form} this
44042          * @param {Action} action The action to be performed
44043          */
44044         beforeaction: true,
44045         /**
44046          * @event actionfailed
44047          * Fires when an action fails.
44048          * @param {Form} this
44049          * @param {Action} action The action that failed
44050          */
44051         actionfailed : true,
44052         /**
44053          * @event actioncomplete
44054          * Fires when an action is completed.
44055          * @param {Form} this
44056          * @param {Action} action The action that completed
44057          */
44058         actioncomplete : true
44059     });
44060     if(el){
44061         this.initEl(el);
44062     }
44063     Roo.form.BasicForm.superclass.constructor.call(this);
44064 };
44065
44066 Roo.extend(Roo.form.BasicForm, Roo.util.Observable, {
44067     /**
44068      * @cfg {String} method
44069      * The request method to use (GET or POST) for form actions if one isn't supplied in the action options.
44070      */
44071     /**
44072      * @cfg {DataReader} reader
44073      * An Roo.data.DataReader (e.g. {@link Roo.data.XmlReader}) to be used to read data when executing "load" actions.
44074      * This is optional as there is built-in support for processing JSON.
44075      */
44076     /**
44077      * @cfg {DataReader} errorReader
44078      * An Roo.data.DataReader (e.g. {@link Roo.data.XmlReader}) to be used to read data when reading validation errors on "submit" actions.
44079      * This is completely optional as there is built-in support for processing JSON.
44080      */
44081     /**
44082      * @cfg {String} url
44083      * The URL to use for form actions if one isn't supplied in the action options.
44084      */
44085     /**
44086      * @cfg {Boolean} fileUpload
44087      * Set to true if this form is a file upload.
44088      */
44089      
44090     /**
44091      * @cfg {Object} baseParams
44092      * Parameters to pass with all requests. e.g. baseParams: {id: '123', foo: 'bar'}.
44093      */
44094      /**
44095      
44096     /**
44097      * @cfg {Number} timeout Timeout for form actions in seconds (default is 30 seconds).
44098      */
44099     timeout: 30,
44100
44101     // private
44102     activeAction : null,
44103
44104     /**
44105      * @cfg {Boolean} trackResetOnLoad If set to true, form.reset() resets to the last loaded
44106      * or setValues() data instead of when the form was first created.
44107      */
44108     trackResetOnLoad : false,
44109     
44110     
44111     /**
44112      * childForms - used for multi-tab forms
44113      * @type {Array}
44114      */
44115     childForms : false,
44116     
44117     /**
44118      * allItems - full list of fields.
44119      * @type {Array}
44120      */
44121     allItems : false,
44122     
44123     /**
44124      * By default wait messages are displayed with Roo.MessageBox.wait. You can target a specific
44125      * element by passing it or its id or mask the form itself by passing in true.
44126      * @type Mixed
44127      */
44128     waitMsgTarget : false,
44129
44130     // private
44131     initEl : function(el){
44132         this.el = Roo.get(el);
44133         this.id = this.el.id || Roo.id();
44134         this.el.on('submit', this.onSubmit, this);
44135         this.el.addClass('x-form');
44136     },
44137
44138     // private
44139     onSubmit : function(e){
44140         e.stopEvent();
44141     },
44142
44143     /**
44144      * Returns true if client-side validation on the form is successful.
44145      * @return Boolean
44146      */
44147     isValid : function(){
44148         var valid = true;
44149         this.items.each(function(f){
44150            if(!f.validate()){
44151                valid = false;
44152            }
44153         });
44154         return valid;
44155     },
44156
44157     /**
44158      * Returns true if any fields in this form have changed since their original load.
44159      * @return Boolean
44160      */
44161     isDirty : function(){
44162         var dirty = false;
44163         this.items.each(function(f){
44164            if(f.isDirty()){
44165                dirty = true;
44166                return false;
44167            }
44168         });
44169         return dirty;
44170     },
44171
44172     /**
44173      * Performs a predefined action (submit or load) or custom actions you define on this form.
44174      * @param {String} actionName The name of the action type
44175      * @param {Object} options (optional) The options to pass to the action.  All of the config options listed
44176      * below are supported by both the submit and load actions unless otherwise noted (custom actions could also
44177      * accept other config options):
44178      * <pre>
44179 Property          Type             Description
44180 ----------------  ---------------  ----------------------------------------------------------------------------------
44181 url               String           The url for the action (defaults to the form's url)
44182 method            String           The form method to use (defaults to the form's method, or POST if not defined)
44183 params            String/Object    The params to pass (defaults to the form's baseParams, or none if not defined)
44184 clientValidation  Boolean          Applies to submit only.  Pass true to call form.isValid() prior to posting to
44185                                    validate the form on the client (defaults to false)
44186      * </pre>
44187      * @return {BasicForm} this
44188      */
44189     doAction : function(action, options){
44190         if(typeof action == 'string'){
44191             action = new Roo.form.Action.ACTION_TYPES[action](this, options);
44192         }
44193         if(this.fireEvent('beforeaction', this, action) !== false){
44194             this.beforeAction(action);
44195             action.run.defer(100, action);
44196         }
44197         return this;
44198     },
44199
44200     /**
44201      * Shortcut to do a submit action.
44202      * @param {Object} options The options to pass to the action (see {@link #doAction} for details)
44203      * @return {BasicForm} this
44204      */
44205     submit : function(options){
44206         this.doAction('submit', options);
44207         return this;
44208     },
44209
44210     /**
44211      * Shortcut to do a load action.
44212      * @param {Object} options The options to pass to the action (see {@link #doAction} for details)
44213      * @return {BasicForm} this
44214      */
44215     load : function(options){
44216         this.doAction('load', options);
44217         return this;
44218     },
44219
44220     /**
44221      * Persists the values in this form into the passed Roo.data.Record object in a beginEdit/endEdit block.
44222      * @param {Record} record The record to edit
44223      * @return {BasicForm} this
44224      */
44225     updateRecord : function(record){
44226         record.beginEdit();
44227         var fs = record.fields;
44228         fs.each(function(f){
44229             var field = this.findField(f.name);
44230             if(field){
44231                 record.set(f.name, field.getValue());
44232             }
44233         }, this);
44234         record.endEdit();
44235         return this;
44236     },
44237
44238     /**
44239      * Loads an Roo.data.Record into this form.
44240      * @param {Record} record The record to load
44241      * @return {BasicForm} this
44242      */
44243     loadRecord : function(record){
44244         this.setValues(record.data);
44245         return this;
44246     },
44247
44248     // private
44249     beforeAction : function(action){
44250         var o = action.options;
44251         
44252        
44253         if(this.waitMsgTarget === true){
44254             this.el.mask(o.waitMsg || "Sending", 'x-mask-loading');
44255         }else if(this.waitMsgTarget){
44256             this.waitMsgTarget = Roo.get(this.waitMsgTarget);
44257             this.waitMsgTarget.mask(o.waitMsg || "Sending", 'x-mask-loading');
44258         }else {
44259             Roo.MessageBox.wait(o.waitMsg || "Sending", o.waitTitle || this.waitTitle || 'Please Wait...');
44260         }
44261          
44262     },
44263
44264     // private
44265     afterAction : function(action, success){
44266         this.activeAction = null;
44267         var o = action.options;
44268         
44269         if(this.waitMsgTarget === true){
44270             this.el.unmask();
44271         }else if(this.waitMsgTarget){
44272             this.waitMsgTarget.unmask();
44273         }else{
44274             Roo.MessageBox.updateProgress(1);
44275             Roo.MessageBox.hide();
44276         }
44277          
44278         if(success){
44279             if(o.reset){
44280                 this.reset();
44281             }
44282             Roo.callback(o.success, o.scope, [this, action]);
44283             this.fireEvent('actioncomplete', this, action);
44284             
44285         }else{
44286             
44287             // failure condition..
44288             // we have a scenario where updates need confirming.
44289             // eg. if a locking scenario exists..
44290             // we look for { errors : { needs_confirm : true }} in the response.
44291             if (
44292                 (typeof(action.result) != 'undefined')  &&
44293                 (typeof(action.result.errors) != 'undefined')  &&
44294                 (typeof(action.result.errors.needs_confirm) != 'undefined')
44295            ){
44296                 var _t = this;
44297                 Roo.MessageBox.confirm(
44298                     "Change requires confirmation",
44299                     action.result.errorMsg,
44300                     function(r) {
44301                         if (r != 'yes') {
44302                             return;
44303                         }
44304                         _t.doAction('submit', { params :  { _submit_confirmed : 1 } }  );
44305                     }
44306                     
44307                 );
44308                 
44309                 
44310                 
44311                 return;
44312             }
44313             
44314             Roo.callback(o.failure, o.scope, [this, action]);
44315             // show an error message if no failed handler is set..
44316             if (!this.hasListener('actionfailed')) {
44317                 Roo.MessageBox.alert("Error",
44318                     (typeof(action.result) != 'undefined' && typeof(action.result.errorMsg) != 'undefined') ?
44319                         action.result.errorMsg :
44320                         "Saving Failed, please check your entries or try again"
44321                 );
44322             }
44323             
44324             this.fireEvent('actionfailed', this, action);
44325         }
44326         
44327     },
44328
44329     /**
44330      * Find a Roo.form.Field in this form by id, dataIndex, name or hiddenName
44331      * @param {String} id The value to search for
44332      * @return Field
44333      */
44334     findField : function(id){
44335         var field = this.items.get(id);
44336         if(!field){
44337             this.items.each(function(f){
44338                 if(f.isFormField && (f.dataIndex == id || f.id == id || f.getName() == id)){
44339                     field = f;
44340                     return false;
44341                 }
44342             });
44343         }
44344         return field || null;
44345     },
44346
44347     /**
44348      * Add a secondary form to this one, 
44349      * Used to provide tabbed forms. One form is primary, with hidden values 
44350      * which mirror the elements from the other forms.
44351      * 
44352      * @param {Roo.form.Form} form to add.
44353      * 
44354      */
44355     addForm : function(form)
44356     {
44357        
44358         if (this.childForms.indexOf(form) > -1) {
44359             // already added..
44360             return;
44361         }
44362         this.childForms.push(form);
44363         var n = '';
44364         Roo.each(form.allItems, function (fe) {
44365             
44366             n = typeof(fe.getName) == 'undefined' ? fe.name : fe.getName();
44367             if (this.findField(n)) { // already added..
44368                 return;
44369             }
44370             var add = new Roo.form.Hidden({
44371                 name : n
44372             });
44373             add.render(this.el);
44374             
44375             this.add( add );
44376         }, this);
44377         
44378     },
44379     /**
44380      * Mark fields in this form invalid in bulk.
44381      * @param {Array/Object} errors Either an array in the form [{id:'fieldId', msg:'The message'},...] or an object hash of {id: msg, id2: msg2}
44382      * @return {BasicForm} this
44383      */
44384     markInvalid : function(errors){
44385         if(errors instanceof Array){
44386             for(var i = 0, len = errors.length; i < len; i++){
44387                 var fieldError = errors[i];
44388                 var f = this.findField(fieldError.id);
44389                 if(f){
44390                     f.markInvalid(fieldError.msg);
44391                 }
44392             }
44393         }else{
44394             var field, id;
44395             for(id in errors){
44396                 if(typeof errors[id] != 'function' && (field = this.findField(id))){
44397                     field.markInvalid(errors[id]);
44398                 }
44399             }
44400         }
44401         Roo.each(this.childForms || [], function (f) {
44402             f.markInvalid(errors);
44403         });
44404         
44405         return this;
44406     },
44407
44408     /**
44409      * Set values for fields in this form in bulk.
44410      * @param {Array/Object} values Either an array in the form [{id:'fieldId', value:'foo'},...] or an object hash of {id: value, id2: value2}
44411      * @return {BasicForm} this
44412      */
44413     setValues : function(values){
44414         if(values instanceof Array){ // array of objects
44415             for(var i = 0, len = values.length; i < len; i++){
44416                 var v = values[i];
44417                 var f = this.findField(v.id);
44418                 if(f){
44419                     f.setValue(v.value);
44420                     if(this.trackResetOnLoad){
44421                         f.originalValue = f.getValue();
44422                     }
44423                 }
44424             }
44425         }else{ // object hash
44426             var field, id;
44427             for(id in values){
44428                 if(typeof values[id] != 'function' && (field = this.findField(id))){
44429                     
44430                     if (field.setFromData && 
44431                         field.valueField && 
44432                         field.displayField &&
44433                         // combos' with local stores can 
44434                         // be queried via setValue()
44435                         // to set their value..
44436                         (field.store && !field.store.isLocal)
44437                         ) {
44438                         // it's a combo
44439                         var sd = { };
44440                         sd[field.valueField] = typeof(values[field.hiddenName]) == 'undefined' ? '' : values[field.hiddenName];
44441                         sd[field.displayField] = typeof(values[field.name]) == 'undefined' ? '' : values[field.name];
44442                         field.setFromData(sd);
44443                         
44444                     } else {
44445                         field.setValue(values[id]);
44446                     }
44447                     
44448                     
44449                     if(this.trackResetOnLoad){
44450                         field.originalValue = field.getValue();
44451                     }
44452                 }
44453             }
44454         }
44455          
44456         Roo.each(this.childForms || [], function (f) {
44457             f.setValues(values);
44458         });
44459                 
44460         return this;
44461     },
44462
44463     /**
44464      * Returns the fields in this form as an object with key/value pairs. If multiple fields exist with the same name
44465      * they are returned as an array.
44466      * @param {Boolean} asString
44467      * @return {Object}
44468      */
44469     getValues : function(asString){
44470         if (this.childForms) {
44471             // copy values from the child forms
44472             Roo.each(this.childForms, function (f) {
44473                 this.setValues(f.getValues());
44474             }, this);
44475         }
44476         
44477         
44478         
44479         var fs = Roo.lib.Ajax.serializeForm(this.el.dom);
44480         if(asString === true){
44481             return fs;
44482         }
44483         return Roo.urlDecode(fs);
44484     },
44485     
44486     /**
44487      * Returns the fields in this form as an object with key/value pairs. 
44488      * This differs from getValues as it calls getValue on each child item, rather than using dom data.
44489      * @return {Object}
44490      */
44491     getFieldValues : function(with_hidden)
44492     {
44493         if (this.childForms) {
44494             // copy values from the child forms
44495             // should this call getFieldValues - probably not as we do not currently copy
44496             // hidden fields when we generate..
44497             Roo.each(this.childForms, function (f) {
44498                 this.setValues(f.getValues());
44499             }, this);
44500         }
44501         
44502         var ret = {};
44503         this.items.each(function(f){
44504             if (!f.getName()) {
44505                 return;
44506             }
44507             var v = f.getValue();
44508             if (f.inputType =='radio') {
44509                 if (typeof(ret[f.getName()]) == 'undefined') {
44510                     ret[f.getName()] = ''; // empty..
44511                 }
44512                 
44513                 if (!f.el.dom.checked) {
44514                     return;
44515                     
44516                 }
44517                 v = f.el.dom.value;
44518                 
44519             }
44520             
44521             // not sure if this supported any more..
44522             if ((typeof(v) == 'object') && f.getRawValue) {
44523                 v = f.getRawValue() ; // dates..
44524             }
44525             // combo boxes where name != hiddenName...
44526             if (f.name != f.getName()) {
44527                 ret[f.name] = f.getRawValue();
44528             }
44529             ret[f.getName()] = v;
44530         });
44531         
44532         return ret;
44533     },
44534
44535     /**
44536      * Clears all invalid messages in this form.
44537      * @return {BasicForm} this
44538      */
44539     clearInvalid : function(){
44540         this.items.each(function(f){
44541            f.clearInvalid();
44542         });
44543         
44544         Roo.each(this.childForms || [], function (f) {
44545             f.clearInvalid();
44546         });
44547         
44548         
44549         return this;
44550     },
44551
44552     /**
44553      * Resets this form.
44554      * @return {BasicForm} this
44555      */
44556     reset : function(){
44557         this.items.each(function(f){
44558             f.reset();
44559         });
44560         
44561         Roo.each(this.childForms || [], function (f) {
44562             f.reset();
44563         });
44564        
44565         
44566         return this;
44567     },
44568
44569     /**
44570      * Add Roo.form components to this form.
44571      * @param {Field} field1
44572      * @param {Field} field2 (optional)
44573      * @param {Field} etc (optional)
44574      * @return {BasicForm} this
44575      */
44576     add : function(){
44577         this.items.addAll(Array.prototype.slice.call(arguments, 0));
44578         return this;
44579     },
44580
44581
44582     /**
44583      * Removes a field from the items collection (does NOT remove its markup).
44584      * @param {Field} field
44585      * @return {BasicForm} this
44586      */
44587     remove : function(field){
44588         this.items.remove(field);
44589         return this;
44590     },
44591
44592     /**
44593      * Looks at the fields in this form, checks them for an id attribute,
44594      * and calls applyTo on the existing dom element with that id.
44595      * @return {BasicForm} this
44596      */
44597     render : function(){
44598         this.items.each(function(f){
44599             if(f.isFormField && !f.rendered && document.getElementById(f.id)){ // if the element exists
44600                 f.applyTo(f.id);
44601             }
44602         });
44603         return this;
44604     },
44605
44606     /**
44607      * Calls {@link Ext#apply} for all fields in this form with the passed object.
44608      * @param {Object} values
44609      * @return {BasicForm} this
44610      */
44611     applyToFields : function(o){
44612         this.items.each(function(f){
44613            Roo.apply(f, o);
44614         });
44615         return this;
44616     },
44617
44618     /**
44619      * Calls {@link Ext#applyIf} for all field in this form with the passed object.
44620      * @param {Object} values
44621      * @return {BasicForm} this
44622      */
44623     applyIfToFields : function(o){
44624         this.items.each(function(f){
44625            Roo.applyIf(f, o);
44626         });
44627         return this;
44628     }
44629 });
44630
44631 // back compat
44632 Roo.BasicForm = Roo.form.BasicForm;/*
44633  * Based on:
44634  * Ext JS Library 1.1.1
44635  * Copyright(c) 2006-2007, Ext JS, LLC.
44636  *
44637  * Originally Released Under LGPL - original licence link has changed is not relivant.
44638  *
44639  * Fork - LGPL
44640  * <script type="text/javascript">
44641  */
44642
44643 /**
44644  * @class Roo.form.Form
44645  * @extends Roo.form.BasicForm
44646  * Adds the ability to dynamically render forms with JavaScript to {@link Roo.form.BasicForm}.
44647  * @constructor
44648  * @param {Object} config Configuration options
44649  */
44650 Roo.form.Form = function(config){
44651     var xitems =  [];
44652     if (config.items) {
44653         xitems = config.items;
44654         delete config.items;
44655     }
44656    
44657     
44658     Roo.form.Form.superclass.constructor.call(this, null, config);
44659     this.url = this.url || this.action;
44660     if(!this.root){
44661         this.root = new Roo.form.Layout(Roo.applyIf({
44662             id: Roo.id()
44663         }, config));
44664     }
44665     this.active = this.root;
44666     /**
44667      * Array of all the buttons that have been added to this form via {@link addButton}
44668      * @type Array
44669      */
44670     this.buttons = [];
44671     this.allItems = [];
44672     this.addEvents({
44673         /**
44674          * @event clientvalidation
44675          * If the monitorValid config option is true, this event fires repetitively to notify of valid state
44676          * @param {Form} this
44677          * @param {Boolean} valid true if the form has passed client-side validation
44678          */
44679         clientvalidation: true,
44680         /**
44681          * @event rendered
44682          * Fires when the form is rendered
44683          * @param {Roo.form.Form} form
44684          */
44685         rendered : true
44686     });
44687     
44688     if (this.progressUrl) {
44689             // push a hidden field onto the list of fields..
44690             this.addxtype( {
44691                     xns: Roo.form, 
44692                     xtype : 'Hidden', 
44693                     name : 'UPLOAD_IDENTIFIER' 
44694             });
44695         }
44696         
44697     
44698     Roo.each(xitems, this.addxtype, this);
44699     
44700     
44701     
44702 };
44703
44704 Roo.extend(Roo.form.Form, Roo.form.BasicForm, {
44705     /**
44706      * @cfg {Number} labelWidth The width of labels. This property cascades to child containers.
44707      */
44708     /**
44709      * @cfg {String} itemCls A css class to apply to the x-form-item of fields. This property cascades to child containers.
44710      */
44711     /**
44712      * @cfg {String} buttonAlign Valid values are "left," "center" and "right" (defaults to "center")
44713      */
44714     buttonAlign:'center',
44715
44716     /**
44717      * @cfg {Number} minButtonWidth Minimum width of all buttons in pixels (defaults to 75)
44718      */
44719     minButtonWidth:75,
44720
44721     /**
44722      * @cfg {String} labelAlign Valid values are "left," "top" and "right" (defaults to "left").
44723      * This property cascades to child containers if not set.
44724      */
44725     labelAlign:'left',
44726
44727     /**
44728      * @cfg {Boolean} monitorValid If true the form monitors its valid state <b>client-side</b> and
44729      * fires a looping event with that state. This is required to bind buttons to the valid
44730      * state using the config value formBind:true on the button.
44731      */
44732     monitorValid : false,
44733
44734     /**
44735      * @cfg {Number} monitorPoll The milliseconds to poll valid state, ignored if monitorValid is not true (defaults to 200)
44736      */
44737     monitorPoll : 200,
44738     
44739     /**
44740      * @cfg {String} progressUrl - Url to return progress data 
44741      */
44742     
44743     progressUrl : false,
44744   
44745     /**
44746      * Opens a new {@link Roo.form.Column} container in the layout stack. If fields are passed after the config, the
44747      * fields are added and the column is closed. If no fields are passed the column remains open
44748      * until end() is called.
44749      * @param {Object} config The config to pass to the column
44750      * @param {Field} field1 (optional)
44751      * @param {Field} field2 (optional)
44752      * @param {Field} etc (optional)
44753      * @return Column The column container object
44754      */
44755     column : function(c){
44756         var col = new Roo.form.Column(c);
44757         this.start(col);
44758         if(arguments.length > 1){ // duplicate code required because of Opera
44759             this.add.apply(this, Array.prototype.slice.call(arguments, 1));
44760             this.end();
44761         }
44762         return col;
44763     },
44764
44765     /**
44766      * Opens a new {@link Roo.form.FieldSet} container in the layout stack. If fields are passed after the config, the
44767      * fields are added and the fieldset is closed. If no fields are passed the fieldset remains open
44768      * until end() is called.
44769      * @param {Object} config The config to pass to the fieldset
44770      * @param {Field} field1 (optional)
44771      * @param {Field} field2 (optional)
44772      * @param {Field} etc (optional)
44773      * @return FieldSet The fieldset container object
44774      */
44775     fieldset : function(c){
44776         var fs = new Roo.form.FieldSet(c);
44777         this.start(fs);
44778         if(arguments.length > 1){ // duplicate code required because of Opera
44779             this.add.apply(this, Array.prototype.slice.call(arguments, 1));
44780             this.end();
44781         }
44782         return fs;
44783     },
44784
44785     /**
44786      * Opens a new {@link Roo.form.Layout} container in the layout stack. If fields are passed after the config, the
44787      * fields are added and the container is closed. If no fields are passed the container remains open
44788      * until end() is called.
44789      * @param {Object} config The config to pass to the Layout
44790      * @param {Field} field1 (optional)
44791      * @param {Field} field2 (optional)
44792      * @param {Field} etc (optional)
44793      * @return Layout The container object
44794      */
44795     container : function(c){
44796         var l = new Roo.form.Layout(c);
44797         this.start(l);
44798         if(arguments.length > 1){ // duplicate code required because of Opera
44799             this.add.apply(this, Array.prototype.slice.call(arguments, 1));
44800             this.end();
44801         }
44802         return l;
44803     },
44804
44805     /**
44806      * Opens the passed container in the layout stack. The container can be any {@link Roo.form.Layout} or subclass.
44807      * @param {Object} container A Roo.form.Layout or subclass of Layout
44808      * @return {Form} this
44809      */
44810     start : function(c){
44811         // cascade label info
44812         Roo.applyIf(c, {'labelAlign': this.active.labelAlign, 'labelWidth': this.active.labelWidth, 'itemCls': this.active.itemCls});
44813         this.active.stack.push(c);
44814         c.ownerCt = this.active;
44815         this.active = c;
44816         return this;
44817     },
44818
44819     /**
44820      * Closes the current open container
44821      * @return {Form} this
44822      */
44823     end : function(){
44824         if(this.active == this.root){
44825             return this;
44826         }
44827         this.active = this.active.ownerCt;
44828         return this;
44829     },
44830
44831     /**
44832      * Add Roo.form components to the current open container (e.g. column, fieldset, etc.).  Fields added via this method
44833      * can also be passed with an additional property of fieldLabel, which if supplied, will provide the text to display
44834      * as the label of the field.
44835      * @param {Field} field1
44836      * @param {Field} field2 (optional)
44837      * @param {Field} etc. (optional)
44838      * @return {Form} this
44839      */
44840     add : function(){
44841         this.active.stack.push.apply(this.active.stack, arguments);
44842         this.allItems.push.apply(this.allItems,arguments);
44843         var r = [];
44844         for(var i = 0, a = arguments, len = a.length; i < len; i++) {
44845             if(a[i].isFormField){
44846                 r.push(a[i]);
44847             }
44848         }
44849         if(r.length > 0){
44850             Roo.form.Form.superclass.add.apply(this, r);
44851         }
44852         return this;
44853     },
44854     
44855
44856     
44857     
44858     
44859      /**
44860      * Find any element that has been added to a form, using it's ID or name
44861      * This can include framesets, columns etc. along with regular fields..
44862      * @param {String} id - id or name to find.
44863      
44864      * @return {Element} e - or false if nothing found.
44865      */
44866     findbyId : function(id)
44867     {
44868         var ret = false;
44869         if (!id) {
44870             return ret;
44871         }
44872         Roo.each(this.allItems, function(f){
44873             if (f.id == id || f.name == id ){
44874                 ret = f;
44875                 return false;
44876             }
44877         });
44878         return ret;
44879     },
44880
44881     
44882     
44883     /**
44884      * Render this form into the passed container. This should only be called once!
44885      * @param {String/HTMLElement/Element} container The element this component should be rendered into
44886      * @return {Form} this
44887      */
44888     render : function(ct)
44889     {
44890         
44891         
44892         
44893         ct = Roo.get(ct);
44894         var o = this.autoCreate || {
44895             tag: 'form',
44896             method : this.method || 'POST',
44897             id : this.id || Roo.id()
44898         };
44899         this.initEl(ct.createChild(o));
44900
44901         this.root.render(this.el);
44902         
44903        
44904              
44905         this.items.each(function(f){
44906             f.render('x-form-el-'+f.id);
44907         });
44908
44909         if(this.buttons.length > 0){
44910             // tables are required to maintain order and for correct IE layout
44911             var tb = this.el.createChild({cls:'x-form-btns-ct', cn: {
44912                 cls:"x-form-btns x-form-btns-"+this.buttonAlign,
44913                 html:'<table cellspacing="0"><tbody><tr></tr></tbody></table><div class="x-clear"></div>'
44914             }}, null, true);
44915             var tr = tb.getElementsByTagName('tr')[0];
44916             for(var i = 0, len = this.buttons.length; i < len; i++) {
44917                 var b = this.buttons[i];
44918                 var td = document.createElement('td');
44919                 td.className = 'x-form-btn-td';
44920                 b.render(tr.appendChild(td));
44921             }
44922         }
44923         if(this.monitorValid){ // initialize after render
44924             this.startMonitoring();
44925         }
44926         this.fireEvent('rendered', this);
44927         return this;
44928     },
44929
44930     /**
44931      * Adds a button to the footer of the form - this <b>must</b> be called before the form is rendered.
44932      * @param {String/Object} config A string becomes the button text, an object can either be a Button config
44933      * object or a valid Roo.DomHelper element config
44934      * @param {Function} handler The function called when the button is clicked
44935      * @param {Object} scope (optional) The scope of the handler function
44936      * @return {Roo.Button}
44937      */
44938     addButton : function(config, handler, scope){
44939         var bc = {
44940             handler: handler,
44941             scope: scope,
44942             minWidth: this.minButtonWidth,
44943             hideParent:true
44944         };
44945         if(typeof config == "string"){
44946             bc.text = config;
44947         }else{
44948             Roo.apply(bc, config);
44949         }
44950         var btn = new Roo.Button(null, bc);
44951         this.buttons.push(btn);
44952         return btn;
44953     },
44954
44955      /**
44956      * Adds a series of form elements (using the xtype property as the factory method.
44957      * Valid xtypes are:  TextField, TextArea .... Button, Layout, FieldSet, Column, (and 'end' to close a block)
44958      * @param {Object} config 
44959      */
44960     
44961     addxtype : function()
44962     {
44963         var ar = Array.prototype.slice.call(arguments, 0);
44964         var ret = false;
44965         for(var i = 0; i < ar.length; i++) {
44966             if (!ar[i]) {
44967                 continue; // skip -- if this happends something invalid got sent, we 
44968                 // should ignore it, as basically that interface element will not show up
44969                 // and that should be pretty obvious!!
44970             }
44971             
44972             if (Roo.form[ar[i].xtype]) {
44973                 ar[i].form = this;
44974                 var fe = Roo.factory(ar[i], Roo.form);
44975                 if (!ret) {
44976                     ret = fe;
44977                 }
44978                 fe.form = this;
44979                 if (fe.store) {
44980                     fe.store.form = this;
44981                 }
44982                 if (fe.isLayout) {  
44983                          
44984                     this.start(fe);
44985                     this.allItems.push(fe);
44986                     if (fe.items && fe.addxtype) {
44987                         fe.addxtype.apply(fe, fe.items);
44988                         delete fe.items;
44989                     }
44990                      this.end();
44991                     continue;
44992                 }
44993                 
44994                 
44995                  
44996                 this.add(fe);
44997               //  console.log('adding ' + ar[i].xtype);
44998             }
44999             if (ar[i].xtype == 'Button') {  
45000                 //console.log('adding button');
45001                 //console.log(ar[i]);
45002                 this.addButton(ar[i]);
45003                 this.allItems.push(fe);
45004                 continue;
45005             }
45006             
45007             if (ar[i].xtype == 'end') { // so we can add fieldsets... / layout etc.
45008                 alert('end is not supported on xtype any more, use items');
45009             //    this.end();
45010             //    //console.log('adding end');
45011             }
45012             
45013         }
45014         return ret;
45015     },
45016     
45017     /**
45018      * Starts monitoring of the valid state of this form. Usually this is done by passing the config
45019      * option "monitorValid"
45020      */
45021     startMonitoring : function(){
45022         if(!this.bound){
45023             this.bound = true;
45024             Roo.TaskMgr.start({
45025                 run : this.bindHandler,
45026                 interval : this.monitorPoll || 200,
45027                 scope: this
45028             });
45029         }
45030     },
45031
45032     /**
45033      * Stops monitoring of the valid state of this form
45034      */
45035     stopMonitoring : function(){
45036         this.bound = false;
45037     },
45038
45039     // private
45040     bindHandler : function(){
45041         if(!this.bound){
45042             return false; // stops binding
45043         }
45044         var valid = true;
45045         this.items.each(function(f){
45046             if(!f.isValid(true)){
45047                 valid = false;
45048                 return false;
45049             }
45050         });
45051         for(var i = 0, len = this.buttons.length; i < len; i++){
45052             var btn = this.buttons[i];
45053             if(btn.formBind === true && btn.disabled === valid){
45054                 btn.setDisabled(!valid);
45055             }
45056         }
45057         this.fireEvent('clientvalidation', this, valid);
45058     }
45059     
45060     
45061     
45062     
45063     
45064     
45065     
45066     
45067 });
45068
45069
45070 // back compat
45071 Roo.Form = Roo.form.Form;
45072 /*
45073  * Based on:
45074  * Ext JS Library 1.1.1
45075  * Copyright(c) 2006-2007, Ext JS, LLC.
45076  *
45077  * Originally Released Under LGPL - original licence link has changed is not relivant.
45078  *
45079  * Fork - LGPL
45080  * <script type="text/javascript">
45081  */
45082
45083 // as we use this in bootstrap.
45084 Roo.namespace('Roo.form');
45085  /**
45086  * @class Roo.form.Action
45087  * Internal Class used to handle form actions
45088  * @constructor
45089  * @param {Roo.form.BasicForm} el The form element or its id
45090  * @param {Object} config Configuration options
45091  */
45092
45093  
45094  
45095 // define the action interface
45096 Roo.form.Action = function(form, options){
45097     this.form = form;
45098     this.options = options || {};
45099 };
45100 /**
45101  * Client Validation Failed
45102  * @const 
45103  */
45104 Roo.form.Action.CLIENT_INVALID = 'client';
45105 /**
45106  * Server Validation Failed
45107  * @const 
45108  */
45109 Roo.form.Action.SERVER_INVALID = 'server';
45110  /**
45111  * Connect to Server Failed
45112  * @const 
45113  */
45114 Roo.form.Action.CONNECT_FAILURE = 'connect';
45115 /**
45116  * Reading Data from Server Failed
45117  * @const 
45118  */
45119 Roo.form.Action.LOAD_FAILURE = 'load';
45120
45121 Roo.form.Action.prototype = {
45122     type : 'default',
45123     failureType : undefined,
45124     response : undefined,
45125     result : undefined,
45126
45127     // interface method
45128     run : function(options){
45129
45130     },
45131
45132     // interface method
45133     success : function(response){
45134
45135     },
45136
45137     // interface method
45138     handleResponse : function(response){
45139
45140     },
45141
45142     // default connection failure
45143     failure : function(response){
45144         
45145         this.response = response;
45146         this.failureType = Roo.form.Action.CONNECT_FAILURE;
45147         this.form.afterAction(this, false);
45148     },
45149
45150     processResponse : function(response){
45151         this.response = response;
45152         if(!response.responseText){
45153             return true;
45154         }
45155         this.result = this.handleResponse(response);
45156         return this.result;
45157     },
45158
45159     // utility functions used internally
45160     getUrl : function(appendParams){
45161         var url = this.options.url || this.form.url || this.form.el.dom.action;
45162         if(appendParams){
45163             var p = this.getParams();
45164             if(p){
45165                 url += (url.indexOf('?') != -1 ? '&' : '?') + p;
45166             }
45167         }
45168         return url;
45169     },
45170
45171     getMethod : function(){
45172         return (this.options.method || this.form.method || this.form.el.dom.method || 'POST').toUpperCase();
45173     },
45174
45175     getParams : function(){
45176         var bp = this.form.baseParams;
45177         var p = this.options.params;
45178         if(p){
45179             if(typeof p == "object"){
45180                 p = Roo.urlEncode(Roo.applyIf(p, bp));
45181             }else if(typeof p == 'string' && bp){
45182                 p += '&' + Roo.urlEncode(bp);
45183             }
45184         }else if(bp){
45185             p = Roo.urlEncode(bp);
45186         }
45187         return p;
45188     },
45189
45190     createCallback : function(){
45191         return {
45192             success: this.success,
45193             failure: this.failure,
45194             scope: this,
45195             timeout: (this.form.timeout*1000),
45196             upload: this.form.fileUpload ? this.success : undefined
45197         };
45198     }
45199 };
45200
45201 Roo.form.Action.Submit = function(form, options){
45202     Roo.form.Action.Submit.superclass.constructor.call(this, form, options);
45203 };
45204
45205 Roo.extend(Roo.form.Action.Submit, Roo.form.Action, {
45206     type : 'submit',
45207
45208     haveProgress : false,
45209     uploadComplete : false,
45210     
45211     // uploadProgress indicator.
45212     uploadProgress : function()
45213     {
45214         if (!this.form.progressUrl) {
45215             return;
45216         }
45217         
45218         if (!this.haveProgress) {
45219             Roo.MessageBox.progress("Uploading", "Uploading");
45220         }
45221         if (this.uploadComplete) {
45222            Roo.MessageBox.hide();
45223            return;
45224         }
45225         
45226         this.haveProgress = true;
45227    
45228         var uid = this.form.findField('UPLOAD_IDENTIFIER').getValue();
45229         
45230         var c = new Roo.data.Connection();
45231         c.request({
45232             url : this.form.progressUrl,
45233             params: {
45234                 id : uid
45235             },
45236             method: 'GET',
45237             success : function(req){
45238                //console.log(data);
45239                 var rdata = false;
45240                 var edata;
45241                 try  {
45242                    rdata = Roo.decode(req.responseText)
45243                 } catch (e) {
45244                     Roo.log("Invalid data from server..");
45245                     Roo.log(edata);
45246                     return;
45247                 }
45248                 if (!rdata || !rdata.success) {
45249                     Roo.log(rdata);
45250                     Roo.MessageBox.alert(Roo.encode(rdata));
45251                     return;
45252                 }
45253                 var data = rdata.data;
45254                 
45255                 if (this.uploadComplete) {
45256                    Roo.MessageBox.hide();
45257                    return;
45258                 }
45259                    
45260                 if (data){
45261                     Roo.MessageBox.updateProgress(data.bytes_uploaded/data.bytes_total,
45262                        Math.floor((data.bytes_total - data.bytes_uploaded)/1000) + 'k remaining'
45263                     );
45264                 }
45265                 this.uploadProgress.defer(2000,this);
45266             },
45267        
45268             failure: function(data) {
45269                 Roo.log('progress url failed ');
45270                 Roo.log(data);
45271             },
45272             scope : this
45273         });
45274            
45275     },
45276     
45277     
45278     run : function()
45279     {
45280         // run get Values on the form, so it syncs any secondary forms.
45281         this.form.getValues();
45282         
45283         var o = this.options;
45284         var method = this.getMethod();
45285         var isPost = method == 'POST';
45286         if(o.clientValidation === false || this.form.isValid()){
45287             
45288             if (this.form.progressUrl) {
45289                 this.form.findField('UPLOAD_IDENTIFIER').setValue(
45290                     (new Date() * 1) + '' + Math.random());
45291                     
45292             } 
45293             
45294             
45295             Roo.Ajax.request(Roo.apply(this.createCallback(), {
45296                 form:this.form.el.dom,
45297                 url:this.getUrl(!isPost),
45298                 method: method,
45299                 params:isPost ? this.getParams() : null,
45300                 isUpload: this.form.fileUpload
45301             }));
45302             
45303             this.uploadProgress();
45304
45305         }else if (o.clientValidation !== false){ // client validation failed
45306             this.failureType = Roo.form.Action.CLIENT_INVALID;
45307             this.form.afterAction(this, false);
45308         }
45309     },
45310
45311     success : function(response)
45312     {
45313         this.uploadComplete= true;
45314         if (this.haveProgress) {
45315             Roo.MessageBox.hide();
45316         }
45317         
45318         
45319         var result = this.processResponse(response);
45320         if(result === true || result.success){
45321             this.form.afterAction(this, true);
45322             return;
45323         }
45324         if(result.errors){
45325             this.form.markInvalid(result.errors);
45326             this.failureType = Roo.form.Action.SERVER_INVALID;
45327         }
45328         this.form.afterAction(this, false);
45329     },
45330     failure : function(response)
45331     {
45332         this.uploadComplete= true;
45333         if (this.haveProgress) {
45334             Roo.MessageBox.hide();
45335         }
45336         
45337         this.response = response;
45338         this.failureType = Roo.form.Action.CONNECT_FAILURE;
45339         this.form.afterAction(this, false);
45340     },
45341     
45342     handleResponse : function(response){
45343         if(this.form.errorReader){
45344             var rs = this.form.errorReader.read(response);
45345             var errors = [];
45346             if(rs.records){
45347                 for(var i = 0, len = rs.records.length; i < len; i++) {
45348                     var r = rs.records[i];
45349                     errors[i] = r.data;
45350                 }
45351             }
45352             if(errors.length < 1){
45353                 errors = null;
45354             }
45355             return {
45356                 success : rs.success,
45357                 errors : errors
45358             };
45359         }
45360         var ret = false;
45361         try {
45362             ret = Roo.decode(response.responseText);
45363         } catch (e) {
45364             ret = {
45365                 success: false,
45366                 errorMsg: "Failed to read server message: " + (response ? response.responseText : ' - no message'),
45367                 errors : []
45368             };
45369         }
45370         return ret;
45371         
45372     }
45373 });
45374
45375
45376 Roo.form.Action.Load = function(form, options){
45377     Roo.form.Action.Load.superclass.constructor.call(this, form, options);
45378     this.reader = this.form.reader;
45379 };
45380
45381 Roo.extend(Roo.form.Action.Load, Roo.form.Action, {
45382     type : 'load',
45383
45384     run : function(){
45385         
45386         Roo.Ajax.request(Roo.apply(
45387                 this.createCallback(), {
45388                     method:this.getMethod(),
45389                     url:this.getUrl(false),
45390                     params:this.getParams()
45391         }));
45392     },
45393
45394     success : function(response){
45395         
45396         var result = this.processResponse(response);
45397         if(result === true || !result.success || !result.data){
45398             this.failureType = Roo.form.Action.LOAD_FAILURE;
45399             this.form.afterAction(this, false);
45400             return;
45401         }
45402         this.form.clearInvalid();
45403         this.form.setValues(result.data);
45404         this.form.afterAction(this, true);
45405     },
45406
45407     handleResponse : function(response){
45408         if(this.form.reader){
45409             var rs = this.form.reader.read(response);
45410             var data = rs.records && rs.records[0] ? rs.records[0].data : null;
45411             return {
45412                 success : rs.success,
45413                 data : data
45414             };
45415         }
45416         return Roo.decode(response.responseText);
45417     }
45418 });
45419
45420 Roo.form.Action.ACTION_TYPES = {
45421     'load' : Roo.form.Action.Load,
45422     'submit' : Roo.form.Action.Submit
45423 };/*
45424  * Based on:
45425  * Ext JS Library 1.1.1
45426  * Copyright(c) 2006-2007, Ext JS, LLC.
45427  *
45428  * Originally Released Under LGPL - original licence link has changed is not relivant.
45429  *
45430  * Fork - LGPL
45431  * <script type="text/javascript">
45432  */
45433  
45434 /**
45435  * @class Roo.form.Layout
45436  * @extends Roo.Component
45437  * Creates a container for layout and rendering of fields in an {@link Roo.form.Form}.
45438  * @constructor
45439  * @param {Object} config Configuration options
45440  */
45441 Roo.form.Layout = function(config){
45442     var xitems = [];
45443     if (config.items) {
45444         xitems = config.items;
45445         delete config.items;
45446     }
45447     Roo.form.Layout.superclass.constructor.call(this, config);
45448     this.stack = [];
45449     Roo.each(xitems, this.addxtype, this);
45450      
45451 };
45452
45453 Roo.extend(Roo.form.Layout, Roo.Component, {
45454     /**
45455      * @cfg {String/Object} autoCreate
45456      * A DomHelper element spec used to autocreate the layout (defaults to {tag: 'div', cls: 'x-form-ct'})
45457      */
45458     /**
45459      * @cfg {String/Object/Function} style
45460      * A style specification string, e.g. "width:100px", or object in the form {width:"100px"}, or
45461      * a function which returns such a specification.
45462      */
45463     /**
45464      * @cfg {String} labelAlign
45465      * Valid values are "left," "top" and "right" (defaults to "left")
45466      */
45467     /**
45468      * @cfg {Number} labelWidth
45469      * Fixed width in pixels of all field labels (defaults to undefined)
45470      */
45471     /**
45472      * @cfg {Boolean} clear
45473      * True to add a clearing element at the end of this layout, equivalent to CSS clear: both (defaults to true)
45474      */
45475     clear : true,
45476     /**
45477      * @cfg {String} labelSeparator
45478      * The separator to use after field labels (defaults to ':')
45479      */
45480     labelSeparator : ':',
45481     /**
45482      * @cfg {Boolean} hideLabels
45483      * True to suppress the display of field labels in this layout (defaults to false)
45484      */
45485     hideLabels : false,
45486
45487     // private
45488     defaultAutoCreate : {tag: 'div', cls: 'x-form-ct'},
45489     
45490     isLayout : true,
45491     
45492     // private
45493     onRender : function(ct, position){
45494         if(this.el){ // from markup
45495             this.el = Roo.get(this.el);
45496         }else {  // generate
45497             var cfg = this.getAutoCreate();
45498             this.el = ct.createChild(cfg, position);
45499         }
45500         if(this.style){
45501             this.el.applyStyles(this.style);
45502         }
45503         if(this.labelAlign){
45504             this.el.addClass('x-form-label-'+this.labelAlign);
45505         }
45506         if(this.hideLabels){
45507             this.labelStyle = "display:none";
45508             this.elementStyle = "padding-left:0;";
45509         }else{
45510             if(typeof this.labelWidth == 'number'){
45511                 this.labelStyle = "width:"+this.labelWidth+"px;";
45512                 this.elementStyle = "padding-left:"+((this.labelWidth+(typeof this.labelPad == 'number' ? this.labelPad : 5))+'px')+";";
45513             }
45514             if(this.labelAlign == 'top'){
45515                 this.labelStyle = "width:auto;";
45516                 this.elementStyle = "padding-left:0;";
45517             }
45518         }
45519         var stack = this.stack;
45520         var slen = stack.length;
45521         if(slen > 0){
45522             if(!this.fieldTpl){
45523                 var t = new Roo.Template(
45524                     '<div class="x-form-item {5}">',
45525                         '<label for="{0}" style="{2}">{1}{4}</label>',
45526                         '<div class="x-form-element" id="x-form-el-{0}" style="{3}">',
45527                         '</div>',
45528                     '</div><div class="x-form-clear-left"></div>'
45529                 );
45530                 t.disableFormats = true;
45531                 t.compile();
45532                 Roo.form.Layout.prototype.fieldTpl = t;
45533             }
45534             for(var i = 0; i < slen; i++) {
45535                 if(stack[i].isFormField){
45536                     this.renderField(stack[i]);
45537                 }else{
45538                     this.renderComponent(stack[i]);
45539                 }
45540             }
45541         }
45542         if(this.clear){
45543             this.el.createChild({cls:'x-form-clear'});
45544         }
45545     },
45546
45547     // private
45548     renderField : function(f){
45549         f.fieldEl = Roo.get(this.fieldTpl.append(this.el, [
45550                f.id, //0
45551                f.fieldLabel, //1
45552                f.labelStyle||this.labelStyle||'', //2
45553                this.elementStyle||'', //3
45554                typeof f.labelSeparator == 'undefined' ? this.labelSeparator : f.labelSeparator, //4
45555                f.itemCls||this.itemCls||''  //5
45556        ], true).getPrevSibling());
45557     },
45558
45559     // private
45560     renderComponent : function(c){
45561         c.render(c.isLayout ? this.el : this.el.createChild());    
45562     },
45563     /**
45564      * Adds a object form elements (using the xtype property as the factory method.)
45565      * Valid xtypes are:  TextField, TextArea .... Button, Layout, FieldSet, Column
45566      * @param {Object} config 
45567      */
45568     addxtype : function(o)
45569     {
45570         // create the lement.
45571         o.form = this.form;
45572         var fe = Roo.factory(o, Roo.form);
45573         this.form.allItems.push(fe);
45574         this.stack.push(fe);
45575         
45576         if (fe.isFormField) {
45577             this.form.items.add(fe);
45578         }
45579          
45580         return fe;
45581     }
45582 });
45583
45584 /**
45585  * @class Roo.form.Column
45586  * @extends Roo.form.Layout
45587  * Creates a column container for layout and rendering of fields in an {@link Roo.form.Form}.
45588  * @constructor
45589  * @param {Object} config Configuration options
45590  */
45591 Roo.form.Column = function(config){
45592     Roo.form.Column.superclass.constructor.call(this, config);
45593 };
45594
45595 Roo.extend(Roo.form.Column, Roo.form.Layout, {
45596     /**
45597      * @cfg {Number/String} width
45598      * The fixed width of the column in pixels or CSS value (defaults to "auto")
45599      */
45600     /**
45601      * @cfg {String/Object} autoCreate
45602      * A DomHelper element spec used to autocreate the column (defaults to {tag: 'div', cls: 'x-form-ct x-form-column'})
45603      */
45604
45605     // private
45606     defaultAutoCreate : {tag: 'div', cls: 'x-form-ct x-form-column'},
45607
45608     // private
45609     onRender : function(ct, position){
45610         Roo.form.Column.superclass.onRender.call(this, ct, position);
45611         if(this.width){
45612             this.el.setWidth(this.width);
45613         }
45614     }
45615 });
45616
45617
45618 /**
45619  * @class Roo.form.Row
45620  * @extends Roo.form.Layout
45621  * Creates a row container for layout and rendering of fields in an {@link Roo.form.Form}.
45622  * @constructor
45623  * @param {Object} config Configuration options
45624  */
45625
45626  
45627 Roo.form.Row = function(config){
45628     Roo.form.Row.superclass.constructor.call(this, config);
45629 };
45630  
45631 Roo.extend(Roo.form.Row, Roo.form.Layout, {
45632       /**
45633      * @cfg {Number/String} width
45634      * The fixed width of the column in pixels or CSS value (defaults to "auto")
45635      */
45636     /**
45637      * @cfg {Number/String} height
45638      * The fixed height of the column in pixels or CSS value (defaults to "auto")
45639      */
45640     defaultAutoCreate : {tag: 'div', cls: 'x-form-ct x-form-row'},
45641     
45642     padWidth : 20,
45643     // private
45644     onRender : function(ct, position){
45645         //console.log('row render');
45646         if(!this.rowTpl){
45647             var t = new Roo.Template(
45648                 '<div class="x-form-item {5}" style="float:left;width:{6}px">',
45649                     '<label for="{0}" style="{2}">{1}{4}</label>',
45650                     '<div class="x-form-element" id="x-form-el-{0}" style="{3}">',
45651                     '</div>',
45652                 '</div>'
45653             );
45654             t.disableFormats = true;
45655             t.compile();
45656             Roo.form.Layout.prototype.rowTpl = t;
45657         }
45658         this.fieldTpl = this.rowTpl;
45659         
45660         //console.log('lw' + this.labelWidth +', la:' + this.labelAlign);
45661         var labelWidth = 100;
45662         
45663         if ((this.labelAlign != 'top')) {
45664             if (typeof this.labelWidth == 'number') {
45665                 labelWidth = this.labelWidth
45666             }
45667             this.padWidth =  20 + labelWidth;
45668             
45669         }
45670         
45671         Roo.form.Column.superclass.onRender.call(this, ct, position);
45672         if(this.width){
45673             this.el.setWidth(this.width);
45674         }
45675         if(this.height){
45676             this.el.setHeight(this.height);
45677         }
45678     },
45679     
45680     // private
45681     renderField : function(f){
45682         f.fieldEl = this.fieldTpl.append(this.el, [
45683                f.id, f.fieldLabel,
45684                f.labelStyle||this.labelStyle||'',
45685                this.elementStyle||'',
45686                typeof f.labelSeparator == 'undefined' ? this.labelSeparator : f.labelSeparator,
45687                f.itemCls||this.itemCls||'',
45688                f.width ? f.width + this.padWidth : 160 + this.padWidth
45689        ],true);
45690     }
45691 });
45692  
45693
45694 /**
45695  * @class Roo.form.FieldSet
45696  * @extends Roo.form.Layout
45697  * Creates a fieldset container for layout and rendering of fields in an {@link Roo.form.Form}.
45698  * @constructor
45699  * @param {Object} config Configuration options
45700  */
45701 Roo.form.FieldSet = function(config){
45702     Roo.form.FieldSet.superclass.constructor.call(this, config);
45703 };
45704
45705 Roo.extend(Roo.form.FieldSet, Roo.form.Layout, {
45706     /**
45707      * @cfg {String} legend
45708      * The text to display as the legend for the FieldSet (defaults to '')
45709      */
45710     /**
45711      * @cfg {String/Object} autoCreate
45712      * A DomHelper element spec used to autocreate the fieldset (defaults to {tag: 'fieldset', cn: {tag:'legend'}})
45713      */
45714
45715     // private
45716     defaultAutoCreate : {tag: 'fieldset', cn: {tag:'legend'}},
45717
45718     // private
45719     onRender : function(ct, position){
45720         Roo.form.FieldSet.superclass.onRender.call(this, ct, position);
45721         if(this.legend){
45722             this.setLegend(this.legend);
45723         }
45724     },
45725
45726     // private
45727     setLegend : function(text){
45728         if(this.rendered){
45729             this.el.child('legend').update(text);
45730         }
45731     }
45732 });/*
45733  * Based on:
45734  * Ext JS Library 1.1.1
45735  * Copyright(c) 2006-2007, Ext JS, LLC.
45736  *
45737  * Originally Released Under LGPL - original licence link has changed is not relivant.
45738  *
45739  * Fork - LGPL
45740  * <script type="text/javascript">
45741  */
45742 /**
45743  * @class Roo.form.VTypes
45744  * Overridable validation definitions. The validations provided are basic and intended to be easily customizable and extended.
45745  * @singleton
45746  */
45747 Roo.form.VTypes = function(){
45748     // closure these in so they are only created once.
45749     var alpha = /^[a-zA-Z_]+$/;
45750     var alphanum = /^[a-zA-Z0-9_]+$/;
45751     var email = /^([\w]+)(.[\w]+)*@([\w-]+\.){1,5}([A-Za-z]){2,4}$/;
45752     var url = /(((https?)|(ftp)):\/\/([\-\w]+\.)+\w{2,3}(\/[%\-\w]+(\.\w{2,})?)*(([\w\-\.\?\\\/+@&#;`~=%!]*)(\.\w{2,})?)*\/?)/i;
45753
45754     // All these messages and functions are configurable
45755     return {
45756         /**
45757          * The function used to validate email addresses
45758          * @param {String} value The email address
45759          */
45760         'email' : function(v){
45761             return email.test(v);
45762         },
45763         /**
45764          * The error text to display when the email validation function returns false
45765          * @type String
45766          */
45767         'emailText' : 'This field should be an e-mail address in the format "user@domain.com"',
45768         /**
45769          * The keystroke filter mask to be applied on email input
45770          * @type RegExp
45771          */
45772         'emailMask' : /[a-z0-9_\.\-@]/i,
45773
45774         /**
45775          * The function used to validate URLs
45776          * @param {String} value The URL
45777          */
45778         'url' : function(v){
45779             return url.test(v);
45780         },
45781         /**
45782          * The error text to display when the url validation function returns false
45783          * @type String
45784          */
45785         'urlText' : 'This field should be a URL in the format "http:/'+'/www.domain.com"',
45786         
45787         /**
45788          * The function used to validate alpha values
45789          * @param {String} value The value
45790          */
45791         'alpha' : function(v){
45792             return alpha.test(v);
45793         },
45794         /**
45795          * The error text to display when the alpha validation function returns false
45796          * @type String
45797          */
45798         'alphaText' : 'This field should only contain letters and _',
45799         /**
45800          * The keystroke filter mask to be applied on alpha input
45801          * @type RegExp
45802          */
45803         'alphaMask' : /[a-z_]/i,
45804
45805         /**
45806          * The function used to validate alphanumeric values
45807          * @param {String} value The value
45808          */
45809         'alphanum' : function(v){
45810             return alphanum.test(v);
45811         },
45812         /**
45813          * The error text to display when the alphanumeric validation function returns false
45814          * @type String
45815          */
45816         'alphanumText' : 'This field should only contain letters, numbers and _',
45817         /**
45818          * The keystroke filter mask to be applied on alphanumeric input
45819          * @type RegExp
45820          */
45821         'alphanumMask' : /[a-z0-9_]/i
45822     };
45823 }();//<script type="text/javascript">
45824
45825 /**
45826  * @class Roo.form.FCKeditor
45827  * @extends Roo.form.TextArea
45828  * Wrapper around the FCKEditor http://www.fckeditor.net
45829  * @constructor
45830  * Creates a new FCKeditor
45831  * @param {Object} config Configuration options
45832  */
45833 Roo.form.FCKeditor = function(config){
45834     Roo.form.FCKeditor.superclass.constructor.call(this, config);
45835     this.addEvents({
45836          /**
45837          * @event editorinit
45838          * Fired when the editor is initialized - you can add extra handlers here..
45839          * @param {FCKeditor} this
45840          * @param {Object} the FCK object.
45841          */
45842         editorinit : true
45843     });
45844     
45845     
45846 };
45847 Roo.form.FCKeditor.editors = { };
45848 Roo.extend(Roo.form.FCKeditor, Roo.form.TextArea,
45849 {
45850     //defaultAutoCreate : {
45851     //    tag : "textarea",style   : "width:100px;height:60px;" ,autocomplete    : "off"
45852     //},
45853     // private
45854     /**
45855      * @cfg {Object} fck options - see fck manual for details.
45856      */
45857     fckconfig : false,
45858     
45859     /**
45860      * @cfg {Object} fck toolbar set (Basic or Default)
45861      */
45862     toolbarSet : 'Basic',
45863     /**
45864      * @cfg {Object} fck BasePath
45865      */ 
45866     basePath : '/fckeditor/',
45867     
45868     
45869     frame : false,
45870     
45871     value : '',
45872     
45873    
45874     onRender : function(ct, position)
45875     {
45876         if(!this.el){
45877             this.defaultAutoCreate = {
45878                 tag: "textarea",
45879                 style:"width:300px;height:60px;",
45880                 autocomplete: "off"
45881             };
45882         }
45883         Roo.form.FCKeditor.superclass.onRender.call(this, ct, position);
45884         /*
45885         if(this.grow){
45886             this.textSizeEl = Roo.DomHelper.append(document.body, {tag: "pre", cls: "x-form-grow-sizer"});
45887             if(this.preventScrollbars){
45888                 this.el.setStyle("overflow", "hidden");
45889             }
45890             this.el.setHeight(this.growMin);
45891         }
45892         */
45893         //console.log('onrender' + this.getId() );
45894         Roo.form.FCKeditor.editors[this.getId()] = this;
45895          
45896
45897         this.replaceTextarea() ;
45898         
45899     },
45900     
45901     getEditor : function() {
45902         return this.fckEditor;
45903     },
45904     /**
45905      * Sets a data value into the field and validates it.  To set the value directly without validation see {@link #setRawValue}.
45906      * @param {Mixed} value The value to set
45907      */
45908     
45909     
45910     setValue : function(value)
45911     {
45912         //console.log('setValue: ' + value);
45913         
45914         if(typeof(value) == 'undefined') { // not sure why this is happending...
45915             return;
45916         }
45917         Roo.form.FCKeditor.superclass.setValue.apply(this,[value]);
45918         
45919         //if(!this.el || !this.getEditor()) {
45920         //    this.value = value;
45921             //this.setValue.defer(100,this,[value]);    
45922         //    return;
45923         //} 
45924         
45925         if(!this.getEditor()) {
45926             return;
45927         }
45928         
45929         this.getEditor().SetData(value);
45930         
45931         //
45932
45933     },
45934
45935     /**
45936      * Returns the normalized data value (undefined or emptyText will be returned as '').  To return the raw value see {@link #getRawValue}.
45937      * @return {Mixed} value The field value
45938      */
45939     getValue : function()
45940     {
45941         
45942         if (this.frame && this.frame.dom.style.display == 'none') {
45943             return Roo.form.FCKeditor.superclass.getValue.call(this);
45944         }
45945         
45946         if(!this.el || !this.getEditor()) {
45947            
45948            // this.getValue.defer(100,this); 
45949             return this.value;
45950         }
45951        
45952         
45953         var value=this.getEditor().GetData();
45954         Roo.form.FCKeditor.superclass.setValue.apply(this,[value]);
45955         return Roo.form.FCKeditor.superclass.getValue.call(this);
45956         
45957
45958     },
45959
45960     /**
45961      * Returns the raw data value which may or may not be a valid, defined value.  To return a normalized value see {@link #getValue}.
45962      * @return {Mixed} value The field value
45963      */
45964     getRawValue : function()
45965     {
45966         if (this.frame && this.frame.dom.style.display == 'none') {
45967             return Roo.form.FCKeditor.superclass.getRawValue.call(this);
45968         }
45969         
45970         if(!this.el || !this.getEditor()) {
45971             //this.getRawValue.defer(100,this); 
45972             return this.value;
45973             return;
45974         }
45975         
45976         
45977         
45978         var value=this.getEditor().GetData();
45979         Roo.form.FCKeditor.superclass.setRawValue.apply(this,[value]);
45980         return Roo.form.FCKeditor.superclass.getRawValue.call(this);
45981          
45982     },
45983     
45984     setSize : function(w,h) {
45985         
45986         
45987         
45988         //if (this.frame && this.frame.dom.style.display == 'none') {
45989         //    Roo.form.FCKeditor.superclass.setSize.apply(this, [w, h]);
45990         //    return;
45991         //}
45992         //if(!this.el || !this.getEditor()) {
45993         //    this.setSize.defer(100,this, [w,h]); 
45994         //    return;
45995         //}
45996         
45997         
45998         
45999         Roo.form.FCKeditor.superclass.setSize.apply(this, [w, h]);
46000         
46001         this.frame.dom.setAttribute('width', w);
46002         this.frame.dom.setAttribute('height', h);
46003         this.frame.setSize(w,h);
46004         
46005     },
46006     
46007     toggleSourceEdit : function(value) {
46008         
46009       
46010          
46011         this.el.dom.style.display = value ? '' : 'none';
46012         this.frame.dom.style.display = value ?  'none' : '';
46013         
46014     },
46015     
46016     
46017     focus: function(tag)
46018     {
46019         if (this.frame.dom.style.display == 'none') {
46020             return Roo.form.FCKeditor.superclass.focus.call(this);
46021         }
46022         if(!this.el || !this.getEditor()) {
46023             this.focus.defer(100,this, [tag]); 
46024             return;
46025         }
46026         
46027         
46028         
46029         
46030         var tgs = this.getEditor().EditorDocument.getElementsByTagName(tag);
46031         this.getEditor().Focus();
46032         if (tgs.length) {
46033             if (!this.getEditor().Selection.GetSelection()) {
46034                 this.focus.defer(100,this, [tag]); 
46035                 return;
46036             }
46037             
46038             
46039             var r = this.getEditor().EditorDocument.createRange();
46040             r.setStart(tgs[0],0);
46041             r.setEnd(tgs[0],0);
46042             this.getEditor().Selection.GetSelection().removeAllRanges();
46043             this.getEditor().Selection.GetSelection().addRange(r);
46044             this.getEditor().Focus();
46045         }
46046         
46047     },
46048     
46049     
46050     
46051     replaceTextarea : function()
46052     {
46053         if ( document.getElementById( this.getId() + '___Frame' ) )
46054             return ;
46055         //if ( !this.checkBrowser || this._isCompatibleBrowser() )
46056         //{
46057             // We must check the elements firstly using the Id and then the name.
46058         var oTextarea = document.getElementById( this.getId() );
46059         
46060         var colElementsByName = document.getElementsByName( this.getId() ) ;
46061          
46062         oTextarea.style.display = 'none' ;
46063
46064         if ( oTextarea.tabIndex ) {            
46065             this.TabIndex = oTextarea.tabIndex ;
46066         }
46067         
46068         this._insertHtmlBefore( this._getConfigHtml(), oTextarea ) ;
46069         this._insertHtmlBefore( this._getIFrameHtml(), oTextarea ) ;
46070         this.frame = Roo.get(this.getId() + '___Frame')
46071     },
46072     
46073     _getConfigHtml : function()
46074     {
46075         var sConfig = '' ;
46076
46077         for ( var o in this.fckconfig ) {
46078             sConfig += sConfig.length > 0  ? '&amp;' : '';
46079             sConfig += encodeURIComponent( o ) + '=' + encodeURIComponent( this.fckconfig[o] ) ;
46080         }
46081
46082         return '<input type="hidden" id="' + this.getId() + '___Config" value="' + sConfig + '" style="display:none" />' ;
46083     },
46084     
46085     
46086     _getIFrameHtml : function()
46087     {
46088         var sFile = 'fckeditor.html' ;
46089         /* no idea what this is about..
46090         try
46091         {
46092             if ( (/fcksource=true/i).test( window.top.location.search ) )
46093                 sFile = 'fckeditor.original.html' ;
46094         }
46095         catch (e) { 
46096         */
46097
46098         var sLink = this.basePath + 'editor/' + sFile + '?InstanceName=' + encodeURIComponent( this.getId() ) ;
46099         sLink += this.toolbarSet ? ( '&amp;Toolbar=' + this.toolbarSet)  : '';
46100         
46101         
46102         var html = '<iframe id="' + this.getId() +
46103             '___Frame" src="' + sLink +
46104             '" width="' + this.width +
46105             '" height="' + this.height + '"' +
46106             (this.tabIndex ?  ' tabindex="' + this.tabIndex + '"' :'' ) +
46107             ' frameborder="0" scrolling="no"></iframe>' ;
46108
46109         return html ;
46110     },
46111     
46112     _insertHtmlBefore : function( html, element )
46113     {
46114         if ( element.insertAdjacentHTML )       {
46115             // IE
46116             element.insertAdjacentHTML( 'beforeBegin', html ) ;
46117         } else { // Gecko
46118             var oRange = document.createRange() ;
46119             oRange.setStartBefore( element ) ;
46120             var oFragment = oRange.createContextualFragment( html );
46121             element.parentNode.insertBefore( oFragment, element ) ;
46122         }
46123     }
46124     
46125     
46126   
46127     
46128     
46129     
46130     
46131
46132 });
46133
46134 //Roo.reg('fckeditor', Roo.form.FCKeditor);
46135
46136 function FCKeditor_OnComplete(editorInstance){
46137     var f = Roo.form.FCKeditor.editors[editorInstance.Name];
46138     f.fckEditor = editorInstance;
46139     //console.log("loaded");
46140     f.fireEvent('editorinit', f, editorInstance);
46141
46142   
46143
46144  
46145
46146
46147
46148
46149
46150
46151
46152
46153
46154
46155
46156
46157
46158
46159
46160 //<script type="text/javascript">
46161 /**
46162  * @class Roo.form.GridField
46163  * @extends Roo.form.Field
46164  * Embed a grid (or editable grid into a form)
46165  * STATUS ALPHA
46166  * 
46167  * This embeds a grid in a form, the value of the field should be the json encoded array of rows
46168  * it needs 
46169  * xgrid.store = Roo.data.Store
46170  * xgrid.store.proxy = Roo.data.MemoryProxy (data = [] )
46171  * xgrid.store.reader = Roo.data.JsonReader 
46172  * 
46173  * 
46174  * @constructor
46175  * Creates a new GridField
46176  * @param {Object} config Configuration options
46177  */
46178 Roo.form.GridField = function(config){
46179     Roo.form.GridField.superclass.constructor.call(this, config);
46180      
46181 };
46182
46183 Roo.extend(Roo.form.GridField, Roo.form.Field,  {
46184     /**
46185      * @cfg {Number} width  - used to restrict width of grid..
46186      */
46187     width : 100,
46188     /**
46189      * @cfg {Number} height - used to restrict height of grid..
46190      */
46191     height : 50,
46192      /**
46193      * @cfg {Object} xgrid (xtype'd description of grid) { xtype : 'Grid', dataSource: .... }
46194          * 
46195          *}
46196      */
46197     xgrid : false, 
46198     /**
46199      * @cfg {String/Object} autoCreate A DomHelper element spec, or true for a default element spec (defaults to
46200      * {tag: "input", type: "checkbox", autocomplete: "off"})
46201      */
46202    // defaultAutoCreate : { tag: 'div' },
46203     defaultAutoCreate : { tag: 'input', type: 'hidden', autocomplete: 'off'},
46204     /**
46205      * @cfg {String} addTitle Text to include for adding a title.
46206      */
46207     addTitle : false,
46208     //
46209     onResize : function(){
46210         Roo.form.Field.superclass.onResize.apply(this, arguments);
46211     },
46212
46213     initEvents : function(){
46214         // Roo.form.Checkbox.superclass.initEvents.call(this);
46215         // has no events...
46216        
46217     },
46218
46219
46220     getResizeEl : function(){
46221         return this.wrap;
46222     },
46223
46224     getPositionEl : function(){
46225         return this.wrap;
46226     },
46227
46228     // private
46229     onRender : function(ct, position){
46230         
46231         this.style = this.style || 'overflow: hidden; border:1px solid #c3daf9;';
46232         var style = this.style;
46233         delete this.style;
46234         
46235         Roo.form.GridField.superclass.onRender.call(this, ct, position);
46236         this.wrap = this.el.wrap({cls: ''}); // not sure why ive done thsi...
46237         this.viewEl = this.wrap.createChild({ tag: 'div' });
46238         if (style) {
46239             this.viewEl.applyStyles(style);
46240         }
46241         if (this.width) {
46242             this.viewEl.setWidth(this.width);
46243         }
46244         if (this.height) {
46245             this.viewEl.setHeight(this.height);
46246         }
46247         //if(this.inputValue !== undefined){
46248         //this.setValue(this.value);
46249         
46250         
46251         this.grid = new Roo.grid[this.xgrid.xtype](this.viewEl, this.xgrid);
46252         
46253         
46254         this.grid.render();
46255         this.grid.getDataSource().on('remove', this.refreshValue, this);
46256         this.grid.getDataSource().on('update', this.refreshValue, this);
46257         this.grid.on('afteredit', this.refreshValue, this);
46258  
46259     },
46260      
46261     
46262     /**
46263      * Sets the value of the item. 
46264      * @param {String} either an object  or a string..
46265      */
46266     setValue : function(v){
46267         //this.value = v;
46268         v = v || []; // empty set..
46269         // this does not seem smart - it really only affects memoryproxy grids..
46270         if (this.grid && this.grid.getDataSource() && typeof(v) != 'undefined') {
46271             var ds = this.grid.getDataSource();
46272             // assumes a json reader..
46273             var data = {}
46274             data[ds.reader.meta.root ] =  typeof(v) == 'string' ? Roo.decode(v) : v;
46275             ds.loadData( data);
46276         }
46277         // clear selection so it does not get stale.
46278         if (this.grid.sm) { 
46279             this.grid.sm.clearSelections();
46280         }
46281         
46282         Roo.form.GridField.superclass.setValue.call(this, v);
46283         this.refreshValue();
46284         // should load data in the grid really....
46285     },
46286     
46287     // private
46288     refreshValue: function() {
46289          var val = [];
46290         this.grid.getDataSource().each(function(r) {
46291             val.push(r.data);
46292         });
46293         this.el.dom.value = Roo.encode(val);
46294     }
46295     
46296      
46297     
46298     
46299 });/*
46300  * Based on:
46301  * Ext JS Library 1.1.1
46302  * Copyright(c) 2006-2007, Ext JS, LLC.
46303  *
46304  * Originally Released Under LGPL - original licence link has changed is not relivant.
46305  *
46306  * Fork - LGPL
46307  * <script type="text/javascript">
46308  */
46309 /**
46310  * @class Roo.form.DisplayField
46311  * @extends Roo.form.Field
46312  * A generic Field to display non-editable data.
46313  * @constructor
46314  * Creates a new Display Field item.
46315  * @param {Object} config Configuration options
46316  */
46317 Roo.form.DisplayField = function(config){
46318     Roo.form.DisplayField.superclass.constructor.call(this, config);
46319     
46320 };
46321
46322 Roo.extend(Roo.form.DisplayField, Roo.form.TextField,  {
46323     inputType:      'hidden',
46324     allowBlank:     true,
46325     readOnly:         true,
46326     
46327  
46328     /**
46329      * @cfg {String} focusClass The CSS class to use when the checkbox receives focus (defaults to undefined)
46330      */
46331     focusClass : undefined,
46332     /**
46333      * @cfg {String} fieldClass The default CSS class for the checkbox (defaults to "x-form-field")
46334      */
46335     fieldClass: 'x-form-field',
46336     
46337      /**
46338      * @cfg {Function} valueRenderer The renderer for the field (so you can reformat output). should return raw HTML
46339      */
46340     valueRenderer: undefined,
46341     
46342     width: 100,
46343     /**
46344      * @cfg {String/Object} autoCreate A DomHelper element spec, or true for a default element spec (defaults to
46345      * {tag: "input", type: "checkbox", autocomplete: "off"})
46346      */
46347      
46348  //   defaultAutoCreate : { tag: 'input', type: 'hidden', autocomplete: 'off'},
46349
46350     onResize : function(){
46351         Roo.form.DisplayField.superclass.onResize.apply(this, arguments);
46352         
46353     },
46354
46355     initEvents : function(){
46356         // Roo.form.Checkbox.superclass.initEvents.call(this);
46357         // has no events...
46358        
46359     },
46360
46361
46362     getResizeEl : function(){
46363         return this.wrap;
46364     },
46365
46366     getPositionEl : function(){
46367         return this.wrap;
46368     },
46369
46370     // private
46371     onRender : function(ct, position){
46372         
46373         Roo.form.DisplayField.superclass.onRender.call(this, ct, position);
46374         //if(this.inputValue !== undefined){
46375         this.wrap = this.el.wrap();
46376         
46377         this.viewEl = this.wrap.createChild({ tag: 'div', cls: 'x-form-displayfield'});
46378         
46379         if (this.bodyStyle) {
46380             this.viewEl.applyStyles(this.bodyStyle);
46381         }
46382         //this.viewEl.setStyle('padding', '2px');
46383         
46384         this.setValue(this.value);
46385         
46386     },
46387 /*
46388     // private
46389     initValue : Roo.emptyFn,
46390
46391   */
46392
46393         // private
46394     onClick : function(){
46395         
46396     },
46397
46398     /**
46399      * Sets the checked state of the checkbox.
46400      * @param {Boolean/String} checked True, 'true', '1', or 'on' to check the checkbox, any other value will uncheck it.
46401      */
46402     setValue : function(v){
46403         this.value = v;
46404         var html = this.valueRenderer ?  this.valueRenderer(v) : String.format('{0}', v);
46405         // this might be called before we have a dom element..
46406         if (!this.viewEl) {
46407             return;
46408         }
46409         this.viewEl.dom.innerHTML = html;
46410         Roo.form.DisplayField.superclass.setValue.call(this, v);
46411
46412     }
46413 });/*
46414  * 
46415  * Licence- LGPL
46416  * 
46417  */
46418
46419 /**
46420  * @class Roo.form.DayPicker
46421  * @extends Roo.form.Field
46422  * A Day picker show [M] [T] [W] ....
46423  * @constructor
46424  * Creates a new Day Picker
46425  * @param {Object} config Configuration options
46426  */
46427 Roo.form.DayPicker= function(config){
46428     Roo.form.DayPicker.superclass.constructor.call(this, config);
46429      
46430 };
46431
46432 Roo.extend(Roo.form.DayPicker, Roo.form.Field,  {
46433     /**
46434      * @cfg {String} focusClass The CSS class to use when the checkbox receives focus (defaults to undefined)
46435      */
46436     focusClass : undefined,
46437     /**
46438      * @cfg {String} fieldClass The default CSS class for the checkbox (defaults to "x-form-field")
46439      */
46440     fieldClass: "x-form-field",
46441    
46442     /**
46443      * @cfg {String/Object} autoCreate A DomHelper element spec, or true for a default element spec (defaults to
46444      * {tag: "input", type: "checkbox", autocomplete: "off"})
46445      */
46446     defaultAutoCreate : { tag: "input", type: 'hidden', autocomplete: "off"},
46447     
46448    
46449     actionMode : 'viewEl', 
46450     //
46451     // private
46452  
46453     inputType : 'hidden',
46454     
46455      
46456     inputElement: false, // real input element?
46457     basedOn: false, // ????
46458     
46459     isFormField: true, // not sure where this is needed!!!!
46460
46461     onResize : function(){
46462         Roo.form.Checkbox.superclass.onResize.apply(this, arguments);
46463         if(!this.boxLabel){
46464             this.el.alignTo(this.wrap, 'c-c');
46465         }
46466     },
46467
46468     initEvents : function(){
46469         Roo.form.Checkbox.superclass.initEvents.call(this);
46470         this.el.on("click", this.onClick,  this);
46471         this.el.on("change", this.onClick,  this);
46472     },
46473
46474
46475     getResizeEl : function(){
46476         return this.wrap;
46477     },
46478
46479     getPositionEl : function(){
46480         return this.wrap;
46481     },
46482
46483     
46484     // private
46485     onRender : function(ct, position){
46486         Roo.form.Checkbox.superclass.onRender.call(this, ct, position);
46487        
46488         this.wrap = this.el.wrap({cls: 'x-form-daypick-item '});
46489         
46490         var r1 = '<table><tr>';
46491         var r2 = '<tr class="x-form-daypick-icons">';
46492         for (var i=0; i < 7; i++) {
46493             r1+= '<td><div>' + Date.dayNames[i].substring(0,3) + '</div></td>';
46494             r2+= '<td><img class="x-menu-item-icon" src="' + Roo.BLANK_IMAGE_URL  +'"></td>';
46495         }
46496         
46497         var viewEl = this.wrap.createChild( r1 + '</tr>' + r2 + '</tr></table>');
46498         viewEl.select('img').on('click', this.onClick, this);
46499         this.viewEl = viewEl;   
46500         
46501         
46502         // this will not work on Chrome!!!
46503         this.el.on('DOMAttrModified', this.setFromHidden,  this); //ff
46504         this.el.on('propertychange', this.setFromHidden,  this);  //ie
46505         
46506         
46507           
46508
46509     },
46510
46511     // private
46512     initValue : Roo.emptyFn,
46513
46514     /**
46515      * Returns the checked state of the checkbox.
46516      * @return {Boolean} True if checked, else false
46517      */
46518     getValue : function(){
46519         return this.el.dom.value;
46520         
46521     },
46522
46523         // private
46524     onClick : function(e){ 
46525         //this.setChecked(!this.checked);
46526         Roo.get(e.target).toggleClass('x-menu-item-checked');
46527         this.refreshValue();
46528         //if(this.el.dom.checked != this.checked){
46529         //    this.setValue(this.el.dom.checked);
46530        // }
46531     },
46532     
46533     // private
46534     refreshValue : function()
46535     {
46536         var val = '';
46537         this.viewEl.select('img',true).each(function(e,i,n)  {
46538             val += e.is(".x-menu-item-checked") ? String(n) : '';
46539         });
46540         this.setValue(val, true);
46541     },
46542
46543     /**
46544      * Sets the checked state of the checkbox.
46545      * On is always based on a string comparison between inputValue and the param.
46546      * @param {Boolean/String} value - the value to set 
46547      * @param {Boolean/String} suppressEvent - whether to suppress the checkchange event.
46548      */
46549     setValue : function(v,suppressEvent){
46550         if (!this.el.dom) {
46551             return;
46552         }
46553         var old = this.el.dom.value ;
46554         this.el.dom.value = v;
46555         if (suppressEvent) {
46556             return ;
46557         }
46558          
46559         // update display..
46560         this.viewEl.select('img',true).each(function(e,i,n)  {
46561             
46562             var on = e.is(".x-menu-item-checked");
46563             var newv = v.indexOf(String(n)) > -1;
46564             if (on != newv) {
46565                 e.toggleClass('x-menu-item-checked');
46566             }
46567             
46568         });
46569         
46570         
46571         this.fireEvent('change', this, v, old);
46572         
46573         
46574     },
46575    
46576     // handle setting of hidden value by some other method!!?!?
46577     setFromHidden: function()
46578     {
46579         if(!this.el){
46580             return;
46581         }
46582         //console.log("SET FROM HIDDEN");
46583         //alert('setFrom hidden');
46584         this.setValue(this.el.dom.value);
46585     },
46586     
46587     onDestroy : function()
46588     {
46589         if(this.viewEl){
46590             Roo.get(this.viewEl).remove();
46591         }
46592          
46593         Roo.form.DayPicker.superclass.onDestroy.call(this);
46594     }
46595
46596 });/*
46597  * RooJS Library 1.1.1
46598  * Copyright(c) 2008-2011  Alan Knowles
46599  *
46600  * License - LGPL
46601  */
46602  
46603
46604 /**
46605  * @class Roo.form.ComboCheck
46606  * @extends Roo.form.ComboBox
46607  * A combobox for multiple select items.
46608  *
46609  * FIXME - could do with a reset button..
46610  * 
46611  * @constructor
46612  * Create a new ComboCheck
46613  * @param {Object} config Configuration options
46614  */
46615 Roo.form.ComboCheck = function(config){
46616     Roo.form.ComboCheck.superclass.constructor.call(this, config);
46617     // should verify some data...
46618     // like
46619     // hiddenName = required..
46620     // displayField = required
46621     // valudField == required
46622     var req= [ 'hiddenName', 'displayField', 'valueField' ];
46623     var _t = this;
46624     Roo.each(req, function(e) {
46625         if ((typeof(_t[e]) == 'undefined' ) || !_t[e].length) {
46626             throw "Roo.form.ComboCheck : missing value for: " + e;
46627         }
46628     });
46629     
46630     
46631 };
46632
46633 Roo.extend(Roo.form.ComboCheck, Roo.form.ComboBox, {
46634      
46635      
46636     editable : false,
46637      
46638     selectedClass: 'x-menu-item-checked', 
46639     
46640     // private
46641     onRender : function(ct, position){
46642         var _t = this;
46643         
46644         
46645         
46646         if(!this.tpl){
46647             var cls = 'x-combo-list';
46648
46649             
46650             this.tpl =  new Roo.Template({
46651                 html :  '<div class="'+cls+'-item x-menu-check-item">' +
46652                    '<img class="x-menu-item-icon" style="margin: 0px;" src="' + Roo.BLANK_IMAGE_URL + '">' + 
46653                    '<span>{' + this.displayField + '}</span>' +
46654                     '</div>' 
46655                 
46656             });
46657         }
46658  
46659         
46660         Roo.form.ComboCheck.superclass.onRender.call(this, ct, position);
46661         this.view.singleSelect = false;
46662         this.view.multiSelect = true;
46663         this.view.toggleSelect = true;
46664         this.pageTb.add(new Roo.Toolbar.Fill(), {
46665             
46666             text: 'Done',
46667             handler: function()
46668             {
46669                 _t.collapse();
46670             }
46671         });
46672     },
46673     
46674     onViewOver : function(e, t){
46675         // do nothing...
46676         return;
46677         
46678     },
46679     
46680     onViewClick : function(doFocus,index){
46681         return;
46682         
46683     },
46684     select: function () {
46685         //Roo.log("SELECT CALLED");
46686     },
46687      
46688     selectByValue : function(xv, scrollIntoView){
46689         var ar = this.getValueArray();
46690         var sels = [];
46691         
46692         Roo.each(ar, function(v) {
46693             if(v === undefined || v === null){
46694                 return;
46695             }
46696             var r = this.findRecord(this.valueField, v);
46697             if(r){
46698                 sels.push(this.store.indexOf(r))
46699                 
46700             }
46701         },this);
46702         this.view.select(sels);
46703         return false;
46704     },
46705     
46706     
46707     
46708     onSelect : function(record, index){
46709        // Roo.log("onselect Called");
46710        // this is only called by the clear button now..
46711         this.view.clearSelections();
46712         this.setValue('[]');
46713         if (this.value != this.valueBefore) {
46714             this.fireEvent('change', this, this.value, this.valueBefore);
46715             this.valueBefore = this.value;
46716         }
46717     },
46718     getValueArray : function()
46719     {
46720         var ar = [] ;
46721         
46722         try {
46723             //Roo.log(this.value);
46724             if (typeof(this.value) == 'undefined') {
46725                 return [];
46726             }
46727             var ar = Roo.decode(this.value);
46728             return  ar instanceof Array ? ar : []; //?? valid?
46729             
46730         } catch(e) {
46731             Roo.log(e + "\nRoo.form.ComboCheck:getValueArray  invalid data:" + this.getValue());
46732             return [];
46733         }
46734          
46735     },
46736     expand : function ()
46737     {
46738         
46739         Roo.form.ComboCheck.superclass.expand.call(this);
46740         this.valueBefore = typeof(this.value) == 'undefined' ? '' : this.value;
46741         //this.valueBefore = typeof(this.valueBefore) == 'undefined' ? '' : this.valueBefore;
46742         
46743
46744     },
46745     
46746     collapse : function(){
46747         Roo.form.ComboCheck.superclass.collapse.call(this);
46748         var sl = this.view.getSelectedIndexes();
46749         var st = this.store;
46750         var nv = [];
46751         var tv = [];
46752         var r;
46753         Roo.each(sl, function(i) {
46754             r = st.getAt(i);
46755             nv.push(r.get(this.valueField));
46756         },this);
46757         this.setValue(Roo.encode(nv));
46758         if (this.value != this.valueBefore) {
46759
46760             this.fireEvent('change', this, this.value, this.valueBefore);
46761             this.valueBefore = this.value;
46762         }
46763         
46764     },
46765     
46766     setValue : function(v){
46767         // Roo.log(v);
46768         this.value = v;
46769         
46770         var vals = this.getValueArray();
46771         var tv = [];
46772         Roo.each(vals, function(k) {
46773             var r = this.findRecord(this.valueField, k);
46774             if(r){
46775                 tv.push(r.data[this.displayField]);
46776             }else if(this.valueNotFoundText !== undefined){
46777                 tv.push( this.valueNotFoundText );
46778             }
46779         },this);
46780        // Roo.log(tv);
46781         
46782         Roo.form.ComboBox.superclass.setValue.call(this, tv.join(', '));
46783         this.hiddenField.value = v;
46784         this.value = v;
46785     }
46786     
46787 });/*
46788  * Based on:
46789  * Ext JS Library 1.1.1
46790  * Copyright(c) 2006-2007, Ext JS, LLC.
46791  *
46792  * Originally Released Under LGPL - original licence link has changed is not relivant.
46793  *
46794  * Fork - LGPL
46795  * <script type="text/javascript">
46796  */
46797  
46798 /**
46799  * @class Roo.form.Signature
46800  * @extends Roo.form.Field
46801  * Signature field.  
46802  * @constructor
46803  * 
46804  * @param {Object} config Configuration options
46805  */
46806
46807 Roo.form.Signature = function(config){
46808     Roo.form.Signature.superclass.constructor.call(this, config);
46809     
46810     this.addEvents({// not in used??
46811          /**
46812          * @event confirm
46813          * Fires when the 'confirm' icon is pressed (add a listener to enable add button)
46814              * @param {Roo.form.Signature} combo This combo box
46815              */
46816         'confirm' : true,
46817         /**
46818          * @event reset
46819          * Fires when the 'edit' icon is pressed (add a listener to enable add button)
46820              * @param {Roo.form.ComboBox} combo This combo box
46821              * @param {Roo.data.Record|false} record The data record returned from the underlying store (or false on nothing selected)
46822              */
46823         'reset' : true
46824     });
46825 };
46826
46827 Roo.extend(Roo.form.Signature, Roo.form.Field,  {
46828     /**
46829      * @cfg {Object} labels Label to use when rendering a form.
46830      * defaults to 
46831      * labels : { 
46832      *      clear : "Clear",
46833      *      confirm : "Confirm"
46834      *  }
46835      */
46836     labels : { 
46837         clear : "Clear",
46838         confirm : "Confirm"
46839     },
46840     /**
46841      * @cfg {Number} width The signature panel width (defaults to 300)
46842      */
46843     width: 300,
46844     /**
46845      * @cfg {Number} height The signature panel height (defaults to 100)
46846      */
46847     height : 100,
46848     /**
46849      * @cfg {Boolean} allowBlank False to validate that the value length > 0 (defaults to false)
46850      */
46851     allowBlank : false,
46852     
46853     //private
46854     // {Object} signPanel The signature SVG panel element (defaults to {})
46855     signPanel : {},
46856     // {Boolean} isMouseDown False to validate that the mouse down event (defaults to false)
46857     isMouseDown : false,
46858     // {Boolean} isConfirmed validate the signature is confirmed or not for submitting form (defaults to false)
46859     isConfirmed : false,
46860     // {String} signatureTmp SVG mapping string (defaults to empty string)
46861     signatureTmp : '',
46862     
46863     
46864     defaultAutoCreate : { // modified by initCompnoent..
46865         tag: "input",
46866         type:"hidden"
46867     },
46868
46869     // private
46870     onRender : function(ct, position){
46871         
46872         Roo.form.Signature.superclass.onRender.call(this, ct, position);
46873         
46874         this.wrap = this.el.wrap({
46875             cls:'x-form-signature-wrap', style : 'width: ' + this.width + 'px', cn:{cls:'x-form-signature'}
46876         });
46877         
46878         this.createToolbar(this);
46879         this.signPanel = this.wrap.createChild({
46880                 tag: 'div',
46881                 style: 'width: ' + this.width + 'px; height: ' + this.height + 'px; border: 0;'
46882             }, this.el
46883         );
46884             
46885         this.svgID = Roo.id();
46886         this.svgEl = this.signPanel.createChild({
46887               xmlns : 'http://www.w3.org/2000/svg',
46888               tag : 'svg',
46889               id : this.svgID + "-svg",
46890               width: this.width,
46891               height: this.height,
46892               viewBox: '0 0 '+this.width+' '+this.height,
46893               cn : [
46894                 {
46895                     tag: "rect",
46896                     id: this.svgID + "-svg-r",
46897                     width: this.width,
46898                     height: this.height,
46899                     fill: "#ffa"
46900                 },
46901                 {
46902                     tag: "line",
46903                     id: this.svgID + "-svg-l",
46904                     x1: "0", // start
46905                     y1: (this.height*0.8), // start set the line in 80% of height
46906                     x2: this.width, // end
46907                     y2: (this.height*0.8), // end set the line in 80% of height
46908                     'stroke': "#666",
46909                     'stroke-width': "1",
46910                     'stroke-dasharray': "3",
46911                     'shape-rendering': "crispEdges",
46912                     'pointer-events': "none"
46913                 },
46914                 {
46915                     tag: "path",
46916                     id: this.svgID + "-svg-p",
46917                     'stroke': "navy",
46918                     'stroke-width': "3",
46919                     'fill': "none",
46920                     'pointer-events': 'none'
46921                 }
46922               ]
46923         });
46924         this.createSVG();
46925         this.svgBox = this.svgEl.dom.getScreenCTM();
46926     },
46927     createSVG : function(){ 
46928         var svg = this.signPanel;
46929         var r = svg.select('#'+ this.svgID + '-svg-r', true).first().dom;
46930         var t = this;
46931
46932         r.addEventListener('mousedown', function(e) { return t.down(e); }, false);
46933         r.addEventListener('mousemove', function(e) { return t.move(e); }, false);
46934         r.addEventListener('mouseup', function(e) { return t.up(e); }, false);
46935         r.addEventListener('mouseout', function(e) { return t.up(e); }, false);
46936         r.addEventListener('touchstart', function(e) { return t.down(e); }, false);
46937         r.addEventListener('touchmove', function(e) { return t.move(e); }, false);
46938         r.addEventListener('touchend', function(e) { return t.up(e); }, false);
46939         
46940     },
46941     isTouchEvent : function(e){
46942         return e.type.match(/^touch/);
46943     },
46944     getCoords : function (e) {
46945         var pt    = this.svgEl.dom.createSVGPoint();
46946         pt.x = e.clientX; 
46947         pt.y = e.clientY;
46948         if (this.isTouchEvent(e)) {
46949             pt.x =  e.targetTouches[0].clientX 
46950             pt.y = e.targetTouches[0].clientY;
46951         }
46952         var a = this.svgEl.dom.getScreenCTM();
46953         var b = a.inverse();
46954         var mx = pt.matrixTransform(b);
46955         return mx.x + ',' + mx.y;
46956     },
46957     //mouse event headler 
46958     down : function (e) {
46959         this.signatureTmp += 'M' + this.getCoords(e) + ' ';
46960         this.signPanel.select('#'+ this.svgID + '-svg-p', true).first().attr('d', this.signatureTmp);
46961         
46962         this.isMouseDown = true;
46963         
46964         e.preventDefault();
46965     },
46966     move : function (e) {
46967         if (this.isMouseDown) {
46968             this.signatureTmp += 'L' + this.getCoords(e) + ' ';
46969             this.signPanel.select('#'+ this.svgID + '-svg-p', true).first().attr( 'd', this.signatureTmp);
46970         }
46971         
46972         e.preventDefault();
46973     },
46974     up : function (e) {
46975         this.isMouseDown = false;
46976         var sp = this.signatureTmp.split(' ');
46977         
46978         if(sp.length > 1){
46979             if(!sp[sp.length-2].match(/^L/)){
46980                 sp.pop();
46981                 sp.pop();
46982                 sp.push("");
46983                 this.signatureTmp = sp.join(" ");
46984             }
46985         }
46986         if(this.getValue() != this.signatureTmp){
46987             this.signPanel.select('#'+ this.svgID + '-svg-r', true).first().attr('fill', '#ffa');
46988             this.isConfirmed = false;
46989         }
46990         e.preventDefault();
46991     },
46992     
46993     /**
46994      * Protected method that will not generally be called directly. It
46995      * is called when the editor creates its toolbar. Override this method if you need to
46996      * add custom toolbar buttons.
46997      * @param {HtmlEditor} editor
46998      */
46999     createToolbar : function(editor){
47000          function btn(id, toggle, handler){
47001             var xid = fid + '-'+ id ;
47002             return {
47003                 id : xid,
47004                 cmd : id,
47005                 cls : 'x-btn-icon x-edit-'+id,
47006                 enableToggle:toggle !== false,
47007                 scope: editor, // was editor...
47008                 handler:handler||editor.relayBtnCmd,
47009                 clickEvent:'mousedown',
47010                 tooltip: etb.buttonTips[id] || undefined, ///tips ???
47011                 tabIndex:-1
47012             };
47013         }
47014         
47015         
47016         var tb = new Roo.Toolbar(editor.wrap.dom.firstChild);
47017         this.tb = tb;
47018         this.tb.add(
47019            {
47020                 cls : ' x-signature-btn x-signature-'+id,
47021                 scope: editor, // was editor...
47022                 handler: this.reset,
47023                 clickEvent:'mousedown',
47024                 text: this.labels.clear
47025             },
47026             {
47027                  xtype : 'Fill',
47028                  xns: Roo.Toolbar
47029             }, 
47030             {
47031                 cls : '  x-signature-btn x-signature-'+id,
47032                 scope: editor, // was editor...
47033                 handler: this.confirmHandler,
47034                 clickEvent:'mousedown',
47035                 text: this.labels.confirm
47036             }
47037         );
47038     
47039     },
47040     //public
47041     /**
47042      * when user is clicked confirm then show this image.....
47043      * 
47044      * @return {String} Image Data URI
47045      */
47046     getImageDataURI : function(){
47047         var svg = this.svgEl.dom.parentNode.innerHTML;
47048         var src = 'data:image/svg+xml;base64,'+window.btoa(svg);
47049         return src; 
47050     },
47051     /**
47052      * 
47053      * @return {Boolean} this.isConfirmed
47054      */
47055     getConfirmed : function(){
47056         return this.isConfirmed;
47057     },
47058     /**
47059      * 
47060      * @return {Number} this.width
47061      */
47062     getWidth : function(){
47063         return this.width;
47064     },
47065     /**
47066      * 
47067      * @return {Number} this.height
47068      */
47069     getHeight : function(){
47070         return this.height;
47071     },
47072     // private
47073     getSignature : function(){
47074         return this.signatureTmp;
47075     },
47076     // private
47077     reset : function(){
47078         this.signatureTmp = '';
47079         this.signPanel.select('#'+ this.svgID + '-svg-r', true).first().attr('fill', '#ffa');
47080         this.signPanel.select('#'+ this.svgID + '-svg-p', true).first().attr( 'd', '');
47081         this.isConfirmed = false;
47082         Roo.form.Signature.superclass.reset.call(this);
47083     },
47084     setSignature : function(s){
47085         this.signatureTmp = s;
47086         this.signPanel.select('#'+ this.svgID + '-svg-r', true).first().attr('fill', '#ffa');
47087         this.signPanel.select('#'+ this.svgID + '-svg-p', true).first().attr( 'd', s);
47088         this.setValue(s);
47089         this.isConfirmed = false;
47090         Roo.form.Signature.superclass.reset.call(this);
47091     }, 
47092     test : function(){
47093 //        Roo.log(this.signPanel.dom.contentWindow.up())
47094     },
47095     //private
47096     setConfirmed : function(){
47097         
47098         
47099         
47100 //        Roo.log(Roo.get(this.signPanel.dom.contentWindow.r).attr('fill', '#cfc'));
47101     },
47102     // private
47103     confirmHandler : function(){
47104         if(!this.getSignature()){
47105             return;
47106         }
47107         
47108         this.signPanel.select('#'+ this.svgID + '-svg-r', true).first().attr('fill', '#cfc');
47109         this.setValue(this.getSignature());
47110         this.isConfirmed = true;
47111         
47112         this.fireEvent('confirm', this);
47113     },
47114     // private
47115     // Subclasses should provide the validation implementation by overriding this
47116     validateValue : function(value){
47117         if(this.allowBlank){
47118             return true;
47119         }
47120         
47121         if(this.isConfirmed){
47122             return true;
47123         }
47124         return false;
47125     }
47126 });/*
47127  * Based on:
47128  * Ext JS Library 1.1.1
47129  * Copyright(c) 2006-2007, Ext JS, LLC.
47130  *
47131  * Originally Released Under LGPL - original licence link has changed is not relivant.
47132  *
47133  * Fork - LGPL
47134  * <script type="text/javascript">
47135  */
47136  
47137
47138 /**
47139  * @class Roo.form.ComboBox
47140  * @extends Roo.form.TriggerField
47141  * A combobox control with support for autocomplete, remote-loading, paging and many other features.
47142  * @constructor
47143  * Create a new ComboBox.
47144  * @param {Object} config Configuration options
47145  */
47146 Roo.form.Select = function(config){
47147     Roo.form.Select.superclass.constructor.call(this, config);
47148      
47149 };
47150
47151 Roo.extend(Roo.form.Select , Roo.form.ComboBox, {
47152     /**
47153      * @cfg {String/HTMLElement/Element} transform The id, DOM node or element of an existing select to convert to a ComboBox
47154      */
47155     /**
47156      * @cfg {Boolean} lazyRender True to prevent the ComboBox from rendering until requested (should always be used when
47157      * rendering into an Roo.Editor, defaults to false)
47158      */
47159     /**
47160      * @cfg {Boolean/Object} autoCreate A DomHelper element spec, or true for a default element spec (defaults to:
47161      * {tag: "input", type: "text", size: "24", autocomplete: "off"})
47162      */
47163     /**
47164      * @cfg {Roo.data.Store} store The data store to which this combo is bound (defaults to undefined)
47165      */
47166     /**
47167      * @cfg {String} title If supplied, a header element is created containing this text and added into the top of
47168      * the dropdown list (defaults to undefined, with no header element)
47169      */
47170
47171      /**
47172      * @cfg {String/Roo.Template} tpl The template to use to render the output
47173      */
47174      
47175     // private
47176     defaultAutoCreate : {tag: "select"  },
47177     /**
47178      * @cfg {Number} listWidth The width in pixels of the dropdown list (defaults to the width of the ComboBox field)
47179      */
47180     listWidth: undefined,
47181     /**
47182      * @cfg {String} displayField The underlying data field name to bind to this CombBox (defaults to undefined if
47183      * mode = 'remote' or 'text' if mode = 'local')
47184      */
47185     displayField: undefined,
47186     /**
47187      * @cfg {String} valueField The underlying data value name to bind to this CombBox (defaults to undefined if
47188      * mode = 'remote' or 'value' if mode = 'local'). 
47189      * Note: use of a valueField requires the user make a selection
47190      * in order for a value to be mapped.
47191      */
47192     valueField: undefined,
47193     
47194     
47195     /**
47196      * @cfg {String} hiddenName If specified, a hidden form field with this name is dynamically generated to store the
47197      * field's data value (defaults to the underlying DOM element's name)
47198      */
47199     hiddenName: undefined,
47200     /**
47201      * @cfg {String} listClass CSS class to apply to the dropdown list element (defaults to '')
47202      */
47203     listClass: '',
47204     /**
47205      * @cfg {String} selectedClass CSS class to apply to the selected item in the dropdown list (defaults to 'x-combo-selected')
47206      */
47207     selectedClass: 'x-combo-selected',
47208     /**
47209      * @cfg {String} triggerClass An additional CSS class used to style the trigger button.  The trigger will always get the
47210      * class 'x-form-trigger' and triggerClass will be <b>appended</b> if specified (defaults to 'x-form-arrow-trigger'
47211      * which displays a downward arrow icon).
47212      */
47213     triggerClass : 'x-form-arrow-trigger',
47214     /**
47215      * @cfg {Boolean/String} shadow True or "sides" for the default effect, "frame" for 4-way shadow, and "drop" for bottom-right
47216      */
47217     shadow:'sides',
47218     /**
47219      * @cfg {String} listAlign A valid anchor position value. See {@link Roo.Element#alignTo} for details on supported
47220      * anchor positions (defaults to 'tl-bl')
47221      */
47222     listAlign: 'tl-bl?',
47223     /**
47224      * @cfg {Number} maxHeight The maximum height in pixels of the dropdown list before scrollbars are shown (defaults to 300)
47225      */
47226     maxHeight: 300,
47227     /**
47228      * @cfg {String} triggerAction The action to execute when the trigger field is activated.  Use 'all' to run the
47229      * query specified by the allQuery config option (defaults to 'query')
47230      */
47231     triggerAction: 'query',
47232     /**
47233      * @cfg {Number} minChars The minimum number of characters the user must type before autocomplete and typeahead activate
47234      * (defaults to 4, does not apply if editable = false)
47235      */
47236     minChars : 4,
47237     /**
47238      * @cfg {Boolean} typeAhead True to populate and autoselect the remainder of the text being typed after a configurable
47239      * delay (typeAheadDelay) if it matches a known value (defaults to false)
47240      */
47241     typeAhead: false,
47242     /**
47243      * @cfg {Number} queryDelay The length of time in milliseconds to delay between the start of typing and sending the
47244      * query to filter the dropdown list (defaults to 500 if mode = 'remote' or 10 if mode = 'local')
47245      */
47246     queryDelay: 500,
47247     /**
47248      * @cfg {Number} pageSize If greater than 0, a paging toolbar is displayed in the footer of the dropdown list and the
47249      * filter queries will execute with page start and limit parameters.  Only applies when mode = 'remote' (defaults to 0)
47250      */
47251     pageSize: 0,
47252     /**
47253      * @cfg {Boolean} selectOnFocus True to select any existing text in the field immediately on focus.  Only applies
47254      * when editable = true (defaults to false)
47255      */
47256     selectOnFocus:false,
47257     /**
47258      * @cfg {String} queryParam Name of the query as it will be passed on the querystring (defaults to 'query')
47259      */
47260     queryParam: 'query',
47261     /**
47262      * @cfg {String} loadingText The text to display in the dropdown list while data is loading.  Only applies
47263      * when mode = 'remote' (defaults to 'Loading...')
47264      */
47265     loadingText: 'Loading...',
47266     /**
47267      * @cfg {Boolean} resizable True to add a resize handle to the bottom of the dropdown list (defaults to false)
47268      */
47269     resizable: false,
47270     /**
47271      * @cfg {Number} handleHeight The height in pixels of the dropdown list resize handle if resizable = true (defaults to 8)
47272      */
47273     handleHeight : 8,
47274     /**
47275      * @cfg {Boolean} editable False to prevent the user from typing text directly into the field, just like a
47276      * traditional select (defaults to true)
47277      */
47278     editable: true,
47279     /**
47280      * @cfg {String} allQuery The text query to send to the server to return all records for the list with no filtering (defaults to '')
47281      */
47282     allQuery: '',
47283     /**
47284      * @cfg {String} mode Set to 'local' if the ComboBox loads local data (defaults to 'remote' which loads from the server)
47285      */
47286     mode: 'remote',
47287     /**
47288      * @cfg {Number} minListWidth The minimum width of the dropdown list in pixels (defaults to 70, will be ignored if
47289      * listWidth has a higher value)
47290      */
47291     minListWidth : 70,
47292     /**
47293      * @cfg {Boolean} forceSelection True to restrict the selected value to one of the values in the list, false to
47294      * allow the user to set arbitrary text into the field (defaults to false)
47295      */
47296     forceSelection:false,
47297     /**
47298      * @cfg {Number} typeAheadDelay The length of time in milliseconds to wait until the typeahead text is displayed
47299      * if typeAhead = true (defaults to 250)
47300      */
47301     typeAheadDelay : 250,
47302     /**
47303      * @cfg {String} valueNotFoundText When using a name/value combo, if the value passed to setValue is not found in
47304      * the store, valueNotFoundText will be displayed as the field text if defined (defaults to undefined)
47305      */
47306     valueNotFoundText : undefined,
47307     
47308     /**
47309      * @cfg {String} defaultValue The value displayed after loading the store.
47310      */
47311     defaultValue: '',
47312     
47313     /**
47314      * @cfg {Boolean} blockFocus Prevents all focus calls, so it can work with things like HTML edtor bar
47315      */
47316     blockFocus : false,
47317     
47318     /**
47319      * @cfg {Boolean} disableClear Disable showing of clear button.
47320      */
47321     disableClear : false,
47322     /**
47323      * @cfg {Boolean} alwaysQuery  Disable caching of results, and always send query
47324      */
47325     alwaysQuery : false,
47326     
47327     //private
47328     addicon : false,
47329     editicon: false,
47330     
47331     // element that contains real text value.. (when hidden is used..)
47332      
47333     // private
47334     onRender : function(ct, position){
47335         Roo.form.Field.prototype.onRender.call(this, ct, position);
47336         
47337         if(this.store){
47338             this.store.on('beforeload', this.onBeforeLoad, this);
47339             this.store.on('load', this.onLoad, this);
47340             this.store.on('loadexception', this.onLoadException, this);
47341             this.store.load({});
47342         }
47343         
47344         
47345         
47346     },
47347
47348     // private
47349     initEvents : function(){
47350         //Roo.form.ComboBox.superclass.initEvents.call(this);
47351  
47352     },
47353
47354     onDestroy : function(){
47355        
47356         if(this.store){
47357             this.store.un('beforeload', this.onBeforeLoad, this);
47358             this.store.un('load', this.onLoad, this);
47359             this.store.un('loadexception', this.onLoadException, this);
47360         }
47361         //Roo.form.ComboBox.superclass.onDestroy.call(this);
47362     },
47363
47364     // private
47365     fireKey : function(e){
47366         if(e.isNavKeyPress() && !this.list.isVisible()){
47367             this.fireEvent("specialkey", this, e);
47368         }
47369     },
47370
47371     // private
47372     onResize: function(w, h){
47373         
47374         return; 
47375     
47376         
47377     },
47378
47379     /**
47380      * Allow or prevent the user from directly editing the field text.  If false is passed,
47381      * the user will only be able to select from the items defined in the dropdown list.  This method
47382      * is the runtime equivalent of setting the 'editable' config option at config time.
47383      * @param {Boolean} value True to allow the user to directly edit the field text
47384      */
47385     setEditable : function(value){
47386          
47387     },
47388
47389     // private
47390     onBeforeLoad : function(){
47391         
47392         Roo.log("Select before load");
47393         return;
47394     
47395         this.innerList.update(this.loadingText ?
47396                '<div class="loading-indicator">'+this.loadingText+'</div>' : '');
47397         //this.restrictHeight();
47398         this.selectedIndex = -1;
47399     },
47400
47401     // private
47402     onLoad : function(){
47403
47404     
47405         var dom = this.el.dom;
47406         dom.innerHTML = '';
47407          var od = dom.ownerDocument;
47408          
47409         if (this.emptyText) {
47410             var op = od.createElement('option');
47411             op.setAttribute('value', '');
47412             op.innerHTML = String.format('{0}', this.emptyText);
47413             dom.appendChild(op);
47414         }
47415         if(this.store.getCount() > 0){
47416            
47417             var vf = this.valueField;
47418             var df = this.displayField;
47419             this.store.data.each(function(r) {
47420                 // which colmsn to use... testing - cdoe / title..
47421                 var op = od.createElement('option');
47422                 op.setAttribute('value', r.data[vf]);
47423                 op.innerHTML = String.format('{0}', r.data[df]);
47424                 dom.appendChild(op);
47425             });
47426             if (typeof(this.defaultValue != 'undefined')) {
47427                 this.setValue(this.defaultValue);
47428             }
47429             
47430              
47431         }else{
47432             //this.onEmptyResults();
47433         }
47434         //this.el.focus();
47435     },
47436     // private
47437     onLoadException : function()
47438     {
47439         dom.innerHTML = '';
47440             
47441         Roo.log("Select on load exception");
47442         return;
47443     
47444         this.collapse();
47445         Roo.log(this.store.reader.jsonData);
47446         if (this.store && typeof(this.store.reader.jsonData.errorMsg) != 'undefined') {
47447             Roo.MessageBox.alert("Error loading",this.store.reader.jsonData.errorMsg);
47448         }
47449         
47450         
47451     },
47452     // private
47453     onTypeAhead : function(){
47454          
47455     },
47456
47457     // private
47458     onSelect : function(record, index){
47459         Roo.log('on select?');
47460         return;
47461         if(this.fireEvent('beforeselect', this, record, index) !== false){
47462             this.setFromData(index > -1 ? record.data : false);
47463             this.collapse();
47464             this.fireEvent('select', this, record, index);
47465         }
47466     },
47467
47468     /**
47469      * Returns the currently selected field value or empty string if no value is set.
47470      * @return {String} value The selected value
47471      */
47472     getValue : function(){
47473         var dom = this.el.dom;
47474         this.value = dom.options[dom.selectedIndex].value;
47475         return this.value;
47476         
47477     },
47478
47479     /**
47480      * Clears any text/value currently set in the field
47481      */
47482     clearValue : function(){
47483         this.value = '';
47484         this.el.dom.selectedIndex = this.emptyText ? 0 : -1;
47485         
47486     },
47487
47488     /**
47489      * Sets the specified value into the field.  If the value finds a match, the corresponding record text
47490      * will be displayed in the field.  If the value does not match the data value of an existing item,
47491      * and the valueNotFoundText config option is defined, it will be displayed as the default field text.
47492      * Otherwise the field will be blank (although the value will still be set).
47493      * @param {String} value The value to match
47494      */
47495     setValue : function(v){
47496         var d = this.el.dom;
47497         for (var i =0; i < d.options.length;i++) {
47498             if (v == d.options[i].value) {
47499                 d.selectedIndex = i;
47500                 this.value = v;
47501                 return;
47502             }
47503         }
47504         this.clearValue();
47505     },
47506     /**
47507      * @property {Object} the last set data for the element
47508      */
47509     
47510     lastData : false,
47511     /**
47512      * Sets the value of the field based on a object which is related to the record format for the store.
47513      * @param {Object} value the value to set as. or false on reset?
47514      */
47515     setFromData : function(o){
47516         Roo.log('setfrom data?');
47517          
47518         
47519         
47520     },
47521     // private
47522     reset : function(){
47523         this.clearValue();
47524     },
47525     // private
47526     findRecord : function(prop, value){
47527         
47528         return false;
47529     
47530         var record;
47531         if(this.store.getCount() > 0){
47532             this.store.each(function(r){
47533                 if(r.data[prop] == value){
47534                     record = r;
47535                     return false;
47536                 }
47537                 return true;
47538             });
47539         }
47540         return record;
47541     },
47542     
47543     getName: function()
47544     {
47545         // returns hidden if it's set..
47546         if (!this.rendered) {return ''};
47547         return !this.hiddenName && this.el.dom.name  ? this.el.dom.name : (this.hiddenName || '');
47548         
47549     },
47550      
47551
47552     
47553
47554     // private
47555     onEmptyResults : function(){
47556         Roo.log('empty results');
47557         //this.collapse();
47558     },
47559
47560     /**
47561      * Returns true if the dropdown list is expanded, else false.
47562      */
47563     isExpanded : function(){
47564         return false;
47565     },
47566
47567     /**
47568      * Select an item in the dropdown list by its data value. This function does NOT cause the select event to fire.
47569      * The store must be loaded and the list expanded for this function to work, otherwise use setValue.
47570      * @param {String} value The data value of the item to select
47571      * @param {Boolean} scrollIntoView False to prevent the dropdown list from autoscrolling to display the
47572      * selected item if it is not currently in view (defaults to true)
47573      * @return {Boolean} True if the value matched an item in the list, else false
47574      */
47575     selectByValue : function(v, scrollIntoView){
47576         Roo.log('select By Value');
47577         return false;
47578     
47579         if(v !== undefined && v !== null){
47580             var r = this.findRecord(this.valueField || this.displayField, v);
47581             if(r){
47582                 this.select(this.store.indexOf(r), scrollIntoView);
47583                 return true;
47584             }
47585         }
47586         return false;
47587     },
47588
47589     /**
47590      * Select an item in the dropdown list by its numeric index in the list. This function does NOT cause the select event to fire.
47591      * The store must be loaded and the list expanded for this function to work, otherwise use setValue.
47592      * @param {Number} index The zero-based index of the list item to select
47593      * @param {Boolean} scrollIntoView False to prevent the dropdown list from autoscrolling to display the
47594      * selected item if it is not currently in view (defaults to true)
47595      */
47596     select : function(index, scrollIntoView){
47597         Roo.log('select ');
47598         return  ;
47599         
47600         this.selectedIndex = index;
47601         this.view.select(index);
47602         if(scrollIntoView !== false){
47603             var el = this.view.getNode(index);
47604             if(el){
47605                 this.innerList.scrollChildIntoView(el, false);
47606             }
47607         }
47608     },
47609
47610       
47611
47612     // private
47613     validateBlur : function(){
47614         
47615         return;
47616         
47617     },
47618
47619     // private
47620     initQuery : function(){
47621         this.doQuery(this.getRawValue());
47622     },
47623
47624     // private
47625     doForce : function(){
47626         if(this.el.dom.value.length > 0){
47627             this.el.dom.value =
47628                 this.lastSelectionText === undefined ? '' : this.lastSelectionText;
47629              
47630         }
47631     },
47632
47633     /**
47634      * Execute a query to filter the dropdown list.  Fires the beforequery event prior to performing the
47635      * query allowing the query action to be canceled if needed.
47636      * @param {String} query The SQL query to execute
47637      * @param {Boolean} forceAll True to force the query to execute even if there are currently fewer characters
47638      * in the field than the minimum specified by the minChars config option.  It also clears any filter previously
47639      * saved in the current store (defaults to false)
47640      */
47641     doQuery : function(q, forceAll){
47642         
47643         Roo.log('doQuery?');
47644         if(q === undefined || q === null){
47645             q = '';
47646         }
47647         var qe = {
47648             query: q,
47649             forceAll: forceAll,
47650             combo: this,
47651             cancel:false
47652         };
47653         if(this.fireEvent('beforequery', qe)===false || qe.cancel){
47654             return false;
47655         }
47656         q = qe.query;
47657         forceAll = qe.forceAll;
47658         if(forceAll === true || (q.length >= this.minChars)){
47659             if(this.lastQuery != q || this.alwaysQuery){
47660                 this.lastQuery = q;
47661                 if(this.mode == 'local'){
47662                     this.selectedIndex = -1;
47663                     if(forceAll){
47664                         this.store.clearFilter();
47665                     }else{
47666                         this.store.filter(this.displayField, q);
47667                     }
47668                     this.onLoad();
47669                 }else{
47670                     this.store.baseParams[this.queryParam] = q;
47671                     this.store.load({
47672                         params: this.getParams(q)
47673                     });
47674                     this.expand();
47675                 }
47676             }else{
47677                 this.selectedIndex = -1;
47678                 this.onLoad();   
47679             }
47680         }
47681     },
47682
47683     // private
47684     getParams : function(q){
47685         var p = {};
47686         //p[this.queryParam] = q;
47687         if(this.pageSize){
47688             p.start = 0;
47689             p.limit = this.pageSize;
47690         }
47691         return p;
47692     },
47693
47694     /**
47695      * Hides the dropdown list if it is currently expanded. Fires the 'collapse' event on completion.
47696      */
47697     collapse : function(){
47698         
47699     },
47700
47701     // private
47702     collapseIf : function(e){
47703         
47704     },
47705
47706     /**
47707      * Expands the dropdown list if it is currently hidden. Fires the 'expand' event on completion.
47708      */
47709     expand : function(){
47710         
47711     } ,
47712
47713     // private
47714      
47715
47716     /** 
47717     * @cfg {Boolean} grow 
47718     * @hide 
47719     */
47720     /** 
47721     * @cfg {Number} growMin 
47722     * @hide 
47723     */
47724     /** 
47725     * @cfg {Number} growMax 
47726     * @hide 
47727     */
47728     /**
47729      * @hide
47730      * @method autoSize
47731      */
47732     
47733     setWidth : function()
47734     {
47735         
47736     },
47737     getResizeEl : function(){
47738         return this.el;
47739     }
47740 });//<script type="text/javasscript">
47741  
47742
47743 /**
47744  * @class Roo.DDView
47745  * A DnD enabled version of Roo.View.
47746  * @param {Element/String} container The Element in which to create the View.
47747  * @param {String} tpl The template string used to create the markup for each element of the View
47748  * @param {Object} config The configuration properties. These include all the config options of
47749  * {@link Roo.View} plus some specific to this class.<br>
47750  * <p>
47751  * Drag/drop is implemented by adding {@link Roo.data.Record}s to the target DDView. If copying is
47752  * not being performed, the original {@link Roo.data.Record} is removed from the source DDView.<br>
47753  * <p>
47754  * The following extra CSS rules are needed to provide insertion point highlighting:<pre><code>
47755 .x-view-drag-insert-above {
47756         border-top:1px dotted #3366cc;
47757 }
47758 .x-view-drag-insert-below {
47759         border-bottom:1px dotted #3366cc;
47760 }
47761 </code></pre>
47762  * 
47763  */
47764  
47765 Roo.DDView = function(container, tpl, config) {
47766     Roo.DDView.superclass.constructor.apply(this, arguments);
47767     this.getEl().setStyle("outline", "0px none");
47768     this.getEl().unselectable();
47769     if (this.dragGroup) {
47770                 this.setDraggable(this.dragGroup.split(","));
47771     }
47772     if (this.dropGroup) {
47773                 this.setDroppable(this.dropGroup.split(","));
47774     }
47775     if (this.deletable) {
47776         this.setDeletable();
47777     }
47778     this.isDirtyFlag = false;
47779         this.addEvents({
47780                 "drop" : true
47781         });
47782 };
47783
47784 Roo.extend(Roo.DDView, Roo.View, {
47785 /**     @cfg {String/Array} dragGroup The ddgroup name(s) for the View's DragZone. */
47786 /**     @cfg {String/Array} dropGroup The ddgroup name(s) for the View's DropZone. */
47787 /**     @cfg {Boolean} copy Causes drag operations to copy nodes rather than move. */
47788 /**     @cfg {Boolean} allowCopy Causes ctrl/drag operations to copy nodes rather than move. */
47789
47790         isFormField: true,
47791
47792         reset: Roo.emptyFn,
47793         
47794         clearInvalid: Roo.form.Field.prototype.clearInvalid,
47795
47796         validate: function() {
47797                 return true;
47798         },
47799         
47800         destroy: function() {
47801                 this.purgeListeners();
47802                 this.getEl.removeAllListeners();
47803                 this.getEl().remove();
47804                 if (this.dragZone) {
47805                         if (this.dragZone.destroy) {
47806                                 this.dragZone.destroy();
47807                         }
47808                 }
47809                 if (this.dropZone) {
47810                         if (this.dropZone.destroy) {
47811                                 this.dropZone.destroy();
47812                         }
47813                 }
47814         },
47815
47816 /**     Allows this class to be an Roo.form.Field so it can be found using {@link Roo.form.BasicForm#findField}. */
47817         getName: function() {
47818                 return this.name;
47819         },
47820
47821 /**     Loads the View from a JSON string representing the Records to put into the Store. */
47822         setValue: function(v) {
47823                 if (!this.store) {
47824                         throw "DDView.setValue(). DDView must be constructed with a valid Store";
47825                 }
47826                 var data = {};
47827                 data[this.store.reader.meta.root] = v ? [].concat(v) : [];
47828                 this.store.proxy = new Roo.data.MemoryProxy(data);
47829                 this.store.load();
47830         },
47831
47832 /**     @return {String} a parenthesised list of the ids of the Records in the View. */
47833         getValue: function() {
47834                 var result = '(';
47835                 this.store.each(function(rec) {
47836                         result += rec.id + ',';
47837                 });
47838                 return result.substr(0, result.length - 1) + ')';
47839         },
47840         
47841         getIds: function() {
47842                 var i = 0, result = new Array(this.store.getCount());
47843                 this.store.each(function(rec) {
47844                         result[i++] = rec.id;
47845                 });
47846                 return result;
47847         },
47848         
47849         isDirty: function() {
47850                 return this.isDirtyFlag;
47851         },
47852
47853 /**
47854  *      Part of the Roo.dd.DropZone interface. If no target node is found, the
47855  *      whole Element becomes the target, and this causes the drop gesture to append.
47856  */
47857     getTargetFromEvent : function(e) {
47858                 var target = e.getTarget();
47859                 while ((target !== null) && (target.parentNode != this.el.dom)) {
47860                 target = target.parentNode;
47861                 }
47862                 if (!target) {
47863                         target = this.el.dom.lastChild || this.el.dom;
47864                 }
47865                 return target;
47866     },
47867
47868 /**
47869  *      Create the drag data which consists of an object which has the property "ddel" as
47870  *      the drag proxy element. 
47871  */
47872     getDragData : function(e) {
47873         var target = this.findItemFromChild(e.getTarget());
47874                 if(target) {
47875                         this.handleSelection(e);
47876                         var selNodes = this.getSelectedNodes();
47877             var dragData = {
47878                 source: this,
47879                 copy: this.copy || (this.allowCopy && e.ctrlKey),
47880                 nodes: selNodes,
47881                 records: []
47882                         };
47883                         var selectedIndices = this.getSelectedIndexes();
47884                         for (var i = 0; i < selectedIndices.length; i++) {
47885                                 dragData.records.push(this.store.getAt(selectedIndices[i]));
47886                         }
47887                         if (selNodes.length == 1) {
47888                                 dragData.ddel = target.cloneNode(true); // the div element
47889                         } else {
47890                                 var div = document.createElement('div'); // create the multi element drag "ghost"
47891                                 div.className = 'multi-proxy';
47892                                 for (var i = 0, len = selNodes.length; i < len; i++) {
47893                                         div.appendChild(selNodes[i].cloneNode(true));
47894                                 }
47895                                 dragData.ddel = div;
47896                         }
47897             //console.log(dragData)
47898             //console.log(dragData.ddel.innerHTML)
47899                         return dragData;
47900                 }
47901         //console.log('nodragData')
47902                 return false;
47903     },
47904     
47905 /**     Specify to which ddGroup items in this DDView may be dragged. */
47906     setDraggable: function(ddGroup) {
47907         if (ddGroup instanceof Array) {
47908                 Roo.each(ddGroup, this.setDraggable, this);
47909                 return;
47910         }
47911         if (this.dragZone) {
47912                 this.dragZone.addToGroup(ddGroup);
47913         } else {
47914                         this.dragZone = new Roo.dd.DragZone(this.getEl(), {
47915                                 containerScroll: true,
47916                                 ddGroup: ddGroup 
47917
47918                         });
47919 //                      Draggability implies selection. DragZone's mousedown selects the element.
47920                         if (!this.multiSelect) { this.singleSelect = true; }
47921
47922 //                      Wire the DragZone's handlers up to methods in *this*
47923                         this.dragZone.getDragData = this.getDragData.createDelegate(this);
47924                 }
47925     },
47926
47927 /**     Specify from which ddGroup this DDView accepts drops. */
47928     setDroppable: function(ddGroup) {
47929         if (ddGroup instanceof Array) {
47930                 Roo.each(ddGroup, this.setDroppable, this);
47931                 return;
47932         }
47933         if (this.dropZone) {
47934                 this.dropZone.addToGroup(ddGroup);
47935         } else {
47936                         this.dropZone = new Roo.dd.DropZone(this.getEl(), {
47937                                 containerScroll: true,
47938                                 ddGroup: ddGroup
47939                         });
47940
47941 //                      Wire the DropZone's handlers up to methods in *this*
47942                         this.dropZone.getTargetFromEvent = this.getTargetFromEvent.createDelegate(this);
47943                         this.dropZone.onNodeEnter = this.onNodeEnter.createDelegate(this);
47944                         this.dropZone.onNodeOver = this.onNodeOver.createDelegate(this);
47945                         this.dropZone.onNodeOut = this.onNodeOut.createDelegate(this);
47946                         this.dropZone.onNodeDrop = this.onNodeDrop.createDelegate(this);
47947                 }
47948     },
47949
47950 /**     Decide whether to drop above or below a View node. */
47951     getDropPoint : function(e, n, dd){
47952         if (n == this.el.dom) { return "above"; }
47953                 var t = Roo.lib.Dom.getY(n), b = t + n.offsetHeight;
47954                 var c = t + (b - t) / 2;
47955                 var y = Roo.lib.Event.getPageY(e);
47956                 if(y <= c) {
47957                         return "above";
47958                 }else{
47959                         return "below";
47960                 }
47961     },
47962
47963     onNodeEnter : function(n, dd, e, data){
47964                 return false;
47965     },
47966     
47967     onNodeOver : function(n, dd, e, data){
47968                 var pt = this.getDropPoint(e, n, dd);
47969                 // set the insert point style on the target node
47970                 var dragElClass = this.dropNotAllowed;
47971                 if (pt) {
47972                         var targetElClass;
47973                         if (pt == "above"){
47974                                 dragElClass = n.previousSibling ? "x-tree-drop-ok-between" : "x-tree-drop-ok-above";
47975                                 targetElClass = "x-view-drag-insert-above";
47976                         } else {
47977                                 dragElClass = n.nextSibling ? "x-tree-drop-ok-between" : "x-tree-drop-ok-below";
47978                                 targetElClass = "x-view-drag-insert-below";
47979                         }
47980                         if (this.lastInsertClass != targetElClass){
47981                                 Roo.fly(n).replaceClass(this.lastInsertClass, targetElClass);
47982                                 this.lastInsertClass = targetElClass;
47983                         }
47984                 }
47985                 return dragElClass;
47986         },
47987
47988     onNodeOut : function(n, dd, e, data){
47989                 this.removeDropIndicators(n);
47990     },
47991
47992     onNodeDrop : function(n, dd, e, data){
47993         if (this.fireEvent("drop", this, n, dd, e, data) === false) {
47994                 return false;
47995         }
47996         var pt = this.getDropPoint(e, n, dd);
47997                 var insertAt = (n == this.el.dom) ? this.nodes.length : n.nodeIndex;
47998                 if (pt == "below") { insertAt++; }
47999                 for (var i = 0; i < data.records.length; i++) {
48000                         var r = data.records[i];
48001                         var dup = this.store.getById(r.id);
48002                         if (dup && (dd != this.dragZone)) {
48003                                 Roo.fly(this.getNode(this.store.indexOf(dup))).frame("red", 1);
48004                         } else {
48005                                 if (data.copy) {
48006                                         this.store.insert(insertAt++, r.copy());
48007                                 } else {
48008                                         data.source.isDirtyFlag = true;
48009                                         r.store.remove(r);
48010                                         this.store.insert(insertAt++, r);
48011                                 }
48012                                 this.isDirtyFlag = true;
48013                         }
48014                 }
48015                 this.dragZone.cachedTarget = null;
48016                 return true;
48017     },
48018
48019     removeDropIndicators : function(n){
48020                 if(n){
48021                         Roo.fly(n).removeClass([
48022                                 "x-view-drag-insert-above",
48023                                 "x-view-drag-insert-below"]);
48024                         this.lastInsertClass = "_noclass";
48025                 }
48026     },
48027
48028 /**
48029  *      Utility method. Add a delete option to the DDView's context menu.
48030  *      @param {String} imageUrl The URL of the "delete" icon image.
48031  */
48032         setDeletable: function(imageUrl) {
48033                 if (!this.singleSelect && !this.multiSelect) {
48034                         this.singleSelect = true;
48035                 }
48036                 var c = this.getContextMenu();
48037                 this.contextMenu.on("itemclick", function(item) {
48038                         switch (item.id) {
48039                                 case "delete":
48040                                         this.remove(this.getSelectedIndexes());
48041                                         break;
48042                         }
48043                 }, this);
48044                 this.contextMenu.add({
48045                         icon: imageUrl,
48046                         id: "delete",
48047                         text: 'Delete'
48048                 });
48049         },
48050         
48051 /**     Return the context menu for this DDView. */
48052         getContextMenu: function() {
48053                 if (!this.contextMenu) {
48054 //                      Create the View's context menu
48055                         this.contextMenu = new Roo.menu.Menu({
48056                                 id: this.id + "-contextmenu"
48057                         });
48058                         this.el.on("contextmenu", this.showContextMenu, this);
48059                 }
48060                 return this.contextMenu;
48061         },
48062         
48063         disableContextMenu: function() {
48064                 if (this.contextMenu) {
48065                         this.el.un("contextmenu", this.showContextMenu, this);
48066                 }
48067         },
48068
48069         showContextMenu: function(e, item) {
48070         item = this.findItemFromChild(e.getTarget());
48071                 if (item) {
48072                         e.stopEvent();
48073                         this.select(this.getNode(item), this.multiSelect && e.ctrlKey, true);
48074                         this.contextMenu.showAt(e.getXY());
48075             }
48076     },
48077
48078 /**
48079  *      Remove {@link Roo.data.Record}s at the specified indices.
48080  *      @param {Array/Number} selectedIndices The index (or Array of indices) of Records to remove.
48081  */
48082     remove: function(selectedIndices) {
48083                 selectedIndices = [].concat(selectedIndices);
48084                 for (var i = 0; i < selectedIndices.length; i++) {
48085                         var rec = this.store.getAt(selectedIndices[i]);
48086                         this.store.remove(rec);
48087                 }
48088     },
48089
48090 /**
48091  *      Double click fires the event, but also, if this is draggable, and there is only one other
48092  *      related DropZone, it transfers the selected node.
48093  */
48094     onDblClick : function(e){
48095         var item = this.findItemFromChild(e.getTarget());
48096         if(item){
48097             if (this.fireEvent("dblclick", this, this.indexOf(item), item, e) === false) {
48098                 return false;
48099             }
48100             if (this.dragGroup) {
48101                     var targets = Roo.dd.DragDropMgr.getRelated(this.dragZone, true);
48102                     while (targets.indexOf(this.dropZone) > -1) {
48103                             targets.remove(this.dropZone);
48104                                 }
48105                     if (targets.length == 1) {
48106                                         this.dragZone.cachedTarget = null;
48107                         var el = Roo.get(targets[0].getEl());
48108                         var box = el.getBox(true);
48109                         targets[0].onNodeDrop(el.dom, {
48110                                 target: el.dom,
48111                                 xy: [box.x, box.y + box.height - 1]
48112                         }, null, this.getDragData(e));
48113                     }
48114                 }
48115         }
48116     },
48117     
48118     handleSelection: function(e) {
48119                 this.dragZone.cachedTarget = null;
48120         var item = this.findItemFromChild(e.getTarget());
48121         if (!item) {
48122                 this.clearSelections(true);
48123                 return;
48124         }
48125                 if (item && (this.multiSelect || this.singleSelect)){
48126                         if(this.multiSelect && e.shiftKey && (!e.ctrlKey) && this.lastSelection){
48127                                 this.select(this.getNodes(this.indexOf(this.lastSelection), item.nodeIndex), false);
48128                         }else if (this.isSelected(this.getNode(item)) && e.ctrlKey){
48129                                 this.unselect(item);
48130                         } else {
48131                                 this.select(item, this.multiSelect && e.ctrlKey);
48132                                 this.lastSelection = item;
48133                         }
48134                 }
48135     },
48136
48137     onItemClick : function(item, index, e){
48138                 if(this.fireEvent("beforeclick", this, index, item, e) === false){
48139                         return false;
48140                 }
48141                 return true;
48142     },
48143
48144     unselect : function(nodeInfo, suppressEvent){
48145                 var node = this.getNode(nodeInfo);
48146                 if(node && this.isSelected(node)){
48147                         if(this.fireEvent("beforeselect", this, node, this.selections) !== false){
48148                                 Roo.fly(node).removeClass(this.selectedClass);
48149                                 this.selections.remove(node);
48150                                 if(!suppressEvent){
48151                                         this.fireEvent("selectionchange", this, this.selections);
48152                                 }
48153                         }
48154                 }
48155     }
48156 });
48157 /*
48158  * Based on:
48159  * Ext JS Library 1.1.1
48160  * Copyright(c) 2006-2007, Ext JS, LLC.
48161  *
48162  * Originally Released Under LGPL - original licence link has changed is not relivant.
48163  *
48164  * Fork - LGPL
48165  * <script type="text/javascript">
48166  */
48167  
48168 /**
48169  * @class Roo.LayoutManager
48170  * @extends Roo.util.Observable
48171  * Base class for layout managers.
48172  */
48173 Roo.LayoutManager = function(container, config){
48174     Roo.LayoutManager.superclass.constructor.call(this);
48175     this.el = Roo.get(container);
48176     // ie scrollbar fix
48177     if(this.el.dom == document.body && Roo.isIE && !config.allowScroll){
48178         document.body.scroll = "no";
48179     }else if(this.el.dom != document.body && this.el.getStyle('position') == 'static'){
48180         this.el.position('relative');
48181     }
48182     this.id = this.el.id;
48183     this.el.addClass("x-layout-container");
48184     /** false to disable window resize monitoring @type Boolean */
48185     this.monitorWindowResize = true;
48186     this.regions = {};
48187     this.addEvents({
48188         /**
48189          * @event layout
48190          * Fires when a layout is performed. 
48191          * @param {Roo.LayoutManager} this
48192          */
48193         "layout" : true,
48194         /**
48195          * @event regionresized
48196          * Fires when the user resizes a region. 
48197          * @param {Roo.LayoutRegion} region The resized region
48198          * @param {Number} newSize The new size (width for east/west, height for north/south)
48199          */
48200         "regionresized" : true,
48201         /**
48202          * @event regioncollapsed
48203          * Fires when a region is collapsed. 
48204          * @param {Roo.LayoutRegion} region The collapsed region
48205          */
48206         "regioncollapsed" : true,
48207         /**
48208          * @event regionexpanded
48209          * Fires when a region is expanded.  
48210          * @param {Roo.LayoutRegion} region The expanded region
48211          */
48212         "regionexpanded" : true
48213     });
48214     this.updating = false;
48215     Roo.EventManager.onWindowResize(this.onWindowResize, this, true);
48216 };
48217
48218 Roo.extend(Roo.LayoutManager, Roo.util.Observable, {
48219     /**
48220      * Returns true if this layout is currently being updated
48221      * @return {Boolean}
48222      */
48223     isUpdating : function(){
48224         return this.updating; 
48225     },
48226     
48227     /**
48228      * Suspend the LayoutManager from doing auto-layouts while
48229      * making multiple add or remove calls
48230      */
48231     beginUpdate : function(){
48232         this.updating = true;    
48233     },
48234     
48235     /**
48236      * Restore auto-layouts and optionally disable the manager from performing a layout
48237      * @param {Boolean} noLayout true to disable a layout update 
48238      */
48239     endUpdate : function(noLayout){
48240         this.updating = false;
48241         if(!noLayout){
48242             this.layout();
48243         }    
48244     },
48245     
48246     layout: function(){
48247         
48248     },
48249     
48250     onRegionResized : function(region, newSize){
48251         this.fireEvent("regionresized", region, newSize);
48252         this.layout();
48253     },
48254     
48255     onRegionCollapsed : function(region){
48256         this.fireEvent("regioncollapsed", region);
48257     },
48258     
48259     onRegionExpanded : function(region){
48260         this.fireEvent("regionexpanded", region);
48261     },
48262         
48263     /**
48264      * Returns the size of the current view. This method normalizes document.body and element embedded layouts and
48265      * performs box-model adjustments.
48266      * @return {Object} The size as an object {width: (the width), height: (the height)}
48267      */
48268     getViewSize : function(){
48269         var size;
48270         if(this.el.dom != document.body){
48271             size = this.el.getSize();
48272         }else{
48273             size = {width: Roo.lib.Dom.getViewWidth(), height: Roo.lib.Dom.getViewHeight()};
48274         }
48275         size.width -= this.el.getBorderWidth("lr")-this.el.getPadding("lr");
48276         size.height -= this.el.getBorderWidth("tb")-this.el.getPadding("tb");
48277         return size;
48278     },
48279     
48280     /**
48281      * Returns the Element this layout is bound to.
48282      * @return {Roo.Element}
48283      */
48284     getEl : function(){
48285         return this.el;
48286     },
48287     
48288     /**
48289      * Returns the specified region.
48290      * @param {String} target The region key ('center', 'north', 'south', 'east' or 'west')
48291      * @return {Roo.LayoutRegion}
48292      */
48293     getRegion : function(target){
48294         return this.regions[target.toLowerCase()];
48295     },
48296     
48297     onWindowResize : function(){
48298         if(this.monitorWindowResize){
48299             this.layout();
48300         }
48301     }
48302 });/*
48303  * Based on:
48304  * Ext JS Library 1.1.1
48305  * Copyright(c) 2006-2007, Ext JS, LLC.
48306  *
48307  * Originally Released Under LGPL - original licence link has changed is not relivant.
48308  *
48309  * Fork - LGPL
48310  * <script type="text/javascript">
48311  */
48312 /**
48313  * @class Roo.BorderLayout
48314  * @extends Roo.LayoutManager
48315  * This class represents a common layout manager used in desktop applications. For screenshots and more details,
48316  * please see: <br><br>
48317  * <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>
48318  * <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>
48319  * Example:
48320  <pre><code>
48321  var layout = new Roo.BorderLayout(document.body, {
48322     north: {
48323         initialSize: 25,
48324         titlebar: false
48325     },
48326     west: {
48327         split:true,
48328         initialSize: 200,
48329         minSize: 175,
48330         maxSize: 400,
48331         titlebar: true,
48332         collapsible: true
48333     },
48334     east: {
48335         split:true,
48336         initialSize: 202,
48337         minSize: 175,
48338         maxSize: 400,
48339         titlebar: true,
48340         collapsible: true
48341     },
48342     south: {
48343         split:true,
48344         initialSize: 100,
48345         minSize: 100,
48346         maxSize: 200,
48347         titlebar: true,
48348         collapsible: true
48349     },
48350     center: {
48351         titlebar: true,
48352         autoScroll:true,
48353         resizeTabs: true,
48354         minTabWidth: 50,
48355         preferredTabWidth: 150
48356     }
48357 });
48358
48359 // shorthand
48360 var CP = Roo.ContentPanel;
48361
48362 layout.beginUpdate();
48363 layout.add("north", new CP("north", "North"));
48364 layout.add("south", new CP("south", {title: "South", closable: true}));
48365 layout.add("west", new CP("west", {title: "West"}));
48366 layout.add("east", new CP("autoTabs", {title: "Auto Tabs", closable: true}));
48367 layout.add("center", new CP("center1", {title: "Close Me", closable: true}));
48368 layout.add("center", new CP("center2", {title: "Center Panel", closable: false}));
48369 layout.getRegion("center").showPanel("center1");
48370 layout.endUpdate();
48371 </code></pre>
48372
48373 <b>The container the layout is rendered into can be either the body element or any other element.
48374 If it is not the body element, the container needs to either be an absolute positioned element,
48375 or you will need to add "position:relative" to the css of the container.  You will also need to specify
48376 the container size if it is not the body element.</b>
48377
48378 * @constructor
48379 * Create a new BorderLayout
48380 * @param {String/HTMLElement/Element} container The container this layout is bound to
48381 * @param {Object} config Configuration options
48382  */
48383 Roo.BorderLayout = function(container, config){
48384     config = config || {};
48385     Roo.BorderLayout.superclass.constructor.call(this, container, config);
48386     this.factory = config.factory || Roo.BorderLayout.RegionFactory;
48387     for(var i = 0, len = this.factory.validRegions.length; i < len; i++) {
48388         var target = this.factory.validRegions[i];
48389         if(config[target]){
48390             this.addRegion(target, config[target]);
48391         }
48392     }
48393 };
48394
48395 Roo.extend(Roo.BorderLayout, Roo.LayoutManager, {
48396     /**
48397      * Creates and adds a new region if it doesn't already exist.
48398      * @param {String} target The target region key (north, south, east, west or center).
48399      * @param {Object} config The regions config object
48400      * @return {BorderLayoutRegion} The new region
48401      */
48402     addRegion : function(target, config){
48403         if(!this.regions[target]){
48404             var r = this.factory.create(target, this, config);
48405             this.bindRegion(target, r);
48406         }
48407         return this.regions[target];
48408     },
48409
48410     // private (kinda)
48411     bindRegion : function(name, r){
48412         this.regions[name] = r;
48413         r.on("visibilitychange", this.layout, this);
48414         r.on("paneladded", this.layout, this);
48415         r.on("panelremoved", this.layout, this);
48416         r.on("invalidated", this.layout, this);
48417         r.on("resized", this.onRegionResized, this);
48418         r.on("collapsed", this.onRegionCollapsed, this);
48419         r.on("expanded", this.onRegionExpanded, this);
48420     },
48421
48422     /**
48423      * Performs a layout update.
48424      */
48425     layout : function(){
48426         if(this.updating) return;
48427         var size = this.getViewSize();
48428         var w = size.width;
48429         var h = size.height;
48430         var centerW = w;
48431         var centerH = h;
48432         var centerY = 0;
48433         var centerX = 0;
48434         //var x = 0, y = 0;
48435
48436         var rs = this.regions;
48437         var north = rs["north"];
48438         var south = rs["south"]; 
48439         var west = rs["west"];
48440         var east = rs["east"];
48441         var center = rs["center"];
48442         //if(this.hideOnLayout){ // not supported anymore
48443             //c.el.setStyle("display", "none");
48444         //}
48445         if(north && north.isVisible()){
48446             var b = north.getBox();
48447             var m = north.getMargins();
48448             b.width = w - (m.left+m.right);
48449             b.x = m.left;
48450             b.y = m.top;
48451             centerY = b.height + b.y + m.bottom;
48452             centerH -= centerY;
48453             north.updateBox(this.safeBox(b));
48454         }
48455         if(south && south.isVisible()){
48456             var b = south.getBox();
48457             var m = south.getMargins();
48458             b.width = w - (m.left+m.right);
48459             b.x = m.left;
48460             var totalHeight = (b.height + m.top + m.bottom);
48461             b.y = h - totalHeight + m.top;
48462             centerH -= totalHeight;
48463             south.updateBox(this.safeBox(b));
48464         }
48465         if(west && west.isVisible()){
48466             var b = west.getBox();
48467             var m = west.getMargins();
48468             b.height = centerH - (m.top+m.bottom);
48469             b.x = m.left;
48470             b.y = centerY + m.top;
48471             var totalWidth = (b.width + m.left + m.right);
48472             centerX += totalWidth;
48473             centerW -= totalWidth;
48474             west.updateBox(this.safeBox(b));
48475         }
48476         if(east && east.isVisible()){
48477             var b = east.getBox();
48478             var m = east.getMargins();
48479             b.height = centerH - (m.top+m.bottom);
48480             var totalWidth = (b.width + m.left + m.right);
48481             b.x = w - totalWidth + m.left;
48482             b.y = centerY + m.top;
48483             centerW -= totalWidth;
48484             east.updateBox(this.safeBox(b));
48485         }
48486         if(center){
48487             var m = center.getMargins();
48488             var centerBox = {
48489                 x: centerX + m.left,
48490                 y: centerY + m.top,
48491                 width: centerW - (m.left+m.right),
48492                 height: centerH - (m.top+m.bottom)
48493             };
48494             //if(this.hideOnLayout){
48495                 //center.el.setStyle("display", "block");
48496             //}
48497             center.updateBox(this.safeBox(centerBox));
48498         }
48499         this.el.repaint();
48500         this.fireEvent("layout", this);
48501     },
48502
48503     // private
48504     safeBox : function(box){
48505         box.width = Math.max(0, box.width);
48506         box.height = Math.max(0, box.height);
48507         return box;
48508     },
48509
48510     /**
48511      * Adds a ContentPanel (or subclass) to this layout.
48512      * @param {String} target The target region key (north, south, east, west or center).
48513      * @param {Roo.ContentPanel} panel The panel to add
48514      * @return {Roo.ContentPanel} The added panel
48515      */
48516     add : function(target, panel){
48517          
48518         target = target.toLowerCase();
48519         return this.regions[target].add(panel);
48520     },
48521
48522     /**
48523      * Remove a ContentPanel (or subclass) to this layout.
48524      * @param {String} target The target region key (north, south, east, west or center).
48525      * @param {Number/String/Roo.ContentPanel} panel The index, id or panel to remove
48526      * @return {Roo.ContentPanel} The removed panel
48527      */
48528     remove : function(target, panel){
48529         target = target.toLowerCase();
48530         return this.regions[target].remove(panel);
48531     },
48532
48533     /**
48534      * Searches all regions for a panel with the specified id
48535      * @param {String} panelId
48536      * @return {Roo.ContentPanel} The panel or null if it wasn't found
48537      */
48538     findPanel : function(panelId){
48539         var rs = this.regions;
48540         for(var target in rs){
48541             if(typeof rs[target] != "function"){
48542                 var p = rs[target].getPanel(panelId);
48543                 if(p){
48544                     return p;
48545                 }
48546             }
48547         }
48548         return null;
48549     },
48550
48551     /**
48552      * Searches all regions for a panel with the specified id and activates (shows) it.
48553      * @param {String/ContentPanel} panelId The panels id or the panel itself
48554      * @return {Roo.ContentPanel} The shown panel or null
48555      */
48556     showPanel : function(panelId) {
48557       var rs = this.regions;
48558       for(var target in rs){
48559          var r = rs[target];
48560          if(typeof r != "function"){
48561             if(r.hasPanel(panelId)){
48562                return r.showPanel(panelId);
48563             }
48564          }
48565       }
48566       return null;
48567    },
48568
48569    /**
48570      * Restores this layout's state using Roo.state.Manager or the state provided by the passed provider.
48571      * @param {Roo.state.Provider} provider (optional) An alternate state provider
48572      */
48573     restoreState : function(provider){
48574         if(!provider){
48575             provider = Roo.state.Manager;
48576         }
48577         var sm = new Roo.LayoutStateManager();
48578         sm.init(this, provider);
48579     },
48580
48581     /**
48582      * Adds a batch of multiple ContentPanels dynamically by passing a special regions config object.  This config
48583      * object should contain properties for each region to add ContentPanels to, and each property's value should be
48584      * a valid ContentPanel config object.  Example:
48585      * <pre><code>
48586 // Create the main layout
48587 var layout = new Roo.BorderLayout('main-ct', {
48588     west: {
48589         split:true,
48590         minSize: 175,
48591         titlebar: true
48592     },
48593     center: {
48594         title:'Components'
48595     }
48596 }, 'main-ct');
48597
48598 // Create and add multiple ContentPanels at once via configs
48599 layout.batchAdd({
48600    west: {
48601        id: 'source-files',
48602        autoCreate:true,
48603        title:'Ext Source Files',
48604        autoScroll:true,
48605        fitToFrame:true
48606    },
48607    center : {
48608        el: cview,
48609        autoScroll:true,
48610        fitToFrame:true,
48611        toolbar: tb,
48612        resizeEl:'cbody'
48613    }
48614 });
48615 </code></pre>
48616      * @param {Object} regions An object containing ContentPanel configs by region name
48617      */
48618     batchAdd : function(regions){
48619         this.beginUpdate();
48620         for(var rname in regions){
48621             var lr = this.regions[rname];
48622             if(lr){
48623                 this.addTypedPanels(lr, regions[rname]);
48624             }
48625         }
48626         this.endUpdate();
48627     },
48628
48629     // private
48630     addTypedPanels : function(lr, ps){
48631         if(typeof ps == 'string'){
48632             lr.add(new Roo.ContentPanel(ps));
48633         }
48634         else if(ps instanceof Array){
48635             for(var i =0, len = ps.length; i < len; i++){
48636                 this.addTypedPanels(lr, ps[i]);
48637             }
48638         }
48639         else if(!ps.events){ // raw config?
48640             var el = ps.el;
48641             delete ps.el; // prevent conflict
48642             lr.add(new Roo.ContentPanel(el || Roo.id(), ps));
48643         }
48644         else {  // panel object assumed!
48645             lr.add(ps);
48646         }
48647     },
48648     /**
48649      * Adds a xtype elements to the layout.
48650      * <pre><code>
48651
48652 layout.addxtype({
48653        xtype : 'ContentPanel',
48654        region: 'west',
48655        items: [ .... ]
48656    }
48657 );
48658
48659 layout.addxtype({
48660         xtype : 'NestedLayoutPanel',
48661         region: 'west',
48662         layout: {
48663            center: { },
48664            west: { }   
48665         },
48666         items : [ ... list of content panels or nested layout panels.. ]
48667    }
48668 );
48669 </code></pre>
48670      * @param {Object} cfg Xtype definition of item to add.
48671      */
48672     addxtype : function(cfg)
48673     {
48674         // basically accepts a pannel...
48675         // can accept a layout region..!?!?
48676         //Roo.log('Roo.BorderLayout add ' + cfg.xtype)
48677         
48678         if (!cfg.xtype.match(/Panel$/)) {
48679             return false;
48680         }
48681         var ret = false;
48682         
48683         if (typeof(cfg.region) == 'undefined') {
48684             Roo.log("Failed to add Panel, region was not set");
48685             Roo.log(cfg);
48686             return false;
48687         }
48688         var region = cfg.region;
48689         delete cfg.region;
48690         
48691           
48692         var xitems = [];
48693         if (cfg.items) {
48694             xitems = cfg.items;
48695             delete cfg.items;
48696         }
48697         var nb = false;
48698         
48699         switch(cfg.xtype) 
48700         {
48701             case 'ContentPanel':  // ContentPanel (el, cfg)
48702             case 'ScrollPanel':  // ContentPanel (el, cfg)
48703             case 'ViewPanel': 
48704                 if(cfg.autoCreate) {
48705                     ret = new Roo[cfg.xtype](cfg); // new panel!!!!!
48706                 } else {
48707                     var el = this.el.createChild();
48708                     ret = new Roo[cfg.xtype](el, cfg); // new panel!!!!!
48709                 }
48710                 
48711                 this.add(region, ret);
48712                 break;
48713             
48714             
48715             case 'TreePanel': // our new panel!
48716                 cfg.el = this.el.createChild();
48717                 ret = new Roo[cfg.xtype](cfg); // new panel!!!!!
48718                 this.add(region, ret);
48719                 break;
48720             
48721             case 'NestedLayoutPanel': 
48722                 // create a new Layout (which is  a Border Layout...
48723                 var el = this.el.createChild();
48724                 var clayout = cfg.layout;
48725                 delete cfg.layout;
48726                 clayout.items   = clayout.items  || [];
48727                 // replace this exitems with the clayout ones..
48728                 xitems = clayout.items;
48729                  
48730                 
48731                 if (region == 'center' && this.active && this.getRegion('center').panels.length < 1) {
48732                     cfg.background = false;
48733                 }
48734                 var layout = new Roo.BorderLayout(el, clayout);
48735                 
48736                 ret = new Roo[cfg.xtype](layout, cfg); // new panel!!!!!
48737                 //console.log('adding nested layout panel '  + cfg.toSource());
48738                 this.add(region, ret);
48739                 nb = {}; /// find first...
48740                 break;
48741                 
48742             case 'GridPanel': 
48743             
48744                 // needs grid and region
48745                 
48746                 //var el = this.getRegion(region).el.createChild();
48747                 var el = this.el.createChild();
48748                 // create the grid first...
48749                 
48750                 var grid = new Roo.grid[cfg.grid.xtype](el, cfg.grid);
48751                 delete cfg.grid;
48752                 if (region == 'center' && this.active ) {
48753                     cfg.background = false;
48754                 }
48755                 ret = new Roo[cfg.xtype](grid, cfg); // new panel!!!!!
48756                 
48757                 this.add(region, ret);
48758                 if (cfg.background) {
48759                     ret.on('activate', function(gp) {
48760                         if (!gp.grid.rendered) {
48761                             gp.grid.render();
48762                         }
48763                     });
48764                 } else {
48765                     grid.render();
48766                 }
48767                 break;
48768            
48769            
48770            
48771                 
48772                 
48773                 
48774             default:
48775                 if (typeof(Roo[cfg.xtype]) != 'undefined') {
48776                     
48777                     ret = new Roo[cfg.xtype](cfg); // new panel!!!!!
48778                     this.add(region, ret);
48779                 } else {
48780                 
48781                     alert("Can not add '" + cfg.xtype + "' to BorderLayout");
48782                     return null;
48783                 }
48784                 
48785              // GridPanel (grid, cfg)
48786             
48787         }
48788         this.beginUpdate();
48789         // add children..
48790         var region = '';
48791         var abn = {};
48792         Roo.each(xitems, function(i)  {
48793             region = nb && i.region ? i.region : false;
48794             
48795             var add = ret.addxtype(i);
48796            
48797             if (region) {
48798                 nb[region] = nb[region] == undefined ? 0 : nb[region]+1;
48799                 if (!i.background) {
48800                     abn[region] = nb[region] ;
48801                 }
48802             }
48803             
48804         });
48805         this.endUpdate();
48806
48807         // make the last non-background panel active..
48808         //if (nb) { Roo.log(abn); }
48809         if (nb) {
48810             
48811             for(var r in abn) {
48812                 region = this.getRegion(r);
48813                 if (region) {
48814                     // tried using nb[r], but it does not work..
48815                      
48816                     region.showPanel(abn[r]);
48817                    
48818                 }
48819             }
48820         }
48821         return ret;
48822         
48823     }
48824 });
48825
48826 /**
48827  * Shortcut for creating a new BorderLayout object and adding one or more ContentPanels to it in a single step, handling
48828  * the beginUpdate and endUpdate calls internally.  The key to this method is the <b>panels</b> property that can be
48829  * provided with each region config, which allows you to add ContentPanel configs in addition to the region configs
48830  * during creation.  The following code is equivalent to the constructor-based example at the beginning of this class:
48831  * <pre><code>
48832 // shorthand
48833 var CP = Roo.ContentPanel;
48834
48835 var layout = Roo.BorderLayout.create({
48836     north: {
48837         initialSize: 25,
48838         titlebar: false,
48839         panels: [new CP("north", "North")]
48840     },
48841     west: {
48842         split:true,
48843         initialSize: 200,
48844         minSize: 175,
48845         maxSize: 400,
48846         titlebar: true,
48847         collapsible: true,
48848         panels: [new CP("west", {title: "West"})]
48849     },
48850     east: {
48851         split:true,
48852         initialSize: 202,
48853         minSize: 175,
48854         maxSize: 400,
48855         titlebar: true,
48856         collapsible: true,
48857         panels: [new CP("autoTabs", {title: "Auto Tabs", closable: true})]
48858     },
48859     south: {
48860         split:true,
48861         initialSize: 100,
48862         minSize: 100,
48863         maxSize: 200,
48864         titlebar: true,
48865         collapsible: true,
48866         panels: [new CP("south", {title: "South", closable: true})]
48867     },
48868     center: {
48869         titlebar: true,
48870         autoScroll:true,
48871         resizeTabs: true,
48872         minTabWidth: 50,
48873         preferredTabWidth: 150,
48874         panels: [
48875             new CP("center1", {title: "Close Me", closable: true}),
48876             new CP("center2", {title: "Center Panel", closable: false})
48877         ]
48878     }
48879 }, document.body);
48880
48881 layout.getRegion("center").showPanel("center1");
48882 </code></pre>
48883  * @param config
48884  * @param targetEl
48885  */
48886 Roo.BorderLayout.create = function(config, targetEl){
48887     var layout = new Roo.BorderLayout(targetEl || document.body, config);
48888     layout.beginUpdate();
48889     var regions = Roo.BorderLayout.RegionFactory.validRegions;
48890     for(var j = 0, jlen = regions.length; j < jlen; j++){
48891         var lr = regions[j];
48892         if(layout.regions[lr] && config[lr].panels){
48893             var r = layout.regions[lr];
48894             var ps = config[lr].panels;
48895             layout.addTypedPanels(r, ps);
48896         }
48897     }
48898     layout.endUpdate();
48899     return layout;
48900 };
48901
48902 // private
48903 Roo.BorderLayout.RegionFactory = {
48904     // private
48905     validRegions : ["north","south","east","west","center"],
48906
48907     // private
48908     create : function(target, mgr, config){
48909         target = target.toLowerCase();
48910         if(config.lightweight || config.basic){
48911             return new Roo.BasicLayoutRegion(mgr, config, target);
48912         }
48913         switch(target){
48914             case "north":
48915                 return new Roo.NorthLayoutRegion(mgr, config);
48916             case "south":
48917                 return new Roo.SouthLayoutRegion(mgr, config);
48918             case "east":
48919                 return new Roo.EastLayoutRegion(mgr, config);
48920             case "west":
48921                 return new Roo.WestLayoutRegion(mgr, config);
48922             case "center":
48923                 return new Roo.CenterLayoutRegion(mgr, config);
48924         }
48925         throw 'Layout region "'+target+'" not supported.';
48926     }
48927 };/*
48928  * Based on:
48929  * Ext JS Library 1.1.1
48930  * Copyright(c) 2006-2007, Ext JS, LLC.
48931  *
48932  * Originally Released Under LGPL - original licence link has changed is not relivant.
48933  *
48934  * Fork - LGPL
48935  * <script type="text/javascript">
48936  */
48937  
48938 /**
48939  * @class Roo.BasicLayoutRegion
48940  * @extends Roo.util.Observable
48941  * This class represents a lightweight region in a layout manager. This region does not move dom nodes
48942  * and does not have a titlebar, tabs or any other features. All it does is size and position 
48943  * panels. To create a BasicLayoutRegion, add lightweight:true or basic:true to your regions config.
48944  */
48945 Roo.BasicLayoutRegion = function(mgr, config, pos, skipConfig){
48946     this.mgr = mgr;
48947     this.position  = pos;
48948     this.events = {
48949         /**
48950          * @scope Roo.BasicLayoutRegion
48951          */
48952         
48953         /**
48954          * @event beforeremove
48955          * Fires before a panel is removed (or closed). To cancel the removal set "e.cancel = true" on the event argument.
48956          * @param {Roo.LayoutRegion} this
48957          * @param {Roo.ContentPanel} panel The panel
48958          * @param {Object} e The cancel event object
48959          */
48960         "beforeremove" : true,
48961         /**
48962          * @event invalidated
48963          * Fires when the layout for this region is changed.
48964          * @param {Roo.LayoutRegion} this
48965          */
48966         "invalidated" : true,
48967         /**
48968          * @event visibilitychange
48969          * Fires when this region is shown or hidden 
48970          * @param {Roo.LayoutRegion} this
48971          * @param {Boolean} visibility true or false
48972          */
48973         "visibilitychange" : true,
48974         /**
48975          * @event paneladded
48976          * Fires when a panel is added. 
48977          * @param {Roo.LayoutRegion} this
48978          * @param {Roo.ContentPanel} panel The panel
48979          */
48980         "paneladded" : true,
48981         /**
48982          * @event panelremoved
48983          * Fires when a panel is removed. 
48984          * @param {Roo.LayoutRegion} this
48985          * @param {Roo.ContentPanel} panel The panel
48986          */
48987         "panelremoved" : true,
48988         /**
48989          * @event collapsed
48990          * Fires when this region is collapsed.
48991          * @param {Roo.LayoutRegion} this
48992          */
48993         "collapsed" : true,
48994         /**
48995          * @event expanded
48996          * Fires when this region is expanded.
48997          * @param {Roo.LayoutRegion} this
48998          */
48999         "expanded" : true,
49000         /**
49001          * @event slideshow
49002          * Fires when this region is slid into view.
49003          * @param {Roo.LayoutRegion} this
49004          */
49005         "slideshow" : true,
49006         /**
49007          * @event slidehide
49008          * Fires when this region slides out of view. 
49009          * @param {Roo.LayoutRegion} this
49010          */
49011         "slidehide" : true,
49012         /**
49013          * @event panelactivated
49014          * Fires when a panel is activated. 
49015          * @param {Roo.LayoutRegion} this
49016          * @param {Roo.ContentPanel} panel The activated panel
49017          */
49018         "panelactivated" : true,
49019         /**
49020          * @event resized
49021          * Fires when the user resizes this region. 
49022          * @param {Roo.LayoutRegion} this
49023          * @param {Number} newSize The new size (width for east/west, height for north/south)
49024          */
49025         "resized" : true
49026     };
49027     /** A collection of panels in this region. @type Roo.util.MixedCollection */
49028     this.panels = new Roo.util.MixedCollection();
49029     this.panels.getKey = this.getPanelId.createDelegate(this);
49030     this.box = null;
49031     this.activePanel = null;
49032     // ensure listeners are added...
49033     
49034     if (config.listeners || config.events) {
49035         Roo.BasicLayoutRegion.superclass.constructor.call(this, {
49036             listeners : config.listeners || {},
49037             events : config.events || {}
49038         });
49039     }
49040     
49041     if(skipConfig !== true){
49042         this.applyConfig(config);
49043     }
49044 };
49045
49046 Roo.extend(Roo.BasicLayoutRegion, Roo.util.Observable, {
49047     getPanelId : function(p){
49048         return p.getId();
49049     },
49050     
49051     applyConfig : function(config){
49052         this.margins = config.margins || this.margins || {top: 0, left: 0, right:0, bottom: 0};
49053         this.config = config;
49054         
49055     },
49056     
49057     /**
49058      * Resizes the region to the specified size. For vertical regions (west, east) this adjusts 
49059      * the width, for horizontal (north, south) the height.
49060      * @param {Number} newSize The new width or height
49061      */
49062     resizeTo : function(newSize){
49063         var el = this.el ? this.el :
49064                  (this.activePanel ? this.activePanel.getEl() : null);
49065         if(el){
49066             switch(this.position){
49067                 case "east":
49068                 case "west":
49069                     el.setWidth(newSize);
49070                     this.fireEvent("resized", this, newSize);
49071                 break;
49072                 case "north":
49073                 case "south":
49074                     el.setHeight(newSize);
49075                     this.fireEvent("resized", this, newSize);
49076                 break;                
49077             }
49078         }
49079     },
49080     
49081     getBox : function(){
49082         return this.activePanel ? this.activePanel.getEl().getBox(false, true) : null;
49083     },
49084     
49085     getMargins : function(){
49086         return this.margins;
49087     },
49088     
49089     updateBox : function(box){
49090         this.box = box;
49091         var el = this.activePanel.getEl();
49092         el.dom.style.left = box.x + "px";
49093         el.dom.style.top = box.y + "px";
49094         this.activePanel.setSize(box.width, box.height);
49095     },
49096     
49097     /**
49098      * Returns the container element for this region.
49099      * @return {Roo.Element}
49100      */
49101     getEl : function(){
49102         return this.activePanel;
49103     },
49104     
49105     /**
49106      * Returns true if this region is currently visible.
49107      * @return {Boolean}
49108      */
49109     isVisible : function(){
49110         return this.activePanel ? true : false;
49111     },
49112     
49113     setActivePanel : function(panel){
49114         panel = this.getPanel(panel);
49115         if(this.activePanel && this.activePanel != panel){
49116             this.activePanel.setActiveState(false);
49117             this.activePanel.getEl().setLeftTop(-10000,-10000);
49118         }
49119         this.activePanel = panel;
49120         panel.setActiveState(true);
49121         if(this.box){
49122             panel.setSize(this.box.width, this.box.height);
49123         }
49124         this.fireEvent("panelactivated", this, panel);
49125         this.fireEvent("invalidated");
49126     },
49127     
49128     /**
49129      * Show the specified panel.
49130      * @param {Number/String/ContentPanel} panelId The panels index, id or the panel itself
49131      * @return {Roo.ContentPanel} The shown panel or null
49132      */
49133     showPanel : function(panel){
49134         if(panel = this.getPanel(panel)){
49135             this.setActivePanel(panel);
49136         }
49137         return panel;
49138     },
49139     
49140     /**
49141      * Get the active panel for this region.
49142      * @return {Roo.ContentPanel} The active panel or null
49143      */
49144     getActivePanel : function(){
49145         return this.activePanel;
49146     },
49147     
49148     /**
49149      * Add the passed ContentPanel(s)
49150      * @param {ContentPanel...} panel The ContentPanel(s) to add (you can pass more than one)
49151      * @return {Roo.ContentPanel} The panel added (if only one was added)
49152      */
49153     add : function(panel){
49154         if(arguments.length > 1){
49155             for(var i = 0, len = arguments.length; i < len; i++) {
49156                 this.add(arguments[i]);
49157             }
49158             return null;
49159         }
49160         if(this.hasPanel(panel)){
49161             this.showPanel(panel);
49162             return panel;
49163         }
49164         var el = panel.getEl();
49165         if(el.dom.parentNode != this.mgr.el.dom){
49166             this.mgr.el.dom.appendChild(el.dom);
49167         }
49168         if(panel.setRegion){
49169             panel.setRegion(this);
49170         }
49171         this.panels.add(panel);
49172         el.setStyle("position", "absolute");
49173         if(!panel.background){
49174             this.setActivePanel(panel);
49175             if(this.config.initialSize && this.panels.getCount()==1){
49176                 this.resizeTo(this.config.initialSize);
49177             }
49178         }
49179         this.fireEvent("paneladded", this, panel);
49180         return panel;
49181     },
49182     
49183     /**
49184      * Returns true if the panel is in this region.
49185      * @param {Number/String/ContentPanel} panel The panels index, id or the panel itself
49186      * @return {Boolean}
49187      */
49188     hasPanel : function(panel){
49189         if(typeof panel == "object"){ // must be panel obj
49190             panel = panel.getId();
49191         }
49192         return this.getPanel(panel) ? true : false;
49193     },
49194     
49195     /**
49196      * Removes the specified panel. If preservePanel is not true (either here or in the config), the panel is destroyed.
49197      * @param {Number/String/ContentPanel} panel The panels index, id or the panel itself
49198      * @param {Boolean} preservePanel Overrides the config preservePanel option
49199      * @return {Roo.ContentPanel} The panel that was removed
49200      */
49201     remove : function(panel, preservePanel){
49202         panel = this.getPanel(panel);
49203         if(!panel){
49204             return null;
49205         }
49206         var e = {};
49207         this.fireEvent("beforeremove", this, panel, e);
49208         if(e.cancel === true){
49209             return null;
49210         }
49211         var panelId = panel.getId();
49212         this.panels.removeKey(panelId);
49213         return panel;
49214     },
49215     
49216     /**
49217      * Returns the panel specified or null if it's not in this region.
49218      * @param {Number/String/ContentPanel} panel The panels index, id or the panel itself
49219      * @return {Roo.ContentPanel}
49220      */
49221     getPanel : function(id){
49222         if(typeof id == "object"){ // must be panel obj
49223             return id;
49224         }
49225         return this.panels.get(id);
49226     },
49227     
49228     /**
49229      * Returns this regions position (north/south/east/west/center).
49230      * @return {String} 
49231      */
49232     getPosition: function(){
49233         return this.position;    
49234     }
49235 });/*
49236  * Based on:
49237  * Ext JS Library 1.1.1
49238  * Copyright(c) 2006-2007, Ext JS, LLC.
49239  *
49240  * Originally Released Under LGPL - original licence link has changed is not relivant.
49241  *
49242  * Fork - LGPL
49243  * <script type="text/javascript">
49244  */
49245  
49246 /**
49247  * @class Roo.LayoutRegion
49248  * @extends Roo.BasicLayoutRegion
49249  * This class represents a region in a layout manager.
49250  * @cfg {Boolean}   collapsible     False to disable collapsing (defaults to true)
49251  * @cfg {Boolean}   collapsed       True to set the initial display to collapsed (defaults to false)
49252  * @cfg {Boolean}   floatable       False to disable floating (defaults to true)
49253  * @cfg {Object}    margins         Margins for the element (defaults to {top: 0, left: 0, right:0, bottom: 0})
49254  * @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})
49255  * @cfg {String}    tabPosition     "top" or "bottom" (defaults to "bottom")
49256  * @cfg {String}    collapsedTitle  Optional string message to display in the collapsed block of a north or south region
49257  * @cfg {Boolean}   alwaysShowTabs  True to always display tabs even when there is only 1 panel (defaults to false)
49258  * @cfg {Boolean}   autoScroll      True to enable overflow scrolling (defaults to false)
49259  * @cfg {Boolean}   titlebar        True to display a title bar (defaults to true)
49260  * @cfg {String}    title           The title for the region (overrides panel titles)
49261  * @cfg {Boolean}   animate         True to animate expand/collapse (defaults to false)
49262  * @cfg {Boolean}   autoHide        False to disable auto hiding when the mouse leaves the "floated" region (defaults to true)
49263  * @cfg {Boolean}   preservePanels  True to preserve removed panels so they can be readded later (defaults to false)
49264  * @cfg {Boolean}   closeOnTab      True to place the close icon on the tabs instead of the region titlebar (defaults to false)
49265  * @cfg {Boolean}   hideTabs        True to hide the tab strip (defaults to false)
49266  * @cfg {Boolean}   resizeTabs      True to enable automatic tab resizing. This will resize the tabs so they are all the same size and fit within
49267  *                      the space available, similar to FireFox 1.5 tabs (defaults to false)
49268  * @cfg {Number}    minTabWidth     The minimum tab width (defaults to 40)
49269  * @cfg {Number}    preferredTabWidth The preferred tab width (defaults to 150)
49270  * @cfg {Boolean}   showPin         True to show a pin button
49271  * @cfg {Boolean}   hidden          True to start the region hidden (defaults to false)
49272  * @cfg {Boolean}   hideWhenEmpty   True to hide the region when it has no panels
49273  * @cfg {Boolean}   disableTabTips  True to disable tab tooltips
49274  * @cfg {Number}    width           For East/West panels
49275  * @cfg {Number}    height          For North/South panels
49276  * @cfg {Boolean}   split           To show the splitter
49277  * @cfg {Boolean}   toolbar         xtype configuration for a toolbar - shows on right of tabbar
49278  */
49279 Roo.LayoutRegion = function(mgr, config, pos){
49280     Roo.LayoutRegion.superclass.constructor.call(this, mgr, config, pos, true);
49281     var dh = Roo.DomHelper;
49282     /** This region's container element 
49283     * @type Roo.Element */
49284     this.el = dh.append(mgr.el.dom, {tag: "div", cls: "x-layout-panel x-layout-panel-" + this.position}, true);
49285     /** This region's title element 
49286     * @type Roo.Element */
49287
49288     this.titleEl = dh.append(this.el.dom, {tag: "div", unselectable: "on", cls: "x-unselectable x-layout-panel-hd x-layout-title-"+this.position, children:[
49289         {tag: "span", cls: "x-unselectable x-layout-panel-hd-text", unselectable: "on", html: "&#160;"},
49290         {tag: "div", cls: "x-unselectable x-layout-panel-hd-tools", unselectable: "on"}
49291     ]}, true);
49292     this.titleEl.enableDisplayMode();
49293     /** This region's title text element 
49294     * @type HTMLElement */
49295     this.titleTextEl = this.titleEl.dom.firstChild;
49296     this.tools = Roo.get(this.titleEl.dom.childNodes[1], true);
49297     this.closeBtn = this.createTool(this.tools.dom, "x-layout-close");
49298     this.closeBtn.enableDisplayMode();
49299     this.closeBtn.on("click", this.closeClicked, this);
49300     this.closeBtn.hide();
49301
49302     this.createBody(config);
49303     this.visible = true;
49304     this.collapsed = false;
49305
49306     if(config.hideWhenEmpty){
49307         this.hide();
49308         this.on("paneladded", this.validateVisibility, this);
49309         this.on("panelremoved", this.validateVisibility, this);
49310     }
49311     this.applyConfig(config);
49312 };
49313
49314 Roo.extend(Roo.LayoutRegion, Roo.BasicLayoutRegion, {
49315
49316     createBody : function(){
49317         /** This region's body element 
49318         * @type Roo.Element */
49319         this.bodyEl = this.el.createChild({tag: "div", cls: "x-layout-panel-body"});
49320     },
49321
49322     applyConfig : function(c){
49323         if(c.collapsible && this.position != "center" && !this.collapsedEl){
49324             var dh = Roo.DomHelper;
49325             if(c.titlebar !== false){
49326                 this.collapseBtn = this.createTool(this.tools.dom, "x-layout-collapse-"+this.position);
49327                 this.collapseBtn.on("click", this.collapse, this);
49328                 this.collapseBtn.enableDisplayMode();
49329
49330                 if(c.showPin === true || this.showPin){
49331                     this.stickBtn = this.createTool(this.tools.dom, "x-layout-stick");
49332                     this.stickBtn.enableDisplayMode();
49333                     this.stickBtn.on("click", this.expand, this);
49334                     this.stickBtn.hide();
49335                 }
49336             }
49337             /** This region's collapsed element
49338             * @type Roo.Element */
49339             this.collapsedEl = dh.append(this.mgr.el.dom, {cls: "x-layout-collapsed x-layout-collapsed-"+this.position, children:[
49340                 {cls: "x-layout-collapsed-tools", children:[{cls: "x-layout-ctools-inner"}]}
49341             ]}, true);
49342             if(c.floatable !== false){
49343                this.collapsedEl.addClassOnOver("x-layout-collapsed-over");
49344                this.collapsedEl.on("click", this.collapseClick, this);
49345             }
49346
49347             if(c.collapsedTitle && (this.position == "north" || this.position== "south")) {
49348                 this.collapsedTitleTextEl = dh.append(this.collapsedEl.dom, {tag: "div", cls: "x-unselectable x-layout-panel-hd-text",
49349                    id: "message", unselectable: "on", style:{"float":"left"}});
49350                this.collapsedTitleTextEl.innerHTML = c.collapsedTitle;
49351              }
49352             this.expandBtn = this.createTool(this.collapsedEl.dom.firstChild.firstChild, "x-layout-expand-"+this.position);
49353             this.expandBtn.on("click", this.expand, this);
49354         }
49355         if(this.collapseBtn){
49356             this.collapseBtn.setVisible(c.collapsible == true);
49357         }
49358         this.cmargins = c.cmargins || this.cmargins ||
49359                          (this.position == "west" || this.position == "east" ?
49360                              {top: 0, left: 2, right:2, bottom: 0} :
49361                              {top: 2, left: 0, right:0, bottom: 2});
49362         this.margins = c.margins || this.margins || {top: 0, left: 0, right:0, bottom: 0};
49363         this.bottomTabs = c.tabPosition != "top";
49364         this.autoScroll = c.autoScroll || false;
49365         if(this.autoScroll){
49366             this.bodyEl.setStyle("overflow", "auto");
49367         }else{
49368             this.bodyEl.setStyle("overflow", "hidden");
49369         }
49370         //if(c.titlebar !== false){
49371             if((!c.titlebar && !c.title) || c.titlebar === false){
49372                 this.titleEl.hide();
49373             }else{
49374                 this.titleEl.show();
49375                 if(c.title){
49376                     this.titleTextEl.innerHTML = c.title;
49377                 }
49378             }
49379         //}
49380         this.duration = c.duration || .30;
49381         this.slideDuration = c.slideDuration || .45;
49382         this.config = c;
49383         if(c.collapsed){
49384             this.collapse(true);
49385         }
49386         if(c.hidden){
49387             this.hide();
49388         }
49389     },
49390     /**
49391      * Returns true if this region is currently visible.
49392      * @return {Boolean}
49393      */
49394     isVisible : function(){
49395         return this.visible;
49396     },
49397
49398     /**
49399      * Updates the title for collapsed north/south regions (used with {@link #collapsedTitle} config option)
49400      * @param {String} title (optional) The title text (accepts HTML markup, defaults to the numeric character reference for a non-breaking space, "&amp;#160;")
49401      */
49402     setCollapsedTitle : function(title){
49403         title = title || "&#160;";
49404         if(this.collapsedTitleTextEl){
49405             this.collapsedTitleTextEl.innerHTML = title;
49406         }
49407     },
49408
49409     getBox : function(){
49410         var b;
49411         if(!this.collapsed){
49412             b = this.el.getBox(false, true);
49413         }else{
49414             b = this.collapsedEl.getBox(false, true);
49415         }
49416         return b;
49417     },
49418
49419     getMargins : function(){
49420         return this.collapsed ? this.cmargins : this.margins;
49421     },
49422
49423     highlight : function(){
49424         this.el.addClass("x-layout-panel-dragover");
49425     },
49426
49427     unhighlight : function(){
49428         this.el.removeClass("x-layout-panel-dragover");
49429     },
49430
49431     updateBox : function(box){
49432         this.box = box;
49433         if(!this.collapsed){
49434             this.el.dom.style.left = box.x + "px";
49435             this.el.dom.style.top = box.y + "px";
49436             this.updateBody(box.width, box.height);
49437         }else{
49438             this.collapsedEl.dom.style.left = box.x + "px";
49439             this.collapsedEl.dom.style.top = box.y + "px";
49440             this.collapsedEl.setSize(box.width, box.height);
49441         }
49442         if(this.tabs){
49443             this.tabs.autoSizeTabs();
49444         }
49445     },
49446
49447     updateBody : function(w, h){
49448         if(w !== null){
49449             this.el.setWidth(w);
49450             w -= this.el.getBorderWidth("rl");
49451             if(this.config.adjustments){
49452                 w += this.config.adjustments[0];
49453             }
49454         }
49455         if(h !== null){
49456             this.el.setHeight(h);
49457             h = this.titleEl && this.titleEl.isDisplayed() ? h - (this.titleEl.getHeight()||0) : h;
49458             h -= this.el.getBorderWidth("tb");
49459             if(this.config.adjustments){
49460                 h += this.config.adjustments[1];
49461             }
49462             this.bodyEl.setHeight(h);
49463             if(this.tabs){
49464                 h = this.tabs.syncHeight(h);
49465             }
49466         }
49467         if(this.panelSize){
49468             w = w !== null ? w : this.panelSize.width;
49469             h = h !== null ? h : this.panelSize.height;
49470         }
49471         if(this.activePanel){
49472             var el = this.activePanel.getEl();
49473             w = w !== null ? w : el.getWidth();
49474             h = h !== null ? h : el.getHeight();
49475             this.panelSize = {width: w, height: h};
49476             this.activePanel.setSize(w, h);
49477         }
49478         if(Roo.isIE && this.tabs){
49479             this.tabs.el.repaint();
49480         }
49481     },
49482
49483     /**
49484      * Returns the container element for this region.
49485      * @return {Roo.Element}
49486      */
49487     getEl : function(){
49488         return this.el;
49489     },
49490
49491     /**
49492      * Hides this region.
49493      */
49494     hide : function(){
49495         if(!this.collapsed){
49496             this.el.dom.style.left = "-2000px";
49497             this.el.hide();
49498         }else{
49499             this.collapsedEl.dom.style.left = "-2000px";
49500             this.collapsedEl.hide();
49501         }
49502         this.visible = false;
49503         this.fireEvent("visibilitychange", this, false);
49504     },
49505
49506     /**
49507      * Shows this region if it was previously hidden.
49508      */
49509     show : function(){
49510         if(!this.collapsed){
49511             this.el.show();
49512         }else{
49513             this.collapsedEl.show();
49514         }
49515         this.visible = true;
49516         this.fireEvent("visibilitychange", this, true);
49517     },
49518
49519     closeClicked : function(){
49520         if(this.activePanel){
49521             this.remove(this.activePanel);
49522         }
49523     },
49524
49525     collapseClick : function(e){
49526         if(this.isSlid){
49527            e.stopPropagation();
49528            this.slideIn();
49529         }else{
49530            e.stopPropagation();
49531            this.slideOut();
49532         }
49533     },
49534
49535     /**
49536      * Collapses this region.
49537      * @param {Boolean} skipAnim (optional) true to collapse the element without animation (if animate is true)
49538      */
49539     collapse : function(skipAnim){
49540         if(this.collapsed) return;
49541         this.collapsed = true;
49542         if(this.split){
49543             this.split.el.hide();
49544         }
49545         if(this.config.animate && skipAnim !== true){
49546             this.fireEvent("invalidated", this);
49547             this.animateCollapse();
49548         }else{
49549             this.el.setLocation(-20000,-20000);
49550             this.el.hide();
49551             this.collapsedEl.show();
49552             this.fireEvent("collapsed", this);
49553             this.fireEvent("invalidated", this);
49554         }
49555     },
49556
49557     animateCollapse : function(){
49558         // overridden
49559     },
49560
49561     /**
49562      * Expands this region if it was previously collapsed.
49563      * @param {Roo.EventObject} e The event that triggered the expand (or null if calling manually)
49564      * @param {Boolean} skipAnim (optional) true to expand the element without animation (if animate is true)
49565      */
49566     expand : function(e, skipAnim){
49567         if(e) e.stopPropagation();
49568         if(!this.collapsed || this.el.hasActiveFx()) return;
49569         if(this.isSlid){
49570             this.afterSlideIn();
49571             skipAnim = true;
49572         }
49573         this.collapsed = false;
49574         if(this.config.animate && skipAnim !== true){
49575             this.animateExpand();
49576         }else{
49577             this.el.show();
49578             if(this.split){
49579                 this.split.el.show();
49580             }
49581             this.collapsedEl.setLocation(-2000,-2000);
49582             this.collapsedEl.hide();
49583             this.fireEvent("invalidated", this);
49584             this.fireEvent("expanded", this);
49585         }
49586     },
49587
49588     animateExpand : function(){
49589         // overridden
49590     },
49591
49592     initTabs : function()
49593     {
49594         this.bodyEl.setStyle("overflow", "hidden");
49595         var ts = new Roo.TabPanel(
49596                 this.bodyEl.dom,
49597                 {
49598                     tabPosition: this.bottomTabs ? 'bottom' : 'top',
49599                     disableTooltips: this.config.disableTabTips,
49600                     toolbar : this.config.toolbar
49601                 }
49602         );
49603         if(this.config.hideTabs){
49604             ts.stripWrap.setDisplayed(false);
49605         }
49606         this.tabs = ts;
49607         ts.resizeTabs = this.config.resizeTabs === true;
49608         ts.minTabWidth = this.config.minTabWidth || 40;
49609         ts.maxTabWidth = this.config.maxTabWidth || 250;
49610         ts.preferredTabWidth = this.config.preferredTabWidth || 150;
49611         ts.monitorResize = false;
49612         ts.bodyEl.setStyle("overflow", this.config.autoScroll ? "auto" : "hidden");
49613         ts.bodyEl.addClass('x-layout-tabs-body');
49614         this.panels.each(this.initPanelAsTab, this);
49615     },
49616
49617     initPanelAsTab : function(panel){
49618         var ti = this.tabs.addTab(panel.getEl().id, panel.getTitle(), null,
49619                     this.config.closeOnTab && panel.isClosable());
49620         if(panel.tabTip !== undefined){
49621             ti.setTooltip(panel.tabTip);
49622         }
49623         ti.on("activate", function(){
49624               this.setActivePanel(panel);
49625         }, this);
49626         if(this.config.closeOnTab){
49627             ti.on("beforeclose", function(t, e){
49628                 e.cancel = true;
49629                 this.remove(panel);
49630             }, this);
49631         }
49632         return ti;
49633     },
49634
49635     updatePanelTitle : function(panel, title){
49636         if(this.activePanel == panel){
49637             this.updateTitle(title);
49638         }
49639         if(this.tabs){
49640             var ti = this.tabs.getTab(panel.getEl().id);
49641             ti.setText(title);
49642             if(panel.tabTip !== undefined){
49643                 ti.setTooltip(panel.tabTip);
49644             }
49645         }
49646     },
49647
49648     updateTitle : function(title){
49649         if(this.titleTextEl && !this.config.title){
49650             this.titleTextEl.innerHTML = (typeof title != "undefined" && title.length > 0 ? title : "&#160;");
49651         }
49652     },
49653
49654     setActivePanel : function(panel){
49655         panel = this.getPanel(panel);
49656         if(this.activePanel && this.activePanel != panel){
49657             this.activePanel.setActiveState(false);
49658         }
49659         this.activePanel = panel;
49660         panel.setActiveState(true);
49661         if(this.panelSize){
49662             panel.setSize(this.panelSize.width, this.panelSize.height);
49663         }
49664         if(this.closeBtn){
49665             this.closeBtn.setVisible(!this.config.closeOnTab && !this.isSlid && panel.isClosable());
49666         }
49667         this.updateTitle(panel.getTitle());
49668         if(this.tabs){
49669             this.fireEvent("invalidated", this);
49670         }
49671         this.fireEvent("panelactivated", this, panel);
49672     },
49673
49674     /**
49675      * Shows the specified panel.
49676      * @param {Number/String/ContentPanel} panelId The panel's index, id or the panel itself
49677      * @return {Roo.ContentPanel} The shown panel, or null if a panel could not be found from panelId
49678      */
49679     showPanel : function(panel){
49680         if(panel = this.getPanel(panel)){
49681             if(this.tabs){
49682                 var tab = this.tabs.getTab(panel.getEl().id);
49683                 if(tab.isHidden()){
49684                     this.tabs.unhideTab(tab.id);
49685                 }
49686                 tab.activate();
49687             }else{
49688                 this.setActivePanel(panel);
49689             }
49690         }
49691         return panel;
49692     },
49693
49694     /**
49695      * Get the active panel for this region.
49696      * @return {Roo.ContentPanel} The active panel or null
49697      */
49698     getActivePanel : function(){
49699         return this.activePanel;
49700     },
49701
49702     validateVisibility : function(){
49703         if(this.panels.getCount() < 1){
49704             this.updateTitle("&#160;");
49705             this.closeBtn.hide();
49706             this.hide();
49707         }else{
49708             if(!this.isVisible()){
49709                 this.show();
49710             }
49711         }
49712     },
49713
49714     /**
49715      * Adds the passed ContentPanel(s) to this region.
49716      * @param {ContentPanel...} panel The ContentPanel(s) to add (you can pass more than one)
49717      * @return {Roo.ContentPanel} The panel added (if only one was added; null otherwise)
49718      */
49719     add : function(panel){
49720         if(arguments.length > 1){
49721             for(var i = 0, len = arguments.length; i < len; i++) {
49722                 this.add(arguments[i]);
49723             }
49724             return null;
49725         }
49726         if(this.hasPanel(panel)){
49727             this.showPanel(panel);
49728             return panel;
49729         }
49730         panel.setRegion(this);
49731         this.panels.add(panel);
49732         if(this.panels.getCount() == 1 && !this.config.alwaysShowTabs){
49733             this.bodyEl.dom.appendChild(panel.getEl().dom);
49734             if(panel.background !== true){
49735                 this.setActivePanel(panel);
49736             }
49737             this.fireEvent("paneladded", this, panel);
49738             return panel;
49739         }
49740         if(!this.tabs){
49741             this.initTabs();
49742         }else{
49743             this.initPanelAsTab(panel);
49744         }
49745         if(panel.background !== true){
49746             this.tabs.activate(panel.getEl().id);
49747         }
49748         this.fireEvent("paneladded", this, panel);
49749         return panel;
49750     },
49751
49752     /**
49753      * Hides the tab for the specified panel.
49754      * @param {Number/String/ContentPanel} panel The panel's index, id or the panel itself
49755      */
49756     hidePanel : function(panel){
49757         if(this.tabs && (panel = this.getPanel(panel))){
49758             this.tabs.hideTab(panel.getEl().id);
49759         }
49760     },
49761
49762     /**
49763      * Unhides the tab for a previously hidden panel.
49764      * @param {Number/String/ContentPanel} panel The panel's index, id or the panel itself
49765      */
49766     unhidePanel : function(panel){
49767         if(this.tabs && (panel = this.getPanel(panel))){
49768             this.tabs.unhideTab(panel.getEl().id);
49769         }
49770     },
49771
49772     clearPanels : function(){
49773         while(this.panels.getCount() > 0){
49774              this.remove(this.panels.first());
49775         }
49776     },
49777
49778     /**
49779      * Removes the specified panel. If preservePanel is not true (either here or in the config), the panel is destroyed.
49780      * @param {Number/String/ContentPanel} panel The panel's index, id or the panel itself
49781      * @param {Boolean} preservePanel Overrides the config preservePanel option
49782      * @return {Roo.ContentPanel} The panel that was removed
49783      */
49784     remove : function(panel, preservePanel){
49785         panel = this.getPanel(panel);
49786         if(!panel){
49787             return null;
49788         }
49789         var e = {};
49790         this.fireEvent("beforeremove", this, panel, e);
49791         if(e.cancel === true){
49792             return null;
49793         }
49794         preservePanel = (typeof preservePanel != "undefined" ? preservePanel : (this.config.preservePanels === true || panel.preserve === true));
49795         var panelId = panel.getId();
49796         this.panels.removeKey(panelId);
49797         if(preservePanel){
49798             document.body.appendChild(panel.getEl().dom);
49799         }
49800         if(this.tabs){
49801             this.tabs.removeTab(panel.getEl().id);
49802         }else if (!preservePanel){
49803             this.bodyEl.dom.removeChild(panel.getEl().dom);
49804         }
49805         if(this.panels.getCount() == 1 && this.tabs && !this.config.alwaysShowTabs){
49806             var p = this.panels.first();
49807             var tempEl = document.createElement("div"); // temp holder to keep IE from deleting the node
49808             tempEl.appendChild(p.getEl().dom);
49809             this.bodyEl.update("");
49810             this.bodyEl.dom.appendChild(p.getEl().dom);
49811             tempEl = null;
49812             this.updateTitle(p.getTitle());
49813             this.tabs = null;
49814             this.bodyEl.setStyle("overflow", this.config.autoScroll ? "auto" : "hidden");
49815             this.setActivePanel(p);
49816         }
49817         panel.setRegion(null);
49818         if(this.activePanel == panel){
49819             this.activePanel = null;
49820         }
49821         if(this.config.autoDestroy !== false && preservePanel !== true){
49822             try{panel.destroy();}catch(e){}
49823         }
49824         this.fireEvent("panelremoved", this, panel);
49825         return panel;
49826     },
49827
49828     /**
49829      * Returns the TabPanel component used by this region
49830      * @return {Roo.TabPanel}
49831      */
49832     getTabs : function(){
49833         return this.tabs;
49834     },
49835
49836     createTool : function(parentEl, className){
49837         var btn = Roo.DomHelper.append(parentEl, {tag: "div", cls: "x-layout-tools-button",
49838             children: [{tag: "div", cls: "x-layout-tools-button-inner " + className, html: "&#160;"}]}, true);
49839         btn.addClassOnOver("x-layout-tools-button-over");
49840         return btn;
49841     }
49842 });/*
49843  * Based on:
49844  * Ext JS Library 1.1.1
49845  * Copyright(c) 2006-2007, Ext JS, LLC.
49846  *
49847  * Originally Released Under LGPL - original licence link has changed is not relivant.
49848  *
49849  * Fork - LGPL
49850  * <script type="text/javascript">
49851  */
49852  
49853
49854
49855 /**
49856  * @class Roo.SplitLayoutRegion
49857  * @extends Roo.LayoutRegion
49858  * Adds a splitbar and other (private) useful functionality to a {@link Roo.LayoutRegion}.
49859  */
49860 Roo.SplitLayoutRegion = function(mgr, config, pos, cursor){
49861     this.cursor = cursor;
49862     Roo.SplitLayoutRegion.superclass.constructor.call(this, mgr, config, pos);
49863 };
49864
49865 Roo.extend(Roo.SplitLayoutRegion, Roo.LayoutRegion, {
49866     splitTip : "Drag to resize.",
49867     collapsibleSplitTip : "Drag to resize. Double click to hide.",
49868     useSplitTips : false,
49869
49870     applyConfig : function(config){
49871         Roo.SplitLayoutRegion.superclass.applyConfig.call(this, config);
49872         if(config.split){
49873             if(!this.split){
49874                 var splitEl = Roo.DomHelper.append(this.mgr.el.dom, 
49875                         {tag: "div", id: this.el.id + "-split", cls: "x-layout-split x-layout-split-"+this.position, html: "&#160;"});
49876                 /** The SplitBar for this region 
49877                 * @type Roo.SplitBar */
49878                 this.split = new Roo.SplitBar(splitEl, this.el, this.orientation);
49879                 this.split.on("moved", this.onSplitMove, this);
49880                 this.split.useShim = config.useShim === true;
49881                 this.split.getMaximumSize = this[this.position == 'north' || this.position == 'south' ? 'getVMaxSize' : 'getHMaxSize'].createDelegate(this);
49882                 if(this.useSplitTips){
49883                     this.split.el.dom.title = config.collapsible ? this.collapsibleSplitTip : this.splitTip;
49884                 }
49885                 if(config.collapsible){
49886                     this.split.el.on("dblclick", this.collapse,  this);
49887                 }
49888             }
49889             if(typeof config.minSize != "undefined"){
49890                 this.split.minSize = config.minSize;
49891             }
49892             if(typeof config.maxSize != "undefined"){
49893                 this.split.maxSize = config.maxSize;
49894             }
49895             if(config.hideWhenEmpty || config.hidden || config.collapsed){
49896                 this.hideSplitter();
49897             }
49898         }
49899     },
49900
49901     getHMaxSize : function(){
49902          var cmax = this.config.maxSize || 10000;
49903          var center = this.mgr.getRegion("center");
49904          return Math.min(cmax, (this.el.getWidth()+center.getEl().getWidth())-center.getMinWidth());
49905     },
49906
49907     getVMaxSize : function(){
49908          var cmax = this.config.maxSize || 10000;
49909          var center = this.mgr.getRegion("center");
49910          return Math.min(cmax, (this.el.getHeight()+center.getEl().getHeight())-center.getMinHeight());
49911     },
49912
49913     onSplitMove : function(split, newSize){
49914         this.fireEvent("resized", this, newSize);
49915     },
49916     
49917     /** 
49918      * Returns the {@link Roo.SplitBar} for this region.
49919      * @return {Roo.SplitBar}
49920      */
49921     getSplitBar : function(){
49922         return this.split;
49923     },
49924     
49925     hide : function(){
49926         this.hideSplitter();
49927         Roo.SplitLayoutRegion.superclass.hide.call(this);
49928     },
49929
49930     hideSplitter : function(){
49931         if(this.split){
49932             this.split.el.setLocation(-2000,-2000);
49933             this.split.el.hide();
49934         }
49935     },
49936
49937     show : function(){
49938         if(this.split){
49939             this.split.el.show();
49940         }
49941         Roo.SplitLayoutRegion.superclass.show.call(this);
49942     },
49943     
49944     beforeSlide: function(){
49945         if(Roo.isGecko){// firefox overflow auto bug workaround
49946             this.bodyEl.clip();
49947             if(this.tabs) this.tabs.bodyEl.clip();
49948             if(this.activePanel){
49949                 this.activePanel.getEl().clip();
49950                 
49951                 if(this.activePanel.beforeSlide){
49952                     this.activePanel.beforeSlide();
49953                 }
49954             }
49955         }
49956     },
49957     
49958     afterSlide : function(){
49959         if(Roo.isGecko){// firefox overflow auto bug workaround
49960             this.bodyEl.unclip();
49961             if(this.tabs) this.tabs.bodyEl.unclip();
49962             if(this.activePanel){
49963                 this.activePanel.getEl().unclip();
49964                 if(this.activePanel.afterSlide){
49965                     this.activePanel.afterSlide();
49966                 }
49967             }
49968         }
49969     },
49970
49971     initAutoHide : function(){
49972         if(this.autoHide !== false){
49973             if(!this.autoHideHd){
49974                 var st = new Roo.util.DelayedTask(this.slideIn, this);
49975                 this.autoHideHd = {
49976                     "mouseout": function(e){
49977                         if(!e.within(this.el, true)){
49978                             st.delay(500);
49979                         }
49980                     },
49981                     "mouseover" : function(e){
49982                         st.cancel();
49983                     },
49984                     scope : this
49985                 };
49986             }
49987             this.el.on(this.autoHideHd);
49988         }
49989     },
49990
49991     clearAutoHide : function(){
49992         if(this.autoHide !== false){
49993             this.el.un("mouseout", this.autoHideHd.mouseout);
49994             this.el.un("mouseover", this.autoHideHd.mouseover);
49995         }
49996     },
49997
49998     clearMonitor : function(){
49999         Roo.get(document).un("click", this.slideInIf, this);
50000     },
50001
50002     // these names are backwards but not changed for compat
50003     slideOut : function(){
50004         if(this.isSlid || this.el.hasActiveFx()){
50005             return;
50006         }
50007         this.isSlid = true;
50008         if(this.collapseBtn){
50009             this.collapseBtn.hide();
50010         }
50011         this.closeBtnState = this.closeBtn.getStyle('display');
50012         this.closeBtn.hide();
50013         if(this.stickBtn){
50014             this.stickBtn.show();
50015         }
50016         this.el.show();
50017         this.el.alignTo(this.collapsedEl, this.getCollapseAnchor());
50018         this.beforeSlide();
50019         this.el.setStyle("z-index", 10001);
50020         this.el.slideIn(this.getSlideAnchor(), {
50021             callback: function(){
50022                 this.afterSlide();
50023                 this.initAutoHide();
50024                 Roo.get(document).on("click", this.slideInIf, this);
50025                 this.fireEvent("slideshow", this);
50026             },
50027             scope: this,
50028             block: true
50029         });
50030     },
50031
50032     afterSlideIn : function(){
50033         this.clearAutoHide();
50034         this.isSlid = false;
50035         this.clearMonitor();
50036         this.el.setStyle("z-index", "");
50037         if(this.collapseBtn){
50038             this.collapseBtn.show();
50039         }
50040         this.closeBtn.setStyle('display', this.closeBtnState);
50041         if(this.stickBtn){
50042             this.stickBtn.hide();
50043         }
50044         this.fireEvent("slidehide", this);
50045     },
50046
50047     slideIn : function(cb){
50048         if(!this.isSlid || this.el.hasActiveFx()){
50049             Roo.callback(cb);
50050             return;
50051         }
50052         this.isSlid = false;
50053         this.beforeSlide();
50054         this.el.slideOut(this.getSlideAnchor(), {
50055             callback: function(){
50056                 this.el.setLeftTop(-10000, -10000);
50057                 this.afterSlide();
50058                 this.afterSlideIn();
50059                 Roo.callback(cb);
50060             },
50061             scope: this,
50062             block: true
50063         });
50064     },
50065     
50066     slideInIf : function(e){
50067         if(!e.within(this.el)){
50068             this.slideIn();
50069         }
50070     },
50071
50072     animateCollapse : function(){
50073         this.beforeSlide();
50074         this.el.setStyle("z-index", 20000);
50075         var anchor = this.getSlideAnchor();
50076         this.el.slideOut(anchor, {
50077             callback : function(){
50078                 this.el.setStyle("z-index", "");
50079                 this.collapsedEl.slideIn(anchor, {duration:.3});
50080                 this.afterSlide();
50081                 this.el.setLocation(-10000,-10000);
50082                 this.el.hide();
50083                 this.fireEvent("collapsed", this);
50084             },
50085             scope: this,
50086             block: true
50087         });
50088     },
50089
50090     animateExpand : function(){
50091         this.beforeSlide();
50092         this.el.alignTo(this.collapsedEl, this.getCollapseAnchor(), this.getExpandAdj());
50093         this.el.setStyle("z-index", 20000);
50094         this.collapsedEl.hide({
50095             duration:.1
50096         });
50097         this.el.slideIn(this.getSlideAnchor(), {
50098             callback : function(){
50099                 this.el.setStyle("z-index", "");
50100                 this.afterSlide();
50101                 if(this.split){
50102                     this.split.el.show();
50103                 }
50104                 this.fireEvent("invalidated", this);
50105                 this.fireEvent("expanded", this);
50106             },
50107             scope: this,
50108             block: true
50109         });
50110     },
50111
50112     anchors : {
50113         "west" : "left",
50114         "east" : "right",
50115         "north" : "top",
50116         "south" : "bottom"
50117     },
50118
50119     sanchors : {
50120         "west" : "l",
50121         "east" : "r",
50122         "north" : "t",
50123         "south" : "b"
50124     },
50125
50126     canchors : {
50127         "west" : "tl-tr",
50128         "east" : "tr-tl",
50129         "north" : "tl-bl",
50130         "south" : "bl-tl"
50131     },
50132
50133     getAnchor : function(){
50134         return this.anchors[this.position];
50135     },
50136
50137     getCollapseAnchor : function(){
50138         return this.canchors[this.position];
50139     },
50140
50141     getSlideAnchor : function(){
50142         return this.sanchors[this.position];
50143     },
50144
50145     getAlignAdj : function(){
50146         var cm = this.cmargins;
50147         switch(this.position){
50148             case "west":
50149                 return [0, 0];
50150             break;
50151             case "east":
50152                 return [0, 0];
50153             break;
50154             case "north":
50155                 return [0, 0];
50156             break;
50157             case "south":
50158                 return [0, 0];
50159             break;
50160         }
50161     },
50162
50163     getExpandAdj : function(){
50164         var c = this.collapsedEl, cm = this.cmargins;
50165         switch(this.position){
50166             case "west":
50167                 return [-(cm.right+c.getWidth()+cm.left), 0];
50168             break;
50169             case "east":
50170                 return [cm.right+c.getWidth()+cm.left, 0];
50171             break;
50172             case "north":
50173                 return [0, -(cm.top+cm.bottom+c.getHeight())];
50174             break;
50175             case "south":
50176                 return [0, cm.top+cm.bottom+c.getHeight()];
50177             break;
50178         }
50179     }
50180 });/*
50181  * Based on:
50182  * Ext JS Library 1.1.1
50183  * Copyright(c) 2006-2007, Ext JS, LLC.
50184  *
50185  * Originally Released Under LGPL - original licence link has changed is not relivant.
50186  *
50187  * Fork - LGPL
50188  * <script type="text/javascript">
50189  */
50190 /*
50191  * These classes are private internal classes
50192  */
50193 Roo.CenterLayoutRegion = function(mgr, config){
50194     Roo.LayoutRegion.call(this, mgr, config, "center");
50195     this.visible = true;
50196     this.minWidth = config.minWidth || 20;
50197     this.minHeight = config.minHeight || 20;
50198 };
50199
50200 Roo.extend(Roo.CenterLayoutRegion, Roo.LayoutRegion, {
50201     hide : function(){
50202         // center panel can't be hidden
50203     },
50204     
50205     show : function(){
50206         // center panel can't be hidden
50207     },
50208     
50209     getMinWidth: function(){
50210         return this.minWidth;
50211     },
50212     
50213     getMinHeight: function(){
50214         return this.minHeight;
50215     }
50216 });
50217
50218
50219 Roo.NorthLayoutRegion = function(mgr, config){
50220     Roo.LayoutRegion.call(this, mgr, config, "north", "n-resize");
50221     if(this.split){
50222         this.split.placement = Roo.SplitBar.TOP;
50223         this.split.orientation = Roo.SplitBar.VERTICAL;
50224         this.split.el.addClass("x-layout-split-v");
50225     }
50226     var size = config.initialSize || config.height;
50227     if(typeof size != "undefined"){
50228         this.el.setHeight(size);
50229     }
50230 };
50231 Roo.extend(Roo.NorthLayoutRegion, Roo.SplitLayoutRegion, {
50232     orientation: Roo.SplitBar.VERTICAL,
50233     getBox : function(){
50234         if(this.collapsed){
50235             return this.collapsedEl.getBox();
50236         }
50237         var box = this.el.getBox();
50238         if(this.split){
50239             box.height += this.split.el.getHeight();
50240         }
50241         return box;
50242     },
50243     
50244     updateBox : function(box){
50245         if(this.split && !this.collapsed){
50246             box.height -= this.split.el.getHeight();
50247             this.split.el.setLeft(box.x);
50248             this.split.el.setTop(box.y+box.height);
50249             this.split.el.setWidth(box.width);
50250         }
50251         if(this.collapsed){
50252             this.updateBody(box.width, null);
50253         }
50254         Roo.LayoutRegion.prototype.updateBox.call(this, box);
50255     }
50256 });
50257
50258 Roo.SouthLayoutRegion = function(mgr, config){
50259     Roo.SplitLayoutRegion.call(this, mgr, config, "south", "s-resize");
50260     if(this.split){
50261         this.split.placement = Roo.SplitBar.BOTTOM;
50262         this.split.orientation = Roo.SplitBar.VERTICAL;
50263         this.split.el.addClass("x-layout-split-v");
50264     }
50265     var size = config.initialSize || config.height;
50266     if(typeof size != "undefined"){
50267         this.el.setHeight(size);
50268     }
50269 };
50270 Roo.extend(Roo.SouthLayoutRegion, Roo.SplitLayoutRegion, {
50271     orientation: Roo.SplitBar.VERTICAL,
50272     getBox : function(){
50273         if(this.collapsed){
50274             return this.collapsedEl.getBox();
50275         }
50276         var box = this.el.getBox();
50277         if(this.split){
50278             var sh = this.split.el.getHeight();
50279             box.height += sh;
50280             box.y -= sh;
50281         }
50282         return box;
50283     },
50284     
50285     updateBox : function(box){
50286         if(this.split && !this.collapsed){
50287             var sh = this.split.el.getHeight();
50288             box.height -= sh;
50289             box.y += sh;
50290             this.split.el.setLeft(box.x);
50291             this.split.el.setTop(box.y-sh);
50292             this.split.el.setWidth(box.width);
50293         }
50294         if(this.collapsed){
50295             this.updateBody(box.width, null);
50296         }
50297         Roo.LayoutRegion.prototype.updateBox.call(this, box);
50298     }
50299 });
50300
50301 Roo.EastLayoutRegion = function(mgr, config){
50302     Roo.SplitLayoutRegion.call(this, mgr, config, "east", "e-resize");
50303     if(this.split){
50304         this.split.placement = Roo.SplitBar.RIGHT;
50305         this.split.orientation = Roo.SplitBar.HORIZONTAL;
50306         this.split.el.addClass("x-layout-split-h");
50307     }
50308     var size = config.initialSize || config.width;
50309     if(typeof size != "undefined"){
50310         this.el.setWidth(size);
50311     }
50312 };
50313 Roo.extend(Roo.EastLayoutRegion, Roo.SplitLayoutRegion, {
50314     orientation: Roo.SplitBar.HORIZONTAL,
50315     getBox : function(){
50316         if(this.collapsed){
50317             return this.collapsedEl.getBox();
50318         }
50319         var box = this.el.getBox();
50320         if(this.split){
50321             var sw = this.split.el.getWidth();
50322             box.width += sw;
50323             box.x -= sw;
50324         }
50325         return box;
50326     },
50327
50328     updateBox : function(box){
50329         if(this.split && !this.collapsed){
50330             var sw = this.split.el.getWidth();
50331             box.width -= sw;
50332             this.split.el.setLeft(box.x);
50333             this.split.el.setTop(box.y);
50334             this.split.el.setHeight(box.height);
50335             box.x += sw;
50336         }
50337         if(this.collapsed){
50338             this.updateBody(null, box.height);
50339         }
50340         Roo.LayoutRegion.prototype.updateBox.call(this, box);
50341     }
50342 });
50343
50344 Roo.WestLayoutRegion = function(mgr, config){
50345     Roo.SplitLayoutRegion.call(this, mgr, config, "west", "w-resize");
50346     if(this.split){
50347         this.split.placement = Roo.SplitBar.LEFT;
50348         this.split.orientation = Roo.SplitBar.HORIZONTAL;
50349         this.split.el.addClass("x-layout-split-h");
50350     }
50351     var size = config.initialSize || config.width;
50352     if(typeof size != "undefined"){
50353         this.el.setWidth(size);
50354     }
50355 };
50356 Roo.extend(Roo.WestLayoutRegion, Roo.SplitLayoutRegion, {
50357     orientation: Roo.SplitBar.HORIZONTAL,
50358     getBox : function(){
50359         if(this.collapsed){
50360             return this.collapsedEl.getBox();
50361         }
50362         var box = this.el.getBox();
50363         if(this.split){
50364             box.width += this.split.el.getWidth();
50365         }
50366         return box;
50367     },
50368     
50369     updateBox : function(box){
50370         if(this.split && !this.collapsed){
50371             var sw = this.split.el.getWidth();
50372             box.width -= sw;
50373             this.split.el.setLeft(box.x+box.width);
50374             this.split.el.setTop(box.y);
50375             this.split.el.setHeight(box.height);
50376         }
50377         if(this.collapsed){
50378             this.updateBody(null, box.height);
50379         }
50380         Roo.LayoutRegion.prototype.updateBox.call(this, box);
50381     }
50382 });
50383 /*
50384  * Based on:
50385  * Ext JS Library 1.1.1
50386  * Copyright(c) 2006-2007, Ext JS, LLC.
50387  *
50388  * Originally Released Under LGPL - original licence link has changed is not relivant.
50389  *
50390  * Fork - LGPL
50391  * <script type="text/javascript">
50392  */
50393  
50394  
50395 /*
50396  * Private internal class for reading and applying state
50397  */
50398 Roo.LayoutStateManager = function(layout){
50399      // default empty state
50400      this.state = {
50401         north: {},
50402         south: {},
50403         east: {},
50404         west: {}       
50405     };
50406 };
50407
50408 Roo.LayoutStateManager.prototype = {
50409     init : function(layout, provider){
50410         this.provider = provider;
50411         var state = provider.get(layout.id+"-layout-state");
50412         if(state){
50413             var wasUpdating = layout.isUpdating();
50414             if(!wasUpdating){
50415                 layout.beginUpdate();
50416             }
50417             for(var key in state){
50418                 if(typeof state[key] != "function"){
50419                     var rstate = state[key];
50420                     var r = layout.getRegion(key);
50421                     if(r && rstate){
50422                         if(rstate.size){
50423                             r.resizeTo(rstate.size);
50424                         }
50425                         if(rstate.collapsed == true){
50426                             r.collapse(true);
50427                         }else{
50428                             r.expand(null, true);
50429                         }
50430                     }
50431                 }
50432             }
50433             if(!wasUpdating){
50434                 layout.endUpdate();
50435             }
50436             this.state = state; 
50437         }
50438         this.layout = layout;
50439         layout.on("regionresized", this.onRegionResized, this);
50440         layout.on("regioncollapsed", this.onRegionCollapsed, this);
50441         layout.on("regionexpanded", this.onRegionExpanded, this);
50442     },
50443     
50444     storeState : function(){
50445         this.provider.set(this.layout.id+"-layout-state", this.state);
50446     },
50447     
50448     onRegionResized : function(region, newSize){
50449         this.state[region.getPosition()].size = newSize;
50450         this.storeState();
50451     },
50452     
50453     onRegionCollapsed : function(region){
50454         this.state[region.getPosition()].collapsed = true;
50455         this.storeState();
50456     },
50457     
50458     onRegionExpanded : function(region){
50459         this.state[region.getPosition()].collapsed = false;
50460         this.storeState();
50461     }
50462 };/*
50463  * Based on:
50464  * Ext JS Library 1.1.1
50465  * Copyright(c) 2006-2007, Ext JS, LLC.
50466  *
50467  * Originally Released Under LGPL - original licence link has changed is not relivant.
50468  *
50469  * Fork - LGPL
50470  * <script type="text/javascript">
50471  */
50472 /**
50473  * @class Roo.ContentPanel
50474  * @extends Roo.util.Observable
50475  * A basic ContentPanel element.
50476  * @cfg {Boolean}   fitToFrame    True for this panel to adjust its size to fit when the region resizes  (defaults to false)
50477  * @cfg {Boolean}   fitContainer   When using {@link #fitToFrame} and {@link #resizeEl}, you can also fit the parent container  (defaults to false)
50478  * @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
50479  * @cfg {Boolean}   closable      True if the panel can be closed/removed
50480  * @cfg {Boolean}   background    True if the panel should not be activated when it is added (defaults to false)
50481  * @cfg {String/HTMLElement/Element} resizeEl An element to resize if {@link #fitToFrame} is true (instead of this panel's element)
50482  * @cfg {Toolbar}   toolbar       A toolbar for this panel
50483  * @cfg {Boolean} autoScroll    True to scroll overflow in this panel (use with {@link #fitToFrame})
50484  * @cfg {String} title          The title for this panel
50485  * @cfg {Array} adjustments     Values to <b>add</b> to the width/height when doing a {@link #fitToFrame} (default is [0, 0])
50486  * @cfg {String} url            Calls {@link #setUrl} with this value
50487  * @cfg {String} region         (center|north|south|east|west) which region to put this panel on (when used with xtype constructors)
50488  * @cfg {String/Object} params  When used with {@link #url}, calls {@link #setUrl} with this value
50489  * @cfg {Boolean} loadOnce      When used with {@link #url}, calls {@link #setUrl} with this value
50490  * @cfg {String}    content        Raw content to fill content panel with (uses setContent on construction.)
50491
50492  * @constructor
50493  * Create a new ContentPanel.
50494  * @param {String/HTMLElement/Roo.Element} el The container element for this panel
50495  * @param {String/Object} config A string to set only the title or a config object
50496  * @param {String} content (optional) Set the HTML content for this panel
50497  * @param {String} region (optional) Used by xtype constructors to add to regions. (values center,east,west,south,north)
50498  */
50499 Roo.ContentPanel = function(el, config, content){
50500     
50501      
50502     /*
50503     if(el.autoCreate || el.xtype){ // xtype is available if this is called from factory
50504         config = el;
50505         el = Roo.id();
50506     }
50507     if (config && config.parentLayout) { 
50508         el = config.parentLayout.el.createChild(); 
50509     }
50510     */
50511     if(el.autoCreate){ // xtype is available if this is called from factory
50512         config = el;
50513         el = Roo.id();
50514     }
50515     this.el = Roo.get(el);
50516     if(!this.el && config && config.autoCreate){
50517         if(typeof config.autoCreate == "object"){
50518             if(!config.autoCreate.id){
50519                 config.autoCreate.id = config.id||el;
50520             }
50521             this.el = Roo.DomHelper.append(document.body,
50522                         config.autoCreate, true);
50523         }else{
50524             this.el = Roo.DomHelper.append(document.body,
50525                         {tag: "div", cls: "x-layout-inactive-content", id: config.id||el}, true);
50526         }
50527     }
50528     this.closable = false;
50529     this.loaded = false;
50530     this.active = false;
50531     if(typeof config == "string"){
50532         this.title = config;
50533     }else{
50534         Roo.apply(this, config);
50535     }
50536     
50537     if (this.toolbar && !this.toolbar.el && this.toolbar.xtype) {
50538         this.wrapEl = this.el.wrap();
50539         this.toolbar.container = this.el.insertSibling(false, 'before');
50540         this.toolbar = new Roo.Toolbar(this.toolbar);
50541     }
50542     
50543     // xtype created footer. - not sure if will work as we normally have to render first..
50544     if (this.footer && !this.footer.el && this.footer.xtype) {
50545         if (!this.wrapEl) {
50546             this.wrapEl = this.el.wrap();
50547         }
50548     
50549         this.footer.container = this.wrapEl.createChild();
50550          
50551         this.footer = Roo.factory(this.footer, Roo);
50552         
50553     }
50554     
50555     if(this.resizeEl){
50556         this.resizeEl = Roo.get(this.resizeEl, true);
50557     }else{
50558         this.resizeEl = this.el;
50559     }
50560     // handle view.xtype
50561     
50562  
50563     
50564     
50565     this.addEvents({
50566         /**
50567          * @event activate
50568          * Fires when this panel is activated. 
50569          * @param {Roo.ContentPanel} this
50570          */
50571         "activate" : true,
50572         /**
50573          * @event deactivate
50574          * Fires when this panel is activated. 
50575          * @param {Roo.ContentPanel} this
50576          */
50577         "deactivate" : true,
50578
50579         /**
50580          * @event resize
50581          * Fires when this panel is resized if fitToFrame is true.
50582          * @param {Roo.ContentPanel} this
50583          * @param {Number} width The width after any component adjustments
50584          * @param {Number} height The height after any component adjustments
50585          */
50586         "resize" : true,
50587         
50588          /**
50589          * @event render
50590          * Fires when this tab is created
50591          * @param {Roo.ContentPanel} this
50592          */
50593         "render" : true
50594         
50595         
50596         
50597     });
50598     
50599
50600     
50601     
50602     if(this.autoScroll){
50603         this.resizeEl.setStyle("overflow", "auto");
50604     } else {
50605         // fix randome scrolling
50606         this.el.on('scroll', function() {
50607             Roo.log('fix random scolling');
50608             this.scrollTo('top',0); 
50609         });
50610     }
50611     content = content || this.content;
50612     if(content){
50613         this.setContent(content);
50614     }
50615     if(config && config.url){
50616         this.setUrl(this.url, this.params, this.loadOnce);
50617     }
50618     
50619     
50620     
50621     Roo.ContentPanel.superclass.constructor.call(this);
50622     
50623     if (this.view && typeof(this.view.xtype) != 'undefined') {
50624         this.view.el = this.el.appendChild(document.createElement("div"));
50625         this.view = Roo.factory(this.view); 
50626         this.view.render  &&  this.view.render(false, '');  
50627     }
50628     
50629     
50630     this.fireEvent('render', this);
50631 };
50632
50633 Roo.extend(Roo.ContentPanel, Roo.util.Observable, {
50634     tabTip:'',
50635     setRegion : function(region){
50636         this.region = region;
50637         if(region){
50638            this.el.replaceClass("x-layout-inactive-content", "x-layout-active-content");
50639         }else{
50640            this.el.replaceClass("x-layout-active-content", "x-layout-inactive-content");
50641         } 
50642     },
50643     
50644     /**
50645      * Returns the toolbar for this Panel if one was configured. 
50646      * @return {Roo.Toolbar} 
50647      */
50648     getToolbar : function(){
50649         return this.toolbar;
50650     },
50651     
50652     setActiveState : function(active){
50653         this.active = active;
50654         if(!active){
50655             this.fireEvent("deactivate", this);
50656         }else{
50657             this.fireEvent("activate", this);
50658         }
50659     },
50660     /**
50661      * Updates this panel's element
50662      * @param {String} content The new content
50663      * @param {Boolean} loadScripts (optional) true to look for and process scripts
50664     */
50665     setContent : function(content, loadScripts){
50666         this.el.update(content, loadScripts);
50667     },
50668
50669     ignoreResize : function(w, h){
50670         if(this.lastSize && this.lastSize.width == w && this.lastSize.height == h){
50671             return true;
50672         }else{
50673             this.lastSize = {width: w, height: h};
50674             return false;
50675         }
50676     },
50677     /**
50678      * Get the {@link Roo.UpdateManager} for this panel. Enables you to perform Ajax updates.
50679      * @return {Roo.UpdateManager} The UpdateManager
50680      */
50681     getUpdateManager : function(){
50682         return this.el.getUpdateManager();
50683     },
50684      /**
50685      * Loads this content panel immediately with content from XHR. Note: to delay loading until the panel is activated, use {@link #setUrl}.
50686      * @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:
50687 <pre><code>
50688 panel.load({
50689     url: "your-url.php",
50690     params: {param1: "foo", param2: "bar"}, // or a URL encoded string
50691     callback: yourFunction,
50692     scope: yourObject, //(optional scope)
50693     discardUrl: false,
50694     nocache: false,
50695     text: "Loading...",
50696     timeout: 30,
50697     scripts: false
50698 });
50699 </code></pre>
50700      * The only required property is <i>url</i>. The optional properties <i>nocache</i>, <i>text</i> and <i>scripts</i>
50701      * 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.
50702      * @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}
50703      * @param {Function} callback (optional) Callback when transaction is complete -- called with signature (oElement, bSuccess, oResponse)
50704      * @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.
50705      * @return {Roo.ContentPanel} this
50706      */
50707     load : function(){
50708         var um = this.el.getUpdateManager();
50709         um.update.apply(um, arguments);
50710         return this;
50711     },
50712
50713
50714     /**
50715      * 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.
50716      * @param {String/Function} url The URL to load the content from or a function to call to get the URL
50717      * @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)
50718      * @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)
50719      * @return {Roo.UpdateManager} The UpdateManager
50720      */
50721     setUrl : function(url, params, loadOnce){
50722         if(this.refreshDelegate){
50723             this.removeListener("activate", this.refreshDelegate);
50724         }
50725         this.refreshDelegate = this._handleRefresh.createDelegate(this, [url, params, loadOnce]);
50726         this.on("activate", this.refreshDelegate);
50727         return this.el.getUpdateManager();
50728     },
50729     
50730     _handleRefresh : function(url, params, loadOnce){
50731         if(!loadOnce || !this.loaded){
50732             var updater = this.el.getUpdateManager();
50733             updater.update(url, params, this._setLoaded.createDelegate(this));
50734         }
50735     },
50736     
50737     _setLoaded : function(){
50738         this.loaded = true;
50739     }, 
50740     
50741     /**
50742      * Returns this panel's id
50743      * @return {String} 
50744      */
50745     getId : function(){
50746         return this.el.id;
50747     },
50748     
50749     /** 
50750      * Returns this panel's element - used by regiosn to add.
50751      * @return {Roo.Element} 
50752      */
50753     getEl : function(){
50754         return this.wrapEl || this.el;
50755     },
50756     
50757     adjustForComponents : function(width, height)
50758     {
50759         //Roo.log('adjustForComponents ');
50760         if(this.resizeEl != this.el){
50761             width -= this.el.getFrameWidth('lr');
50762             height -= this.el.getFrameWidth('tb');
50763         }
50764         if(this.toolbar){
50765             var te = this.toolbar.getEl();
50766             height -= te.getHeight();
50767             te.setWidth(width);
50768         }
50769         if(this.footer){
50770             var te = this.footer.getEl();
50771             Roo.log("footer:" + te.getHeight());
50772             
50773             height -= te.getHeight();
50774             te.setWidth(width);
50775         }
50776         
50777         
50778         if(this.adjustments){
50779             width += this.adjustments[0];
50780             height += this.adjustments[1];
50781         }
50782         return {"width": width, "height": height};
50783     },
50784     
50785     setSize : function(width, height){
50786         if(this.fitToFrame && !this.ignoreResize(width, height)){
50787             if(this.fitContainer && this.resizeEl != this.el){
50788                 this.el.setSize(width, height);
50789             }
50790             var size = this.adjustForComponents(width, height);
50791             this.resizeEl.setSize(this.autoWidth ? "auto" : size.width, this.autoHeight ? "auto" : size.height);
50792             this.fireEvent('resize', this, size.width, size.height);
50793         }
50794     },
50795     
50796     /**
50797      * Returns this panel's title
50798      * @return {String} 
50799      */
50800     getTitle : function(){
50801         return this.title;
50802     },
50803     
50804     /**
50805      * Set this panel's title
50806      * @param {String} title
50807      */
50808     setTitle : function(title){
50809         this.title = title;
50810         if(this.region){
50811             this.region.updatePanelTitle(this, title);
50812         }
50813     },
50814     
50815     /**
50816      * Returns true is this panel was configured to be closable
50817      * @return {Boolean} 
50818      */
50819     isClosable : function(){
50820         return this.closable;
50821     },
50822     
50823     beforeSlide : function(){
50824         this.el.clip();
50825         this.resizeEl.clip();
50826     },
50827     
50828     afterSlide : function(){
50829         this.el.unclip();
50830         this.resizeEl.unclip();
50831     },
50832     
50833     /**
50834      *   Force a content refresh from the URL specified in the {@link #setUrl} method.
50835      *   Will fail silently if the {@link #setUrl} method has not been called.
50836      *   This does not activate the panel, just updates its content.
50837      */
50838     refresh : function(){
50839         if(this.refreshDelegate){
50840            this.loaded = false;
50841            this.refreshDelegate();
50842         }
50843     },
50844     
50845     /**
50846      * Destroys this panel
50847      */
50848     destroy : function(){
50849         this.el.removeAllListeners();
50850         var tempEl = document.createElement("span");
50851         tempEl.appendChild(this.el.dom);
50852         tempEl.innerHTML = "";
50853         this.el.remove();
50854         this.el = null;
50855     },
50856     
50857     /**
50858      * form - if the content panel contains a form - this is a reference to it.
50859      * @type {Roo.form.Form}
50860      */
50861     form : false,
50862     /**
50863      * view - if the content panel contains a view (Roo.DatePicker / Roo.View / Roo.JsonView)
50864      *    This contains a reference to it.
50865      * @type {Roo.View}
50866      */
50867     view : false,
50868     
50869       /**
50870      * Adds a xtype elements to the panel - currently only supports Forms, View, JsonView.
50871      * <pre><code>
50872
50873 layout.addxtype({
50874        xtype : 'Form',
50875        items: [ .... ]
50876    }
50877 );
50878
50879 </code></pre>
50880      * @param {Object} cfg Xtype definition of item to add.
50881      */
50882     
50883     addxtype : function(cfg) {
50884         // add form..
50885         if (cfg.xtype.match(/^Form$/)) {
50886             
50887             var el;
50888             //if (this.footer) {
50889             //    el = this.footer.container.insertSibling(false, 'before');
50890             //} else {
50891                 el = this.el.createChild();
50892             //}
50893
50894             this.form = new  Roo.form.Form(cfg);
50895             
50896             
50897             if ( this.form.allItems.length) this.form.render(el.dom);
50898             return this.form;
50899         }
50900         // should only have one of theses..
50901         if ([ 'View', 'JsonView', 'DatePicker'].indexOf(cfg.xtype) > -1) {
50902             // views.. should not be just added - used named prop 'view''
50903             
50904             cfg.el = this.el.appendChild(document.createElement("div"));
50905             // factory?
50906             
50907             var ret = new Roo.factory(cfg);
50908              
50909              ret.render && ret.render(false, ''); // render blank..
50910             this.view = ret;
50911             return ret;
50912         }
50913         return false;
50914     }
50915 });
50916
50917 /**
50918  * @class Roo.GridPanel
50919  * @extends Roo.ContentPanel
50920  * @constructor
50921  * Create a new GridPanel.
50922  * @param {Roo.grid.Grid} grid The grid for this panel
50923  * @param {String/Object} config A string to set only the panel's title, or a config object
50924  */
50925 Roo.GridPanel = function(grid, config){
50926     
50927   
50928     this.wrapper = Roo.DomHelper.append(document.body, // wrapper for IE7 strict & safari scroll issue
50929         {tag: "div", cls: "x-layout-grid-wrapper x-layout-inactive-content"}, true);
50930         
50931     this.wrapper.dom.appendChild(grid.getGridEl().dom);
50932     
50933     Roo.GridPanel.superclass.constructor.call(this, this.wrapper, config);
50934     
50935     if(this.toolbar){
50936         this.toolbar.el.insertBefore(this.wrapper.dom.firstChild);
50937     }
50938     // xtype created footer. - not sure if will work as we normally have to render first..
50939     if (this.footer && !this.footer.el && this.footer.xtype) {
50940         
50941         this.footer.container = this.grid.getView().getFooterPanel(true);
50942         this.footer.dataSource = this.grid.dataSource;
50943         this.footer = Roo.factory(this.footer, Roo);
50944         
50945     }
50946     
50947     grid.monitorWindowResize = false; // turn off autosizing
50948     grid.autoHeight = false;
50949     grid.autoWidth = false;
50950     this.grid = grid;
50951     this.grid.getGridEl().replaceClass("x-layout-inactive-content", "x-layout-component-panel");
50952 };
50953
50954 Roo.extend(Roo.GridPanel, Roo.ContentPanel, {
50955     getId : function(){
50956         return this.grid.id;
50957     },
50958     
50959     /**
50960      * Returns the grid for this panel
50961      * @return {Roo.grid.Grid} 
50962      */
50963     getGrid : function(){
50964         return this.grid;    
50965     },
50966     
50967     setSize : function(width, height){
50968         if(!this.ignoreResize(width, height)){
50969             var grid = this.grid;
50970             var size = this.adjustForComponents(width, height);
50971             grid.getGridEl().setSize(size.width, size.height);
50972             grid.autoSize();
50973         }
50974     },
50975     
50976     beforeSlide : function(){
50977         this.grid.getView().scroller.clip();
50978     },
50979     
50980     afterSlide : function(){
50981         this.grid.getView().scroller.unclip();
50982     },
50983     
50984     destroy : function(){
50985         this.grid.destroy();
50986         delete this.grid;
50987         Roo.GridPanel.superclass.destroy.call(this); 
50988     }
50989 });
50990
50991
50992 /**
50993  * @class Roo.NestedLayoutPanel
50994  * @extends Roo.ContentPanel
50995  * @constructor
50996  * Create a new NestedLayoutPanel.
50997  * 
50998  * 
50999  * @param {Roo.BorderLayout} layout The layout for this panel
51000  * @param {String/Object} config A string to set only the title or a config object
51001  */
51002 Roo.NestedLayoutPanel = function(layout, config)
51003 {
51004     // construct with only one argument..
51005     /* FIXME - implement nicer consturctors
51006     if (layout.layout) {
51007         config = layout;
51008         layout = config.layout;
51009         delete config.layout;
51010     }
51011     if (layout.xtype && !layout.getEl) {
51012         // then layout needs constructing..
51013         layout = Roo.factory(layout, Roo);
51014     }
51015     */
51016     
51017     
51018     Roo.NestedLayoutPanel.superclass.constructor.call(this, layout.getEl(), config);
51019     
51020     layout.monitorWindowResize = false; // turn off autosizing
51021     this.layout = layout;
51022     this.layout.getEl().addClass("x-layout-nested-layout");
51023     
51024     
51025     
51026     
51027 };
51028
51029 Roo.extend(Roo.NestedLayoutPanel, Roo.ContentPanel, {
51030
51031     setSize : function(width, height){
51032         if(!this.ignoreResize(width, height)){
51033             var size = this.adjustForComponents(width, height);
51034             var el = this.layout.getEl();
51035             el.setSize(size.width, size.height);
51036             var touch = el.dom.offsetWidth;
51037             this.layout.layout();
51038             // ie requires a double layout on the first pass
51039             if(Roo.isIE && !this.initialized){
51040                 this.initialized = true;
51041                 this.layout.layout();
51042             }
51043         }
51044     },
51045     
51046     // activate all subpanels if not currently active..
51047     
51048     setActiveState : function(active){
51049         this.active = active;
51050         if(!active){
51051             this.fireEvent("deactivate", this);
51052             return;
51053         }
51054         
51055         this.fireEvent("activate", this);
51056         // not sure if this should happen before or after..
51057         if (!this.layout) {
51058             return; // should not happen..
51059         }
51060         var reg = false;
51061         for (var r in this.layout.regions) {
51062             reg = this.layout.getRegion(r);
51063             if (reg.getActivePanel()) {
51064                 //reg.showPanel(reg.getActivePanel()); // force it to activate.. 
51065                 reg.setActivePanel(reg.getActivePanel());
51066                 continue;
51067             }
51068             if (!reg.panels.length) {
51069                 continue;
51070             }
51071             reg.showPanel(reg.getPanel(0));
51072         }
51073         
51074         
51075         
51076         
51077     },
51078     
51079     /**
51080      * Returns the nested BorderLayout for this panel
51081      * @return {Roo.BorderLayout} 
51082      */
51083     getLayout : function(){
51084         return this.layout;
51085     },
51086     
51087      /**
51088      * Adds a xtype elements to the layout of the nested panel
51089      * <pre><code>
51090
51091 panel.addxtype({
51092        xtype : 'ContentPanel',
51093        region: 'west',
51094        items: [ .... ]
51095    }
51096 );
51097
51098 panel.addxtype({
51099         xtype : 'NestedLayoutPanel',
51100         region: 'west',
51101         layout: {
51102            center: { },
51103            west: { }   
51104         },
51105         items : [ ... list of content panels or nested layout panels.. ]
51106    }
51107 );
51108 </code></pre>
51109      * @param {Object} cfg Xtype definition of item to add.
51110      */
51111     addxtype : function(cfg) {
51112         return this.layout.addxtype(cfg);
51113     
51114     }
51115 });
51116
51117 Roo.ScrollPanel = function(el, config, content){
51118     config = config || {};
51119     config.fitToFrame = true;
51120     Roo.ScrollPanel.superclass.constructor.call(this, el, config, content);
51121     
51122     this.el.dom.style.overflow = "hidden";
51123     var wrap = this.el.wrap({cls: "x-scroller x-layout-inactive-content"});
51124     this.el.removeClass("x-layout-inactive-content");
51125     this.el.on("mousewheel", this.onWheel, this);
51126
51127     var up = wrap.createChild({cls: "x-scroller-up", html: "&#160;"}, this.el.dom);
51128     var down = wrap.createChild({cls: "x-scroller-down", html: "&#160;"});
51129     up.unselectable(); down.unselectable();
51130     up.on("click", this.scrollUp, this);
51131     down.on("click", this.scrollDown, this);
51132     up.addClassOnOver("x-scroller-btn-over");
51133     down.addClassOnOver("x-scroller-btn-over");
51134     up.addClassOnClick("x-scroller-btn-click");
51135     down.addClassOnClick("x-scroller-btn-click");
51136     this.adjustments = [0, -(up.getHeight() + down.getHeight())];
51137
51138     this.resizeEl = this.el;
51139     this.el = wrap; this.up = up; this.down = down;
51140 };
51141
51142 Roo.extend(Roo.ScrollPanel, Roo.ContentPanel, {
51143     increment : 100,
51144     wheelIncrement : 5,
51145     scrollUp : function(){
51146         this.resizeEl.scroll("up", this.increment, {callback: this.afterScroll, scope: this});
51147     },
51148
51149     scrollDown : function(){
51150         this.resizeEl.scroll("down", this.increment, {callback: this.afterScroll, scope: this});
51151     },
51152
51153     afterScroll : function(){
51154         var el = this.resizeEl;
51155         var t = el.dom.scrollTop, h = el.dom.scrollHeight, ch = el.dom.clientHeight;
51156         this.up[t == 0 ? "addClass" : "removeClass"]("x-scroller-btn-disabled");
51157         this.down[h - t <= ch ? "addClass" : "removeClass"]("x-scroller-btn-disabled");
51158     },
51159
51160     setSize : function(){
51161         Roo.ScrollPanel.superclass.setSize.apply(this, arguments);
51162         this.afterScroll();
51163     },
51164
51165     onWheel : function(e){
51166         var d = e.getWheelDelta();
51167         this.resizeEl.dom.scrollTop -= (d*this.wheelIncrement);
51168         this.afterScroll();
51169         e.stopEvent();
51170     },
51171
51172     setContent : function(content, loadScripts){
51173         this.resizeEl.update(content, loadScripts);
51174     }
51175
51176 });
51177
51178
51179
51180
51181
51182
51183
51184
51185
51186 /**
51187  * @class Roo.TreePanel
51188  * @extends Roo.ContentPanel
51189  * @constructor
51190  * Create a new TreePanel. - defaults to fit/scoll contents.
51191  * @param {String/Object} config A string to set only the panel's title, or a config object
51192  * @cfg {Roo.tree.TreePanel} tree The tree TreePanel, with config etc.
51193  */
51194 Roo.TreePanel = function(config){
51195     var el = config.el;
51196     var tree = config.tree;
51197     delete config.tree; 
51198     delete config.el; // hopefull!
51199     
51200     // wrapper for IE7 strict & safari scroll issue
51201     
51202     var treeEl = el.createChild();
51203     config.resizeEl = treeEl;
51204     
51205     
51206     
51207     Roo.TreePanel.superclass.constructor.call(this, el, config);
51208  
51209  
51210     this.tree = new Roo.tree.TreePanel(treeEl , tree);
51211     //console.log(tree);
51212     this.on('activate', function()
51213     {
51214         if (this.tree.rendered) {
51215             return;
51216         }
51217         //console.log('render tree');
51218         this.tree.render();
51219     });
51220     // this should not be needed.. - it's actually the 'el' that resizes?
51221     // actuall it breaks the containerScroll - dragging nodes auto scroll at top
51222     
51223     //this.on('resize',  function (cp, w, h) {
51224     //        this.tree.innerCt.setWidth(w);
51225     //        this.tree.innerCt.setHeight(h);
51226     //        //this.tree.innerCt.setStyle('overflow-y', 'auto');
51227     //});
51228
51229         
51230     
51231 };
51232
51233 Roo.extend(Roo.TreePanel, Roo.ContentPanel, {   
51234     fitToFrame : true,
51235     autoScroll : true
51236 });
51237
51238
51239
51240
51241
51242
51243
51244
51245
51246
51247
51248 /*
51249  * Based on:
51250  * Ext JS Library 1.1.1
51251  * Copyright(c) 2006-2007, Ext JS, LLC.
51252  *
51253  * Originally Released Under LGPL - original licence link has changed is not relivant.
51254  *
51255  * Fork - LGPL
51256  * <script type="text/javascript">
51257  */
51258  
51259
51260 /**
51261  * @class Roo.ReaderLayout
51262  * @extends Roo.BorderLayout
51263  * This is a pre-built layout that represents a classic, 5-pane application.  It consists of a header, a primary
51264  * center region containing two nested regions (a top one for a list view and one for item preview below),
51265  * and regions on either side that can be used for navigation, application commands, informational displays, etc.
51266  * The setup and configuration work exactly the same as it does for a {@link Roo.BorderLayout} - this class simply
51267  * expedites the setup of the overall layout and regions for this common application style.
51268  * Example:
51269  <pre><code>
51270 var reader = new Roo.ReaderLayout();
51271 var CP = Roo.ContentPanel;  // shortcut for adding
51272
51273 reader.beginUpdate();
51274 reader.add("north", new CP("north", "North"));
51275 reader.add("west", new CP("west", {title: "West"}));
51276 reader.add("east", new CP("east", {title: "East"}));
51277
51278 reader.regions.listView.add(new CP("listView", "List"));
51279 reader.regions.preview.add(new CP("preview", "Preview"));
51280 reader.endUpdate();
51281 </code></pre>
51282 * @constructor
51283 * Create a new ReaderLayout
51284 * @param {Object} config Configuration options
51285 * @param {String/HTMLElement/Element} container (optional) The container this layout is bound to (defaults to
51286 * document.body if omitted)
51287 */
51288 Roo.ReaderLayout = function(config, renderTo){
51289     var c = config || {size:{}};
51290     Roo.ReaderLayout.superclass.constructor.call(this, renderTo || document.body, {
51291         north: c.north !== false ? Roo.apply({
51292             split:false,
51293             initialSize: 32,
51294             titlebar: false
51295         }, c.north) : false,
51296         west: c.west !== false ? Roo.apply({
51297             split:true,
51298             initialSize: 200,
51299             minSize: 175,
51300             maxSize: 400,
51301             titlebar: true,
51302             collapsible: true,
51303             animate: true,
51304             margins:{left:5,right:0,bottom:5,top:5},
51305             cmargins:{left:5,right:5,bottom:5,top:5}
51306         }, c.west) : false,
51307         east: c.east !== false ? Roo.apply({
51308             split:true,
51309             initialSize: 200,
51310             minSize: 175,
51311             maxSize: 400,
51312             titlebar: true,
51313             collapsible: true,
51314             animate: true,
51315             margins:{left:0,right:5,bottom:5,top:5},
51316             cmargins:{left:5,right:5,bottom:5,top:5}
51317         }, c.east) : false,
51318         center: Roo.apply({
51319             tabPosition: 'top',
51320             autoScroll:false,
51321             closeOnTab: true,
51322             titlebar:false,
51323             margins:{left:c.west!==false ? 0 : 5,right:c.east!==false ? 0 : 5,bottom:5,top:2}
51324         }, c.center)
51325     });
51326
51327     this.el.addClass('x-reader');
51328
51329     this.beginUpdate();
51330
51331     var inner = new Roo.BorderLayout(Roo.get(document.body).createChild(), {
51332         south: c.preview !== false ? Roo.apply({
51333             split:true,
51334             initialSize: 200,
51335             minSize: 100,
51336             autoScroll:true,
51337             collapsible:true,
51338             titlebar: true,
51339             cmargins:{top:5,left:0, right:0, bottom:0}
51340         }, c.preview) : false,
51341         center: Roo.apply({
51342             autoScroll:false,
51343             titlebar:false,
51344             minHeight:200
51345         }, c.listView)
51346     });
51347     this.add('center', new Roo.NestedLayoutPanel(inner,
51348             Roo.apply({title: c.mainTitle || '',tabTip:''},c.innerPanelCfg)));
51349
51350     this.endUpdate();
51351
51352     this.regions.preview = inner.getRegion('south');
51353     this.regions.listView = inner.getRegion('center');
51354 };
51355
51356 Roo.extend(Roo.ReaderLayout, Roo.BorderLayout);/*
51357  * Based on:
51358  * Ext JS Library 1.1.1
51359  * Copyright(c) 2006-2007, Ext JS, LLC.
51360  *
51361  * Originally Released Under LGPL - original licence link has changed is not relivant.
51362  *
51363  * Fork - LGPL
51364  * <script type="text/javascript">
51365  */
51366  
51367 /**
51368  * @class Roo.grid.Grid
51369  * @extends Roo.util.Observable
51370  * This class represents the primary interface of a component based grid control.
51371  * <br><br>Usage:<pre><code>
51372  var grid = new Roo.grid.Grid("my-container-id", {
51373      ds: myDataStore,
51374      cm: myColModel,
51375      selModel: mySelectionModel,
51376      autoSizeColumns: true,
51377      monitorWindowResize: false,
51378      trackMouseOver: true
51379  });
51380  // set any options
51381  grid.render();
51382  * </code></pre>
51383  * <b>Common Problems:</b><br/>
51384  * - Grid does not resize properly when going smaller: Setting overflow hidden on the container
51385  * element will correct this<br/>
51386  * - If you get el.style[camel]= NaNpx or -2px or something related, be certain you have given your container element
51387  * dimensions. The grid adapts to your container's size, if your container has no size defined then the results
51388  * are unpredictable.<br/>
51389  * - Do not render the grid into an element with display:none. Try using visibility:hidden. Otherwise there is no way for the
51390  * grid to calculate dimensions/offsets.<br/>
51391   * @constructor
51392  * @param {String/HTMLElement/Roo.Element} container The element into which this grid will be rendered -
51393  * The container MUST have some type of size defined for the grid to fill. The container will be
51394  * automatically set to position relative if it isn't already.
51395  * @param {Object} config A config object that sets properties on this grid.
51396  */
51397 Roo.grid.Grid = function(container, config){
51398         // initialize the container
51399         this.container = Roo.get(container);
51400         this.container.update("");
51401         this.container.setStyle("overflow", "hidden");
51402     this.container.addClass('x-grid-container');
51403
51404     this.id = this.container.id;
51405
51406     Roo.apply(this, config);
51407     // check and correct shorthanded configs
51408     if(this.ds){
51409         this.dataSource = this.ds;
51410         delete this.ds;
51411     }
51412     if(this.cm){
51413         this.colModel = this.cm;
51414         delete this.cm;
51415     }
51416     if(this.sm){
51417         this.selModel = this.sm;
51418         delete this.sm;
51419     }
51420
51421     if (this.selModel) {
51422         this.selModel = Roo.factory(this.selModel, Roo.grid);
51423         this.sm = this.selModel;
51424         this.sm.xmodule = this.xmodule || false;
51425     }
51426     if (typeof(this.colModel.config) == 'undefined') {
51427         this.colModel = new Roo.grid.ColumnModel(this.colModel);
51428         this.cm = this.colModel;
51429         this.cm.xmodule = this.xmodule || false;
51430     }
51431     if (this.dataSource) {
51432         this.dataSource= Roo.factory(this.dataSource, Roo.data);
51433         this.ds = this.dataSource;
51434         this.ds.xmodule = this.xmodule || false;
51435          
51436     }
51437     
51438     
51439     
51440     if(this.width){
51441         this.container.setWidth(this.width);
51442     }
51443
51444     if(this.height){
51445         this.container.setHeight(this.height);
51446     }
51447     /** @private */
51448         this.addEvents({
51449         // raw events
51450         /**
51451          * @event click
51452          * The raw click event for the entire grid.
51453          * @param {Roo.EventObject} e
51454          */
51455         "click" : true,
51456         /**
51457          * @event dblclick
51458          * The raw dblclick event for the entire grid.
51459          * @param {Roo.EventObject} e
51460          */
51461         "dblclick" : true,
51462         /**
51463          * @event contextmenu
51464          * The raw contextmenu event for the entire grid.
51465          * @param {Roo.EventObject} e
51466          */
51467         "contextmenu" : true,
51468         /**
51469          * @event mousedown
51470          * The raw mousedown event for the entire grid.
51471          * @param {Roo.EventObject} e
51472          */
51473         "mousedown" : true,
51474         /**
51475          * @event mouseup
51476          * The raw mouseup event for the entire grid.
51477          * @param {Roo.EventObject} e
51478          */
51479         "mouseup" : true,
51480         /**
51481          * @event mouseover
51482          * The raw mouseover event for the entire grid.
51483          * @param {Roo.EventObject} e
51484          */
51485         "mouseover" : true,
51486         /**
51487          * @event mouseout
51488          * The raw mouseout event for the entire grid.
51489          * @param {Roo.EventObject} e
51490          */
51491         "mouseout" : true,
51492         /**
51493          * @event keypress
51494          * The raw keypress event for the entire grid.
51495          * @param {Roo.EventObject} e
51496          */
51497         "keypress" : true,
51498         /**
51499          * @event keydown
51500          * The raw keydown event for the entire grid.
51501          * @param {Roo.EventObject} e
51502          */
51503         "keydown" : true,
51504
51505         // custom events
51506
51507         /**
51508          * @event cellclick
51509          * Fires when a cell is clicked
51510          * @param {Grid} this
51511          * @param {Number} rowIndex
51512          * @param {Number} columnIndex
51513          * @param {Roo.EventObject} e
51514          */
51515         "cellclick" : true,
51516         /**
51517          * @event celldblclick
51518          * Fires when a cell is double clicked
51519          * @param {Grid} this
51520          * @param {Number} rowIndex
51521          * @param {Number} columnIndex
51522          * @param {Roo.EventObject} e
51523          */
51524         "celldblclick" : true,
51525         /**
51526          * @event rowclick
51527          * Fires when a row is clicked
51528          * @param {Grid} this
51529          * @param {Number} rowIndex
51530          * @param {Roo.EventObject} e
51531          */
51532         "rowclick" : true,
51533         /**
51534          * @event rowdblclick
51535          * Fires when a row is double clicked
51536          * @param {Grid} this
51537          * @param {Number} rowIndex
51538          * @param {Roo.EventObject} e
51539          */
51540         "rowdblclick" : true,
51541         /**
51542          * @event headerclick
51543          * Fires when a header is clicked
51544          * @param {Grid} this
51545          * @param {Number} columnIndex
51546          * @param {Roo.EventObject} e
51547          */
51548         "headerclick" : true,
51549         /**
51550          * @event headerdblclick
51551          * Fires when a header cell is double clicked
51552          * @param {Grid} this
51553          * @param {Number} columnIndex
51554          * @param {Roo.EventObject} e
51555          */
51556         "headerdblclick" : true,
51557         /**
51558          * @event rowcontextmenu
51559          * Fires when a row is right clicked
51560          * @param {Grid} this
51561          * @param {Number} rowIndex
51562          * @param {Roo.EventObject} e
51563          */
51564         "rowcontextmenu" : true,
51565         /**
51566          * @event cellcontextmenu
51567          * Fires when a cell is right clicked
51568          * @param {Grid} this
51569          * @param {Number} rowIndex
51570          * @param {Number} cellIndex
51571          * @param {Roo.EventObject} e
51572          */
51573          "cellcontextmenu" : true,
51574         /**
51575          * @event headercontextmenu
51576          * Fires when a header is right clicked
51577          * @param {Grid} this
51578          * @param {Number} columnIndex
51579          * @param {Roo.EventObject} e
51580          */
51581         "headercontextmenu" : true,
51582         /**
51583          * @event bodyscroll
51584          * Fires when the body element is scrolled
51585          * @param {Number} scrollLeft
51586          * @param {Number} scrollTop
51587          */
51588         "bodyscroll" : true,
51589         /**
51590          * @event columnresize
51591          * Fires when the user resizes a column
51592          * @param {Number} columnIndex
51593          * @param {Number} newSize
51594          */
51595         "columnresize" : true,
51596         /**
51597          * @event columnmove
51598          * Fires when the user moves a column
51599          * @param {Number} oldIndex
51600          * @param {Number} newIndex
51601          */
51602         "columnmove" : true,
51603         /**
51604          * @event startdrag
51605          * Fires when row(s) start being dragged
51606          * @param {Grid} this
51607          * @param {Roo.GridDD} dd The drag drop object
51608          * @param {event} e The raw browser event
51609          */
51610         "startdrag" : true,
51611         /**
51612          * @event enddrag
51613          * Fires when a drag operation is complete
51614          * @param {Grid} this
51615          * @param {Roo.GridDD} dd The drag drop object
51616          * @param {event} e The raw browser event
51617          */
51618         "enddrag" : true,
51619         /**
51620          * @event dragdrop
51621          * Fires when dragged row(s) are dropped on a valid DD target
51622          * @param {Grid} this
51623          * @param {Roo.GridDD} dd The drag drop object
51624          * @param {String} targetId The target drag drop object
51625          * @param {event} e The raw browser event
51626          */
51627         "dragdrop" : true,
51628         /**
51629          * @event dragover
51630          * Fires while row(s) are being dragged. "targetId" is the id of the Yahoo.util.DD object the selected rows are being dragged over.
51631          * @param {Grid} this
51632          * @param {Roo.GridDD} dd The drag drop object
51633          * @param {String} targetId The target drag drop object
51634          * @param {event} e The raw browser event
51635          */
51636         "dragover" : true,
51637         /**
51638          * @event dragenter
51639          *  Fires when the dragged row(s) first cross another DD target while being dragged
51640          * @param {Grid} this
51641          * @param {Roo.GridDD} dd The drag drop object
51642          * @param {String} targetId The target drag drop object
51643          * @param {event} e The raw browser event
51644          */
51645         "dragenter" : true,
51646         /**
51647          * @event dragout
51648          * Fires when the dragged row(s) leave another DD target while being dragged
51649          * @param {Grid} this
51650          * @param {Roo.GridDD} dd The drag drop object
51651          * @param {String} targetId The target drag drop object
51652          * @param {event} e The raw browser event
51653          */
51654         "dragout" : true,
51655         /**
51656          * @event rowclass
51657          * Fires when a row is rendered, so you can change add a style to it.
51658          * @param {GridView} gridview   The grid view
51659          * @param {Object} rowcfg   contains record  rowIndex and rowClass - set rowClass to add a style.
51660          */
51661         'rowclass' : true,
51662
51663         /**
51664          * @event render
51665          * Fires when the grid is rendered
51666          * @param {Grid} grid
51667          */
51668         'render' : true
51669     });
51670
51671     Roo.grid.Grid.superclass.constructor.call(this);
51672 };
51673 Roo.extend(Roo.grid.Grid, Roo.util.Observable, {
51674     
51675     /**
51676      * @cfg {String} ddGroup - drag drop group.
51677      */
51678
51679     /**
51680      * @cfg {Number} minColumnWidth The minimum width a column can be resized to. Default is 25.
51681      */
51682     minColumnWidth : 25,
51683
51684     /**
51685      * @cfg {Boolean} autoSizeColumns True to automatically resize the columns to fit their content
51686      * <b>on initial render.</b> It is more efficient to explicitly size the columns
51687      * through the ColumnModel's {@link Roo.grid.ColumnModel#width} config option.  Default is false.
51688      */
51689     autoSizeColumns : false,
51690
51691     /**
51692      * @cfg {Boolean} autoSizeHeaders True to measure headers with column data when auto sizing columns. Default is true.
51693      */
51694     autoSizeHeaders : true,
51695
51696     /**
51697      * @cfg {Boolean} monitorWindowResize True to autoSize the grid when the window resizes. Default is true.
51698      */
51699     monitorWindowResize : true,
51700
51701     /**
51702      * @cfg {Boolean} maxRowsToMeasure If autoSizeColumns is on, maxRowsToMeasure can be used to limit the number of
51703      * rows measured to get a columns size. Default is 0 (all rows).
51704      */
51705     maxRowsToMeasure : 0,
51706
51707     /**
51708      * @cfg {Boolean} trackMouseOver True to highlight rows when the mouse is over. Default is true.
51709      */
51710     trackMouseOver : true,
51711
51712     /**
51713     * @cfg {Boolean} enableDrag  True to enable drag of rows. Default is false. (double check if this is needed?)
51714     */
51715     
51716     /**
51717     * @cfg {Boolean} enableDragDrop True to enable drag and drop of rows. Default is false.
51718     */
51719     enableDragDrop : false,
51720     
51721     /**
51722     * @cfg {Boolean} enableColumnMove True to enable drag and drop reorder of columns. Default is true.
51723     */
51724     enableColumnMove : true,
51725     
51726     /**
51727     * @cfg {Boolean} enableColumnHide True to enable hiding of columns with the header context menu. Default is true.
51728     */
51729     enableColumnHide : true,
51730     
51731     /**
51732     * @cfg {Boolean} enableRowHeightSync True to manually sync row heights across locked and not locked rows. Default is false.
51733     */
51734     enableRowHeightSync : false,
51735     
51736     /**
51737     * @cfg {Boolean} stripeRows True to stripe the rows.  Default is true.
51738     */
51739     stripeRows : true,
51740     
51741     /**
51742     * @cfg {Boolean} autoHeight True to fit the height of the grid container to the height of the data. Default is false.
51743     */
51744     autoHeight : false,
51745
51746     /**
51747      * @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.
51748      */
51749     autoExpandColumn : false,
51750
51751     /**
51752     * @cfg {Number} autoExpandMin The minimum width the autoExpandColumn can have (if enabled).
51753     * Default is 50.
51754     */
51755     autoExpandMin : 50,
51756
51757     /**
51758     * @cfg {Number} autoExpandMax The maximum width the autoExpandColumn can have (if enabled). Default is 1000.
51759     */
51760     autoExpandMax : 1000,
51761
51762     /**
51763     * @cfg {Object} view The {@link Roo.grid.GridView} used by the grid. This can be set before a call to render().
51764     */
51765     view : null,
51766
51767     /**
51768     * @cfg {Object} loadMask An {@link Roo.LoadMask} config or true to mask the grid while loading. Default is false.
51769     */
51770     loadMask : false,
51771     /**
51772     * @cfg {Roo.dd.DropTarget} dropTarget An {@link Roo.dd.DropTarget} config
51773     */
51774     dropTarget: false,
51775     
51776    
51777     
51778     // private
51779     rendered : false,
51780
51781     /**
51782     * @cfg {Boolean} autoWidth True to set the grid's width to the default total width of the grid's columns instead
51783     * of a fixed width. Default is false.
51784     */
51785     /**
51786     * @cfg {Number} maxHeight Sets the maximum height of the grid - ignored if autoHeight is not on.
51787     */
51788     /**
51789      * Called once after all setup has been completed and the grid is ready to be rendered.
51790      * @return {Roo.grid.Grid} this
51791      */
51792     render : function()
51793     {
51794         var c = this.container;
51795         // try to detect autoHeight/width mode
51796         if((!c.dom.offsetHeight || c.dom.offsetHeight < 20) || c.getStyle("height") == "auto"){
51797             this.autoHeight = true;
51798         }
51799         var view = this.getView();
51800         view.init(this);
51801
51802         c.on("click", this.onClick, this);
51803         c.on("dblclick", this.onDblClick, this);
51804         c.on("contextmenu", this.onContextMenu, this);
51805         c.on("keydown", this.onKeyDown, this);
51806         if (Roo.isTouch) {
51807             c.on("touchstart", this.onTouchStart, this);
51808         }
51809
51810         this.relayEvents(c, ["mousedown","mouseup","mouseover","mouseout","keypress"]);
51811
51812         this.getSelectionModel().init(this);
51813
51814         view.render();
51815
51816         if(this.loadMask){
51817             this.loadMask = new Roo.LoadMask(this.container,
51818                     Roo.apply({store:this.dataSource}, this.loadMask));
51819         }
51820         
51821         
51822         if (this.toolbar && this.toolbar.xtype) {
51823             this.toolbar.container = this.getView().getHeaderPanel(true);
51824             this.toolbar = new Roo.Toolbar(this.toolbar);
51825         }
51826         if (this.footer && this.footer.xtype) {
51827             this.footer.dataSource = this.getDataSource();
51828             this.footer.container = this.getView().getFooterPanel(true);
51829             this.footer = Roo.factory(this.footer, Roo);
51830         }
51831         if (this.dropTarget && this.dropTarget.xtype) {
51832             delete this.dropTarget.xtype;
51833             this.dropTarget =  new Roo.dd.DropTarget(this.getView().mainBody, this.dropTarget);
51834         }
51835         
51836         
51837         this.rendered = true;
51838         this.fireEvent('render', this);
51839         return this;
51840     },
51841
51842         /**
51843          * Reconfigures the grid to use a different Store and Column Model.
51844          * The View will be bound to the new objects and refreshed.
51845          * @param {Roo.data.Store} dataSource The new {@link Roo.data.Store} object
51846          * @param {Roo.grid.ColumnModel} The new {@link Roo.grid.ColumnModel} object
51847          */
51848     reconfigure : function(dataSource, colModel){
51849         if(this.loadMask){
51850             this.loadMask.destroy();
51851             this.loadMask = new Roo.LoadMask(this.container,
51852                     Roo.apply({store:dataSource}, this.loadMask));
51853         }
51854         this.view.bind(dataSource, colModel);
51855         this.dataSource = dataSource;
51856         this.colModel = colModel;
51857         this.view.refresh(true);
51858     },
51859
51860     // private
51861     onKeyDown : function(e){
51862         this.fireEvent("keydown", e);
51863     },
51864
51865     /**
51866      * Destroy this grid.
51867      * @param {Boolean} removeEl True to remove the element
51868      */
51869     destroy : function(removeEl, keepListeners){
51870         if(this.loadMask){
51871             this.loadMask.destroy();
51872         }
51873         var c = this.container;
51874         c.removeAllListeners();
51875         this.view.destroy();
51876         this.colModel.purgeListeners();
51877         if(!keepListeners){
51878             this.purgeListeners();
51879         }
51880         c.update("");
51881         if(removeEl === true){
51882             c.remove();
51883         }
51884     },
51885
51886     // private
51887     processEvent : function(name, e){
51888         // does this fire select???
51889         Roo.log('grid:processEvent '  + name);
51890         
51891         if (name != 'touchstart' ) {
51892             this.fireEvent(name, e);    
51893         }
51894         
51895         var t = e.getTarget();
51896         var v = this.view;
51897         var header = v.findHeaderIndex(t);
51898         if(header !== false){
51899             var ename = name == 'touchstart' ? 'click' : name;
51900              
51901             this.fireEvent("header" + ename, this, header, e);
51902         }else{
51903             var row = v.findRowIndex(t);
51904             var cell = v.findCellIndex(t);
51905             if (name == 'touchstart') {
51906                 // first touch is always a click.
51907                 // hopefull this happens after selection is updated.?
51908                 name = false;
51909                 
51910                 if (typeof(this.selModel.getSelectedCell) != 'undefined') {
51911                     var cs = this.selModel.getSelectedCell();
51912                     if (row == cs[0] && cell == cs[1]){
51913                         name = 'dblclick';
51914                     }
51915                 }
51916                 if (typeof(this.selModel.getSelections) != 'undefined') {
51917                     var cs = this.selModel.getSelections();
51918                     var ds = this.dataSource;
51919                     if (cs.length == 1 && ds.getAt(row) == cs[0]){
51920                         name = 'dblclick';
51921                     }
51922                 }
51923                 if (!name) {
51924                     return;
51925                 }
51926             }
51927             
51928             
51929             if(row !== false){
51930                 this.fireEvent("row" + name, this, row, e);
51931                 if(cell !== false){
51932                     this.fireEvent("cell" + name, this, row, cell, e);
51933                 }
51934             }
51935         }
51936     },
51937
51938     // private
51939     onClick : function(e){
51940         this.processEvent("click", e);
51941     },
51942    // private
51943     onTouchStart : function(e){
51944         this.processEvent("touchstart", e);
51945     },
51946
51947     // private
51948     onContextMenu : function(e, t){
51949         this.processEvent("contextmenu", e);
51950     },
51951
51952     // private
51953     onDblClick : function(e){
51954         this.processEvent("dblclick", e);
51955     },
51956
51957     // private
51958     walkCells : function(row, col, step, fn, scope){
51959         var cm = this.colModel, clen = cm.getColumnCount();
51960         var ds = this.dataSource, rlen = ds.getCount(), first = true;
51961         if(step < 0){
51962             if(col < 0){
51963                 row--;
51964                 first = false;
51965             }
51966             while(row >= 0){
51967                 if(!first){
51968                     col = clen-1;
51969                 }
51970                 first = false;
51971                 while(col >= 0){
51972                     if(fn.call(scope || this, row, col, cm) === true){
51973                         return [row, col];
51974                     }
51975                     col--;
51976                 }
51977                 row--;
51978             }
51979         } else {
51980             if(col >= clen){
51981                 row++;
51982                 first = false;
51983             }
51984             while(row < rlen){
51985                 if(!first){
51986                     col = 0;
51987                 }
51988                 first = false;
51989                 while(col < clen){
51990                     if(fn.call(scope || this, row, col, cm) === true){
51991                         return [row, col];
51992                     }
51993                     col++;
51994                 }
51995                 row++;
51996             }
51997         }
51998         return null;
51999     },
52000
52001     // private
52002     getSelections : function(){
52003         return this.selModel.getSelections();
52004     },
52005
52006     /**
52007      * Causes the grid to manually recalculate its dimensions. Generally this is done automatically,
52008      * but if manual update is required this method will initiate it.
52009      */
52010     autoSize : function(){
52011         if(this.rendered){
52012             this.view.layout();
52013             if(this.view.adjustForScroll){
52014                 this.view.adjustForScroll();
52015             }
52016         }
52017     },
52018
52019     /**
52020      * Returns the grid's underlying element.
52021      * @return {Element} The element
52022      */
52023     getGridEl : function(){
52024         return this.container;
52025     },
52026
52027     // private for compatibility, overridden by editor grid
52028     stopEditing : function(){},
52029
52030     /**
52031      * Returns the grid's SelectionModel.
52032      * @return {SelectionModel}
52033      */
52034     getSelectionModel : function(){
52035         if(!this.selModel){
52036             this.selModel = new Roo.grid.RowSelectionModel();
52037         }
52038         return this.selModel;
52039     },
52040
52041     /**
52042      * Returns the grid's DataSource.
52043      * @return {DataSource}
52044      */
52045     getDataSource : function(){
52046         return this.dataSource;
52047     },
52048
52049     /**
52050      * Returns the grid's ColumnModel.
52051      * @return {ColumnModel}
52052      */
52053     getColumnModel : function(){
52054         return this.colModel;
52055     },
52056
52057     /**
52058      * Returns the grid's GridView object.
52059      * @return {GridView}
52060      */
52061     getView : function(){
52062         if(!this.view){
52063             this.view = new Roo.grid.GridView(this.viewConfig);
52064         }
52065         return this.view;
52066     },
52067     /**
52068      * Called to get grid's drag proxy text, by default returns this.ddText.
52069      * @return {String}
52070      */
52071     getDragDropText : function(){
52072         var count = this.selModel.getCount();
52073         return String.format(this.ddText, count, count == 1 ? '' : 's');
52074     }
52075 });
52076 /**
52077  * Configures the text is the drag proxy (defaults to "%0 selected row(s)").
52078  * %0 is replaced with the number of selected rows.
52079  * @type String
52080  */
52081 Roo.grid.Grid.prototype.ddText = "{0} selected row{1}";/*
52082  * Based on:
52083  * Ext JS Library 1.1.1
52084  * Copyright(c) 2006-2007, Ext JS, LLC.
52085  *
52086  * Originally Released Under LGPL - original licence link has changed is not relivant.
52087  *
52088  * Fork - LGPL
52089  * <script type="text/javascript">
52090  */
52091  
52092 Roo.grid.AbstractGridView = function(){
52093         this.grid = null;
52094         
52095         this.events = {
52096             "beforerowremoved" : true,
52097             "beforerowsinserted" : true,
52098             "beforerefresh" : true,
52099             "rowremoved" : true,
52100             "rowsinserted" : true,
52101             "rowupdated" : true,
52102             "refresh" : true
52103         };
52104     Roo.grid.AbstractGridView.superclass.constructor.call(this);
52105 };
52106
52107 Roo.extend(Roo.grid.AbstractGridView, Roo.util.Observable, {
52108     rowClass : "x-grid-row",
52109     cellClass : "x-grid-cell",
52110     tdClass : "x-grid-td",
52111     hdClass : "x-grid-hd",
52112     splitClass : "x-grid-hd-split",
52113     
52114     init: function(grid){
52115         this.grid = grid;
52116                 var cid = this.grid.getGridEl().id;
52117         this.colSelector = "#" + cid + " ." + this.cellClass + "-";
52118         this.tdSelector = "#" + cid + " ." + this.tdClass + "-";
52119         this.hdSelector = "#" + cid + " ." + this.hdClass + "-";
52120         this.splitSelector = "#" + cid + " ." + this.splitClass + "-";
52121         },
52122         
52123     getColumnRenderers : function(){
52124         var renderers = [];
52125         var cm = this.grid.colModel;
52126         var colCount = cm.getColumnCount();
52127         for(var i = 0; i < colCount; i++){
52128             renderers[i] = cm.getRenderer(i);
52129         }
52130         return renderers;
52131     },
52132     
52133     getColumnIds : function(){
52134         var ids = [];
52135         var cm = this.grid.colModel;
52136         var colCount = cm.getColumnCount();
52137         for(var i = 0; i < colCount; i++){
52138             ids[i] = cm.getColumnId(i);
52139         }
52140         return ids;
52141     },
52142     
52143     getDataIndexes : function(){
52144         if(!this.indexMap){
52145             this.indexMap = this.buildIndexMap();
52146         }
52147         return this.indexMap.colToData;
52148     },
52149     
52150     getColumnIndexByDataIndex : function(dataIndex){
52151         if(!this.indexMap){
52152             this.indexMap = this.buildIndexMap();
52153         }
52154         return this.indexMap.dataToCol[dataIndex];
52155     },
52156     
52157     /**
52158      * Set a css style for a column dynamically. 
52159      * @param {Number} colIndex The index of the column
52160      * @param {String} name The css property name
52161      * @param {String} value The css value
52162      */
52163     setCSSStyle : function(colIndex, name, value){
52164         var selector = "#" + this.grid.id + " .x-grid-col-" + colIndex;
52165         Roo.util.CSS.updateRule(selector, name, value);
52166     },
52167     
52168     generateRules : function(cm){
52169         var ruleBuf = [], rulesId = this.grid.id + '-cssrules';
52170         Roo.util.CSS.removeStyleSheet(rulesId);
52171         for(var i = 0, len = cm.getColumnCount(); i < len; i++){
52172             var cid = cm.getColumnId(i);
52173             ruleBuf.push(this.colSelector, cid, " {\n", cm.config[i].css, "}\n",
52174                          this.tdSelector, cid, " {\n}\n",
52175                          this.hdSelector, cid, " {\n}\n",
52176                          this.splitSelector, cid, " {\n}\n");
52177         }
52178         return Roo.util.CSS.createStyleSheet(ruleBuf.join(""), rulesId);
52179     }
52180 });/*
52181  * Based on:
52182  * Ext JS Library 1.1.1
52183  * Copyright(c) 2006-2007, Ext JS, LLC.
52184  *
52185  * Originally Released Under LGPL - original licence link has changed is not relivant.
52186  *
52187  * Fork - LGPL
52188  * <script type="text/javascript">
52189  */
52190
52191 // private
52192 // This is a support class used internally by the Grid components
52193 Roo.grid.HeaderDragZone = function(grid, hd, hd2){
52194     this.grid = grid;
52195     this.view = grid.getView();
52196     this.ddGroup = "gridHeader" + this.grid.getGridEl().id;
52197     Roo.grid.HeaderDragZone.superclass.constructor.call(this, hd);
52198     if(hd2){
52199         this.setHandleElId(Roo.id(hd));
52200         this.setOuterHandleElId(Roo.id(hd2));
52201     }
52202     this.scroll = false;
52203 };
52204 Roo.extend(Roo.grid.HeaderDragZone, Roo.dd.DragZone, {
52205     maxDragWidth: 120,
52206     getDragData : function(e){
52207         var t = Roo.lib.Event.getTarget(e);
52208         var h = this.view.findHeaderCell(t);
52209         if(h){
52210             return {ddel: h.firstChild, header:h};
52211         }
52212         return false;
52213     },
52214
52215     onInitDrag : function(e){
52216         this.view.headersDisabled = true;
52217         var clone = this.dragData.ddel.cloneNode(true);
52218         clone.id = Roo.id();
52219         clone.style.width = Math.min(this.dragData.header.offsetWidth,this.maxDragWidth) + "px";
52220         this.proxy.update(clone);
52221         return true;
52222     },
52223
52224     afterValidDrop : function(){
52225         var v = this.view;
52226         setTimeout(function(){
52227             v.headersDisabled = false;
52228         }, 50);
52229     },
52230
52231     afterInvalidDrop : function(){
52232         var v = this.view;
52233         setTimeout(function(){
52234             v.headersDisabled = false;
52235         }, 50);
52236     }
52237 });
52238 /*
52239  * Based on:
52240  * Ext JS Library 1.1.1
52241  * Copyright(c) 2006-2007, Ext JS, LLC.
52242  *
52243  * Originally Released Under LGPL - original licence link has changed is not relivant.
52244  *
52245  * Fork - LGPL
52246  * <script type="text/javascript">
52247  */
52248 // private
52249 // This is a support class used internally by the Grid components
52250 Roo.grid.HeaderDropZone = function(grid, hd, hd2){
52251     this.grid = grid;
52252     this.view = grid.getView();
52253     // split the proxies so they don't interfere with mouse events
52254     this.proxyTop = Roo.DomHelper.append(document.body, {
52255         cls:"col-move-top", html:"&#160;"
52256     }, true);
52257     this.proxyBottom = Roo.DomHelper.append(document.body, {
52258         cls:"col-move-bottom", html:"&#160;"
52259     }, true);
52260     this.proxyTop.hide = this.proxyBottom.hide = function(){
52261         this.setLeftTop(-100,-100);
52262         this.setStyle("visibility", "hidden");
52263     };
52264     this.ddGroup = "gridHeader" + this.grid.getGridEl().id;
52265     // temporarily disabled
52266     //Roo.dd.ScrollManager.register(this.view.scroller.dom);
52267     Roo.grid.HeaderDropZone.superclass.constructor.call(this, grid.getGridEl().dom);
52268 };
52269 Roo.extend(Roo.grid.HeaderDropZone, Roo.dd.DropZone, {
52270     proxyOffsets : [-4, -9],
52271     fly: Roo.Element.fly,
52272
52273     getTargetFromEvent : function(e){
52274         var t = Roo.lib.Event.getTarget(e);
52275         var cindex = this.view.findCellIndex(t);
52276         if(cindex !== false){
52277             return this.view.getHeaderCell(cindex);
52278         }
52279         return null;
52280     },
52281
52282     nextVisible : function(h){
52283         var v = this.view, cm = this.grid.colModel;
52284         h = h.nextSibling;
52285         while(h){
52286             if(!cm.isHidden(v.getCellIndex(h))){
52287                 return h;
52288             }
52289             h = h.nextSibling;
52290         }
52291         return null;
52292     },
52293
52294     prevVisible : function(h){
52295         var v = this.view, cm = this.grid.colModel;
52296         h = h.prevSibling;
52297         while(h){
52298             if(!cm.isHidden(v.getCellIndex(h))){
52299                 return h;
52300             }
52301             h = h.prevSibling;
52302         }
52303         return null;
52304     },
52305
52306     positionIndicator : function(h, n, e){
52307         var x = Roo.lib.Event.getPageX(e);
52308         var r = Roo.lib.Dom.getRegion(n.firstChild);
52309         var px, pt, py = r.top + this.proxyOffsets[1];
52310         if((r.right - x) <= (r.right-r.left)/2){
52311             px = r.right+this.view.borderWidth;
52312             pt = "after";
52313         }else{
52314             px = r.left;
52315             pt = "before";
52316         }
52317         var oldIndex = this.view.getCellIndex(h);
52318         var newIndex = this.view.getCellIndex(n);
52319
52320         if(this.grid.colModel.isFixed(newIndex)){
52321             return false;
52322         }
52323
52324         var locked = this.grid.colModel.isLocked(newIndex);
52325
52326         if(pt == "after"){
52327             newIndex++;
52328         }
52329         if(oldIndex < newIndex){
52330             newIndex--;
52331         }
52332         if(oldIndex == newIndex && (locked == this.grid.colModel.isLocked(oldIndex))){
52333             return false;
52334         }
52335         px +=  this.proxyOffsets[0];
52336         this.proxyTop.setLeftTop(px, py);
52337         this.proxyTop.show();
52338         if(!this.bottomOffset){
52339             this.bottomOffset = this.view.mainHd.getHeight();
52340         }
52341         this.proxyBottom.setLeftTop(px, py+this.proxyTop.dom.offsetHeight+this.bottomOffset);
52342         this.proxyBottom.show();
52343         return pt;
52344     },
52345
52346     onNodeEnter : function(n, dd, e, data){
52347         if(data.header != n){
52348             this.positionIndicator(data.header, n, e);
52349         }
52350     },
52351
52352     onNodeOver : function(n, dd, e, data){
52353         var result = false;
52354         if(data.header != n){
52355             result = this.positionIndicator(data.header, n, e);
52356         }
52357         if(!result){
52358             this.proxyTop.hide();
52359             this.proxyBottom.hide();
52360         }
52361         return result ? this.dropAllowed : this.dropNotAllowed;
52362     },
52363
52364     onNodeOut : function(n, dd, e, data){
52365         this.proxyTop.hide();
52366         this.proxyBottom.hide();
52367     },
52368
52369     onNodeDrop : function(n, dd, e, data){
52370         var h = data.header;
52371         if(h != n){
52372             var cm = this.grid.colModel;
52373             var x = Roo.lib.Event.getPageX(e);
52374             var r = Roo.lib.Dom.getRegion(n.firstChild);
52375             var pt = (r.right - x) <= ((r.right-r.left)/2) ? "after" : "before";
52376             var oldIndex = this.view.getCellIndex(h);
52377             var newIndex = this.view.getCellIndex(n);
52378             var locked = cm.isLocked(newIndex);
52379             if(pt == "after"){
52380                 newIndex++;
52381             }
52382             if(oldIndex < newIndex){
52383                 newIndex--;
52384             }
52385             if(oldIndex == newIndex && (locked == cm.isLocked(oldIndex))){
52386                 return false;
52387             }
52388             cm.setLocked(oldIndex, locked, true);
52389             cm.moveColumn(oldIndex, newIndex);
52390             this.grid.fireEvent("columnmove", oldIndex, newIndex);
52391             return true;
52392         }
52393         return false;
52394     }
52395 });
52396 /*
52397  * Based on:
52398  * Ext JS Library 1.1.1
52399  * Copyright(c) 2006-2007, Ext JS, LLC.
52400  *
52401  * Originally Released Under LGPL - original licence link has changed is not relivant.
52402  *
52403  * Fork - LGPL
52404  * <script type="text/javascript">
52405  */
52406   
52407 /**
52408  * @class Roo.grid.GridView
52409  * @extends Roo.util.Observable
52410  *
52411  * @constructor
52412  * @param {Object} config
52413  */
52414 Roo.grid.GridView = function(config){
52415     Roo.grid.GridView.superclass.constructor.call(this);
52416     this.el = null;
52417
52418     Roo.apply(this, config);
52419 };
52420
52421 Roo.extend(Roo.grid.GridView, Roo.grid.AbstractGridView, {
52422
52423     unselectable :  'unselectable="on"',
52424     unselectableCls :  'x-unselectable',
52425     
52426     
52427     rowClass : "x-grid-row",
52428
52429     cellClass : "x-grid-col",
52430
52431     tdClass : "x-grid-td",
52432
52433     hdClass : "x-grid-hd",
52434
52435     splitClass : "x-grid-split",
52436
52437     sortClasses : ["sort-asc", "sort-desc"],
52438
52439     enableMoveAnim : false,
52440
52441     hlColor: "C3DAF9",
52442
52443     dh : Roo.DomHelper,
52444
52445     fly : Roo.Element.fly,
52446
52447     css : Roo.util.CSS,
52448
52449     borderWidth: 1,
52450
52451     splitOffset: 3,
52452
52453     scrollIncrement : 22,
52454
52455     cellRE: /(?:.*?)x-grid-(?:hd|cell|csplit)-(?:[\d]+)-([\d]+)(?:.*?)/,
52456
52457     findRE: /\s?(?:x-grid-hd|x-grid-col|x-grid-csplit)\s/,
52458
52459     bind : function(ds, cm){
52460         if(this.ds){
52461             this.ds.un("load", this.onLoad, this);
52462             this.ds.un("datachanged", this.onDataChange, this);
52463             this.ds.un("add", this.onAdd, this);
52464             this.ds.un("remove", this.onRemove, this);
52465             this.ds.un("update", this.onUpdate, this);
52466             this.ds.un("clear", this.onClear, this);
52467         }
52468         if(ds){
52469             ds.on("load", this.onLoad, this);
52470             ds.on("datachanged", this.onDataChange, this);
52471             ds.on("add", this.onAdd, this);
52472             ds.on("remove", this.onRemove, this);
52473             ds.on("update", this.onUpdate, this);
52474             ds.on("clear", this.onClear, this);
52475         }
52476         this.ds = ds;
52477
52478         if(this.cm){
52479             this.cm.un("widthchange", this.onColWidthChange, this);
52480             this.cm.un("headerchange", this.onHeaderChange, this);
52481             this.cm.un("hiddenchange", this.onHiddenChange, this);
52482             this.cm.un("columnmoved", this.onColumnMove, this);
52483             this.cm.un("columnlockchange", this.onColumnLock, this);
52484         }
52485         if(cm){
52486             this.generateRules(cm);
52487             cm.on("widthchange", this.onColWidthChange, this);
52488             cm.on("headerchange", this.onHeaderChange, this);
52489             cm.on("hiddenchange", this.onHiddenChange, this);
52490             cm.on("columnmoved", this.onColumnMove, this);
52491             cm.on("columnlockchange", this.onColumnLock, this);
52492         }
52493         this.cm = cm;
52494     },
52495
52496     init: function(grid){
52497         Roo.grid.GridView.superclass.init.call(this, grid);
52498
52499         this.bind(grid.dataSource, grid.colModel);
52500
52501         grid.on("headerclick", this.handleHeaderClick, this);
52502
52503         if(grid.trackMouseOver){
52504             grid.on("mouseover", this.onRowOver, this);
52505             grid.on("mouseout", this.onRowOut, this);
52506         }
52507         grid.cancelTextSelection = function(){};
52508         this.gridId = grid.id;
52509
52510         var tpls = this.templates || {};
52511
52512         if(!tpls.master){
52513             tpls.master = new Roo.Template(
52514                '<div class="x-grid" hidefocus="true">',
52515                 '<a href="#" class="x-grid-focus" tabIndex="-1"></a>',
52516                   '<div class="x-grid-topbar"></div>',
52517                   '<div class="x-grid-scroller"><div></div></div>',
52518                   '<div class="x-grid-locked">',
52519                       '<div class="x-grid-header">{lockedHeader}</div>',
52520                       '<div class="x-grid-body">{lockedBody}</div>',
52521                   "</div>",
52522                   '<div class="x-grid-viewport">',
52523                       '<div class="x-grid-header">{header}</div>',
52524                       '<div class="x-grid-body">{body}</div>',
52525                   "</div>",
52526                   '<div class="x-grid-bottombar"></div>',
52527                  
52528                   '<div class="x-grid-resize-proxy">&#160;</div>',
52529                "</div>"
52530             );
52531             tpls.master.disableformats = true;
52532         }
52533
52534         if(!tpls.header){
52535             tpls.header = new Roo.Template(
52536                '<table border="0" cellspacing="0" cellpadding="0">',
52537                '<tbody><tr class="x-grid-hd-row">{cells}</tr></tbody>',
52538                "</table>{splits}"
52539             );
52540             tpls.header.disableformats = true;
52541         }
52542         tpls.header.compile();
52543
52544         if(!tpls.hcell){
52545             tpls.hcell = new Roo.Template(
52546                 '<td class="x-grid-hd x-grid-td-{id} {cellId}"><div title="{title}" class="x-grid-hd-inner x-grid-hd-{id}">',
52547                 '<div class="x-grid-hd-text ' + this.unselectableCls +  '" ' + this.unselectable +'>{value}<img class="x-grid-sort-icon" src="', Roo.BLANK_IMAGE_URL, '" /></div>',
52548                 "</div></td>"
52549              );
52550              tpls.hcell.disableFormats = true;
52551         }
52552         tpls.hcell.compile();
52553
52554         if(!tpls.hsplit){
52555             tpls.hsplit = new Roo.Template('<div class="x-grid-split {splitId} x-grid-split-{id}" style="{style} ' +
52556                                             this.unselectableCls +  '" ' + this.unselectable +'>&#160;</div>');
52557             tpls.hsplit.disableFormats = true;
52558         }
52559         tpls.hsplit.compile();
52560
52561         if(!tpls.body){
52562             tpls.body = new Roo.Template(
52563                '<table border="0" cellspacing="0" cellpadding="0">',
52564                "<tbody>{rows}</tbody>",
52565                "</table>"
52566             );
52567             tpls.body.disableFormats = true;
52568         }
52569         tpls.body.compile();
52570
52571         if(!tpls.row){
52572             tpls.row = new Roo.Template('<tr class="x-grid-row {alt}">{cells}</tr>');
52573             tpls.row.disableFormats = true;
52574         }
52575         tpls.row.compile();
52576
52577         if(!tpls.cell){
52578             tpls.cell = new Roo.Template(
52579                 '<td class="x-grid-col x-grid-td-{id} {cellId} {css}" tabIndex="0">',
52580                 '<div class="x-grid-col-{id} x-grid-cell-inner"><div class="x-grid-cell-text ' +
52581                     this.unselectableCls +  '" ' + this.unselectable +'" {attr}>{value}</div></div>',
52582                 "</td>"
52583             );
52584             tpls.cell.disableFormats = true;
52585         }
52586         tpls.cell.compile();
52587
52588         this.templates = tpls;
52589     },
52590
52591     // remap these for backwards compat
52592     onColWidthChange : function(){
52593         this.updateColumns.apply(this, arguments);
52594     },
52595     onHeaderChange : function(){
52596         this.updateHeaders.apply(this, arguments);
52597     }, 
52598     onHiddenChange : function(){
52599         this.handleHiddenChange.apply(this, arguments);
52600     },
52601     onColumnMove : function(){
52602         this.handleColumnMove.apply(this, arguments);
52603     },
52604     onColumnLock : function(){
52605         this.handleLockChange.apply(this, arguments);
52606     },
52607
52608     onDataChange : function(){
52609         this.refresh();
52610         this.updateHeaderSortState();
52611     },
52612
52613     onClear : function(){
52614         this.refresh();
52615     },
52616
52617     onUpdate : function(ds, record){
52618         this.refreshRow(record);
52619     },
52620
52621     refreshRow : function(record){
52622         var ds = this.ds, index;
52623         if(typeof record == 'number'){
52624             index = record;
52625             record = ds.getAt(index);
52626         }else{
52627             index = ds.indexOf(record);
52628         }
52629         this.insertRows(ds, index, index, true);
52630         this.onRemove(ds, record, index+1, true);
52631         this.syncRowHeights(index, index);
52632         this.layout();
52633         this.fireEvent("rowupdated", this, index, record);
52634     },
52635
52636     onAdd : function(ds, records, index){
52637         this.insertRows(ds, index, index + (records.length-1));
52638     },
52639
52640     onRemove : function(ds, record, index, isUpdate){
52641         if(isUpdate !== true){
52642             this.fireEvent("beforerowremoved", this, index, record);
52643         }
52644         var bt = this.getBodyTable(), lt = this.getLockedTable();
52645         if(bt.rows[index]){
52646             bt.firstChild.removeChild(bt.rows[index]);
52647         }
52648         if(lt.rows[index]){
52649             lt.firstChild.removeChild(lt.rows[index]);
52650         }
52651         if(isUpdate !== true){
52652             this.stripeRows(index);
52653             this.syncRowHeights(index, index);
52654             this.layout();
52655             this.fireEvent("rowremoved", this, index, record);
52656         }
52657     },
52658
52659     onLoad : function(){
52660         this.scrollToTop();
52661     },
52662
52663     /**
52664      * Scrolls the grid to the top
52665      */
52666     scrollToTop : function(){
52667         if(this.scroller){
52668             this.scroller.dom.scrollTop = 0;
52669             this.syncScroll();
52670         }
52671     },
52672
52673     /**
52674      * Gets a panel in the header of the grid that can be used for toolbars etc.
52675      * After modifying the contents of this panel a call to grid.autoSize() may be
52676      * required to register any changes in size.
52677      * @param {Boolean} doShow By default the header is hidden. Pass true to show the panel
52678      * @return Roo.Element
52679      */
52680     getHeaderPanel : function(doShow){
52681         if(doShow){
52682             this.headerPanel.show();
52683         }
52684         return this.headerPanel;
52685     },
52686
52687     /**
52688      * Gets a panel in the footer of the grid that can be used for toolbars etc.
52689      * After modifying the contents of this panel a call to grid.autoSize() may be
52690      * required to register any changes in size.
52691      * @param {Boolean} doShow By default the footer is hidden. Pass true to show the panel
52692      * @return Roo.Element
52693      */
52694     getFooterPanel : function(doShow){
52695         if(doShow){
52696             this.footerPanel.show();
52697         }
52698         return this.footerPanel;
52699     },
52700
52701     initElements : function(){
52702         var E = Roo.Element;
52703         var el = this.grid.getGridEl().dom.firstChild;
52704         var cs = el.childNodes;
52705
52706         this.el = new E(el);
52707         
52708          this.focusEl = new E(el.firstChild);
52709         this.focusEl.swallowEvent("click", true);
52710         
52711         this.headerPanel = new E(cs[1]);
52712         this.headerPanel.enableDisplayMode("block");
52713
52714         this.scroller = new E(cs[2]);
52715         this.scrollSizer = new E(this.scroller.dom.firstChild);
52716
52717         this.lockedWrap = new E(cs[3]);
52718         this.lockedHd = new E(this.lockedWrap.dom.firstChild);
52719         this.lockedBody = new E(this.lockedWrap.dom.childNodes[1]);
52720
52721         this.mainWrap = new E(cs[4]);
52722         this.mainHd = new E(this.mainWrap.dom.firstChild);
52723         this.mainBody = new E(this.mainWrap.dom.childNodes[1]);
52724
52725         this.footerPanel = new E(cs[5]);
52726         this.footerPanel.enableDisplayMode("block");
52727
52728         this.resizeProxy = new E(cs[6]);
52729
52730         this.headerSelector = String.format(
52731            '#{0} td.x-grid-hd, #{1} td.x-grid-hd',
52732            this.lockedHd.id, this.mainHd.id
52733         );
52734
52735         this.splitterSelector = String.format(
52736            '#{0} div.x-grid-split, #{1} div.x-grid-split',
52737            this.idToCssName(this.lockedHd.id), this.idToCssName(this.mainHd.id)
52738         );
52739     },
52740     idToCssName : function(s)
52741     {
52742         return s.replace(/[^a-z0-9]+/ig, '-');
52743     },
52744
52745     getHeaderCell : function(index){
52746         return Roo.DomQuery.select(this.headerSelector)[index];
52747     },
52748
52749     getHeaderCellMeasure : function(index){
52750         return this.getHeaderCell(index).firstChild;
52751     },
52752
52753     getHeaderCellText : function(index){
52754         return this.getHeaderCell(index).firstChild.firstChild;
52755     },
52756
52757     getLockedTable : function(){
52758         return this.lockedBody.dom.firstChild;
52759     },
52760
52761     getBodyTable : function(){
52762         return this.mainBody.dom.firstChild;
52763     },
52764
52765     getLockedRow : function(index){
52766         return this.getLockedTable().rows[index];
52767     },
52768
52769     getRow : function(index){
52770         return this.getBodyTable().rows[index];
52771     },
52772
52773     getRowComposite : function(index){
52774         if(!this.rowEl){
52775             this.rowEl = new Roo.CompositeElementLite();
52776         }
52777         var els = [], lrow, mrow;
52778         if(lrow = this.getLockedRow(index)){
52779             els.push(lrow);
52780         }
52781         if(mrow = this.getRow(index)){
52782             els.push(mrow);
52783         }
52784         this.rowEl.elements = els;
52785         return this.rowEl;
52786     },
52787     /**
52788      * Gets the 'td' of the cell
52789      * 
52790      * @param {Integer} rowIndex row to select
52791      * @param {Integer} colIndex column to select
52792      * 
52793      * @return {Object} 
52794      */
52795     getCell : function(rowIndex, colIndex){
52796         var locked = this.cm.getLockedCount();
52797         var source;
52798         if(colIndex < locked){
52799             source = this.lockedBody.dom.firstChild;
52800         }else{
52801             source = this.mainBody.dom.firstChild;
52802             colIndex -= locked;
52803         }
52804         return source.rows[rowIndex].childNodes[colIndex];
52805     },
52806
52807     getCellText : function(rowIndex, colIndex){
52808         return this.getCell(rowIndex, colIndex).firstChild.firstChild;
52809     },
52810
52811     getCellBox : function(cell){
52812         var b = this.fly(cell).getBox();
52813         if(Roo.isOpera){ // opera fails to report the Y
52814             b.y = cell.offsetTop + this.mainBody.getY();
52815         }
52816         return b;
52817     },
52818
52819     getCellIndex : function(cell){
52820         var id = String(cell.className).match(this.cellRE);
52821         if(id){
52822             return parseInt(id[1], 10);
52823         }
52824         return 0;
52825     },
52826
52827     findHeaderIndex : function(n){
52828         var r = Roo.fly(n).findParent("td." + this.hdClass, 6);
52829         return r ? this.getCellIndex(r) : false;
52830     },
52831
52832     findHeaderCell : function(n){
52833         var r = Roo.fly(n).findParent("td." + this.hdClass, 6);
52834         return r ? r : false;
52835     },
52836
52837     findRowIndex : function(n){
52838         if(!n){
52839             return false;
52840         }
52841         var r = Roo.fly(n).findParent("tr." + this.rowClass, 6);
52842         return r ? r.rowIndex : false;
52843     },
52844
52845     findCellIndex : function(node){
52846         var stop = this.el.dom;
52847         while(node && node != stop){
52848             if(this.findRE.test(node.className)){
52849                 return this.getCellIndex(node);
52850             }
52851             node = node.parentNode;
52852         }
52853         return false;
52854     },
52855
52856     getColumnId : function(index){
52857         return this.cm.getColumnId(index);
52858     },
52859
52860     getSplitters : function()
52861     {
52862         if(this.splitterSelector){
52863            return Roo.DomQuery.select(this.splitterSelector);
52864         }else{
52865             return null;
52866       }
52867     },
52868
52869     getSplitter : function(index){
52870         return this.getSplitters()[index];
52871     },
52872
52873     onRowOver : function(e, t){
52874         var row;
52875         if((row = this.findRowIndex(t)) !== false){
52876             this.getRowComposite(row).addClass("x-grid-row-over");
52877         }
52878     },
52879
52880     onRowOut : function(e, t){
52881         var row;
52882         if((row = this.findRowIndex(t)) !== false && row !== this.findRowIndex(e.getRelatedTarget())){
52883             this.getRowComposite(row).removeClass("x-grid-row-over");
52884         }
52885     },
52886
52887     renderHeaders : function(){
52888         var cm = this.cm;
52889         var ct = this.templates.hcell, ht = this.templates.header, st = this.templates.hsplit;
52890         var cb = [], lb = [], sb = [], lsb = [], p = {};
52891         for(var i = 0, len = cm.getColumnCount(); i < len; i++){
52892             p.cellId = "x-grid-hd-0-" + i;
52893             p.splitId = "x-grid-csplit-0-" + i;
52894             p.id = cm.getColumnId(i);
52895             p.title = cm.getColumnTooltip(i) || "";
52896             p.value = cm.getColumnHeader(i) || "";
52897             p.style = (this.grid.enableColumnResize === false || !cm.isResizable(i) || cm.isFixed(i)) ? 'cursor:default' : '';
52898             if(!cm.isLocked(i)){
52899                 cb[cb.length] = ct.apply(p);
52900                 sb[sb.length] = st.apply(p);
52901             }else{
52902                 lb[lb.length] = ct.apply(p);
52903                 lsb[lsb.length] = st.apply(p);
52904             }
52905         }
52906         return [ht.apply({cells: lb.join(""), splits:lsb.join("")}),
52907                 ht.apply({cells: cb.join(""), splits:sb.join("")})];
52908     },
52909
52910     updateHeaders : function(){
52911         var html = this.renderHeaders();
52912         this.lockedHd.update(html[0]);
52913         this.mainHd.update(html[1]);
52914     },
52915
52916     /**
52917      * Focuses the specified row.
52918      * @param {Number} row The row index
52919      */
52920     focusRow : function(row)
52921     {
52922         //Roo.log('GridView.focusRow');
52923         var x = this.scroller.dom.scrollLeft;
52924         this.focusCell(row, 0, false);
52925         this.scroller.dom.scrollLeft = x;
52926     },
52927
52928     /**
52929      * Focuses the specified cell.
52930      * @param {Number} row The row index
52931      * @param {Number} col The column index
52932      * @param {Boolean} hscroll false to disable horizontal scrolling
52933      */
52934     focusCell : function(row, col, hscroll)
52935     {
52936         //Roo.log('GridView.focusCell');
52937         var el = this.ensureVisible(row, col, hscroll);
52938         this.focusEl.alignTo(el, "tl-tl");
52939         if(Roo.isGecko){
52940             this.focusEl.focus();
52941         }else{
52942             this.focusEl.focus.defer(1, this.focusEl);
52943         }
52944     },
52945
52946     /**
52947      * Scrolls the specified cell into view
52948      * @param {Number} row The row index
52949      * @param {Number} col The column index
52950      * @param {Boolean} hscroll false to disable horizontal scrolling
52951      */
52952     ensureVisible : function(row, col, hscroll)
52953     {
52954         //Roo.log('GridView.ensureVisible,' + row + ',' + col);
52955         //return null; //disable for testing.
52956         if(typeof row != "number"){
52957             row = row.rowIndex;
52958         }
52959         if(row < 0 && row >= this.ds.getCount()){
52960             return  null;
52961         }
52962         col = (col !== undefined ? col : 0);
52963         var cm = this.grid.colModel;
52964         while(cm.isHidden(col)){
52965             col++;
52966         }
52967
52968         var el = this.getCell(row, col);
52969         if(!el){
52970             return null;
52971         }
52972         var c = this.scroller.dom;
52973
52974         var ctop = parseInt(el.offsetTop, 10);
52975         var cleft = parseInt(el.offsetLeft, 10);
52976         var cbot = ctop + el.offsetHeight;
52977         var cright = cleft + el.offsetWidth;
52978         
52979         var ch = c.clientHeight - this.mainHd.dom.offsetHeight;
52980         var stop = parseInt(c.scrollTop, 10);
52981         var sleft = parseInt(c.scrollLeft, 10);
52982         var sbot = stop + ch;
52983         var sright = sleft + c.clientWidth;
52984         /*
52985         Roo.log('GridView.ensureVisible:' +
52986                 ' ctop:' + ctop +
52987                 ' c.clientHeight:' + c.clientHeight +
52988                 ' this.mainHd.dom.offsetHeight:' + this.mainHd.dom.offsetHeight +
52989                 ' stop:' + stop +
52990                 ' cbot:' + cbot +
52991                 ' sbot:' + sbot +
52992                 ' ch:' + ch  
52993                 );
52994         */
52995         if(ctop < stop){
52996              c.scrollTop = ctop;
52997             //Roo.log("set scrolltop to ctop DISABLE?");
52998         }else if(cbot > sbot){
52999             //Roo.log("set scrolltop to cbot-ch");
53000             c.scrollTop = cbot-ch;
53001         }
53002         
53003         if(hscroll !== false){
53004             if(cleft < sleft){
53005                 c.scrollLeft = cleft;
53006             }else if(cright > sright){
53007                 c.scrollLeft = cright-c.clientWidth;
53008             }
53009         }
53010          
53011         return el;
53012     },
53013
53014     updateColumns : function(){
53015         this.grid.stopEditing();
53016         var cm = this.grid.colModel, colIds = this.getColumnIds();
53017         //var totalWidth = cm.getTotalWidth();
53018         var pos = 0;
53019         for(var i = 0, len = cm.getColumnCount(); i < len; i++){
53020             //if(cm.isHidden(i)) continue;
53021             var w = cm.getColumnWidth(i);
53022             this.css.updateRule(this.colSelector+this.idToCssName(colIds[i]), "width", (w - this.borderWidth) + "px");
53023             this.css.updateRule(this.hdSelector+this.idToCssName(colIds[i]), "width", (w - this.borderWidth) + "px");
53024         }
53025         this.updateSplitters();
53026     },
53027
53028     generateRules : function(cm){
53029         var ruleBuf = [], rulesId = this.idToCssName(this.grid.id)+ '-cssrules';
53030         Roo.util.CSS.removeStyleSheet(rulesId);
53031         for(var i = 0, len = cm.getColumnCount(); i < len; i++){
53032             var cid = cm.getColumnId(i);
53033             var align = '';
53034             if(cm.config[i].align){
53035                 align = 'text-align:'+cm.config[i].align+';';
53036             }
53037             var hidden = '';
53038             if(cm.isHidden(i)){
53039                 hidden = 'display:none;';
53040             }
53041             var width = "width:" + (cm.getColumnWidth(i) - this.borderWidth) + "px;";
53042             ruleBuf.push(
53043                     this.colSelector, cid, " {\n", cm.config[i].css, align, width, "\n}\n",
53044                     this.hdSelector, cid, " {\n", align, width, "}\n",
53045                     this.tdSelector, cid, " {\n",hidden,"\n}\n",
53046                     this.splitSelector, cid, " {\n", hidden , "\n}\n");
53047         }
53048         return Roo.util.CSS.createStyleSheet(ruleBuf.join(""), rulesId);
53049     },
53050
53051     updateSplitters : function(){
53052         var cm = this.cm, s = this.getSplitters();
53053         if(s){ // splitters not created yet
53054             var pos = 0, locked = true;
53055             for(var i = 0, len = cm.getColumnCount(); i < len; i++){
53056                 if(cm.isHidden(i)) continue;
53057                 var w = cm.getColumnWidth(i); // make sure it's a number
53058                 if(!cm.isLocked(i) && locked){
53059                     pos = 0;
53060                     locked = false;
53061                 }
53062                 pos += w;
53063                 s[i].style.left = (pos-this.splitOffset) + "px";
53064             }
53065         }
53066     },
53067
53068     handleHiddenChange : function(colModel, colIndex, hidden){
53069         if(hidden){
53070             this.hideColumn(colIndex);
53071         }else{
53072             this.unhideColumn(colIndex);
53073         }
53074     },
53075
53076     hideColumn : function(colIndex){
53077         var cid = this.getColumnId(colIndex);
53078         this.css.updateRule(this.tdSelector+this.idToCssName(cid), "display", "none");
53079         this.css.updateRule(this.splitSelector+this.idToCssName(cid), "display", "none");
53080         if(Roo.isSafari){
53081             this.updateHeaders();
53082         }
53083         this.updateSplitters();
53084         this.layout();
53085     },
53086
53087     unhideColumn : function(colIndex){
53088         var cid = this.getColumnId(colIndex);
53089         this.css.updateRule(this.tdSelector+this.idToCssName(cid), "display", "");
53090         this.css.updateRule(this.splitSelector+this.idToCssName(cid), "display", "");
53091
53092         if(Roo.isSafari){
53093             this.updateHeaders();
53094         }
53095         this.updateSplitters();
53096         this.layout();
53097     },
53098
53099     insertRows : function(dm, firstRow, lastRow, isUpdate){
53100         if(firstRow == 0 && lastRow == dm.getCount()-1){
53101             this.refresh();
53102         }else{
53103             if(!isUpdate){
53104                 this.fireEvent("beforerowsinserted", this, firstRow, lastRow);
53105             }
53106             var s = this.getScrollState();
53107             var markup = this.renderRows(firstRow, lastRow);
53108             this.bufferRows(markup[0], this.getLockedTable(), firstRow);
53109             this.bufferRows(markup[1], this.getBodyTable(), firstRow);
53110             this.restoreScroll(s);
53111             if(!isUpdate){
53112                 this.fireEvent("rowsinserted", this, firstRow, lastRow);
53113                 this.syncRowHeights(firstRow, lastRow);
53114                 this.stripeRows(firstRow);
53115                 this.layout();
53116             }
53117         }
53118     },
53119
53120     bufferRows : function(markup, target, index){
53121         var before = null, trows = target.rows, tbody = target.tBodies[0];
53122         if(index < trows.length){
53123             before = trows[index];
53124         }
53125         var b = document.createElement("div");
53126         b.innerHTML = "<table><tbody>"+markup+"</tbody></table>";
53127         var rows = b.firstChild.rows;
53128         for(var i = 0, len = rows.length; i < len; i++){
53129             if(before){
53130                 tbody.insertBefore(rows[0], before);
53131             }else{
53132                 tbody.appendChild(rows[0]);
53133             }
53134         }
53135         b.innerHTML = "";
53136         b = null;
53137     },
53138
53139     deleteRows : function(dm, firstRow, lastRow){
53140         if(dm.getRowCount()<1){
53141             this.fireEvent("beforerefresh", this);
53142             this.mainBody.update("");
53143             this.lockedBody.update("");
53144             this.fireEvent("refresh", this);
53145         }else{
53146             this.fireEvent("beforerowsdeleted", this, firstRow, lastRow);
53147             var bt = this.getBodyTable();
53148             var tbody = bt.firstChild;
53149             var rows = bt.rows;
53150             for(var rowIndex = firstRow; rowIndex <= lastRow; rowIndex++){
53151                 tbody.removeChild(rows[firstRow]);
53152             }
53153             this.stripeRows(firstRow);
53154             this.fireEvent("rowsdeleted", this, firstRow, lastRow);
53155         }
53156     },
53157
53158     updateRows : function(dataSource, firstRow, lastRow){
53159         var s = this.getScrollState();
53160         this.refresh();
53161         this.restoreScroll(s);
53162     },
53163
53164     handleSort : function(dataSource, sortColumnIndex, sortDir, noRefresh){
53165         if(!noRefresh){
53166            this.refresh();
53167         }
53168         this.updateHeaderSortState();
53169     },
53170
53171     getScrollState : function(){
53172         
53173         var sb = this.scroller.dom;
53174         return {left: sb.scrollLeft, top: sb.scrollTop};
53175     },
53176
53177     stripeRows : function(startRow){
53178         if(!this.grid.stripeRows || this.ds.getCount() < 1){
53179             return;
53180         }
53181         startRow = startRow || 0;
53182         var rows = this.getBodyTable().rows;
53183         var lrows = this.getLockedTable().rows;
53184         var cls = ' x-grid-row-alt ';
53185         for(var i = startRow, len = rows.length; i < len; i++){
53186             var row = rows[i], lrow = lrows[i];
53187             var isAlt = ((i+1) % 2 == 0);
53188             var hasAlt = (' '+row.className + ' ').indexOf(cls) != -1;
53189             if(isAlt == hasAlt){
53190                 continue;
53191             }
53192             if(isAlt){
53193                 row.className += " x-grid-row-alt";
53194             }else{
53195                 row.className = row.className.replace("x-grid-row-alt", "");
53196             }
53197             if(lrow){
53198                 lrow.className = row.className;
53199             }
53200         }
53201     },
53202
53203     restoreScroll : function(state){
53204         //Roo.log('GridView.restoreScroll');
53205         var sb = this.scroller.dom;
53206         sb.scrollLeft = state.left;
53207         sb.scrollTop = state.top;
53208         this.syncScroll();
53209     },
53210
53211     syncScroll : function(){
53212         //Roo.log('GridView.syncScroll');
53213         var sb = this.scroller.dom;
53214         var sh = this.mainHd.dom;
53215         var bs = this.mainBody.dom;
53216         var lv = this.lockedBody.dom;
53217         sh.scrollLeft = bs.scrollLeft = sb.scrollLeft;
53218         lv.scrollTop = bs.scrollTop = sb.scrollTop;
53219     },
53220
53221     handleScroll : function(e){
53222         this.syncScroll();
53223         var sb = this.scroller.dom;
53224         this.grid.fireEvent("bodyscroll", sb.scrollLeft, sb.scrollTop);
53225         e.stopEvent();
53226     },
53227
53228     handleWheel : function(e){
53229         var d = e.getWheelDelta();
53230         this.scroller.dom.scrollTop -= d*22;
53231         // set this here to prevent jumpy scrolling on large tables
53232         this.lockedBody.dom.scrollTop = this.mainBody.dom.scrollTop = this.scroller.dom.scrollTop;
53233         e.stopEvent();
53234     },
53235
53236     renderRows : function(startRow, endRow){
53237         // pull in all the crap needed to render rows
53238         var g = this.grid, cm = g.colModel, ds = g.dataSource, stripe = g.stripeRows;
53239         var colCount = cm.getColumnCount();
53240
53241         if(ds.getCount() < 1){
53242             return ["", ""];
53243         }
53244
53245         // build a map for all the columns
53246         var cs = [];
53247         for(var i = 0; i < colCount; i++){
53248             var name = cm.getDataIndex(i);
53249             cs[i] = {
53250                 name : typeof name == 'undefined' ? ds.fields.get(i).name : name,
53251                 renderer : cm.getRenderer(i),
53252                 id : cm.getColumnId(i),
53253                 locked : cm.isLocked(i)
53254             };
53255         }
53256
53257         startRow = startRow || 0;
53258         endRow = typeof endRow == "undefined"? ds.getCount()-1 : endRow;
53259
53260         // records to render
53261         var rs = ds.getRange(startRow, endRow);
53262
53263         return this.doRender(cs, rs, ds, startRow, colCount, stripe);
53264     },
53265
53266     // As much as I hate to duplicate code, this was branched because FireFox really hates
53267     // [].join("") on strings. The performance difference was substantial enough to
53268     // branch this function
53269     doRender : Roo.isGecko ?
53270             function(cs, rs, ds, startRow, colCount, stripe){
53271                 var ts = this.templates, ct = ts.cell, rt = ts.row;
53272                 // buffers
53273                 var buf = "", lbuf = "", cb, lcb, c, p = {}, rp = {}, r, rowIndex;
53274                 
53275                 var hasListener = this.grid.hasListener('rowclass');
53276                 var rowcfg = {};
53277                 for(var j = 0, len = rs.length; j < len; j++){
53278                     r = rs[j]; cb = ""; lcb = ""; rowIndex = (j+startRow);
53279                     for(var i = 0; i < colCount; i++){
53280                         c = cs[i];
53281                         p.cellId = "x-grid-cell-" + rowIndex + "-" + i;
53282                         p.id = c.id;
53283                         p.css = p.attr = "";
53284                         p.value = c.renderer(r.data[c.name], p, r, rowIndex, i, ds);
53285                         if(p.value == undefined || p.value === "") p.value = "&#160;";
53286                         if(r.dirty && typeof r.modified[c.name] !== 'undefined'){
53287                             p.css += p.css ? ' x-grid-dirty-cell' : 'x-grid-dirty-cell';
53288                         }
53289                         var markup = ct.apply(p);
53290                         if(!c.locked){
53291                             cb+= markup;
53292                         }else{
53293                             lcb+= markup;
53294                         }
53295                     }
53296                     var alt = [];
53297                     if(stripe && ((rowIndex+1) % 2 == 0)){
53298                         alt.push("x-grid-row-alt")
53299                     }
53300                     if(r.dirty){
53301                         alt.push(  " x-grid-dirty-row");
53302                     }
53303                     rp.cells = lcb;
53304                     if(this.getRowClass){
53305                         alt.push(this.getRowClass(r, rowIndex));
53306                     }
53307                     if (hasListener) {
53308                         rowcfg = {
53309                              
53310                             record: r,
53311                             rowIndex : rowIndex,
53312                             rowClass : ''
53313                         }
53314                         this.grid.fireEvent('rowclass', this, rowcfg);
53315                         alt.push(rowcfg.rowClass);
53316                     }
53317                     rp.alt = alt.join(" ");
53318                     lbuf+= rt.apply(rp);
53319                     rp.cells = cb;
53320                     buf+=  rt.apply(rp);
53321                 }
53322                 return [lbuf, buf];
53323             } :
53324             function(cs, rs, ds, startRow, colCount, stripe){
53325                 var ts = this.templates, ct = ts.cell, rt = ts.row;
53326                 // buffers
53327                 var buf = [], lbuf = [], cb, lcb, c, p = {}, rp = {}, r, rowIndex;
53328                 var hasListener = this.grid.hasListener('rowclass');
53329  
53330                 var rowcfg = {};
53331                 for(var j = 0, len = rs.length; j < len; j++){
53332                     r = rs[j]; cb = []; lcb = []; rowIndex = (j+startRow);
53333                     for(var i = 0; i < colCount; i++){
53334                         c = cs[i];
53335                         p.cellId = "x-grid-cell-" + rowIndex + "-" + i;
53336                         p.id = c.id;
53337                         p.css = p.attr = "";
53338                         p.value = c.renderer(r.data[c.name], p, r, rowIndex, i, ds);
53339                         if(p.value == undefined || p.value === "") p.value = "&#160;";
53340                         if(r.dirty && typeof r.modified[c.name] !== 'undefined'){
53341                             p.css += p.css ? ' x-grid-dirty-cell' : 'x-grid-dirty-cell';
53342                         }
53343                         
53344                         var markup = ct.apply(p);
53345                         if(!c.locked){
53346                             cb[cb.length] = markup;
53347                         }else{
53348                             lcb[lcb.length] = markup;
53349                         }
53350                     }
53351                     var alt = [];
53352                     if(stripe && ((rowIndex+1) % 2 == 0)){
53353                         alt.push( "x-grid-row-alt");
53354                     }
53355                     if(r.dirty){
53356                         alt.push(" x-grid-dirty-row");
53357                     }
53358                     rp.cells = lcb;
53359                     if(this.getRowClass){
53360                         alt.push( this.getRowClass(r, rowIndex));
53361                     }
53362                     if (hasListener) {
53363                         rowcfg = {
53364                              
53365                             record: r,
53366                             rowIndex : rowIndex,
53367                             rowClass : ''
53368                         }
53369                         this.grid.fireEvent('rowclass', this, rowcfg);
53370                         alt.push(rowcfg.rowClass);
53371                     }
53372                     rp.alt = alt.join(" ");
53373                     rp.cells = lcb.join("");
53374                     lbuf[lbuf.length] = rt.apply(rp);
53375                     rp.cells = cb.join("");
53376                     buf[buf.length] =  rt.apply(rp);
53377                 }
53378                 return [lbuf.join(""), buf.join("")];
53379             },
53380
53381     renderBody : function(){
53382         var markup = this.renderRows();
53383         var bt = this.templates.body;
53384         return [bt.apply({rows: markup[0]}), bt.apply({rows: markup[1]})];
53385     },
53386
53387     /**
53388      * Refreshes the grid
53389      * @param {Boolean} headersToo
53390      */
53391     refresh : function(headersToo){
53392         this.fireEvent("beforerefresh", this);
53393         this.grid.stopEditing();
53394         var result = this.renderBody();
53395         this.lockedBody.update(result[0]);
53396         this.mainBody.update(result[1]);
53397         if(headersToo === true){
53398             this.updateHeaders();
53399             this.updateColumns();
53400             this.updateSplitters();
53401             this.updateHeaderSortState();
53402         }
53403         this.syncRowHeights();
53404         this.layout();
53405         this.fireEvent("refresh", this);
53406     },
53407
53408     handleColumnMove : function(cm, oldIndex, newIndex){
53409         this.indexMap = null;
53410         var s = this.getScrollState();
53411         this.refresh(true);
53412         this.restoreScroll(s);
53413         this.afterMove(newIndex);
53414     },
53415
53416     afterMove : function(colIndex){
53417         if(this.enableMoveAnim && Roo.enableFx){
53418             this.fly(this.getHeaderCell(colIndex).firstChild).highlight(this.hlColor);
53419         }
53420         // if multisort - fix sortOrder, and reload..
53421         if (this.grid.dataSource.multiSort) {
53422             // the we can call sort again..
53423             var dm = this.grid.dataSource;
53424             var cm = this.grid.colModel;
53425             var so = [];
53426             for(var i = 0; i < cm.config.length; i++ ) {
53427                 
53428                 if ((typeof(dm.sortToggle[cm.config[i].dataIndex]) == 'undefined')) {
53429                     continue; // dont' bother, it's not in sort list or being set.
53430                 }
53431                 
53432                 so.push(cm.config[i].dataIndex);
53433             };
53434             dm.sortOrder = so;
53435             dm.load(dm.lastOptions);
53436             
53437             
53438         }
53439         
53440     },
53441
53442     updateCell : function(dm, rowIndex, dataIndex){
53443         var colIndex = this.getColumnIndexByDataIndex(dataIndex);
53444         if(typeof colIndex == "undefined"){ // not present in grid
53445             return;
53446         }
53447         var cm = this.grid.colModel;
53448         var cell = this.getCell(rowIndex, colIndex);
53449         var cellText = this.getCellText(rowIndex, colIndex);
53450
53451         var p = {
53452             cellId : "x-grid-cell-" + rowIndex + "-" + colIndex,
53453             id : cm.getColumnId(colIndex),
53454             css: colIndex == cm.getColumnCount()-1 ? "x-grid-col-last" : ""
53455         };
53456         var renderer = cm.getRenderer(colIndex);
53457         var val = renderer(dm.getValueAt(rowIndex, dataIndex), p, rowIndex, colIndex, dm);
53458         if(typeof val == "undefined" || val === "") val = "&#160;";
53459         cellText.innerHTML = val;
53460         cell.className = this.cellClass + " " + this.idToCssName(p.cellId) + " " + p.css;
53461         this.syncRowHeights(rowIndex, rowIndex);
53462     },
53463
53464     calcColumnWidth : function(colIndex, maxRowsToMeasure){
53465         var maxWidth = 0;
53466         if(this.grid.autoSizeHeaders){
53467             var h = this.getHeaderCellMeasure(colIndex);
53468             maxWidth = Math.max(maxWidth, h.scrollWidth);
53469         }
53470         var tb, index;
53471         if(this.cm.isLocked(colIndex)){
53472             tb = this.getLockedTable();
53473             index = colIndex;
53474         }else{
53475             tb = this.getBodyTable();
53476             index = colIndex - this.cm.getLockedCount();
53477         }
53478         if(tb && tb.rows){
53479             var rows = tb.rows;
53480             var stopIndex = Math.min(maxRowsToMeasure || rows.length, rows.length);
53481             for(var i = 0; i < stopIndex; i++){
53482                 var cell = rows[i].childNodes[index].firstChild;
53483                 maxWidth = Math.max(maxWidth, cell.scrollWidth);
53484             }
53485         }
53486         return maxWidth + /*margin for error in IE*/ 5;
53487     },
53488     /**
53489      * Autofit a column to its content.
53490      * @param {Number} colIndex
53491      * @param {Boolean} forceMinSize true to force the column to go smaller if possible
53492      */
53493      autoSizeColumn : function(colIndex, forceMinSize, suppressEvent){
53494          if(this.cm.isHidden(colIndex)){
53495              return; // can't calc a hidden column
53496          }
53497         if(forceMinSize){
53498             var cid = this.cm.getColumnId(colIndex);
53499             this.css.updateRule(this.colSelector +this.idToCssName( cid), "width", this.grid.minColumnWidth + "px");
53500            if(this.grid.autoSizeHeaders){
53501                this.css.updateRule(this.hdSelector + this.idToCssName(cid), "width", this.grid.minColumnWidth + "px");
53502            }
53503         }
53504         var newWidth = this.calcColumnWidth(colIndex);
53505         this.cm.setColumnWidth(colIndex,
53506             Math.max(this.grid.minColumnWidth, newWidth), suppressEvent);
53507         if(!suppressEvent){
53508             this.grid.fireEvent("columnresize", colIndex, newWidth);
53509         }
53510     },
53511
53512     /**
53513      * Autofits all columns to their content and then expands to fit any extra space in the grid
53514      */
53515      autoSizeColumns : function(){
53516         var cm = this.grid.colModel;
53517         var colCount = cm.getColumnCount();
53518         for(var i = 0; i < colCount; i++){
53519             this.autoSizeColumn(i, true, true);
53520         }
53521         if(cm.getTotalWidth() < this.scroller.dom.clientWidth){
53522             this.fitColumns();
53523         }else{
53524             this.updateColumns();
53525             this.layout();
53526         }
53527     },
53528
53529     /**
53530      * Autofits all columns to the grid's width proportionate with their current size
53531      * @param {Boolean} reserveScrollSpace Reserve space for a scrollbar
53532      */
53533     fitColumns : function(reserveScrollSpace){
53534         var cm = this.grid.colModel;
53535         var colCount = cm.getColumnCount();
53536         var cols = [];
53537         var width = 0;
53538         var i, w;
53539         for (i = 0; i < colCount; i++){
53540             if(!cm.isHidden(i) && !cm.isFixed(i)){
53541                 w = cm.getColumnWidth(i);
53542                 cols.push(i);
53543                 cols.push(w);
53544                 width += w;
53545             }
53546         }
53547         var avail = Math.min(this.scroller.dom.clientWidth, this.el.getWidth());
53548         if(reserveScrollSpace){
53549             avail -= 17;
53550         }
53551         var frac = (avail - cm.getTotalWidth())/width;
53552         while (cols.length){
53553             w = cols.pop();
53554             i = cols.pop();
53555             cm.setColumnWidth(i, Math.floor(w + w*frac), true);
53556         }
53557         this.updateColumns();
53558         this.layout();
53559     },
53560
53561     onRowSelect : function(rowIndex){
53562         var row = this.getRowComposite(rowIndex);
53563         row.addClass("x-grid-row-selected");
53564     },
53565
53566     onRowDeselect : function(rowIndex){
53567         var row = this.getRowComposite(rowIndex);
53568         row.removeClass("x-grid-row-selected");
53569     },
53570
53571     onCellSelect : function(row, col){
53572         var cell = this.getCell(row, col);
53573         if(cell){
53574             Roo.fly(cell).addClass("x-grid-cell-selected");
53575         }
53576     },
53577
53578     onCellDeselect : function(row, col){
53579         var cell = this.getCell(row, col);
53580         if(cell){
53581             Roo.fly(cell).removeClass("x-grid-cell-selected");
53582         }
53583     },
53584
53585     updateHeaderSortState : function(){
53586         
53587         // sort state can be single { field: xxx, direction : yyy}
53588         // or   { xxx=>ASC , yyy : DESC ..... }
53589         
53590         var mstate = {};
53591         if (!this.ds.multiSort) { 
53592             var state = this.ds.getSortState();
53593             if(!state){
53594                 return;
53595             }
53596             mstate[state.field] = state.direction;
53597             // FIXME... - this is not used here.. but might be elsewhere..
53598             this.sortState = state;
53599             
53600         } else {
53601             mstate = this.ds.sortToggle;
53602         }
53603         //remove existing sort classes..
53604         
53605         var sc = this.sortClasses;
53606         var hds = this.el.select(this.headerSelector).removeClass(sc);
53607         
53608         for(var f in mstate) {
53609         
53610             var sortColumn = this.cm.findColumnIndex(f);
53611             
53612             if(sortColumn != -1){
53613                 var sortDir = mstate[f];        
53614                 hds.item(sortColumn).addClass(sc[sortDir == "DESC" ? 1 : 0]);
53615             }
53616         }
53617         
53618          
53619         
53620     },
53621
53622
53623     handleHeaderClick : function(g, index,e){
53624         
53625         Roo.log("header click");
53626         
53627         if (Roo.isTouch) {
53628             // touch events on header are handled by context
53629             this.handleHdCtx(g,index,e);
53630             return;
53631         }
53632         
53633         
53634         if(this.headersDisabled){
53635             return;
53636         }
53637         var dm = g.dataSource, cm = g.colModel;
53638         if(!cm.isSortable(index)){
53639             return;
53640         }
53641         g.stopEditing();
53642         
53643         if (dm.multiSort) {
53644             // update the sortOrder
53645             var so = [];
53646             for(var i = 0; i < cm.config.length; i++ ) {
53647                 
53648                 if ((typeof(dm.sortToggle[cm.config[i].dataIndex]) == 'undefined') && (index != i)) {
53649                     continue; // dont' bother, it's not in sort list or being set.
53650                 }
53651                 
53652                 so.push(cm.config[i].dataIndex);
53653             };
53654             dm.sortOrder = so;
53655         }
53656         
53657         
53658         dm.sort(cm.getDataIndex(index));
53659     },
53660
53661
53662     destroy : function(){
53663         if(this.colMenu){
53664             this.colMenu.removeAll();
53665             Roo.menu.MenuMgr.unregister(this.colMenu);
53666             this.colMenu.getEl().remove();
53667             delete this.colMenu;
53668         }
53669         if(this.hmenu){
53670             this.hmenu.removeAll();
53671             Roo.menu.MenuMgr.unregister(this.hmenu);
53672             this.hmenu.getEl().remove();
53673             delete this.hmenu;
53674         }
53675         if(this.grid.enableColumnMove){
53676             var dds = Roo.dd.DDM.ids['gridHeader' + this.grid.getGridEl().id];
53677             if(dds){
53678                 for(var dd in dds){
53679                     if(!dds[dd].config.isTarget && dds[dd].dragElId){
53680                         var elid = dds[dd].dragElId;
53681                         dds[dd].unreg();
53682                         Roo.get(elid).remove();
53683                     } else if(dds[dd].config.isTarget){
53684                         dds[dd].proxyTop.remove();
53685                         dds[dd].proxyBottom.remove();
53686                         dds[dd].unreg();
53687                     }
53688                     if(Roo.dd.DDM.locationCache[dd]){
53689                         delete Roo.dd.DDM.locationCache[dd];
53690                     }
53691                 }
53692                 delete Roo.dd.DDM.ids['gridHeader' + this.grid.getGridEl().id];
53693             }
53694         }
53695         Roo.util.CSS.removeStyleSheet(this.idToCssName(this.grid.id) + '-cssrules');
53696         this.bind(null, null);
53697         Roo.EventManager.removeResizeListener(this.onWindowResize, this);
53698     },
53699
53700     handleLockChange : function(){
53701         this.refresh(true);
53702     },
53703
53704     onDenyColumnLock : function(){
53705
53706     },
53707
53708     onDenyColumnHide : function(){
53709
53710     },
53711
53712     handleHdMenuClick : function(item){
53713         var index = this.hdCtxIndex;
53714         var cm = this.cm, ds = this.ds;
53715         switch(item.id){
53716             case "asc":
53717                 ds.sort(cm.getDataIndex(index), "ASC");
53718                 break;
53719             case "desc":
53720                 ds.sort(cm.getDataIndex(index), "DESC");
53721                 break;
53722             case "lock":
53723                 var lc = cm.getLockedCount();
53724                 if(cm.getColumnCount(true) <= lc+1){
53725                     this.onDenyColumnLock();
53726                     return;
53727                 }
53728                 if(lc != index){
53729                     cm.setLocked(index, true, true);
53730                     cm.moveColumn(index, lc);
53731                     this.grid.fireEvent("columnmove", index, lc);
53732                 }else{
53733                     cm.setLocked(index, true);
53734                 }
53735             break;
53736             case "unlock":
53737                 var lc = cm.getLockedCount();
53738                 if((lc-1) != index){
53739                     cm.setLocked(index, false, true);
53740                     cm.moveColumn(index, lc-1);
53741                     this.grid.fireEvent("columnmove", index, lc-1);
53742                 }else{
53743                     cm.setLocked(index, false);
53744                 }
53745             break;
53746             case 'wider': // used to expand cols on touch..
53747             case 'narrow':
53748                 var cw = cm.getColumnWidth(index);
53749                 cw += (item.id == 'wider' ? 1 : -1) * 50;
53750                 cw = Math.max(0, cw);
53751                 cw = Math.min(cw,4000);
53752                 cm.setColumnWidth(index, cw);
53753                 break;
53754                 
53755             default:
53756                 index = cm.getIndexById(item.id.substr(4));
53757                 if(index != -1){
53758                     if(item.checked && cm.getColumnCount(true) <= 1){
53759                         this.onDenyColumnHide();
53760                         return false;
53761                     }
53762                     cm.setHidden(index, item.checked);
53763                 }
53764         }
53765         return true;
53766     },
53767
53768     beforeColMenuShow : function(){
53769         var cm = this.cm,  colCount = cm.getColumnCount();
53770         this.colMenu.removeAll();
53771         for(var i = 0; i < colCount; i++){
53772             this.colMenu.add(new Roo.menu.CheckItem({
53773                 id: "col-"+cm.getColumnId(i),
53774                 text: cm.getColumnHeader(i),
53775                 checked: !cm.isHidden(i),
53776                 hideOnClick:false
53777             }));
53778         }
53779     },
53780
53781     handleHdCtx : function(g, index, e){
53782         e.stopEvent();
53783         var hd = this.getHeaderCell(index);
53784         this.hdCtxIndex = index;
53785         var ms = this.hmenu.items, cm = this.cm;
53786         ms.get("asc").setDisabled(!cm.isSortable(index));
53787         ms.get("desc").setDisabled(!cm.isSortable(index));
53788         if(this.grid.enableColLock !== false){
53789             ms.get("lock").setDisabled(cm.isLocked(index));
53790             ms.get("unlock").setDisabled(!cm.isLocked(index));
53791         }
53792         this.hmenu.show(hd, "tl-bl");
53793     },
53794
53795     handleHdOver : function(e){
53796         var hd = this.findHeaderCell(e.getTarget());
53797         if(hd && !this.headersDisabled){
53798             if(this.grid.colModel.isSortable(this.getCellIndex(hd))){
53799                this.fly(hd).addClass("x-grid-hd-over");
53800             }
53801         }
53802     },
53803
53804     handleHdOut : function(e){
53805         var hd = this.findHeaderCell(e.getTarget());
53806         if(hd){
53807             this.fly(hd).removeClass("x-grid-hd-over");
53808         }
53809     },
53810
53811     handleSplitDblClick : function(e, t){
53812         var i = this.getCellIndex(t);
53813         if(this.grid.enableColumnResize !== false && this.cm.isResizable(i) && !this.cm.isFixed(i)){
53814             this.autoSizeColumn(i, true);
53815             this.layout();
53816         }
53817     },
53818
53819     render : function(){
53820
53821         var cm = this.cm;
53822         var colCount = cm.getColumnCount();
53823
53824         if(this.grid.monitorWindowResize === true){
53825             Roo.EventManager.onWindowResize(this.onWindowResize, this, true);
53826         }
53827         var header = this.renderHeaders();
53828         var body = this.templates.body.apply({rows:""});
53829         var html = this.templates.master.apply({
53830             lockedBody: body,
53831             body: body,
53832             lockedHeader: header[0],
53833             header: header[1]
53834         });
53835
53836         //this.updateColumns();
53837
53838         this.grid.getGridEl().dom.innerHTML = html;
53839
53840         this.initElements();
53841         
53842         // a kludge to fix the random scolling effect in webkit
53843         this.el.on("scroll", function() {
53844             this.el.dom.scrollTop=0; // hopefully not recursive..
53845         },this);
53846
53847         this.scroller.on("scroll", this.handleScroll, this);
53848         this.lockedBody.on("mousewheel", this.handleWheel, this);
53849         this.mainBody.on("mousewheel", this.handleWheel, this);
53850
53851         this.mainHd.on("mouseover", this.handleHdOver, this);
53852         this.mainHd.on("mouseout", this.handleHdOut, this);
53853         this.mainHd.on("dblclick", this.handleSplitDblClick, this,
53854                 {delegate: "."+this.splitClass});
53855
53856         this.lockedHd.on("mouseover", this.handleHdOver, this);
53857         this.lockedHd.on("mouseout", this.handleHdOut, this);
53858         this.lockedHd.on("dblclick", this.handleSplitDblClick, this,
53859                 {delegate: "."+this.splitClass});
53860
53861         if(this.grid.enableColumnResize !== false && Roo.grid.SplitDragZone){
53862             new Roo.grid.SplitDragZone(this.grid, this.lockedHd.dom, this.mainHd.dom);
53863         }
53864
53865         this.updateSplitters();
53866
53867         if(this.grid.enableColumnMove && Roo.grid.HeaderDragZone){
53868             new Roo.grid.HeaderDragZone(this.grid, this.lockedHd.dom, this.mainHd.dom);
53869             new Roo.grid.HeaderDropZone(this.grid, this.lockedHd.dom, this.mainHd.dom);
53870         }
53871
53872         if(this.grid.enableCtxMenu !== false && Roo.menu.Menu){
53873             this.hmenu = new Roo.menu.Menu({id: this.grid.id + "-hctx"});
53874             this.hmenu.add(
53875                 {id:"asc", text: this.sortAscText, cls: "xg-hmenu-sort-asc"},
53876                 {id:"desc", text: this.sortDescText, cls: "xg-hmenu-sort-desc"}
53877             );
53878             if(this.grid.enableColLock !== false){
53879                 this.hmenu.add('-',
53880                     {id:"lock", text: this.lockText, cls: "xg-hmenu-lock"},
53881                     {id:"unlock", text: this.unlockText, cls: "xg-hmenu-unlock"}
53882                 );
53883             }
53884             if (Roo.isTouch) {
53885                  this.hmenu.add('-',
53886                     {id:"wider", text: this.columnsWiderText},
53887                     {id:"narrow", text: this.columnsNarrowText }
53888                 );
53889                 
53890                  
53891             }
53892             
53893             if(this.grid.enableColumnHide !== false){
53894
53895                 this.colMenu = new Roo.menu.Menu({id:this.grid.id + "-hcols-menu"});
53896                 this.colMenu.on("beforeshow", this.beforeColMenuShow, this);
53897                 this.colMenu.on("itemclick", this.handleHdMenuClick, this);
53898
53899                 this.hmenu.add('-',
53900                     {id:"columns", text: this.columnsText, menu: this.colMenu}
53901                 );
53902             }
53903             this.hmenu.on("itemclick", this.handleHdMenuClick, this);
53904
53905             this.grid.on("headercontextmenu", this.handleHdCtx, this);
53906         }
53907
53908         if((this.grid.enableDragDrop || this.grid.enableDrag) && Roo.grid.GridDragZone){
53909             this.dd = new Roo.grid.GridDragZone(this.grid, {
53910                 ddGroup : this.grid.ddGroup || 'GridDD'
53911             });
53912             
53913         }
53914
53915         /*
53916         for(var i = 0; i < colCount; i++){
53917             if(cm.isHidden(i)){
53918                 this.hideColumn(i);
53919             }
53920             if(cm.config[i].align){
53921                 this.css.updateRule(this.colSelector + i, "textAlign", cm.config[i].align);
53922                 this.css.updateRule(this.hdSelector + i, "textAlign", cm.config[i].align);
53923             }
53924         }*/
53925         
53926         this.updateHeaderSortState();
53927
53928         this.beforeInitialResize();
53929         this.layout(true);
53930
53931         // two part rendering gives faster view to the user
53932         this.renderPhase2.defer(1, this);
53933     },
53934
53935     renderPhase2 : function(){
53936         // render the rows now
53937         this.refresh();
53938         if(this.grid.autoSizeColumns){
53939             this.autoSizeColumns();
53940         }
53941     },
53942
53943     beforeInitialResize : function(){
53944
53945     },
53946
53947     onColumnSplitterMoved : function(i, w){
53948         this.userResized = true;
53949         var cm = this.grid.colModel;
53950         cm.setColumnWidth(i, w, true);
53951         var cid = cm.getColumnId(i);
53952         this.css.updateRule(this.colSelector + this.idToCssName(cid), "width", (w-this.borderWidth) + "px");
53953         this.css.updateRule(this.hdSelector + this.idToCssName(cid), "width", (w-this.borderWidth) + "px");
53954         this.updateSplitters();
53955         this.layout();
53956         this.grid.fireEvent("columnresize", i, w);
53957     },
53958
53959     syncRowHeights : function(startIndex, endIndex){
53960         if(this.grid.enableRowHeightSync === true && this.cm.getLockedCount() > 0){
53961             startIndex = startIndex || 0;
53962             var mrows = this.getBodyTable().rows;
53963             var lrows = this.getLockedTable().rows;
53964             var len = mrows.length-1;
53965             endIndex = Math.min(endIndex || len, len);
53966             for(var i = startIndex; i <= endIndex; i++){
53967                 var m = mrows[i], l = lrows[i];
53968                 var h = Math.max(m.offsetHeight, l.offsetHeight);
53969                 m.style.height = l.style.height = h + "px";
53970             }
53971         }
53972     },
53973
53974     layout : function(initialRender, is2ndPass){
53975         var g = this.grid;
53976         var auto = g.autoHeight;
53977         var scrollOffset = 16;
53978         var c = g.getGridEl(), cm = this.cm,
53979                 expandCol = g.autoExpandColumn,
53980                 gv = this;
53981         //c.beginMeasure();
53982
53983         if(!c.dom.offsetWidth){ // display:none?
53984             if(initialRender){
53985                 this.lockedWrap.show();
53986                 this.mainWrap.show();
53987             }
53988             return;
53989         }
53990
53991         var hasLock = this.cm.isLocked(0);
53992
53993         var tbh = this.headerPanel.getHeight();
53994         var bbh = this.footerPanel.getHeight();
53995
53996         if(auto){
53997             var ch = this.getBodyTable().offsetHeight + tbh + bbh + this.mainHd.getHeight();
53998             var newHeight = ch + c.getBorderWidth("tb");
53999             if(g.maxHeight){
54000                 newHeight = Math.min(g.maxHeight, newHeight);
54001             }
54002             c.setHeight(newHeight);
54003         }
54004
54005         if(g.autoWidth){
54006             c.setWidth(cm.getTotalWidth()+c.getBorderWidth('lr'));
54007         }
54008
54009         var s = this.scroller;
54010
54011         var csize = c.getSize(true);
54012
54013         this.el.setSize(csize.width, csize.height);
54014
54015         this.headerPanel.setWidth(csize.width);
54016         this.footerPanel.setWidth(csize.width);
54017
54018         var hdHeight = this.mainHd.getHeight();
54019         var vw = csize.width;
54020         var vh = csize.height - (tbh + bbh);
54021
54022         s.setSize(vw, vh);
54023
54024         var bt = this.getBodyTable();
54025         var ltWidth = hasLock ?
54026                       Math.max(this.getLockedTable().offsetWidth, this.lockedHd.dom.firstChild.offsetWidth) : 0;
54027
54028         var scrollHeight = bt.offsetHeight;
54029         var scrollWidth = ltWidth + bt.offsetWidth;
54030         var vscroll = false, hscroll = false;
54031
54032         this.scrollSizer.setSize(scrollWidth, scrollHeight+hdHeight);
54033
54034         var lw = this.lockedWrap, mw = this.mainWrap;
54035         var lb = this.lockedBody, mb = this.mainBody;
54036
54037         setTimeout(function(){
54038             var t = s.dom.offsetTop;
54039             var w = s.dom.clientWidth,
54040                 h = s.dom.clientHeight;
54041
54042             lw.setTop(t);
54043             lw.setSize(ltWidth, h);
54044
54045             mw.setLeftTop(ltWidth, t);
54046             mw.setSize(w-ltWidth, h);
54047
54048             lb.setHeight(h-hdHeight);
54049             mb.setHeight(h-hdHeight);
54050
54051             if(is2ndPass !== true && !gv.userResized && expandCol){
54052                 // high speed resize without full column calculation
54053                 
54054                 var ci = cm.getIndexById(expandCol);
54055                 if (ci < 0) {
54056                     ci = cm.findColumnIndex(expandCol);
54057                 }
54058                 ci = Math.max(0, ci); // make sure it's got at least the first col.
54059                 var expandId = cm.getColumnId(ci);
54060                 var  tw = cm.getTotalWidth(false);
54061                 var currentWidth = cm.getColumnWidth(ci);
54062                 var cw = Math.min(Math.max(((w-tw)+currentWidth-2)-/*scrollbar*/(w <= s.dom.offsetWidth ? 0 : 18), g.autoExpandMin), g.autoExpandMax);
54063                 if(currentWidth != cw){
54064                     cm.setColumnWidth(ci, cw, true);
54065                     gv.css.updateRule(gv.colSelector+gv.idToCssName(expandId), "width", (cw - gv.borderWidth) + "px");
54066                     gv.css.updateRule(gv.hdSelector+gv.idToCssName(expandId), "width", (cw - gv.borderWidth) + "px");
54067                     gv.updateSplitters();
54068                     gv.layout(false, true);
54069                 }
54070             }
54071
54072             if(initialRender){
54073                 lw.show();
54074                 mw.show();
54075             }
54076             //c.endMeasure();
54077         }, 10);
54078     },
54079
54080     onWindowResize : function(){
54081         if(!this.grid.monitorWindowResize || this.grid.autoHeight){
54082             return;
54083         }
54084         this.layout();
54085     },
54086
54087     appendFooter : function(parentEl){
54088         return null;
54089     },
54090
54091     sortAscText : "Sort Ascending",
54092     sortDescText : "Sort Descending",
54093     lockText : "Lock Column",
54094     unlockText : "Unlock Column",
54095     columnsText : "Columns",
54096  
54097     columnsWiderText : "Wider",
54098     columnsNarrowText : "Thinner"
54099 });
54100
54101
54102 Roo.grid.GridView.ColumnDragZone = function(grid, hd){
54103     Roo.grid.GridView.ColumnDragZone.superclass.constructor.call(this, grid, hd, null);
54104     this.proxy.el.addClass('x-grid3-col-dd');
54105 };
54106
54107 Roo.extend(Roo.grid.GridView.ColumnDragZone, Roo.grid.HeaderDragZone, {
54108     handleMouseDown : function(e){
54109
54110     },
54111
54112     callHandleMouseDown : function(e){
54113         Roo.grid.GridView.ColumnDragZone.superclass.handleMouseDown.call(this, e);
54114     }
54115 });
54116 /*
54117  * Based on:
54118  * Ext JS Library 1.1.1
54119  * Copyright(c) 2006-2007, Ext JS, LLC.
54120  *
54121  * Originally Released Under LGPL - original licence link has changed is not relivant.
54122  *
54123  * Fork - LGPL
54124  * <script type="text/javascript">
54125  */
54126  
54127 // private
54128 // This is a support class used internally by the Grid components
54129 Roo.grid.SplitDragZone = function(grid, hd, hd2){
54130     this.grid = grid;
54131     this.view = grid.getView();
54132     this.proxy = this.view.resizeProxy;
54133     Roo.grid.SplitDragZone.superclass.constructor.call(this, hd,
54134         "gridSplitters" + this.grid.getGridEl().id, {
54135         dragElId : Roo.id(this.proxy.dom), resizeFrame:false
54136     });
54137     this.setHandleElId(Roo.id(hd));
54138     this.setOuterHandleElId(Roo.id(hd2));
54139     this.scroll = false;
54140 };
54141 Roo.extend(Roo.grid.SplitDragZone, Roo.dd.DDProxy, {
54142     fly: Roo.Element.fly,
54143
54144     b4StartDrag : function(x, y){
54145         this.view.headersDisabled = true;
54146         this.proxy.setHeight(this.view.mainWrap.getHeight());
54147         var w = this.cm.getColumnWidth(this.cellIndex);
54148         var minw = Math.max(w-this.grid.minColumnWidth, 0);
54149         this.resetConstraints();
54150         this.setXConstraint(minw, 1000);
54151         this.setYConstraint(0, 0);
54152         this.minX = x - minw;
54153         this.maxX = x + 1000;
54154         this.startPos = x;
54155         Roo.dd.DDProxy.prototype.b4StartDrag.call(this, x, y);
54156     },
54157
54158
54159     handleMouseDown : function(e){
54160         ev = Roo.EventObject.setEvent(e);
54161         var t = this.fly(ev.getTarget());
54162         if(t.hasClass("x-grid-split")){
54163             this.cellIndex = this.view.getCellIndex(t.dom);
54164             this.split = t.dom;
54165             this.cm = this.grid.colModel;
54166             if(this.cm.isResizable(this.cellIndex) && !this.cm.isFixed(this.cellIndex)){
54167                 Roo.grid.SplitDragZone.superclass.handleMouseDown.apply(this, arguments);
54168             }
54169         }
54170     },
54171
54172     endDrag : function(e){
54173         this.view.headersDisabled = false;
54174         var endX = Math.max(this.minX, Roo.lib.Event.getPageX(e));
54175         var diff = endX - this.startPos;
54176         this.view.onColumnSplitterMoved(this.cellIndex, this.cm.getColumnWidth(this.cellIndex)+diff);
54177     },
54178
54179     autoOffset : function(){
54180         this.setDelta(0,0);
54181     }
54182 });/*
54183  * Based on:
54184  * Ext JS Library 1.1.1
54185  * Copyright(c) 2006-2007, Ext JS, LLC.
54186  *
54187  * Originally Released Under LGPL - original licence link has changed is not relivant.
54188  *
54189  * Fork - LGPL
54190  * <script type="text/javascript">
54191  */
54192  
54193 // private
54194 // This is a support class used internally by the Grid components
54195 Roo.grid.GridDragZone = function(grid, config){
54196     this.view = grid.getView();
54197     Roo.grid.GridDragZone.superclass.constructor.call(this, this.view.mainBody.dom, config);
54198     if(this.view.lockedBody){
54199         this.setHandleElId(Roo.id(this.view.mainBody.dom));
54200         this.setOuterHandleElId(Roo.id(this.view.lockedBody.dom));
54201     }
54202     this.scroll = false;
54203     this.grid = grid;
54204     this.ddel = document.createElement('div');
54205     this.ddel.className = 'x-grid-dd-wrap';
54206 };
54207
54208 Roo.extend(Roo.grid.GridDragZone, Roo.dd.DragZone, {
54209     ddGroup : "GridDD",
54210
54211     getDragData : function(e){
54212         var t = Roo.lib.Event.getTarget(e);
54213         var rowIndex = this.view.findRowIndex(t);
54214         var sm = this.grid.selModel;
54215             
54216         //Roo.log(rowIndex);
54217         
54218         if (sm.getSelectedCell) {
54219             // cell selection..
54220             if (!sm.getSelectedCell()) {
54221                 return false;
54222             }
54223             if (rowIndex != sm.getSelectedCell()[0]) {
54224                 return false;
54225             }
54226         
54227         }
54228         
54229         if(rowIndex !== false){
54230             
54231             // if editorgrid.. 
54232             
54233             
54234             //Roo.log([ sm.getSelectedCell() ? sm.getSelectedCell()[0] : 'NO' , rowIndex ]);
54235                
54236             //if(!sm.isSelected(rowIndex) || e.hasModifier()){
54237               //  
54238             //}
54239             if (e.hasModifier()){
54240                 sm.handleMouseDown(e, t); // non modifier buttons are handled by row select.
54241             }
54242             
54243             Roo.log("getDragData");
54244             
54245             return {
54246                 grid: this.grid,
54247                 ddel: this.ddel,
54248                 rowIndex: rowIndex,
54249                 selections:sm.getSelections ? sm.getSelections() : (
54250                     sm.getSelectedCell() ? [ this.grid.ds.getAt(sm.getSelectedCell()[0]) ] : []
54251                 )
54252             };
54253         }
54254         return false;
54255     },
54256
54257     onInitDrag : function(e){
54258         var data = this.dragData;
54259         this.ddel.innerHTML = this.grid.getDragDropText();
54260         this.proxy.update(this.ddel);
54261         // fire start drag?
54262     },
54263
54264     afterRepair : function(){
54265         this.dragging = false;
54266     },
54267
54268     getRepairXY : function(e, data){
54269         return false;
54270     },
54271
54272     onEndDrag : function(data, e){
54273         // fire end drag?
54274     },
54275
54276     onValidDrop : function(dd, e, id){
54277         // fire drag drop?
54278         this.hideProxy();
54279     },
54280
54281     beforeInvalidDrop : function(e, id){
54282
54283     }
54284 });/*
54285  * Based on:
54286  * Ext JS Library 1.1.1
54287  * Copyright(c) 2006-2007, Ext JS, LLC.
54288  *
54289  * Originally Released Under LGPL - original licence link has changed is not relivant.
54290  *
54291  * Fork - LGPL
54292  * <script type="text/javascript">
54293  */
54294  
54295
54296 /**
54297  * @class Roo.grid.ColumnModel
54298  * @extends Roo.util.Observable
54299  * This is the default implementation of a ColumnModel used by the Grid. It defines
54300  * the columns in the grid.
54301  * <br>Usage:<br>
54302  <pre><code>
54303  var colModel = new Roo.grid.ColumnModel([
54304         {header: "Ticker", width: 60, sortable: true, locked: true},
54305         {header: "Company Name", width: 150, sortable: true},
54306         {header: "Market Cap.", width: 100, sortable: true},
54307         {header: "$ Sales", width: 100, sortable: true, renderer: money},
54308         {header: "Employees", width: 100, sortable: true, resizable: false}
54309  ]);
54310  </code></pre>
54311  * <p>
54312  
54313  * The config options listed for this class are options which may appear in each
54314  * individual column definition.
54315  * <br/>RooJS Fix - column id's are not sequential but use Roo.id() - fixes bugs with layouts.
54316  * @constructor
54317  * @param {Object} config An Array of column config objects. See this class's
54318  * config objects for details.
54319 */
54320 Roo.grid.ColumnModel = function(config){
54321         /**
54322      * The config passed into the constructor
54323      */
54324     this.config = config;
54325     this.lookup = {};
54326
54327     // if no id, create one
54328     // if the column does not have a dataIndex mapping,
54329     // map it to the order it is in the config
54330     for(var i = 0, len = config.length; i < len; i++){
54331         var c = config[i];
54332         if(typeof c.dataIndex == "undefined"){
54333             c.dataIndex = i;
54334         }
54335         if(typeof c.renderer == "string"){
54336             c.renderer = Roo.util.Format[c.renderer];
54337         }
54338         if(typeof c.id == "undefined"){
54339             c.id = Roo.id();
54340         }
54341         if(c.editor && c.editor.xtype){
54342             c.editor  = Roo.factory(c.editor, Roo.grid);
54343         }
54344         if(c.editor && c.editor.isFormField){
54345             c.editor = new Roo.grid.GridEditor(c.editor);
54346         }
54347         this.lookup[c.id] = c;
54348     }
54349
54350     /**
54351      * The width of columns which have no width specified (defaults to 100)
54352      * @type Number
54353      */
54354     this.defaultWidth = 100;
54355
54356     /**
54357      * Default sortable of columns which have no sortable specified (defaults to false)
54358      * @type Boolean
54359      */
54360     this.defaultSortable = false;
54361
54362     this.addEvents({
54363         /**
54364              * @event widthchange
54365              * Fires when the width of a column changes.
54366              * @param {ColumnModel} this
54367              * @param {Number} columnIndex The column index
54368              * @param {Number} newWidth The new width
54369              */
54370             "widthchange": true,
54371         /**
54372              * @event headerchange
54373              * Fires when the text of a header changes.
54374              * @param {ColumnModel} this
54375              * @param {Number} columnIndex The column index
54376              * @param {Number} newText The new header text
54377              */
54378             "headerchange": true,
54379         /**
54380              * @event hiddenchange
54381              * Fires when a column is hidden or "unhidden".
54382              * @param {ColumnModel} this
54383              * @param {Number} columnIndex The column index
54384              * @param {Boolean} hidden true if hidden, false otherwise
54385              */
54386             "hiddenchange": true,
54387             /**
54388          * @event columnmoved
54389          * Fires when a column is moved.
54390          * @param {ColumnModel} this
54391          * @param {Number} oldIndex
54392          * @param {Number} newIndex
54393          */
54394         "columnmoved" : true,
54395         /**
54396          * @event columlockchange
54397          * Fires when a column's locked state is changed
54398          * @param {ColumnModel} this
54399          * @param {Number} colIndex
54400          * @param {Boolean} locked true if locked
54401          */
54402         "columnlockchange" : true
54403     });
54404     Roo.grid.ColumnModel.superclass.constructor.call(this);
54405 };
54406 Roo.extend(Roo.grid.ColumnModel, Roo.util.Observable, {
54407     /**
54408      * @cfg {String} header The header text to display in the Grid view.
54409      */
54410     /**
54411      * @cfg {String} dataIndex (Optional) The name of the field in the grid's {@link Roo.data.Store}'s
54412      * {@link Roo.data.Record} definition from which to draw the column's value. If not
54413      * specified, the column's index is used as an index into the Record's data Array.
54414      */
54415     /**
54416      * @cfg {Number} width (Optional) The initial width in pixels of the column. Using this
54417      * instead of {@link Roo.grid.Grid#autoSizeColumns} is more efficient.
54418      */
54419     /**
54420      * @cfg {Boolean} sortable (Optional) True if sorting is to be allowed on this column.
54421      * Defaults to the value of the {@link #defaultSortable} property.
54422      * Whether local/remote sorting is used is specified in {@link Roo.data.Store#remoteSort}.
54423      */
54424     /**
54425      * @cfg {Boolean} locked (Optional) True to lock the column in place while scrolling the Grid.  Defaults to false.
54426      */
54427     /**
54428      * @cfg {Boolean} fixed (Optional) True if the column width cannot be changed.  Defaults to false.
54429      */
54430     /**
54431      * @cfg {Boolean} resizable (Optional) False to disable column resizing. Defaults to true.
54432      */
54433     /**
54434      * @cfg {Boolean} hidden (Optional) True to hide the column. Defaults to false.
54435      */
54436     /**
54437      * @cfg {Function} renderer (Optional) A function used to generate HTML markup for a cell
54438      * given the cell's data value. See {@link #setRenderer}. If not specified, the
54439      * default renderer uses the raw data value.
54440      */
54441        /**
54442      * @cfg {Roo.grid.GridEditor} editor (Optional) For grid editors - returns the grid editor 
54443      */
54444     /**
54445      * @cfg {String} align (Optional) Set the CSS text-align property of the column.  Defaults to undefined.
54446      */
54447
54448     /**
54449      * Returns the id of the column at the specified index.
54450      * @param {Number} index The column index
54451      * @return {String} the id
54452      */
54453     getColumnId : function(index){
54454         return this.config[index].id;
54455     },
54456
54457     /**
54458      * Returns the column for a specified id.
54459      * @param {String} id The column id
54460      * @return {Object} the column
54461      */
54462     getColumnById : function(id){
54463         return this.lookup[id];
54464     },
54465
54466     
54467     /**
54468      * Returns the column for a specified dataIndex.
54469      * @param {String} dataIndex The column dataIndex
54470      * @return {Object|Boolean} the column or false if not found
54471      */
54472     getColumnByDataIndex: function(dataIndex){
54473         var index = this.findColumnIndex(dataIndex);
54474         return index > -1 ? this.config[index] : false;
54475     },
54476     
54477     /**
54478      * Returns the index for a specified column id.
54479      * @param {String} id The column id
54480      * @return {Number} the index, or -1 if not found
54481      */
54482     getIndexById : function(id){
54483         for(var i = 0, len = this.config.length; i < len; i++){
54484             if(this.config[i].id == id){
54485                 return i;
54486             }
54487         }
54488         return -1;
54489     },
54490     
54491     /**
54492      * Returns the index for a specified column dataIndex.
54493      * @param {String} dataIndex The column dataIndex
54494      * @return {Number} the index, or -1 if not found
54495      */
54496     
54497     findColumnIndex : function(dataIndex){
54498         for(var i = 0, len = this.config.length; i < len; i++){
54499             if(this.config[i].dataIndex == dataIndex){
54500                 return i;
54501             }
54502         }
54503         return -1;
54504     },
54505     
54506     
54507     moveColumn : function(oldIndex, newIndex){
54508         var c = this.config[oldIndex];
54509         this.config.splice(oldIndex, 1);
54510         this.config.splice(newIndex, 0, c);
54511         this.dataMap = null;
54512         this.fireEvent("columnmoved", this, oldIndex, newIndex);
54513     },
54514
54515     isLocked : function(colIndex){
54516         return this.config[colIndex].locked === true;
54517     },
54518
54519     setLocked : function(colIndex, value, suppressEvent){
54520         if(this.isLocked(colIndex) == value){
54521             return;
54522         }
54523         this.config[colIndex].locked = value;
54524         if(!suppressEvent){
54525             this.fireEvent("columnlockchange", this, colIndex, value);
54526         }
54527     },
54528
54529     getTotalLockedWidth : function(){
54530         var totalWidth = 0;
54531         for(var i = 0; i < this.config.length; i++){
54532             if(this.isLocked(i) && !this.isHidden(i)){
54533                 this.totalWidth += this.getColumnWidth(i);
54534             }
54535         }
54536         return totalWidth;
54537     },
54538
54539     getLockedCount : function(){
54540         for(var i = 0, len = this.config.length; i < len; i++){
54541             if(!this.isLocked(i)){
54542                 return i;
54543             }
54544         }
54545     },
54546
54547     /**
54548      * Returns the number of columns.
54549      * @return {Number}
54550      */
54551     getColumnCount : function(visibleOnly){
54552         if(visibleOnly === true){
54553             var c = 0;
54554             for(var i = 0, len = this.config.length; i < len; i++){
54555                 if(!this.isHidden(i)){
54556                     c++;
54557                 }
54558             }
54559             return c;
54560         }
54561         return this.config.length;
54562     },
54563
54564     /**
54565      * Returns the column configs that return true by the passed function that is called with (columnConfig, index)
54566      * @param {Function} fn
54567      * @param {Object} scope (optional)
54568      * @return {Array} result
54569      */
54570     getColumnsBy : function(fn, scope){
54571         var r = [];
54572         for(var i = 0, len = this.config.length; i < len; i++){
54573             var c = this.config[i];
54574             if(fn.call(scope||this, c, i) === true){
54575                 r[r.length] = c;
54576             }
54577         }
54578         return r;
54579     },
54580
54581     /**
54582      * Returns true if the specified column is sortable.
54583      * @param {Number} col The column index
54584      * @return {Boolean}
54585      */
54586     isSortable : function(col){
54587         if(typeof this.config[col].sortable == "undefined"){
54588             return this.defaultSortable;
54589         }
54590         return this.config[col].sortable;
54591     },
54592
54593     /**
54594      * Returns the rendering (formatting) function defined for the column.
54595      * @param {Number} col The column index.
54596      * @return {Function} The function used to render the cell. See {@link #setRenderer}.
54597      */
54598     getRenderer : function(col){
54599         if(!this.config[col].renderer){
54600             return Roo.grid.ColumnModel.defaultRenderer;
54601         }
54602         return this.config[col].renderer;
54603     },
54604
54605     /**
54606      * Sets the rendering (formatting) function for a column.
54607      * @param {Number} col The column index
54608      * @param {Function} fn The function to use to process the cell's raw data
54609      * to return HTML markup for the grid view. The render function is called with
54610      * the following parameters:<ul>
54611      * <li>Data value.</li>
54612      * <li>Cell metadata. An object in which you may set the following attributes:<ul>
54613      * <li>css A CSS style string to apply to the table cell.</li>
54614      * <li>attr An HTML attribute definition string to apply to the data container element <i>within</i> the table cell.</li></ul>
54615      * <li>The {@link Roo.data.Record} from which the data was extracted.</li>
54616      * <li>Row index</li>
54617      * <li>Column index</li>
54618      * <li>The {@link Roo.data.Store} object from which the Record was extracted</li></ul>
54619      */
54620     setRenderer : function(col, fn){
54621         this.config[col].renderer = fn;
54622     },
54623
54624     /**
54625      * Returns the width for the specified column.
54626      * @param {Number} col The column index
54627      * @return {Number}
54628      */
54629     getColumnWidth : function(col){
54630         return this.config[col].width * 1 || this.defaultWidth;
54631     },
54632
54633     /**
54634      * Sets the width for a column.
54635      * @param {Number} col The column index
54636      * @param {Number} width The new width
54637      */
54638     setColumnWidth : function(col, width, suppressEvent){
54639         this.config[col].width = width;
54640         this.totalWidth = null;
54641         if(!suppressEvent){
54642              this.fireEvent("widthchange", this, col, width);
54643         }
54644     },
54645
54646     /**
54647      * Returns the total width of all columns.
54648      * @param {Boolean} includeHidden True to include hidden column widths
54649      * @return {Number}
54650      */
54651     getTotalWidth : function(includeHidden){
54652         if(!this.totalWidth){
54653             this.totalWidth = 0;
54654             for(var i = 0, len = this.config.length; i < len; i++){
54655                 if(includeHidden || !this.isHidden(i)){
54656                     this.totalWidth += this.getColumnWidth(i);
54657                 }
54658             }
54659         }
54660         return this.totalWidth;
54661     },
54662
54663     /**
54664      * Returns the header for the specified column.
54665      * @param {Number} col The column index
54666      * @return {String}
54667      */
54668     getColumnHeader : function(col){
54669         return this.config[col].header;
54670     },
54671
54672     /**
54673      * Sets the header for a column.
54674      * @param {Number} col The column index
54675      * @param {String} header The new header
54676      */
54677     setColumnHeader : function(col, header){
54678         this.config[col].header = header;
54679         this.fireEvent("headerchange", this, col, header);
54680     },
54681
54682     /**
54683      * Returns the tooltip for the specified column.
54684      * @param {Number} col The column index
54685      * @return {String}
54686      */
54687     getColumnTooltip : function(col){
54688             return this.config[col].tooltip;
54689     },
54690     /**
54691      * Sets the tooltip for a column.
54692      * @param {Number} col The column index
54693      * @param {String} tooltip The new tooltip
54694      */
54695     setColumnTooltip : function(col, tooltip){
54696             this.config[col].tooltip = tooltip;
54697     },
54698
54699     /**
54700      * Returns the dataIndex for the specified column.
54701      * @param {Number} col The column index
54702      * @return {Number}
54703      */
54704     getDataIndex : function(col){
54705         return this.config[col].dataIndex;
54706     },
54707
54708     /**
54709      * Sets the dataIndex for a column.
54710      * @param {Number} col The column index
54711      * @param {Number} dataIndex The new dataIndex
54712      */
54713     setDataIndex : function(col, dataIndex){
54714         this.config[col].dataIndex = dataIndex;
54715     },
54716
54717     
54718     
54719     /**
54720      * Returns true if the cell is editable.
54721      * @param {Number} colIndex The column index
54722      * @param {Number} rowIndex The row index
54723      * @return {Boolean}
54724      */
54725     isCellEditable : function(colIndex, rowIndex){
54726         return (this.config[colIndex].editable || (typeof this.config[colIndex].editable == "undefined" && this.config[colIndex].editor)) ? true : false;
54727     },
54728
54729     /**
54730      * Returns the editor defined for the cell/column.
54731      * return false or null to disable editing.
54732      * @param {Number} colIndex The column index
54733      * @param {Number} rowIndex The row index
54734      * @return {Object}
54735      */
54736     getCellEditor : function(colIndex, rowIndex){
54737         return this.config[colIndex].editor;
54738     },
54739
54740     /**
54741      * Sets if a column is editable.
54742      * @param {Number} col The column index
54743      * @param {Boolean} editable True if the column is editable
54744      */
54745     setEditable : function(col, editable){
54746         this.config[col].editable = editable;
54747     },
54748
54749
54750     /**
54751      * Returns true if the column is hidden.
54752      * @param {Number} colIndex The column index
54753      * @return {Boolean}
54754      */
54755     isHidden : function(colIndex){
54756         return this.config[colIndex].hidden;
54757     },
54758
54759
54760     /**
54761      * Returns true if the column width cannot be changed
54762      */
54763     isFixed : function(colIndex){
54764         return this.config[colIndex].fixed;
54765     },
54766
54767     /**
54768      * Returns true if the column can be resized
54769      * @return {Boolean}
54770      */
54771     isResizable : function(colIndex){
54772         return colIndex >= 0 && this.config[colIndex].resizable !== false && this.config[colIndex].fixed !== true;
54773     },
54774     /**
54775      * Sets if a column is hidden.
54776      * @param {Number} colIndex The column index
54777      * @param {Boolean} hidden True if the column is hidden
54778      */
54779     setHidden : function(colIndex, hidden){
54780         this.config[colIndex].hidden = hidden;
54781         this.totalWidth = null;
54782         this.fireEvent("hiddenchange", this, colIndex, hidden);
54783     },
54784
54785     /**
54786      * Sets the editor for a column.
54787      * @param {Number} col The column index
54788      * @param {Object} editor The editor object
54789      */
54790     setEditor : function(col, editor){
54791         this.config[col].editor = editor;
54792     }
54793 });
54794
54795 Roo.grid.ColumnModel.defaultRenderer = function(value){
54796         if(typeof value == "string" && value.length < 1){
54797             return "&#160;";
54798         }
54799         return value;
54800 };
54801
54802 // Alias for backwards compatibility
54803 Roo.grid.DefaultColumnModel = Roo.grid.ColumnModel;
54804 /*
54805  * Based on:
54806  * Ext JS Library 1.1.1
54807  * Copyright(c) 2006-2007, Ext JS, LLC.
54808  *
54809  * Originally Released Under LGPL - original licence link has changed is not relivant.
54810  *
54811  * Fork - LGPL
54812  * <script type="text/javascript">
54813  */
54814
54815 /**
54816  * @class Roo.grid.AbstractSelectionModel
54817  * @extends Roo.util.Observable
54818  * Abstract base class for grid SelectionModels.  It provides the interface that should be
54819  * implemented by descendant classes.  This class should not be directly instantiated.
54820  * @constructor
54821  */
54822 Roo.grid.AbstractSelectionModel = function(){
54823     this.locked = false;
54824     Roo.grid.AbstractSelectionModel.superclass.constructor.call(this);
54825 };
54826
54827 Roo.extend(Roo.grid.AbstractSelectionModel, Roo.util.Observable,  {
54828     /** @ignore Called by the grid automatically. Do not call directly. */
54829     init : function(grid){
54830         this.grid = grid;
54831         this.initEvents();
54832     },
54833
54834     /**
54835      * Locks the selections.
54836      */
54837     lock : function(){
54838         this.locked = true;
54839     },
54840
54841     /**
54842      * Unlocks the selections.
54843      */
54844     unlock : function(){
54845         this.locked = false;
54846     },
54847
54848     /**
54849      * Returns true if the selections are locked.
54850      * @return {Boolean}
54851      */
54852     isLocked : function(){
54853         return this.locked;
54854     }
54855 });/*
54856  * Based on:
54857  * Ext JS Library 1.1.1
54858  * Copyright(c) 2006-2007, Ext JS, LLC.
54859  *
54860  * Originally Released Under LGPL - original licence link has changed is not relivant.
54861  *
54862  * Fork - LGPL
54863  * <script type="text/javascript">
54864  */
54865 /**
54866  * @extends Roo.grid.AbstractSelectionModel
54867  * @class Roo.grid.RowSelectionModel
54868  * The default SelectionModel used by {@link Roo.grid.Grid}.
54869  * It supports multiple selections and keyboard selection/navigation. 
54870  * @constructor
54871  * @param {Object} config
54872  */
54873 Roo.grid.RowSelectionModel = function(config){
54874     Roo.apply(this, config);
54875     this.selections = new Roo.util.MixedCollection(false, function(o){
54876         return o.id;
54877     });
54878
54879     this.last = false;
54880     this.lastActive = false;
54881
54882     this.addEvents({
54883         /**
54884              * @event selectionchange
54885              * Fires when the selection changes
54886              * @param {SelectionModel} this
54887              */
54888             "selectionchange" : true,
54889         /**
54890              * @event afterselectionchange
54891              * Fires after the selection changes (eg. by key press or clicking)
54892              * @param {SelectionModel} this
54893              */
54894             "afterselectionchange" : true,
54895         /**
54896              * @event beforerowselect
54897              * Fires when a row is selected being selected, return false to cancel.
54898              * @param {SelectionModel} this
54899              * @param {Number} rowIndex The selected index
54900              * @param {Boolean} keepExisting False if other selections will be cleared
54901              */
54902             "beforerowselect" : true,
54903         /**
54904              * @event rowselect
54905              * Fires when a row is selected.
54906              * @param {SelectionModel} this
54907              * @param {Number} rowIndex The selected index
54908              * @param {Roo.data.Record} r The record
54909              */
54910             "rowselect" : true,
54911         /**
54912              * @event rowdeselect
54913              * Fires when a row is deselected.
54914              * @param {SelectionModel} this
54915              * @param {Number} rowIndex The selected index
54916              */
54917         "rowdeselect" : true
54918     });
54919     Roo.grid.RowSelectionModel.superclass.constructor.call(this);
54920     this.locked = false;
54921 };
54922
54923 Roo.extend(Roo.grid.RowSelectionModel, Roo.grid.AbstractSelectionModel,  {
54924     /**
54925      * @cfg {Boolean} singleSelect
54926      * True to allow selection of only one row at a time (defaults to false)
54927      */
54928     singleSelect : false,
54929
54930     // private
54931     initEvents : function(){
54932
54933         if(!this.grid.enableDragDrop && !this.grid.enableDrag){
54934             this.grid.on("mousedown", this.handleMouseDown, this);
54935         }else{ // allow click to work like normal
54936             this.grid.on("rowclick", this.handleDragableRowClick, this);
54937         }
54938
54939         this.rowNav = new Roo.KeyNav(this.grid.getGridEl(), {
54940             "up" : function(e){
54941                 if(!e.shiftKey){
54942                     this.selectPrevious(e.shiftKey);
54943                 }else if(this.last !== false && this.lastActive !== false){
54944                     var last = this.last;
54945                     this.selectRange(this.last,  this.lastActive-1);
54946                     this.grid.getView().focusRow(this.lastActive);
54947                     if(last !== false){
54948                         this.last = last;
54949                     }
54950                 }else{
54951                     this.selectFirstRow();
54952                 }
54953                 this.fireEvent("afterselectionchange", this);
54954             },
54955             "down" : function(e){
54956                 if(!e.shiftKey){
54957                     this.selectNext(e.shiftKey);
54958                 }else if(this.last !== false && this.lastActive !== false){
54959                     var last = this.last;
54960                     this.selectRange(this.last,  this.lastActive+1);
54961                     this.grid.getView().focusRow(this.lastActive);
54962                     if(last !== false){
54963                         this.last = last;
54964                     }
54965                 }else{
54966                     this.selectFirstRow();
54967                 }
54968                 this.fireEvent("afterselectionchange", this);
54969             },
54970             scope: this
54971         });
54972
54973         var view = this.grid.view;
54974         view.on("refresh", this.onRefresh, this);
54975         view.on("rowupdated", this.onRowUpdated, this);
54976         view.on("rowremoved", this.onRemove, this);
54977     },
54978
54979     // private
54980     onRefresh : function(){
54981         var ds = this.grid.dataSource, i, v = this.grid.view;
54982         var s = this.selections;
54983         s.each(function(r){
54984             if((i = ds.indexOfId(r.id)) != -1){
54985                 v.onRowSelect(i);
54986             }else{
54987                 s.remove(r);
54988             }
54989         });
54990     },
54991
54992     // private
54993     onRemove : function(v, index, r){
54994         this.selections.remove(r);
54995     },
54996
54997     // private
54998     onRowUpdated : function(v, index, r){
54999         if(this.isSelected(r)){
55000             v.onRowSelect(index);
55001         }
55002     },
55003
55004     /**
55005      * Select records.
55006      * @param {Array} records The records to select
55007      * @param {Boolean} keepExisting (optional) True to keep existing selections
55008      */
55009     selectRecords : function(records, keepExisting){
55010         if(!keepExisting){
55011             this.clearSelections();
55012         }
55013         var ds = this.grid.dataSource;
55014         for(var i = 0, len = records.length; i < len; i++){
55015             this.selectRow(ds.indexOf(records[i]), true);
55016         }
55017     },
55018
55019     /**
55020      * Gets the number of selected rows.
55021      * @return {Number}
55022      */
55023     getCount : function(){
55024         return this.selections.length;
55025     },
55026
55027     /**
55028      * Selects the first row in the grid.
55029      */
55030     selectFirstRow : function(){
55031         this.selectRow(0);
55032     },
55033
55034     /**
55035      * Select the last row.
55036      * @param {Boolean} keepExisting (optional) True to keep existing selections
55037      */
55038     selectLastRow : function(keepExisting){
55039         this.selectRow(this.grid.dataSource.getCount() - 1, keepExisting);
55040     },
55041
55042     /**
55043      * Selects the row immediately following the last selected row.
55044      * @param {Boolean} keepExisting (optional) True to keep existing selections
55045      */
55046     selectNext : function(keepExisting){
55047         if(this.last !== false && (this.last+1) < this.grid.dataSource.getCount()){
55048             this.selectRow(this.last+1, keepExisting);
55049             this.grid.getView().focusRow(this.last);
55050         }
55051     },
55052
55053     /**
55054      * Selects the row that precedes the last selected row.
55055      * @param {Boolean} keepExisting (optional) True to keep existing selections
55056      */
55057     selectPrevious : function(keepExisting){
55058         if(this.last){
55059             this.selectRow(this.last-1, keepExisting);
55060             this.grid.getView().focusRow(this.last);
55061         }
55062     },
55063
55064     /**
55065      * Returns the selected records
55066      * @return {Array} Array of selected records
55067      */
55068     getSelections : function(){
55069         return [].concat(this.selections.items);
55070     },
55071
55072     /**
55073      * Returns the first selected record.
55074      * @return {Record}
55075      */
55076     getSelected : function(){
55077         return this.selections.itemAt(0);
55078     },
55079
55080
55081     /**
55082      * Clears all selections.
55083      */
55084     clearSelections : function(fast){
55085         if(this.locked) return;
55086         if(fast !== true){
55087             var ds = this.grid.dataSource;
55088             var s = this.selections;
55089             s.each(function(r){
55090                 this.deselectRow(ds.indexOfId(r.id));
55091             }, this);
55092             s.clear();
55093         }else{
55094             this.selections.clear();
55095         }
55096         this.last = false;
55097     },
55098
55099
55100     /**
55101      * Selects all rows.
55102      */
55103     selectAll : function(){
55104         if(this.locked) return;
55105         this.selections.clear();
55106         for(var i = 0, len = this.grid.dataSource.getCount(); i < len; i++){
55107             this.selectRow(i, true);
55108         }
55109     },
55110
55111     /**
55112      * Returns True if there is a selection.
55113      * @return {Boolean}
55114      */
55115     hasSelection : function(){
55116         return this.selections.length > 0;
55117     },
55118
55119     /**
55120      * Returns True if the specified row is selected.
55121      * @param {Number/Record} record The record or index of the record to check
55122      * @return {Boolean}
55123      */
55124     isSelected : function(index){
55125         var r = typeof index == "number" ? this.grid.dataSource.getAt(index) : index;
55126         return (r && this.selections.key(r.id) ? true : false);
55127     },
55128
55129     /**
55130      * Returns True if the specified record id is selected.
55131      * @param {String} id The id of record to check
55132      * @return {Boolean}
55133      */
55134     isIdSelected : function(id){
55135         return (this.selections.key(id) ? true : false);
55136     },
55137
55138     // private
55139     handleMouseDown : function(e, t){
55140         var view = this.grid.getView(), rowIndex;
55141         if(this.isLocked() || (rowIndex = view.findRowIndex(t)) === false){
55142             return;
55143         };
55144         if(e.shiftKey && this.last !== false){
55145             var last = this.last;
55146             this.selectRange(last, rowIndex, e.ctrlKey);
55147             this.last = last; // reset the last
55148             view.focusRow(rowIndex);
55149         }else{
55150             var isSelected = this.isSelected(rowIndex);
55151             if(e.button !== 0 && isSelected){
55152                 view.focusRow(rowIndex);
55153             }else if(e.ctrlKey && isSelected){
55154                 this.deselectRow(rowIndex);
55155             }else if(!isSelected){
55156                 this.selectRow(rowIndex, e.button === 0 && (e.ctrlKey || e.shiftKey));
55157                 view.focusRow(rowIndex);
55158             }
55159         }
55160         this.fireEvent("afterselectionchange", this);
55161     },
55162     // private
55163     handleDragableRowClick :  function(grid, rowIndex, e) 
55164     {
55165         if(e.button === 0 && !e.shiftKey && !e.ctrlKey) {
55166             this.selectRow(rowIndex, false);
55167             grid.view.focusRow(rowIndex);
55168              this.fireEvent("afterselectionchange", this);
55169         }
55170     },
55171     
55172     /**
55173      * Selects multiple rows.
55174      * @param {Array} rows Array of the indexes of the row to select
55175      * @param {Boolean} keepExisting (optional) True to keep existing selections
55176      */
55177     selectRows : function(rows, keepExisting){
55178         if(!keepExisting){
55179             this.clearSelections();
55180         }
55181         for(var i = 0, len = rows.length; i < len; i++){
55182             this.selectRow(rows[i], true);
55183         }
55184     },
55185
55186     /**
55187      * Selects a range of rows. All rows in between startRow and endRow are also selected.
55188      * @param {Number} startRow The index of the first row in the range
55189      * @param {Number} endRow The index of the last row in the range
55190      * @param {Boolean} keepExisting (optional) True to retain existing selections
55191      */
55192     selectRange : function(startRow, endRow, keepExisting){
55193         if(this.locked) return;
55194         if(!keepExisting){
55195             this.clearSelections();
55196         }
55197         if(startRow <= endRow){
55198             for(var i = startRow; i <= endRow; i++){
55199                 this.selectRow(i, true);
55200             }
55201         }else{
55202             for(var i = startRow; i >= endRow; i--){
55203                 this.selectRow(i, true);
55204             }
55205         }
55206     },
55207
55208     /**
55209      * Deselects a range of rows. All rows in between startRow and endRow are also deselected.
55210      * @param {Number} startRow The index of the first row in the range
55211      * @param {Number} endRow The index of the last row in the range
55212      */
55213     deselectRange : function(startRow, endRow, preventViewNotify){
55214         if(this.locked) return;
55215         for(var i = startRow; i <= endRow; i++){
55216             this.deselectRow(i, preventViewNotify);
55217         }
55218     },
55219
55220     /**
55221      * Selects a row.
55222      * @param {Number} row The index of the row to select
55223      * @param {Boolean} keepExisting (optional) True to keep existing selections
55224      */
55225     selectRow : function(index, keepExisting, preventViewNotify){
55226         if(this.locked || (index < 0 || index >= this.grid.dataSource.getCount())) return;
55227         if(this.fireEvent("beforerowselect", this, index, keepExisting) !== false){
55228             if(!keepExisting || this.singleSelect){
55229                 this.clearSelections();
55230             }
55231             var r = this.grid.dataSource.getAt(index);
55232             this.selections.add(r);
55233             this.last = this.lastActive = index;
55234             if(!preventViewNotify){
55235                 this.grid.getView().onRowSelect(index);
55236             }
55237             this.fireEvent("rowselect", this, index, r);
55238             this.fireEvent("selectionchange", this);
55239         }
55240     },
55241
55242     /**
55243      * Deselects a row.
55244      * @param {Number} row The index of the row to deselect
55245      */
55246     deselectRow : function(index, preventViewNotify){
55247         if(this.locked) return;
55248         if(this.last == index){
55249             this.last = false;
55250         }
55251         if(this.lastActive == index){
55252             this.lastActive = false;
55253         }
55254         var r = this.grid.dataSource.getAt(index);
55255         this.selections.remove(r);
55256         if(!preventViewNotify){
55257             this.grid.getView().onRowDeselect(index);
55258         }
55259         this.fireEvent("rowdeselect", this, index);
55260         this.fireEvent("selectionchange", this);
55261     },
55262
55263     // private
55264     restoreLast : function(){
55265         if(this._last){
55266             this.last = this._last;
55267         }
55268     },
55269
55270     // private
55271     acceptsNav : function(row, col, cm){
55272         return !cm.isHidden(col) && cm.isCellEditable(col, row);
55273     },
55274
55275     // private
55276     onEditorKey : function(field, e){
55277         var k = e.getKey(), newCell, g = this.grid, ed = g.activeEditor;
55278         if(k == e.TAB){
55279             e.stopEvent();
55280             ed.completeEdit();
55281             if(e.shiftKey){
55282                 newCell = g.walkCells(ed.row, ed.col-1, -1, this.acceptsNav, this);
55283             }else{
55284                 newCell = g.walkCells(ed.row, ed.col+1, 1, this.acceptsNav, this);
55285             }
55286         }else if(k == e.ENTER && !e.ctrlKey){
55287             e.stopEvent();
55288             ed.completeEdit();
55289             if(e.shiftKey){
55290                 newCell = g.walkCells(ed.row-1, ed.col, -1, this.acceptsNav, this);
55291             }else{
55292                 newCell = g.walkCells(ed.row+1, ed.col, 1, this.acceptsNav, this);
55293             }
55294         }else if(k == e.ESC){
55295             ed.cancelEdit();
55296         }
55297         if(newCell){
55298             g.startEditing(newCell[0], newCell[1]);
55299         }
55300     }
55301 });/*
55302  * Based on:
55303  * Ext JS Library 1.1.1
55304  * Copyright(c) 2006-2007, Ext JS, LLC.
55305  *
55306  * Originally Released Under LGPL - original licence link has changed is not relivant.
55307  *
55308  * Fork - LGPL
55309  * <script type="text/javascript">
55310  */
55311 /**
55312  * @class Roo.grid.CellSelectionModel
55313  * @extends Roo.grid.AbstractSelectionModel
55314  * This class provides the basic implementation for cell selection in a grid.
55315  * @constructor
55316  * @param {Object} config The object containing the configuration of this model.
55317  * @cfg {Boolean} enter_is_tab Enter behaves the same as tab. (eg. goes to next cell) default: false
55318  */
55319 Roo.grid.CellSelectionModel = function(config){
55320     Roo.apply(this, config);
55321
55322     this.selection = null;
55323
55324     this.addEvents({
55325         /**
55326              * @event beforerowselect
55327              * Fires before a cell is selected.
55328              * @param {SelectionModel} this
55329              * @param {Number} rowIndex The selected row index
55330              * @param {Number} colIndex The selected cell index
55331              */
55332             "beforecellselect" : true,
55333         /**
55334              * @event cellselect
55335              * Fires when a cell is selected.
55336              * @param {SelectionModel} this
55337              * @param {Number} rowIndex The selected row index
55338              * @param {Number} colIndex The selected cell index
55339              */
55340             "cellselect" : true,
55341         /**
55342              * @event selectionchange
55343              * Fires when the active selection changes.
55344              * @param {SelectionModel} this
55345              * @param {Object} selection null for no selection or an object (o) with two properties
55346                 <ul>
55347                 <li>o.record: the record object for the row the selection is in</li>
55348                 <li>o.cell: An array of [rowIndex, columnIndex]</li>
55349                 </ul>
55350              */
55351             "selectionchange" : true,
55352         /**
55353              * @event tabend
55354              * Fires when the tab (or enter) was pressed on the last editable cell
55355              * You can use this to trigger add new row.
55356              * @param {SelectionModel} this
55357              */
55358             "tabend" : true,
55359          /**
55360              * @event beforeeditnext
55361              * Fires before the next editable sell is made active
55362              * You can use this to skip to another cell or fire the tabend
55363              *    if you set cell to false
55364              * @param {Object} eventdata object : { cell : [ row, col ] } 
55365              */
55366             "beforeeditnext" : true
55367     });
55368     Roo.grid.CellSelectionModel.superclass.constructor.call(this);
55369 };
55370
55371 Roo.extend(Roo.grid.CellSelectionModel, Roo.grid.AbstractSelectionModel,  {
55372     
55373     enter_is_tab: false,
55374
55375     /** @ignore */
55376     initEvents : function(){
55377         this.grid.on("mousedown", this.handleMouseDown, this);
55378         this.grid.getGridEl().on(Roo.isIE ? "keydown" : "keypress", this.handleKeyDown, this);
55379         var view = this.grid.view;
55380         view.on("refresh", this.onViewChange, this);
55381         view.on("rowupdated", this.onRowUpdated, this);
55382         view.on("beforerowremoved", this.clearSelections, this);
55383         view.on("beforerowsinserted", this.clearSelections, this);
55384         if(this.grid.isEditor){
55385             this.grid.on("beforeedit", this.beforeEdit,  this);
55386         }
55387     },
55388
55389         //private
55390     beforeEdit : function(e){
55391         this.select(e.row, e.column, false, true, e.record);
55392     },
55393
55394         //private
55395     onRowUpdated : function(v, index, r){
55396         if(this.selection && this.selection.record == r){
55397             v.onCellSelect(index, this.selection.cell[1]);
55398         }
55399     },
55400
55401         //private
55402     onViewChange : function(){
55403         this.clearSelections(true);
55404     },
55405
55406         /**
55407          * Returns the currently selected cell,.
55408          * @return {Array} The selected cell (row, column) or null if none selected.
55409          */
55410     getSelectedCell : function(){
55411         return this.selection ? this.selection.cell : null;
55412     },
55413
55414     /**
55415      * Clears all selections.
55416      * @param {Boolean} true to prevent the gridview from being notified about the change.
55417      */
55418     clearSelections : function(preventNotify){
55419         var s = this.selection;
55420         if(s){
55421             if(preventNotify !== true){
55422                 this.grid.view.onCellDeselect(s.cell[0], s.cell[1]);
55423             }
55424             this.selection = null;
55425             this.fireEvent("selectionchange", this, null);
55426         }
55427     },
55428
55429     /**
55430      * Returns true if there is a selection.
55431      * @return {Boolean}
55432      */
55433     hasSelection : function(){
55434         return this.selection ? true : false;
55435     },
55436
55437     /** @ignore */
55438     handleMouseDown : function(e, t){
55439         var v = this.grid.getView();
55440         if(this.isLocked()){
55441             return;
55442         };
55443         var row = v.findRowIndex(t);
55444         var cell = v.findCellIndex(t);
55445         if(row !== false && cell !== false){
55446             this.select(row, cell);
55447         }
55448     },
55449
55450     /**
55451      * Selects a cell.
55452      * @param {Number} rowIndex
55453      * @param {Number} collIndex
55454      */
55455     select : function(rowIndex, colIndex, preventViewNotify, preventFocus, /*internal*/ r){
55456         if(this.fireEvent("beforecellselect", this, rowIndex, colIndex) !== false){
55457             this.clearSelections();
55458             r = r || this.grid.dataSource.getAt(rowIndex);
55459             this.selection = {
55460                 record : r,
55461                 cell : [rowIndex, colIndex]
55462             };
55463             if(!preventViewNotify){
55464                 var v = this.grid.getView();
55465                 v.onCellSelect(rowIndex, colIndex);
55466                 if(preventFocus !== true){
55467                     v.focusCell(rowIndex, colIndex);
55468                 }
55469             }
55470             this.fireEvent("cellselect", this, rowIndex, colIndex);
55471             this.fireEvent("selectionchange", this, this.selection);
55472         }
55473     },
55474
55475         //private
55476     isSelectable : function(rowIndex, colIndex, cm){
55477         return !cm.isHidden(colIndex);
55478     },
55479
55480     /** @ignore */
55481     handleKeyDown : function(e){
55482         //Roo.log('Cell Sel Model handleKeyDown');
55483         if(!e.isNavKeyPress()){
55484             return;
55485         }
55486         var g = this.grid, s = this.selection;
55487         if(!s){
55488             e.stopEvent();
55489             var cell = g.walkCells(0, 0, 1, this.isSelectable,  this);
55490             if(cell){
55491                 this.select(cell[0], cell[1]);
55492             }
55493             return;
55494         }
55495         var sm = this;
55496         var walk = function(row, col, step){
55497             return g.walkCells(row, col, step, sm.isSelectable,  sm);
55498         };
55499         var k = e.getKey(), r = s.cell[0], c = s.cell[1];
55500         var newCell;
55501
55502       
55503
55504         switch(k){
55505             case e.TAB:
55506                 // handled by onEditorKey
55507                 if (g.isEditor && g.editing) {
55508                     return;
55509                 }
55510                 if(e.shiftKey) {
55511                     newCell = walk(r, c-1, -1);
55512                 } else {
55513                     newCell = walk(r, c+1, 1);
55514                 }
55515                 break;
55516             
55517             case e.DOWN:
55518                newCell = walk(r+1, c, 1);
55519                 break;
55520             
55521             case e.UP:
55522                 newCell = walk(r-1, c, -1);
55523                 break;
55524             
55525             case e.RIGHT:
55526                 newCell = walk(r, c+1, 1);
55527                 break;
55528             
55529             case e.LEFT:
55530                 newCell = walk(r, c-1, -1);
55531                 break;
55532             
55533             case e.ENTER:
55534                 
55535                 if(g.isEditor && !g.editing){
55536                    g.startEditing(r, c);
55537                    e.stopEvent();
55538                    return;
55539                 }
55540                 
55541                 
55542              break;
55543         };
55544         if(newCell){
55545             this.select(newCell[0], newCell[1]);
55546             e.stopEvent();
55547             
55548         }
55549     },
55550
55551     acceptsNav : function(row, col, cm){
55552         return !cm.isHidden(col) && cm.isCellEditable(col, row);
55553     },
55554     /**
55555      * Selects a cell.
55556      * @param {Number} field (not used) - as it's normally used as a listener
55557      * @param {Number} e - event - fake it by using
55558      *
55559      * var e = Roo.EventObjectImpl.prototype;
55560      * e.keyCode = e.TAB
55561      *
55562      * 
55563      */
55564     onEditorKey : function(field, e){
55565         
55566         var k = e.getKey(),
55567             newCell,
55568             g = this.grid,
55569             ed = g.activeEditor,
55570             forward = false;
55571         ///Roo.log('onEditorKey' + k);
55572         
55573         
55574         if (this.enter_is_tab && k == e.ENTER) {
55575             k = e.TAB;
55576         }
55577         
55578         if(k == e.TAB){
55579             if(e.shiftKey){
55580                 newCell = g.walkCells(ed.row, ed.col-1, -1, this.acceptsNav, this);
55581             }else{
55582                 newCell = g.walkCells(ed.row, ed.col+1, 1, this.acceptsNav, this);
55583                 forward = true;
55584             }
55585             
55586             e.stopEvent();
55587             
55588         } else if(k == e.ENTER &&  !e.ctrlKey){
55589             ed.completeEdit();
55590             e.stopEvent();
55591             newCell = g.walkCells(ed.row, ed.col+1, 1, this.acceptsNav, this);
55592         
55593                 } else if(k == e.ESC){
55594             ed.cancelEdit();
55595         }
55596                 
55597         if (newCell) {
55598             var ecall = { cell : newCell, forward : forward };
55599             this.fireEvent('beforeeditnext', ecall );
55600             newCell = ecall.cell;
55601                         forward = ecall.forward;
55602         }
55603                 
55604         if(newCell){
55605             //Roo.log('next cell after edit');
55606             g.startEditing.defer(100, g, [newCell[0], newCell[1]]);
55607         } else if (forward) {
55608             // tabbed past last
55609             this.fireEvent.defer(100, this, ['tabend',this]);
55610         }
55611     }
55612 });/*
55613  * Based on:
55614  * Ext JS Library 1.1.1
55615  * Copyright(c) 2006-2007, Ext JS, LLC.
55616  *
55617  * Originally Released Under LGPL - original licence link has changed is not relivant.
55618  *
55619  * Fork - LGPL
55620  * <script type="text/javascript">
55621  */
55622  
55623 /**
55624  * @class Roo.grid.EditorGrid
55625  * @extends Roo.grid.Grid
55626  * Class for creating and editable grid.
55627  * @param {String/HTMLElement/Roo.Element} container The element into which this grid will be rendered - 
55628  * The container MUST have some type of size defined for the grid to fill. The container will be 
55629  * automatically set to position relative if it isn't already.
55630  * @param {Object} dataSource The data model to bind to
55631  * @param {Object} colModel The column model with info about this grid's columns
55632  */
55633 Roo.grid.EditorGrid = function(container, config){
55634     Roo.grid.EditorGrid.superclass.constructor.call(this, container, config);
55635     this.getGridEl().addClass("xedit-grid");
55636
55637     if(!this.selModel){
55638         this.selModel = new Roo.grid.CellSelectionModel();
55639     }
55640
55641     this.activeEditor = null;
55642
55643         this.addEvents({
55644             /**
55645              * @event beforeedit
55646              * Fires before cell editing is triggered. The edit event object has the following properties <br />
55647              * <ul style="padding:5px;padding-left:16px;">
55648              * <li>grid - This grid</li>
55649              * <li>record - The record being edited</li>
55650              * <li>field - The field name being edited</li>
55651              * <li>value - The value for the field being edited.</li>
55652              * <li>row - The grid row index</li>
55653              * <li>column - The grid column index</li>
55654              * <li>cancel - Set this to true to cancel the edit or return false from your handler.</li>
55655              * </ul>
55656              * @param {Object} e An edit event (see above for description)
55657              */
55658             "beforeedit" : true,
55659             /**
55660              * @event afteredit
55661              * Fires after a cell is edited. <br />
55662              * <ul style="padding:5px;padding-left:16px;">
55663              * <li>grid - This grid</li>
55664              * <li>record - The record being edited</li>
55665              * <li>field - The field name being edited</li>
55666              * <li>value - The value being set</li>
55667              * <li>originalValue - The original value for the field, before the edit.</li>
55668              * <li>row - The grid row index</li>
55669              * <li>column - The grid column index</li>
55670              * </ul>
55671              * @param {Object} e An edit event (see above for description)
55672              */
55673             "afteredit" : true,
55674             /**
55675              * @event validateedit
55676              * Fires after a cell is edited, but before the value is set in the record. 
55677          * You can use this to modify the value being set in the field, Return false
55678              * to cancel the change. The edit event object has the following properties <br />
55679              * <ul style="padding:5px;padding-left:16px;">
55680          * <li>editor - This editor</li>
55681              * <li>grid - This grid</li>
55682              * <li>record - The record being edited</li>
55683              * <li>field - The field name being edited</li>
55684              * <li>value - The value being set</li>
55685              * <li>originalValue - The original value for the field, before the edit.</li>
55686              * <li>row - The grid row index</li>
55687              * <li>column - The grid column index</li>
55688              * <li>cancel - Set this to true to cancel the edit or return false from your handler.</li>
55689              * </ul>
55690              * @param {Object} e An edit event (see above for description)
55691              */
55692             "validateedit" : true
55693         });
55694     this.on("bodyscroll", this.stopEditing,  this);
55695     this.on(this.clicksToEdit == 1 ? "cellclick" : "celldblclick", this.onCellDblClick,  this);
55696 };
55697
55698 Roo.extend(Roo.grid.EditorGrid, Roo.grid.Grid, {
55699     /**
55700      * @cfg {Number} clicksToEdit
55701      * The number of clicks on a cell required to display the cell's editor (defaults to 2)
55702      */
55703     clicksToEdit: 2,
55704
55705     // private
55706     isEditor : true,
55707     // private
55708     trackMouseOver: false, // causes very odd FF errors
55709
55710     onCellDblClick : function(g, row, col){
55711         this.startEditing(row, col);
55712     },
55713
55714     onEditComplete : function(ed, value, startValue){
55715         this.editing = false;
55716         this.activeEditor = null;
55717         ed.un("specialkey", this.selModel.onEditorKey, this.selModel);
55718         var r = ed.record;
55719         var field = this.colModel.getDataIndex(ed.col);
55720         var e = {
55721             grid: this,
55722             record: r,
55723             field: field,
55724             originalValue: startValue,
55725             value: value,
55726             row: ed.row,
55727             column: ed.col,
55728             cancel:false,
55729             editor: ed
55730         };
55731         var cell = Roo.get(this.view.getCell(ed.row,ed.col))
55732         cell.show();
55733           
55734         if(String(value) !== String(startValue)){
55735             
55736             if(this.fireEvent("validateedit", e) !== false && !e.cancel){
55737                 r.set(field, e.value);
55738                 // if we are dealing with a combo box..
55739                 // then we also set the 'name' colum to be the displayField
55740                 if (ed.field.displayField && ed.field.name) {
55741                     r.set(ed.field.name, ed.field.el.dom.value);
55742                 }
55743                 
55744                 delete e.cancel; //?? why!!!
55745                 this.fireEvent("afteredit", e);
55746             }
55747         } else {
55748             this.fireEvent("afteredit", e); // always fire it!
55749         }
55750         this.view.focusCell(ed.row, ed.col);
55751     },
55752
55753     /**
55754      * Starts editing the specified for the specified row/column
55755      * @param {Number} rowIndex
55756      * @param {Number} colIndex
55757      */
55758     startEditing : function(row, col){
55759         this.stopEditing();
55760         if(this.colModel.isCellEditable(col, row)){
55761             this.view.ensureVisible(row, col, true);
55762           
55763             var r = this.dataSource.getAt(row);
55764             var field = this.colModel.getDataIndex(col);
55765             var cell = Roo.get(this.view.getCell(row,col));
55766             var e = {
55767                 grid: this,
55768                 record: r,
55769                 field: field,
55770                 value: r.data[field],
55771                 row: row,
55772                 column: col,
55773                 cancel:false 
55774             };
55775             if(this.fireEvent("beforeedit", e) !== false && !e.cancel){
55776                 this.editing = true;
55777                 var ed = this.colModel.getCellEditor(col, row);
55778                 
55779                 if (!ed) {
55780                     return;
55781                 }
55782                 if(!ed.rendered){
55783                     ed.render(ed.parentEl || document.body);
55784                 }
55785                 ed.field.reset();
55786                
55787                 cell.hide();
55788                 
55789                 (function(){ // complex but required for focus issues in safari, ie and opera
55790                     ed.row = row;
55791                     ed.col = col;
55792                     ed.record = r;
55793                     ed.on("complete",   this.onEditComplete,        this,       {single: true});
55794                     ed.on("specialkey", this.selModel.onEditorKey,  this.selModel);
55795                     this.activeEditor = ed;
55796                     var v = r.data[field];
55797                     ed.startEdit(this.view.getCell(row, col), v);
55798                     // combo's with 'displayField and name set
55799                     if (ed.field.displayField && ed.field.name) {
55800                         ed.field.el.dom.value = r.data[ed.field.name];
55801                     }
55802                     
55803                     
55804                 }).defer(50, this);
55805             }
55806         }
55807     },
55808         
55809     /**
55810      * Stops any active editing
55811      */
55812     stopEditing : function(){
55813         if(this.activeEditor){
55814             this.activeEditor.completeEdit();
55815         }
55816         this.activeEditor = null;
55817     },
55818         
55819          /**
55820      * Called to get grid's drag proxy text, by default returns this.ddText.
55821      * @return {String}
55822      */
55823     getDragDropText : function(){
55824         var count = this.selModel.getSelectedCell() ? 1 : 0;
55825         return String.format(this.ddText, count, count == 1 ? '' : 's');
55826     }
55827         
55828 });/*
55829  * Based on:
55830  * Ext JS Library 1.1.1
55831  * Copyright(c) 2006-2007, Ext JS, LLC.
55832  *
55833  * Originally Released Under LGPL - original licence link has changed is not relivant.
55834  *
55835  * Fork - LGPL
55836  * <script type="text/javascript">
55837  */
55838
55839 // private - not really -- you end up using it !
55840 // This is a support class used internally by the Grid components
55841
55842 /**
55843  * @class Roo.grid.GridEditor
55844  * @extends Roo.Editor
55845  * Class for creating and editable grid elements.
55846  * @param {Object} config any settings (must include field)
55847  */
55848 Roo.grid.GridEditor = function(field, config){
55849     if (!config && field.field) {
55850         config = field;
55851         field = Roo.factory(config.field, Roo.form);
55852     }
55853     Roo.grid.GridEditor.superclass.constructor.call(this, field, config);
55854     field.monitorTab = false;
55855 };
55856
55857 Roo.extend(Roo.grid.GridEditor, Roo.Editor, {
55858     
55859     /**
55860      * @cfg {Roo.form.Field} field Field to wrap (or xtyped)
55861      */
55862     
55863     alignment: "tl-tl",
55864     autoSize: "width",
55865     hideEl : false,
55866     cls: "x-small-editor x-grid-editor",
55867     shim:false,
55868     shadow:"frame"
55869 });/*
55870  * Based on:
55871  * Ext JS Library 1.1.1
55872  * Copyright(c) 2006-2007, Ext JS, LLC.
55873  *
55874  * Originally Released Under LGPL - original licence link has changed is not relivant.
55875  *
55876  * Fork - LGPL
55877  * <script type="text/javascript">
55878  */
55879   
55880
55881   
55882 Roo.grid.PropertyRecord = Roo.data.Record.create([
55883     {name:'name',type:'string'},  'value'
55884 ]);
55885
55886
55887 Roo.grid.PropertyStore = function(grid, source){
55888     this.grid = grid;
55889     this.store = new Roo.data.Store({
55890         recordType : Roo.grid.PropertyRecord
55891     });
55892     this.store.on('update', this.onUpdate,  this);
55893     if(source){
55894         this.setSource(source);
55895     }
55896     Roo.grid.PropertyStore.superclass.constructor.call(this);
55897 };
55898
55899
55900
55901 Roo.extend(Roo.grid.PropertyStore, Roo.util.Observable, {
55902     setSource : function(o){
55903         this.source = o;
55904         this.store.removeAll();
55905         var data = [];
55906         for(var k in o){
55907             if(this.isEditableValue(o[k])){
55908                 data.push(new Roo.grid.PropertyRecord({name: k, value: o[k]}, k));
55909             }
55910         }
55911         this.store.loadRecords({records: data}, {}, true);
55912     },
55913
55914     onUpdate : function(ds, record, type){
55915         if(type == Roo.data.Record.EDIT){
55916             var v = record.data['value'];
55917             var oldValue = record.modified['value'];
55918             if(this.grid.fireEvent('beforepropertychange', this.source, record.id, v, oldValue) !== false){
55919                 this.source[record.id] = v;
55920                 record.commit();
55921                 this.grid.fireEvent('propertychange', this.source, record.id, v, oldValue);
55922             }else{
55923                 record.reject();
55924             }
55925         }
55926     },
55927
55928     getProperty : function(row){
55929        return this.store.getAt(row);
55930     },
55931
55932     isEditableValue: function(val){
55933         if(val && val instanceof Date){
55934             return true;
55935         }else if(typeof val == 'object' || typeof val == 'function'){
55936             return false;
55937         }
55938         return true;
55939     },
55940
55941     setValue : function(prop, value){
55942         this.source[prop] = value;
55943         this.store.getById(prop).set('value', value);
55944     },
55945
55946     getSource : function(){
55947         return this.source;
55948     }
55949 });
55950
55951 Roo.grid.PropertyColumnModel = function(grid, store){
55952     this.grid = grid;
55953     var g = Roo.grid;
55954     g.PropertyColumnModel.superclass.constructor.call(this, [
55955         {header: this.nameText, sortable: true, dataIndex:'name', id: 'name'},
55956         {header: this.valueText, resizable:false, dataIndex: 'value', id: 'value'}
55957     ]);
55958     this.store = store;
55959     this.bselect = Roo.DomHelper.append(document.body, {
55960         tag: 'select', style:'display:none', cls: 'x-grid-editor', children: [
55961             {tag: 'option', value: 'true', html: 'true'},
55962             {tag: 'option', value: 'false', html: 'false'}
55963         ]
55964     });
55965     Roo.id(this.bselect);
55966     var f = Roo.form;
55967     this.editors = {
55968         'date' : new g.GridEditor(new f.DateField({selectOnFocus:true})),
55969         'string' : new g.GridEditor(new f.TextField({selectOnFocus:true})),
55970         'number' : new g.GridEditor(new f.NumberField({selectOnFocus:true, style:'text-align:left;'})),
55971         'int' : new g.GridEditor(new f.NumberField({selectOnFocus:true, allowDecimals:false, style:'text-align:left;'})),
55972         'boolean' : new g.GridEditor(new f.Field({el:this.bselect,selectOnFocus:true}))
55973     };
55974     this.renderCellDelegate = this.renderCell.createDelegate(this);
55975     this.renderPropDelegate = this.renderProp.createDelegate(this);
55976 };
55977
55978 Roo.extend(Roo.grid.PropertyColumnModel, Roo.grid.ColumnModel, {
55979     
55980     
55981     nameText : 'Name',
55982     valueText : 'Value',
55983     
55984     dateFormat : 'm/j/Y',
55985     
55986     
55987     renderDate : function(dateVal){
55988         return dateVal.dateFormat(this.dateFormat);
55989     },
55990
55991     renderBool : function(bVal){
55992         return bVal ? 'true' : 'false';
55993     },
55994
55995     isCellEditable : function(colIndex, rowIndex){
55996         return colIndex == 1;
55997     },
55998
55999     getRenderer : function(col){
56000         return col == 1 ?
56001             this.renderCellDelegate : this.renderPropDelegate;
56002     },
56003
56004     renderProp : function(v){
56005         return this.getPropertyName(v);
56006     },
56007
56008     renderCell : function(val){
56009         var rv = val;
56010         if(val instanceof Date){
56011             rv = this.renderDate(val);
56012         }else if(typeof val == 'boolean'){
56013             rv = this.renderBool(val);
56014         }
56015         return Roo.util.Format.htmlEncode(rv);
56016     },
56017
56018     getPropertyName : function(name){
56019         var pn = this.grid.propertyNames;
56020         return pn && pn[name] ? pn[name] : name;
56021     },
56022
56023     getCellEditor : function(colIndex, rowIndex){
56024         var p = this.store.getProperty(rowIndex);
56025         var n = p.data['name'], val = p.data['value'];
56026         
56027         if(typeof(this.grid.customEditors[n]) == 'string'){
56028             return this.editors[this.grid.customEditors[n]];
56029         }
56030         if(typeof(this.grid.customEditors[n]) != 'undefined'){
56031             return this.grid.customEditors[n];
56032         }
56033         if(val instanceof Date){
56034             return this.editors['date'];
56035         }else if(typeof val == 'number'){
56036             return this.editors['number'];
56037         }else if(typeof val == 'boolean'){
56038             return this.editors['boolean'];
56039         }else{
56040             return this.editors['string'];
56041         }
56042     }
56043 });
56044
56045 /**
56046  * @class Roo.grid.PropertyGrid
56047  * @extends Roo.grid.EditorGrid
56048  * This class represents the  interface of a component based property grid control.
56049  * <br><br>Usage:<pre><code>
56050  var grid = new Roo.grid.PropertyGrid("my-container-id", {
56051       
56052  });
56053  // set any options
56054  grid.render();
56055  * </code></pre>
56056   
56057  * @constructor
56058  * @param {String/HTMLElement/Roo.Element} container The element into which this grid will be rendered -
56059  * The container MUST have some type of size defined for the grid to fill. The container will be
56060  * automatically set to position relative if it isn't already.
56061  * @param {Object} config A config object that sets properties on this grid.
56062  */
56063 Roo.grid.PropertyGrid = function(container, config){
56064     config = config || {};
56065     var store = new Roo.grid.PropertyStore(this);
56066     this.store = store;
56067     var cm = new Roo.grid.PropertyColumnModel(this, store);
56068     store.store.sort('name', 'ASC');
56069     Roo.grid.PropertyGrid.superclass.constructor.call(this, container, Roo.apply({
56070         ds: store.store,
56071         cm: cm,
56072         enableColLock:false,
56073         enableColumnMove:false,
56074         stripeRows:false,
56075         trackMouseOver: false,
56076         clicksToEdit:1
56077     }, config));
56078     this.getGridEl().addClass('x-props-grid');
56079     this.lastEditRow = null;
56080     this.on('columnresize', this.onColumnResize, this);
56081     this.addEvents({
56082          /**
56083              * @event beforepropertychange
56084              * Fires before a property changes (return false to stop?)
56085              * @param {Roo.grid.PropertyGrid} grid property grid? (check could be store)
56086              * @param {String} id Record Id
56087              * @param {String} newval New Value
56088          * @param {String} oldval Old Value
56089              */
56090         "beforepropertychange": true,
56091         /**
56092              * @event propertychange
56093              * Fires after a property changes
56094              * @param {Roo.grid.PropertyGrid} grid property grid? (check could be store)
56095              * @param {String} id Record Id
56096              * @param {String} newval New Value
56097          * @param {String} oldval Old Value
56098              */
56099         "propertychange": true
56100     });
56101     this.customEditors = this.customEditors || {};
56102 };
56103 Roo.extend(Roo.grid.PropertyGrid, Roo.grid.EditorGrid, {
56104     
56105      /**
56106      * @cfg {Object} customEditors map of colnames=> custom editors.
56107      * the custom editor can be one of the standard ones (date|string|number|int|boolean), or a
56108      * grid editor eg. Roo.grid.GridEditor(new Roo.form.TextArea({selectOnFocus:true})),
56109      * false disables editing of the field.
56110          */
56111     
56112       /**
56113      * @cfg {Object} propertyNames map of property Names to their displayed value
56114          */
56115     
56116     render : function(){
56117         Roo.grid.PropertyGrid.superclass.render.call(this);
56118         this.autoSize.defer(100, this);
56119     },
56120
56121     autoSize : function(){
56122         Roo.grid.PropertyGrid.superclass.autoSize.call(this);
56123         if(this.view){
56124             this.view.fitColumns();
56125         }
56126     },
56127
56128     onColumnResize : function(){
56129         this.colModel.setColumnWidth(1, this.container.getWidth(true)-this.colModel.getColumnWidth(0));
56130         this.autoSize();
56131     },
56132     /**
56133      * Sets the data for the Grid
56134      * accepts a Key => Value object of all the elements avaiable.
56135      * @param {Object} data  to appear in grid.
56136      */
56137     setSource : function(source){
56138         this.store.setSource(source);
56139         //this.autoSize();
56140     },
56141     /**
56142      * Gets all the data from the grid.
56143      * @return {Object} data  data stored in grid
56144      */
56145     getSource : function(){
56146         return this.store.getSource();
56147     }
56148 });/*
56149   
56150  * Licence LGPL
56151  
56152  */
56153  
56154 /**
56155  * @class Roo.grid.Calendar
56156  * @extends Roo.util.Grid
56157  * This class extends the Grid to provide a calendar widget
56158  * <br><br>Usage:<pre><code>
56159  var grid = new Roo.grid.Calendar("my-container-id", {
56160      ds: myDataStore,
56161      cm: myColModel,
56162      selModel: mySelectionModel,
56163      autoSizeColumns: true,
56164      monitorWindowResize: false,
56165      trackMouseOver: true
56166      eventstore : real data store..
56167  });
56168  // set any options
56169  grid.render();
56170   
56171   * @constructor
56172  * @param {String/HTMLElement/Roo.Element} container The element into which this grid will be rendered -
56173  * The container MUST have some type of size defined for the grid to fill. The container will be
56174  * automatically set to position relative if it isn't already.
56175  * @param {Object} config A config object that sets properties on this grid.
56176  */
56177 Roo.grid.Calendar = function(container, config){
56178         // initialize the container
56179         this.container = Roo.get(container);
56180         this.container.update("");
56181         this.container.setStyle("overflow", "hidden");
56182     this.container.addClass('x-grid-container');
56183
56184     this.id = this.container.id;
56185
56186     Roo.apply(this, config);
56187     // check and correct shorthanded configs
56188     
56189     var rows = [];
56190     var d =1;
56191     for (var r = 0;r < 6;r++) {
56192         
56193         rows[r]=[];
56194         for (var c =0;c < 7;c++) {
56195             rows[r][c]= '';
56196         }
56197     }
56198     if (this.eventStore) {
56199         this.eventStore= Roo.factory(this.eventStore, Roo.data);
56200         this.eventStore.on('load',this.onLoad, this);
56201         this.eventStore.on('beforeload',this.clearEvents, this);
56202          
56203     }
56204     
56205     this.dataSource = new Roo.data.Store({
56206             proxy: new Roo.data.MemoryProxy(rows),
56207             reader: new Roo.data.ArrayReader({}, [
56208                    'weekday0', 'weekday1', 'weekday2', 'weekday3', 'weekday4', 'weekday5', 'weekday6' ])
56209     });
56210
56211     this.dataSource.load();
56212     this.ds = this.dataSource;
56213     this.ds.xmodule = this.xmodule || false;
56214     
56215     
56216     var cellRender = function(v,x,r)
56217     {
56218         return String.format(
56219             '<div class="fc-day  fc-widget-content"><div>' +
56220                 '<div class="fc-event-container"></div>' +
56221                 '<div class="fc-day-number">{0}</div>'+
56222                 
56223                 '<div class="fc-day-content"><div style="position:relative"></div></div>' +
56224             '</div></div>', v);
56225     
56226     }
56227     
56228     
56229     this.colModel = new Roo.grid.ColumnModel( [
56230         {
56231             xtype: 'ColumnModel',
56232             xns: Roo.grid,
56233             dataIndex : 'weekday0',
56234             header : 'Sunday',
56235             renderer : cellRender
56236         },
56237         {
56238             xtype: 'ColumnModel',
56239             xns: Roo.grid,
56240             dataIndex : 'weekday1',
56241             header : 'Monday',
56242             renderer : cellRender
56243         },
56244         {
56245             xtype: 'ColumnModel',
56246             xns: Roo.grid,
56247             dataIndex : 'weekday2',
56248             header : 'Tuesday',
56249             renderer : cellRender
56250         },
56251         {
56252             xtype: 'ColumnModel',
56253             xns: Roo.grid,
56254             dataIndex : 'weekday3',
56255             header : 'Wednesday',
56256             renderer : cellRender
56257         },
56258         {
56259             xtype: 'ColumnModel',
56260             xns: Roo.grid,
56261             dataIndex : 'weekday4',
56262             header : 'Thursday',
56263             renderer : cellRender
56264         },
56265         {
56266             xtype: 'ColumnModel',
56267             xns: Roo.grid,
56268             dataIndex : 'weekday5',
56269             header : 'Friday',
56270             renderer : cellRender
56271         },
56272         {
56273             xtype: 'ColumnModel',
56274             xns: Roo.grid,
56275             dataIndex : 'weekday6',
56276             header : 'Saturday',
56277             renderer : cellRender
56278         }
56279     ]);
56280     this.cm = this.colModel;
56281     this.cm.xmodule = this.xmodule || false;
56282  
56283         
56284           
56285     //this.selModel = new Roo.grid.CellSelectionModel();
56286     //this.sm = this.selModel;
56287     //this.selModel.init(this);
56288     
56289     
56290     if(this.width){
56291         this.container.setWidth(this.width);
56292     }
56293
56294     if(this.height){
56295         this.container.setHeight(this.height);
56296     }
56297     /** @private */
56298         this.addEvents({
56299         // raw events
56300         /**
56301          * @event click
56302          * The raw click event for the entire grid.
56303          * @param {Roo.EventObject} e
56304          */
56305         "click" : true,
56306         /**
56307          * @event dblclick
56308          * The raw dblclick event for the entire grid.
56309          * @param {Roo.EventObject} e
56310          */
56311         "dblclick" : true,
56312         /**
56313          * @event contextmenu
56314          * The raw contextmenu event for the entire grid.
56315          * @param {Roo.EventObject} e
56316          */
56317         "contextmenu" : true,
56318         /**
56319          * @event mousedown
56320          * The raw mousedown event for the entire grid.
56321          * @param {Roo.EventObject} e
56322          */
56323         "mousedown" : true,
56324         /**
56325          * @event mouseup
56326          * The raw mouseup event for the entire grid.
56327          * @param {Roo.EventObject} e
56328          */
56329         "mouseup" : true,
56330         /**
56331          * @event mouseover
56332          * The raw mouseover event for the entire grid.
56333          * @param {Roo.EventObject} e
56334          */
56335         "mouseover" : true,
56336         /**
56337          * @event mouseout
56338          * The raw mouseout event for the entire grid.
56339          * @param {Roo.EventObject} e
56340          */
56341         "mouseout" : true,
56342         /**
56343          * @event keypress
56344          * The raw keypress event for the entire grid.
56345          * @param {Roo.EventObject} e
56346          */
56347         "keypress" : true,
56348         /**
56349          * @event keydown
56350          * The raw keydown event for the entire grid.
56351          * @param {Roo.EventObject} e
56352          */
56353         "keydown" : true,
56354
56355         // custom events
56356
56357         /**
56358          * @event cellclick
56359          * Fires when a cell is clicked
56360          * @param {Grid} this
56361          * @param {Number} rowIndex
56362          * @param {Number} columnIndex
56363          * @param {Roo.EventObject} e
56364          */
56365         "cellclick" : true,
56366         /**
56367          * @event celldblclick
56368          * Fires when a cell is double clicked
56369          * @param {Grid} this
56370          * @param {Number} rowIndex
56371          * @param {Number} columnIndex
56372          * @param {Roo.EventObject} e
56373          */
56374         "celldblclick" : true,
56375         /**
56376          * @event rowclick
56377          * Fires when a row is clicked
56378          * @param {Grid} this
56379          * @param {Number} rowIndex
56380          * @param {Roo.EventObject} e
56381          */
56382         "rowclick" : true,
56383         /**
56384          * @event rowdblclick
56385          * Fires when a row is double clicked
56386          * @param {Grid} this
56387          * @param {Number} rowIndex
56388          * @param {Roo.EventObject} e
56389          */
56390         "rowdblclick" : true,
56391         /**
56392          * @event headerclick
56393          * Fires when a header is clicked
56394          * @param {Grid} this
56395          * @param {Number} columnIndex
56396          * @param {Roo.EventObject} e
56397          */
56398         "headerclick" : true,
56399         /**
56400          * @event headerdblclick
56401          * Fires when a header cell is double clicked
56402          * @param {Grid} this
56403          * @param {Number} columnIndex
56404          * @param {Roo.EventObject} e
56405          */
56406         "headerdblclick" : true,
56407         /**
56408          * @event rowcontextmenu
56409          * Fires when a row is right clicked
56410          * @param {Grid} this
56411          * @param {Number} rowIndex
56412          * @param {Roo.EventObject} e
56413          */
56414         "rowcontextmenu" : true,
56415         /**
56416          * @event cellcontextmenu
56417          * Fires when a cell is right clicked
56418          * @param {Grid} this
56419          * @param {Number} rowIndex
56420          * @param {Number} cellIndex
56421          * @param {Roo.EventObject} e
56422          */
56423          "cellcontextmenu" : true,
56424         /**
56425          * @event headercontextmenu
56426          * Fires when a header is right clicked
56427          * @param {Grid} this
56428          * @param {Number} columnIndex
56429          * @param {Roo.EventObject} e
56430          */
56431         "headercontextmenu" : true,
56432         /**
56433          * @event bodyscroll
56434          * Fires when the body element is scrolled
56435          * @param {Number} scrollLeft
56436          * @param {Number} scrollTop
56437          */
56438         "bodyscroll" : true,
56439         /**
56440          * @event columnresize
56441          * Fires when the user resizes a column
56442          * @param {Number} columnIndex
56443          * @param {Number} newSize
56444          */
56445         "columnresize" : true,
56446         /**
56447          * @event columnmove
56448          * Fires when the user moves a column
56449          * @param {Number} oldIndex
56450          * @param {Number} newIndex
56451          */
56452         "columnmove" : true,
56453         /**
56454          * @event startdrag
56455          * Fires when row(s) start being dragged
56456          * @param {Grid} this
56457          * @param {Roo.GridDD} dd The drag drop object
56458          * @param {event} e The raw browser event
56459          */
56460         "startdrag" : true,
56461         /**
56462          * @event enddrag
56463          * Fires when a drag operation is complete
56464          * @param {Grid} this
56465          * @param {Roo.GridDD} dd The drag drop object
56466          * @param {event} e The raw browser event
56467          */
56468         "enddrag" : true,
56469         /**
56470          * @event dragdrop
56471          * Fires when dragged row(s) are dropped on a valid DD target
56472          * @param {Grid} this
56473          * @param {Roo.GridDD} dd The drag drop object
56474          * @param {String} targetId The target drag drop object
56475          * @param {event} e The raw browser event
56476          */
56477         "dragdrop" : true,
56478         /**
56479          * @event dragover
56480          * Fires while row(s) are being dragged. "targetId" is the id of the Yahoo.util.DD object the selected rows are being dragged over.
56481          * @param {Grid} this
56482          * @param {Roo.GridDD} dd The drag drop object
56483          * @param {String} targetId The target drag drop object
56484          * @param {event} e The raw browser event
56485          */
56486         "dragover" : true,
56487         /**
56488          * @event dragenter
56489          *  Fires when the dragged row(s) first cross another DD target while being dragged
56490          * @param {Grid} this
56491          * @param {Roo.GridDD} dd The drag drop object
56492          * @param {String} targetId The target drag drop object
56493          * @param {event} e The raw browser event
56494          */
56495         "dragenter" : true,
56496         /**
56497          * @event dragout
56498          * Fires when the dragged row(s) leave another DD target while being dragged
56499          * @param {Grid} this
56500          * @param {Roo.GridDD} dd The drag drop object
56501          * @param {String} targetId The target drag drop object
56502          * @param {event} e The raw browser event
56503          */
56504         "dragout" : true,
56505         /**
56506          * @event rowclass
56507          * Fires when a row is rendered, so you can change add a style to it.
56508          * @param {GridView} gridview   The grid view
56509          * @param {Object} rowcfg   contains record  rowIndex and rowClass - set rowClass to add a style.
56510          */
56511         'rowclass' : true,
56512
56513         /**
56514          * @event render
56515          * Fires when the grid is rendered
56516          * @param {Grid} grid
56517          */
56518         'render' : true,
56519             /**
56520              * @event select
56521              * Fires when a date is selected
56522              * @param {DatePicker} this
56523              * @param {Date} date The selected date
56524              */
56525         'select': true,
56526         /**
56527              * @event monthchange
56528              * Fires when the displayed month changes 
56529              * @param {DatePicker} this
56530              * @param {Date} date The selected month
56531              */
56532         'monthchange': true,
56533         /**
56534              * @event evententer
56535              * Fires when mouse over an event
56536              * @param {Calendar} this
56537              * @param {event} Event
56538              */
56539         'evententer': true,
56540         /**
56541              * @event eventleave
56542              * Fires when the mouse leaves an
56543              * @param {Calendar} this
56544              * @param {event}
56545              */
56546         'eventleave': true,
56547         /**
56548              * @event eventclick
56549              * Fires when the mouse click an
56550              * @param {Calendar} this
56551              * @param {event}
56552              */
56553         'eventclick': true,
56554         /**
56555              * @event eventrender
56556              * Fires before each cell is rendered, so you can modify the contents, like cls / title / qtip
56557              * @param {Calendar} this
56558              * @param {data} data to be modified
56559              */
56560         'eventrender': true
56561         
56562     });
56563
56564     Roo.grid.Grid.superclass.constructor.call(this);
56565     this.on('render', function() {
56566         this.view.el.addClass('x-grid-cal'); 
56567         
56568         (function() { this.setDate(new Date()); }).defer(100,this); //default today..
56569
56570     },this);
56571     
56572     if (!Roo.grid.Calendar.style) {
56573         Roo.grid.Calendar.style = Roo.util.CSS.createStyleSheet({
56574             
56575             
56576             '.x-grid-cal .x-grid-col' :  {
56577                 height: 'auto !important',
56578                 'vertical-align': 'top'
56579             },
56580             '.x-grid-cal  .fc-event-hori' : {
56581                 height: '14px'
56582             }
56583              
56584             
56585         }, Roo.id());
56586     }
56587
56588     
56589     
56590 };
56591 Roo.extend(Roo.grid.Calendar, Roo.grid.Grid, {
56592     /**
56593      * @cfg {Store} eventStore The store that loads events.
56594      */
56595     eventStore : 25,
56596
56597      
56598     activeDate : false,
56599     startDay : 0,
56600     autoWidth : true,
56601     monitorWindowResize : false,
56602
56603     
56604     resizeColumns : function() {
56605         var col = (this.view.el.getWidth() / 7) - 3;
56606         // loop through cols, and setWidth
56607         for(var i =0 ; i < 7 ; i++){
56608             this.cm.setColumnWidth(i, col);
56609         }
56610     },
56611      setDate :function(date) {
56612         
56613         Roo.log('setDate?');
56614         
56615         this.resizeColumns();
56616         var vd = this.activeDate;
56617         this.activeDate = date;
56618 //        if(vd && this.el){
56619 //            var t = date.getTime();
56620 //            if(vd.getMonth() == date.getMonth() && vd.getFullYear() == date.getFullYear()){
56621 //                Roo.log('using add remove');
56622 //                
56623 //                this.fireEvent('monthchange', this, date);
56624 //                
56625 //                this.cells.removeClass("fc-state-highlight");
56626 //                this.cells.each(function(c){
56627 //                   if(c.dateValue == t){
56628 //                       c.addClass("fc-state-highlight");
56629 //                       setTimeout(function(){
56630 //                            try{c.dom.firstChild.focus();}catch(e){}
56631 //                       }, 50);
56632 //                       return false;
56633 //                   }
56634 //                   return true;
56635 //                });
56636 //                return;
56637 //            }
56638 //        }
56639         
56640         var days = date.getDaysInMonth();
56641         
56642         var firstOfMonth = date.getFirstDateOfMonth();
56643         var startingPos = firstOfMonth.getDay()-this.startDay;
56644         
56645         if(startingPos < this.startDay){
56646             startingPos += 7;
56647         }
56648         
56649         var pm = date.add(Date.MONTH, -1);
56650         var prevStart = pm.getDaysInMonth()-startingPos;
56651 //        
56652         
56653         
56654         this.cells = this.view.el.select('.x-grid-row .x-grid-col',true);
56655         
56656         this.textNodes = this.view.el.query('.x-grid-row .x-grid-col .x-grid-cell-text');
56657         //this.cells.addClassOnOver('fc-state-hover');
56658         
56659         var cells = this.cells.elements;
56660         var textEls = this.textNodes;
56661         
56662         //Roo.each(cells, function(cell){
56663         //    cell.removeClass([ 'fc-past', 'fc-other-month', 'fc-future', 'fc-state-highlight', 'fc-state-disabled']);
56664         //});
56665         
56666         days += startingPos;
56667
56668         // convert everything to numbers so it's fast
56669         var day = 86400000;
56670         var d = (new Date(pm.getFullYear(), pm.getMonth(), prevStart)).clearTime();
56671         //Roo.log(d);
56672         //Roo.log(pm);
56673         //Roo.log(prevStart);
56674         
56675         var today = new Date().clearTime().getTime();
56676         var sel = date.clearTime().getTime();
56677         var min = this.minDate ? this.minDate.clearTime() : Number.NEGATIVE_INFINITY;
56678         var max = this.maxDate ? this.maxDate.clearTime() : Number.POSITIVE_INFINITY;
56679         var ddMatch = this.disabledDatesRE;
56680         var ddText = this.disabledDatesText;
56681         var ddays = this.disabledDays ? this.disabledDays.join("") : false;
56682         var ddaysText = this.disabledDaysText;
56683         var format = this.format;
56684         
56685         var setCellClass = function(cal, cell){
56686             
56687             //Roo.log('set Cell Class');
56688             cell.title = "";
56689             var t = d.getTime();
56690             
56691             //Roo.log(d);
56692             
56693             
56694             cell.dateValue = t;
56695             if(t == today){
56696                 cell.className += " fc-today";
56697                 cell.className += " fc-state-highlight";
56698                 cell.title = cal.todayText;
56699             }
56700             if(t == sel){
56701                 // disable highlight in other month..
56702                 cell.className += " fc-state-highlight";
56703                 
56704             }
56705             // disabling
56706             if(t < min) {
56707                 //cell.className = " fc-state-disabled";
56708                 cell.title = cal.minText;
56709                 return;
56710             }
56711             if(t > max) {
56712                 //cell.className = " fc-state-disabled";
56713                 cell.title = cal.maxText;
56714                 return;
56715             }
56716             if(ddays){
56717                 if(ddays.indexOf(d.getDay()) != -1){
56718                     // cell.title = ddaysText;
56719                    // cell.className = " fc-state-disabled";
56720                 }
56721             }
56722             if(ddMatch && format){
56723                 var fvalue = d.dateFormat(format);
56724                 if(ddMatch.test(fvalue)){
56725                     cell.title = ddText.replace("%0", fvalue);
56726                    cell.className = " fc-state-disabled";
56727                 }
56728             }
56729             
56730             if (!cell.initialClassName) {
56731                 cell.initialClassName = cell.dom.className;
56732             }
56733             
56734             cell.dom.className = cell.initialClassName  + ' ' +  cell.className;
56735         };
56736
56737         var i = 0;
56738         
56739         for(; i < startingPos; i++) {
56740             cells[i].dayName =  (++prevStart);
56741             Roo.log(textEls[i]);
56742             d.setDate(d.getDate()+1);
56743             
56744             //cells[i].className = "fc-past fc-other-month";
56745             setCellClass(this, cells[i]);
56746         }
56747         
56748         var intDay = 0;
56749         
56750         for(; i < days; i++){
56751             intDay = i - startingPos + 1;
56752             cells[i].dayName =  (intDay);
56753             d.setDate(d.getDate()+1);
56754             
56755             cells[i].className = ''; // "x-date-active";
56756             setCellClass(this, cells[i]);
56757         }
56758         var extraDays = 0;
56759         
56760         for(; i < 42; i++) {
56761             //textEls[i].innerHTML = (++extraDays);
56762             
56763             d.setDate(d.getDate()+1);
56764             cells[i].dayName = (++extraDays);
56765             cells[i].className = "fc-future fc-other-month";
56766             setCellClass(this, cells[i]);
56767         }
56768         
56769         //this.el.select('.fc-header-title h2',true).update(Date.monthNames[date.getMonth()] + " " + date.getFullYear());
56770         
56771         var totalRows = Math.ceil((date.getDaysInMonth() + date.getFirstDateOfMonth().getDay()) / 7);
56772         
56773         // this will cause all the cells to mis
56774         var rows= [];
56775         var i =0;
56776         for (var r = 0;r < 6;r++) {
56777             for (var c =0;c < 7;c++) {
56778                 this.ds.getAt(r).set('weekday' + c ,cells[i++].dayName );
56779             }    
56780         }
56781         
56782         this.cells = this.view.el.select('.x-grid-row .x-grid-col',true);
56783         for(i=0;i<cells.length;i++) {
56784             
56785             this.cells.elements[i].dayName = cells[i].dayName ;
56786             this.cells.elements[i].className = cells[i].className;
56787             this.cells.elements[i].initialClassName = cells[i].initialClassName ;
56788             this.cells.elements[i].title = cells[i].title ;
56789             this.cells.elements[i].dateValue = cells[i].dateValue ;
56790         }
56791         
56792         
56793         
56794         
56795         //this.el.select('tr.fc-week.fc-prev-last',true).removeClass('fc-last');
56796         //this.el.select('tr.fc-week.fc-next-last',true).addClass('fc-last').show();
56797         
56798         ////if(totalRows != 6){
56799             //this.el.select('tr.fc-week.fc-last',true).removeClass('fc-last').addClass('fc-next-last').hide();
56800            // this.el.select('tr.fc-week.fc-prev-last',true).addClass('fc-last');
56801        // }
56802         
56803         this.fireEvent('monthchange', this, date);
56804         
56805         
56806     },
56807  /**
56808      * Returns the grid's SelectionModel.
56809      * @return {SelectionModel}
56810      */
56811     getSelectionModel : function(){
56812         if(!this.selModel){
56813             this.selModel = new Roo.grid.CellSelectionModel();
56814         }
56815         return this.selModel;
56816     },
56817
56818     load: function() {
56819         this.eventStore.load()
56820         
56821         
56822         
56823     },
56824     
56825     findCell : function(dt) {
56826         dt = dt.clearTime().getTime();
56827         var ret = false;
56828         this.cells.each(function(c){
56829             //Roo.log("check " +c.dateValue + '?=' + dt);
56830             if(c.dateValue == dt){
56831                 ret = c;
56832                 return false;
56833             }
56834             return true;
56835         });
56836         
56837         return ret;
56838     },
56839     
56840     findCells : function(rec) {
56841         var s = rec.data.start_dt.clone().clearTime().getTime();
56842        // Roo.log(s);
56843         var e= rec.data.end_dt.clone().clearTime().getTime();
56844        // Roo.log(e);
56845         var ret = [];
56846         this.cells.each(function(c){
56847              ////Roo.log("check " +c.dateValue + '<' + e + ' > ' + s);
56848             
56849             if(c.dateValue > e){
56850                 return ;
56851             }
56852             if(c.dateValue < s){
56853                 return ;
56854             }
56855             ret.push(c);
56856         });
56857         
56858         return ret;    
56859     },
56860     
56861     findBestRow: function(cells)
56862     {
56863         var ret = 0;
56864         
56865         for (var i =0 ; i < cells.length;i++) {
56866             ret  = Math.max(cells[i].rows || 0,ret);
56867         }
56868         return ret;
56869         
56870     },
56871     
56872     
56873     addItem : function(rec)
56874     {
56875         // look for vertical location slot in
56876         var cells = this.findCells(rec);
56877         
56878         rec.row = this.findBestRow(cells);
56879         
56880         // work out the location.
56881         
56882         var crow = false;
56883         var rows = [];
56884         for(var i =0; i < cells.length; i++) {
56885             if (!crow) {
56886                 crow = {
56887                     start : cells[i],
56888                     end :  cells[i]
56889                 };
56890                 continue;
56891             }
56892             if (crow.start.getY() == cells[i].getY()) {
56893                 // on same row.
56894                 crow.end = cells[i];
56895                 continue;
56896             }
56897             // different row.
56898             rows.push(crow);
56899             crow = {
56900                 start: cells[i],
56901                 end : cells[i]
56902             };
56903             
56904         }
56905         
56906         rows.push(crow);
56907         rec.els = [];
56908         rec.rows = rows;
56909         rec.cells = cells;
56910         for (var i = 0; i < cells.length;i++) {
56911             cells[i].rows = Math.max(cells[i].rows || 0 , rec.row + 1 );
56912             
56913         }
56914         
56915         
56916     },
56917     
56918     clearEvents: function() {
56919         
56920         if (!this.eventStore.getCount()) {
56921             return;
56922         }
56923         // reset number of rows in cells.
56924         Roo.each(this.cells.elements, function(c){
56925             c.rows = 0;
56926         });
56927         
56928         this.eventStore.each(function(e) {
56929             this.clearEvent(e);
56930         },this);
56931         
56932     },
56933     
56934     clearEvent : function(ev)
56935     {
56936         if (ev.els) {
56937             Roo.each(ev.els, function(el) {
56938                 el.un('mouseenter' ,this.onEventEnter, this);
56939                 el.un('mouseleave' ,this.onEventLeave, this);
56940                 el.remove();
56941             },this);
56942             ev.els = [];
56943         }
56944     },
56945     
56946     
56947     renderEvent : function(ev,ctr) {
56948         if (!ctr) {
56949              ctr = this.view.el.select('.fc-event-container',true).first();
56950         }
56951         
56952          
56953         this.clearEvent(ev);
56954             //code
56955        
56956         
56957         
56958         ev.els = [];
56959         var cells = ev.cells;
56960         var rows = ev.rows;
56961         this.fireEvent('eventrender', this, ev);
56962         
56963         for(var i =0; i < rows.length; i++) {
56964             
56965             cls = '';
56966             if (i == 0) {
56967                 cls += ' fc-event-start';
56968             }
56969             if ((i+1) == rows.length) {
56970                 cls += ' fc-event-end';
56971             }
56972             
56973             //Roo.log(ev.data);
56974             // how many rows should it span..
56975             var cg = this.eventTmpl.append(ctr,Roo.apply({
56976                 fccls : cls
56977                 
56978             }, ev.data) , true);
56979             
56980             
56981             cg.on('mouseenter' ,this.onEventEnter, this, ev);
56982             cg.on('mouseleave' ,this.onEventLeave, this, ev);
56983             cg.on('click', this.onEventClick, this, ev);
56984             
56985             ev.els.push(cg);
56986             
56987             var sbox = rows[i].start.select('.fc-day-content',true).first().getBox();
56988             var ebox = rows[i].end.select('.fc-day-content',true).first().getBox();
56989             //Roo.log(cg);
56990              
56991             cg.setXY([sbox.x +2, sbox.y +(ev.row * 20)]);    
56992             cg.setWidth(ebox.right - sbox.x -2);
56993         }
56994     },
56995     
56996     renderEvents: function()
56997     {   
56998         // first make sure there is enough space..
56999         
57000         if (!this.eventTmpl) {
57001             this.eventTmpl = new Roo.Template(
57002                 '<div class="roo-dynamic fc-event fc-event-hori fc-event-draggable ui-draggable {fccls} {cls}"  style="position: absolute" unselectable="on">' +
57003                     '<div class="fc-event-inner">' +
57004                         '<span class="fc-event-time">{time}</span>' +
57005                         '<span class="fc-event-title" qtip="{qtip}">{title}</span>' +
57006                     '</div>' +
57007                     '<div class="ui-resizable-heandle ui-resizable-e">&nbsp;&nbsp;&nbsp;</div>' +
57008                 '</div>'
57009             );
57010                 
57011         }
57012                
57013         
57014         
57015         this.cells.each(function(c) {
57016             //Roo.log(c.select('.fc-day-content div',true).first());
57017             c.select('.fc-day-content div',true).first().setHeight(Math.max(34, (c.rows || 1) * 20));
57018         });
57019         
57020         var ctr = this.view.el.select('.fc-event-container',true).first();
57021         
57022         var cls;
57023         this.eventStore.each(function(ev){
57024             
57025             this.renderEvent(ev);
57026              
57027              
57028         }, this);
57029         this.view.layout();
57030         
57031     },
57032     
57033     onEventEnter: function (e, el,event,d) {
57034         this.fireEvent('evententer', this, el, event);
57035     },
57036     
57037     onEventLeave: function (e, el,event,d) {
57038         this.fireEvent('eventleave', this, el, event);
57039     },
57040     
57041     onEventClick: function (e, el,event,d) {
57042         this.fireEvent('eventclick', this, el, event);
57043     },
57044     
57045     onMonthChange: function () {
57046         this.store.load();
57047     },
57048     
57049     onLoad: function () {
57050         
57051         //Roo.log('calendar onload');
57052 //         
57053         if(this.eventStore.getCount() > 0){
57054             
57055            
57056             
57057             this.eventStore.each(function(d){
57058                 
57059                 
57060                 // FIXME..
57061                 var add =   d.data;
57062                 if (typeof(add.end_dt) == 'undefined')  {
57063                     Roo.log("Missing End time in calendar data: ");
57064                     Roo.log(d);
57065                     return;
57066                 }
57067                 if (typeof(add.start_dt) == 'undefined')  {
57068                     Roo.log("Missing Start time in calendar data: ");
57069                     Roo.log(d);
57070                     return;
57071                 }
57072                 add.start_dt = typeof(add.start_dt) == 'string' ? Date.parseDate(add.start_dt,'Y-m-d H:i:s') : add.start_dt,
57073                 add.end_dt = typeof(add.end_dt) == 'string' ? Date.parseDate(add.end_dt,'Y-m-d H:i:s') : add.end_dt,
57074                 add.id = add.id || d.id;
57075                 add.title = add.title || '??';
57076                 
57077                 this.addItem(d);
57078                 
57079              
57080             },this);
57081         }
57082         
57083         this.renderEvents();
57084     }
57085     
57086
57087 });
57088 /*
57089  grid : {
57090                 xtype: 'Grid',
57091                 xns: Roo.grid,
57092                 listeners : {
57093                     render : function ()
57094                     {
57095                         _this.grid = this;
57096                         
57097                         if (!this.view.el.hasClass('course-timesheet')) {
57098                             this.view.el.addClass('course-timesheet');
57099                         }
57100                         if (this.tsStyle) {
57101                             this.ds.load({});
57102                             return; 
57103                         }
57104                         Roo.log('width');
57105                         Roo.log(_this.grid.view.el.getWidth());
57106                         
57107                         
57108                         this.tsStyle =  Roo.util.CSS.createStyleSheet({
57109                             '.course-timesheet .x-grid-row' : {
57110                                 height: '80px'
57111                             },
57112                             '.x-grid-row td' : {
57113                                 'vertical-align' : 0
57114                             },
57115                             '.course-edit-link' : {
57116                                 'color' : 'blue',
57117                                 'text-overflow' : 'ellipsis',
57118                                 'overflow' : 'hidden',
57119                                 'white-space' : 'nowrap',
57120                                 'cursor' : 'pointer'
57121                             },
57122                             '.sub-link' : {
57123                                 'color' : 'green'
57124                             },
57125                             '.de-act-sup-link' : {
57126                                 'color' : 'purple',
57127                                 'text-decoration' : 'line-through'
57128                             },
57129                             '.de-act-link' : {
57130                                 'color' : 'red',
57131                                 'text-decoration' : 'line-through'
57132                             },
57133                             '.course-timesheet .course-highlight' : {
57134                                 'border-top-style': 'dashed !important',
57135                                 'border-bottom-bottom': 'dashed !important'
57136                             },
57137                             '.course-timesheet .course-item' : {
57138                                 'font-family'   : 'tahoma, arial, helvetica',
57139                                 'font-size'     : '11px',
57140                                 'overflow'      : 'hidden',
57141                                 'padding-left'  : '10px',
57142                                 'padding-right' : '10px',
57143                                 'padding-top' : '10px' 
57144                             }
57145                             
57146                         }, Roo.id());
57147                                 this.ds.load({});
57148                     }
57149                 },
57150                 autoWidth : true,
57151                 monitorWindowResize : false,
57152                 cellrenderer : function(v,x,r)
57153                 {
57154                     return v;
57155                 },
57156                 sm : {
57157                     xtype: 'CellSelectionModel',
57158                     xns: Roo.grid
57159                 },
57160                 dataSource : {
57161                     xtype: 'Store',
57162                     xns: Roo.data,
57163                     listeners : {
57164                         beforeload : function (_self, options)
57165                         {
57166                             options.params = options.params || {};
57167                             options.params._month = _this.monthField.getValue();
57168                             options.params.limit = 9999;
57169                             options.params['sort'] = 'when_dt';    
57170                             options.params['dir'] = 'ASC';    
57171                             this.proxy.loadResponse = this.loadResponse;
57172                             Roo.log("load?");
57173                             //this.addColumns();
57174                         },
57175                         load : function (_self, records, options)
57176                         {
57177                             _this.grid.view.el.select('.course-edit-link', true).on('click', function() {
57178                                 // if you click on the translation.. you can edit it...
57179                                 var el = Roo.get(this);
57180                                 var id = el.dom.getAttribute('data-id');
57181                                 var d = el.dom.getAttribute('data-date');
57182                                 var t = el.dom.getAttribute('data-time');
57183                                 //var id = this.child('span').dom.textContent;
57184                                 
57185                                 //Roo.log(this);
57186                                 Pman.Dialog.CourseCalendar.show({
57187                                     id : id,
57188                                     when_d : d,
57189                                     when_t : t,
57190                                     productitem_active : id ? 1 : 0
57191                                 }, function() {
57192                                     _this.grid.ds.load({});
57193                                 });
57194                            
57195                            });
57196                            
57197                            _this.panel.fireEvent('resize', [ '', '' ]);
57198                         }
57199                     },
57200                     loadResponse : function(o, success, response){
57201                             // this is overridden on before load..
57202                             
57203                             Roo.log("our code?");       
57204                             //Roo.log(success);
57205                             //Roo.log(response)
57206                             delete this.activeRequest;
57207                             if(!success){
57208                                 this.fireEvent("loadexception", this, o, response);
57209                                 o.request.callback.call(o.request.scope, null, o.request.arg, false);
57210                                 return;
57211                             }
57212                             var result;
57213                             try {
57214                                 result = o.reader.read(response);
57215                             }catch(e){
57216                                 Roo.log("load exception?");
57217                                 this.fireEvent("loadexception", this, o, response, e);
57218                                 o.request.callback.call(o.request.scope, null, o.request.arg, false);
57219                                 return;
57220                             }
57221                             Roo.log("ready...");        
57222                             // loop through result.records;
57223                             // and set this.tdate[date] = [] << array of records..
57224                             _this.tdata  = {};
57225                             Roo.each(result.records, function(r){
57226                                 //Roo.log(r.data);
57227                                 if(typeof(_this.tdata[r.data.when_dt.format('j')]) == 'undefined'){
57228                                     _this.tdata[r.data.when_dt.format('j')] = [];
57229                                 }
57230                                 _this.tdata[r.data.when_dt.format('j')].push(r.data);
57231                             });
57232                             
57233                             //Roo.log(_this.tdata);
57234                             
57235                             result.records = [];
57236                             result.totalRecords = 6;
57237                     
57238                             // let's generate some duumy records for the rows.
57239                             //var st = _this.dateField.getValue();
57240                             
57241                             // work out monday..
57242                             //st = st.add(Date.DAY, -1 * st.format('w'));
57243                             
57244                             var date = Date.parseDate(_this.monthField.getValue(), "Y-m-d");
57245                             
57246                             var firstOfMonth = date.getFirstDayOfMonth();
57247                             var days = date.getDaysInMonth();
57248                             var d = 1;
57249                             var firstAdded = false;
57250                             for (var i = 0; i < result.totalRecords ; i++) {
57251                                 //var d= st.add(Date.DAY, i);
57252                                 var row = {};
57253                                 var added = 0;
57254                                 for(var w = 0 ; w < 7 ; w++){
57255                                     if(!firstAdded && firstOfMonth != w){
57256                                         continue;
57257                                     }
57258                                     if(d > days){
57259                                         continue;
57260                                     }
57261                                     firstAdded = true;
57262                                     var dd = (d > 0 && d < 10) ? "0"+d : d;
57263                                     row['weekday'+w] = String.format(
57264                                                     '<span style="font-size: 16px;"><b>{0}</b></span>'+
57265                                                     '<span class="course-edit-link" style="color:blue;" data-id="0" data-date="{1}"> Add New</span>',
57266                                                     d,
57267                                                     date.format('Y-m-')+dd
57268                                                 );
57269                                     added++;
57270                                     if(typeof(_this.tdata[d]) != 'undefined'){
57271                                         Roo.each(_this.tdata[d], function(r){
57272                                             var is_sub = '';
57273                                             var deactive = '';
57274                                             var id = r.id;
57275                                             var desc = (r.productitem_id_descrip) ? r.productitem_id_descrip : '';
57276                                             if(r.parent_id*1>0){
57277                                                 is_sub = (r.productitem_id_visible*1 < 1) ? 'de-act-sup-link' :'sub-link';
57278                                                 id = r.parent_id;
57279                                             }
57280                                             if(r.productitem_id_visible*1 < 1 && r.parent_id*1 < 1){
57281                                                 deactive = 'de-act-link';
57282                                             }
57283                                             
57284                                             row['weekday'+w] += String.format(
57285                                                     '<br /><span class="course-edit-link {3} {4}" qtip="{5}" data-id="{0}">{2} - {1}</span>',
57286                                                     id, //0
57287                                                     r.product_id_name, //1
57288                                                     r.when_dt.format('h:ia'), //2
57289                                                     is_sub, //3
57290                                                     deactive, //4
57291                                                     desc // 5
57292                                             );
57293                                         });
57294                                     }
57295                                     d++;
57296                                 }
57297                                 
57298                                 // only do this if something added..
57299                                 if(added > 0){ 
57300                                     result.records.push(_this.grid.dataSource.reader.newRow(row));
57301                                 }
57302                                 
57303                                 
57304                                 // push it twice. (second one with an hour..
57305                                 
57306                             }
57307                             //Roo.log(result);
57308                             this.fireEvent("load", this, o, o.request.arg);
57309                             o.request.callback.call(o.request.scope, result, o.request.arg, true);
57310                         },
57311                     sortInfo : {field: 'when_dt', direction : 'ASC' },
57312                     proxy : {
57313                         xtype: 'HttpProxy',
57314                         xns: Roo.data,
57315                         method : 'GET',
57316                         url : baseURL + '/Roo/Shop_course.php'
57317                     },
57318                     reader : {
57319                         xtype: 'JsonReader',
57320                         xns: Roo.data,
57321                         id : 'id',
57322                         fields : [
57323                             {
57324                                 'name': 'id',
57325                                 'type': 'int'
57326                             },
57327                             {
57328                                 'name': 'when_dt',
57329                                 'type': 'string'
57330                             },
57331                             {
57332                                 'name': 'end_dt',
57333                                 'type': 'string'
57334                             },
57335                             {
57336                                 'name': 'parent_id',
57337                                 'type': 'int'
57338                             },
57339                             {
57340                                 'name': 'product_id',
57341                                 'type': 'int'
57342                             },
57343                             {
57344                                 'name': 'productitem_id',
57345                                 'type': 'int'
57346                             },
57347                             {
57348                                 'name': 'guid',
57349                                 'type': 'int'
57350                             }
57351                         ]
57352                     }
57353                 },
57354                 toolbar : {
57355                     xtype: 'Toolbar',
57356                     xns: Roo,
57357                     items : [
57358                         {
57359                             xtype: 'Button',
57360                             xns: Roo.Toolbar,
57361                             listeners : {
57362                                 click : function (_self, e)
57363                                 {
57364                                     var sd = Date.parseDate(_this.monthField.getValue(), "Y-m-d");
57365                                     sd.setMonth(sd.getMonth()-1);
57366                                     _this.monthField.setValue(sd.format('Y-m-d'));
57367                                     _this.grid.ds.load({});
57368                                 }
57369                             },
57370                             text : "Back"
57371                         },
57372                         {
57373                             xtype: 'Separator',
57374                             xns: Roo.Toolbar
57375                         },
57376                         {
57377                             xtype: 'MonthField',
57378                             xns: Roo.form,
57379                             listeners : {
57380                                 render : function (_self)
57381                                 {
57382                                     _this.monthField = _self;
57383                                    // _this.monthField.set  today
57384                                 },
57385                                 select : function (combo, date)
57386                                 {
57387                                     _this.grid.ds.load({});
57388                                 }
57389                             },
57390                             value : (function() { return new Date(); })()
57391                         },
57392                         {
57393                             xtype: 'Separator',
57394                             xns: Roo.Toolbar
57395                         },
57396                         {
57397                             xtype: 'TextItem',
57398                             xns: Roo.Toolbar,
57399                             text : "Blue: in-active, green: in-active sup-event, red: de-active, purple: de-active sup-event"
57400                         },
57401                         {
57402                             xtype: 'Fill',
57403                             xns: Roo.Toolbar
57404                         },
57405                         {
57406                             xtype: 'Button',
57407                             xns: Roo.Toolbar,
57408                             listeners : {
57409                                 click : function (_self, e)
57410                                 {
57411                                     var sd = Date.parseDate(_this.monthField.getValue(), "Y-m-d");
57412                                     sd.setMonth(sd.getMonth()+1);
57413                                     _this.monthField.setValue(sd.format('Y-m-d'));
57414                                     _this.grid.ds.load({});
57415                                 }
57416                             },
57417                             text : "Next"
57418                         }
57419                     ]
57420                 },
57421                  
57422             }
57423         };
57424         
57425         *//*
57426  * Based on:
57427  * Ext JS Library 1.1.1
57428  * Copyright(c) 2006-2007, Ext JS, LLC.
57429  *
57430  * Originally Released Under LGPL - original licence link has changed is not relivant.
57431  *
57432  * Fork - LGPL
57433  * <script type="text/javascript">
57434  */
57435  
57436 /**
57437  * @class Roo.LoadMask
57438  * A simple utility class for generically masking elements while loading data.  If the element being masked has
57439  * an underlying {@link Roo.data.Store}, the masking will be automatically synchronized with the store's loading
57440  * process and the mask element will be cached for reuse.  For all other elements, this mask will replace the
57441  * element's UpdateManager load indicator and will be destroyed after the initial load.
57442  * @constructor
57443  * Create a new LoadMask
57444  * @param {String/HTMLElement/Roo.Element} el The element or DOM node, or its id
57445  * @param {Object} config The config object
57446  */
57447 Roo.LoadMask = function(el, config){
57448     this.el = Roo.get(el);
57449     Roo.apply(this, config);
57450     if(this.store){
57451         this.store.on('beforeload', this.onBeforeLoad, this);
57452         this.store.on('load', this.onLoad, this);
57453         this.store.on('loadexception', this.onLoadException, this);
57454         this.removeMask = false;
57455     }else{
57456         var um = this.el.getUpdateManager();
57457         um.showLoadIndicator = false; // disable the default indicator
57458         um.on('beforeupdate', this.onBeforeLoad, this);
57459         um.on('update', this.onLoad, this);
57460         um.on('failure', this.onLoad, this);
57461         this.removeMask = true;
57462     }
57463 };
57464
57465 Roo.LoadMask.prototype = {
57466     /**
57467      * @cfg {Boolean} removeMask
57468      * True to create a single-use mask that is automatically destroyed after loading (useful for page loads),
57469      * False to persist the mask element reference for multiple uses (e.g., for paged data widgets).  Defaults to false.
57470      */
57471     /**
57472      * @cfg {String} msg
57473      * The text to display in a centered loading message box (defaults to 'Loading...')
57474      */
57475     msg : 'Loading...',
57476     /**
57477      * @cfg {String} msgCls
57478      * The CSS class to apply to the loading message element (defaults to "x-mask-loading")
57479      */
57480     msgCls : 'x-mask-loading',
57481
57482     /**
57483      * Read-only. True if the mask is currently disabled so that it will not be displayed (defaults to false)
57484      * @type Boolean
57485      */
57486     disabled: false,
57487
57488     /**
57489      * Disables the mask to prevent it from being displayed
57490      */
57491     disable : function(){
57492        this.disabled = true;
57493     },
57494
57495     /**
57496      * Enables the mask so that it can be displayed
57497      */
57498     enable : function(){
57499         this.disabled = false;
57500     },
57501     
57502     onLoadException : function()
57503     {
57504         Roo.log(arguments);
57505         
57506         if (typeof(arguments[3]) != 'undefined') {
57507             Roo.MessageBox.alert("Error loading",arguments[3]);
57508         } 
57509         /*
57510         try {
57511             if (this.store && typeof(this.store.reader.jsonData.errorMsg) != 'undefined') {
57512                 Roo.MessageBox.alert("Error loading",this.store.reader.jsonData.errorMsg);
57513             }   
57514         } catch(e) {
57515             
57516         }
57517         */
57518     
57519         
57520         
57521         this.el.unmask(this.removeMask);
57522     },
57523     // private
57524     onLoad : function()
57525     {
57526         this.el.unmask(this.removeMask);
57527     },
57528
57529     // private
57530     onBeforeLoad : function(){
57531         if(!this.disabled){
57532             this.el.mask(this.msg, this.msgCls);
57533         }
57534     },
57535
57536     // private
57537     destroy : function(){
57538         if(this.store){
57539             this.store.un('beforeload', this.onBeforeLoad, this);
57540             this.store.un('load', this.onLoad, this);
57541             this.store.un('loadexception', this.onLoadException, this);
57542         }else{
57543             var um = this.el.getUpdateManager();
57544             um.un('beforeupdate', this.onBeforeLoad, this);
57545             um.un('update', this.onLoad, this);
57546             um.un('failure', this.onLoad, this);
57547         }
57548     }
57549 };/*
57550  * Based on:
57551  * Ext JS Library 1.1.1
57552  * Copyright(c) 2006-2007, Ext JS, LLC.
57553  *
57554  * Originally Released Under LGPL - original licence link has changed is not relivant.
57555  *
57556  * Fork - LGPL
57557  * <script type="text/javascript">
57558  */
57559
57560
57561 /**
57562  * @class Roo.XTemplate
57563  * @extends Roo.Template
57564  * Provides a template that can have nested templates for loops or conditionals. The syntax is:
57565 <pre><code>
57566 var t = new Roo.XTemplate(
57567         '&lt;select name="{name}"&gt;',
57568                 '&lt;tpl for="options"&gt;&lt;option value="{value:trim}"&gt;{text:ellipsis(10)}&lt;/option&gt;&lt;/tpl&gt;',
57569         '&lt;/select&gt;'
57570 );
57571  
57572 // then append, applying the master template values
57573  </code></pre>
57574  *
57575  * Supported features:
57576  *
57577  *  Tags:
57578
57579 <pre><code>
57580       {a_variable} - output encoded.
57581       {a_variable.format:("Y-m-d")} - call a method on the variable
57582       {a_variable:raw} - unencoded output
57583       {a_variable:toFixed(1,2)} - Roo.util.Format."toFixed"
57584       {a_variable:this.method_on_template(...)} - call a method on the template object.
57585  
57586 </code></pre>
57587  *  The tpl tag:
57588 <pre><code>
57589         &lt;tpl for="a_variable or condition.."&gt;&lt;/tpl&gt;
57590         &lt;tpl if="a_variable or condition"&gt;&lt;/tpl&gt;
57591         &lt;tpl exec="some javascript"&gt;&lt;/tpl&gt;
57592         &lt;tpl name="named_template"&gt;&lt;/tpl&gt; (experimental)
57593   
57594         &lt;tpl for="."&gt;&lt;/tpl&gt; - just iterate the property..
57595         &lt;tpl for=".."&gt;&lt;/tpl&gt; - iterates with the parent (probably the template) 
57596 </code></pre>
57597  *      
57598  */
57599 Roo.XTemplate = function()
57600 {
57601     Roo.XTemplate.superclass.constructor.apply(this, arguments);
57602     if (this.html) {
57603         this.compile();
57604     }
57605 };
57606
57607
57608 Roo.extend(Roo.XTemplate, Roo.Template, {
57609
57610     /**
57611      * The various sub templates
57612      */
57613     tpls : false,
57614     /**
57615      *
57616      * basic tag replacing syntax
57617      * WORD:WORD()
57618      *
57619      * // you can fake an object call by doing this
57620      *  x.t:(test,tesT) 
57621      * 
57622      */
57623     re : /\{([\w-\.]+)(?:\:([\w\.]*)(?:\((.*?)?\))?)?\}/g,
57624
57625     /**
57626      * compile the template
57627      *
57628      * This is not recursive, so I'm not sure how nested templates are really going to be handled..
57629      *
57630      */
57631     compile: function()
57632     {
57633         var s = this.html;
57634      
57635         s = ['<tpl>', s, '</tpl>'].join('');
57636     
57637         var re     = /<tpl\b[^>]*>((?:(?=([^<]+))\2|<(?!tpl\b[^>]*>))*?)<\/tpl>/,
57638             nameRe = /^<tpl\b[^>]*?for="(.*?)"/,
57639             ifRe   = /^<tpl\b[^>]*?if="(.*?)"/,
57640             execRe = /^<tpl\b[^>]*?exec="(.*?)"/,
57641             namedRe = /^<tpl\b[^>]*?name="(\w+)"/,  // named templates..
57642             m,
57643             id     = 0,
57644             tpls   = [];
57645     
57646         while(true == !!(m = s.match(re))){
57647             var forMatch   = m[0].match(nameRe),
57648                 ifMatch   = m[0].match(ifRe),
57649                 execMatch   = m[0].match(execRe),
57650                 namedMatch   = m[0].match(namedRe),
57651                 
57652                 exp  = null, 
57653                 fn   = null,
57654                 exec = null,
57655                 name = forMatch && forMatch[1] ? forMatch[1] : '';
57656                 
57657             if (ifMatch) {
57658                 // if - puts fn into test..
57659                 exp = ifMatch && ifMatch[1] ? ifMatch[1] : null;
57660                 if(exp){
57661                    fn = new Function('values', 'parent', 'with(values){ return '+(Roo.util.Format.htmlDecode(exp))+'; }');
57662                 }
57663             }
57664             
57665             if (execMatch) {
57666                 // exec - calls a function... returns empty if true is  returned.
57667                 exp = execMatch && execMatch[1] ? execMatch[1] : null;
57668                 if(exp){
57669                    exec = new Function('values', 'parent', 'with(values){ '+(Roo.util.Format.htmlDecode(exp))+'; }');
57670                 }
57671             }
57672             
57673             
57674             if (name) {
57675                 // for = 
57676                 switch(name){
57677                     case '.':  name = new Function('values', 'parent', 'with(values){ return values; }'); break;
57678                     case '..': name = new Function('values', 'parent', 'with(values){ return parent; }'); break;
57679                     default:   name = new Function('values', 'parent', 'with(values){ return '+name+'; }');
57680                 }
57681             }
57682             var uid = namedMatch ? namedMatch[1] : id;
57683             
57684             
57685             tpls.push({
57686                 id:     namedMatch ? namedMatch[1] : id,
57687                 target: name,
57688                 exec:   exec,
57689                 test:   fn,
57690                 body:   m[1] || ''
57691             });
57692             if (namedMatch) {
57693                 s = s.replace(m[0], '');
57694             } else { 
57695                 s = s.replace(m[0], '{xtpl'+ id + '}');
57696             }
57697             ++id;
57698         }
57699         this.tpls = [];
57700         for(var i = tpls.length-1; i >= 0; --i){
57701             this.compileTpl(tpls[i]);
57702             this.tpls[tpls[i].id] = tpls[i];
57703         }
57704         this.master = tpls[tpls.length-1];
57705         return this;
57706     },
57707     /**
57708      * same as applyTemplate, except it's done to one of the subTemplates
57709      * when using named templates, you can do:
57710      *
57711      * var str = pl.applySubTemplate('your-name', values);
57712      *
57713      * 
57714      * @param {Number} id of the template
57715      * @param {Object} values to apply to template
57716      * @param {Object} parent (normaly the instance of this object)
57717      */
57718     applySubTemplate : function(id, values, parent)
57719     {
57720         
57721         
57722         var t = this.tpls[id];
57723         
57724         
57725         try { 
57726             if(t.test && !t.test.call(this, values, parent)){
57727                 return '';
57728             }
57729         } catch(e) {
57730             Roo.log("Xtemplate.applySubTemplate 'test': Exception thrown");
57731             Roo.log(e.toString());
57732             Roo.log(t.test);
57733             return ''
57734         }
57735         try { 
57736             
57737             if(t.exec && t.exec.call(this, values, parent)){
57738                 return '';
57739             }
57740         } catch(e) {
57741             Roo.log("Xtemplate.applySubTemplate 'exec': Exception thrown");
57742             Roo.log(e.toString());
57743             Roo.log(t.exec);
57744             return ''
57745         }
57746         try {
57747             var vs = t.target ? t.target.call(this, values, parent) : values;
57748             parent = t.target ? values : parent;
57749             if(t.target && vs instanceof Array){
57750                 var buf = [];
57751                 for(var i = 0, len = vs.length; i < len; i++){
57752                     buf[buf.length] = t.compiled.call(this, vs[i], parent);
57753                 }
57754                 return buf.join('');
57755             }
57756             return t.compiled.call(this, vs, parent);
57757         } catch (e) {
57758             Roo.log("Xtemplate.applySubTemplate : Exception thrown");
57759             Roo.log(e.toString());
57760             Roo.log(t.compiled);
57761             return '';
57762         }
57763     },
57764
57765     compileTpl : function(tpl)
57766     {
57767         var fm = Roo.util.Format;
57768         var useF = this.disableFormats !== true;
57769         var sep = Roo.isGecko ? "+" : ",";
57770         var undef = function(str) {
57771             Roo.log("Property not found :"  + str);
57772             return '';
57773         };
57774         
57775         var fn = function(m, name, format, args)
57776         {
57777             //Roo.log(arguments);
57778             args = args ? args.replace(/\\'/g,"'") : args;
57779             //["{TEST:(a,b,c)}", "TEST", "", "a,b,c", 0, "{TEST:(a,b,c)}"]
57780             if (typeof(format) == 'undefined') {
57781                 format= 'htmlEncode';
57782             }
57783             if (format == 'raw' ) {
57784                 format = false;
57785             }
57786             
57787             if(name.substr(0, 4) == 'xtpl'){
57788                 return "'"+ sep +'this.applySubTemplate('+name.substr(4)+', values, parent)'+sep+"'";
57789             }
57790             
57791             // build an array of options to determine if value is undefined..
57792             
57793             // basically get 'xxxx.yyyy' then do
57794             // (typeof(xxxx) == 'undefined' || typeof(xxx.yyyy) == 'undefined') ?
57795             //    (function () { Roo.log("Property not found"); return ''; })() :
57796             //    ......
57797             
57798             var udef_ar = [];
57799             var lookfor = '';
57800             Roo.each(name.split('.'), function(st) {
57801                 lookfor += (lookfor.length ? '.': '') + st;
57802                 udef_ar.push(  "(typeof(" + lookfor + ") == 'undefined')"  );
57803             });
57804             
57805             var udef_st = '((' + udef_ar.join(" || ") +") ? undef('" + name + "') : "; // .. needs )
57806             
57807             
57808             if(format && useF){
57809                 
57810                 args = args ? ',' + args : "";
57811                  
57812                 if(format.substr(0, 5) != "this."){
57813                     format = "fm." + format + '(';
57814                 }else{
57815                     format = 'this.call("'+ format.substr(5) + '", ';
57816                     args = ", values";
57817                 }
57818                 
57819                 return "'"+ sep +   udef_st   +    format + name + args + "))"+sep+"'";
57820             }
57821              
57822             if (args.length) {
57823                 // called with xxyx.yuu:(test,test)
57824                 // change to ()
57825                 return "'"+ sep + udef_st  + name + '(' +  args + "))"+sep+"'";
57826             }
57827             // raw.. - :raw modifier..
57828             return "'"+ sep + udef_st  + name + ")"+sep+"'";
57829             
57830         };
57831         var body;
57832         // branched to use + in gecko and [].join() in others
57833         if(Roo.isGecko){
57834             body = "tpl.compiled = function(values, parent){  with(values) { return '" +
57835                    tpl.body.replace(/(\r\n|\n)/g, '\\n').replace(/'/g, "\\'").replace(this.re, fn) +
57836                     "';};};";
57837         }else{
57838             body = ["tpl.compiled = function(values, parent){  with (values) { return ['"];
57839             body.push(tpl.body.replace(/(\r\n|\n)/g,
57840                             '\\n').replace(/'/g, "\\'").replace(this.re, fn));
57841             body.push("'].join('');};};");
57842             body = body.join('');
57843         }
57844         
57845         Roo.debug && Roo.log(body.replace(/\\n/,'\n'));
57846        
57847         /** eval:var:tpl eval:var:fm eval:var:useF eval:var:undef  */
57848         eval(body);
57849         
57850         return this;
57851     },
57852
57853     applyTemplate : function(values){
57854         return this.master.compiled.call(this, values, {});
57855         //var s = this.subs;
57856     },
57857
57858     apply : function(){
57859         return this.applyTemplate.apply(this, arguments);
57860     }
57861
57862  });
57863
57864 Roo.XTemplate.from = function(el){
57865     el = Roo.getDom(el);
57866     return new Roo.XTemplate(el.value || el.innerHTML);
57867 };