roojs-core.js
[roojs1] / roojs-debug.js
1 /*
2  * Based on:
3  * Ext JS Library 1.1.1
4  * Copyright(c) 2006-2007, Ext JS, LLC.
5  *
6  * Originally Released Under LGPL - original licence link has changed is not relivant.
7  *
8  * Fork - LGPL
9  * <script type="text/javascript">
10  */
11  
12
13
14
15
16 // for old browsers
17 window["undefined"] = window["undefined"];
18
19 /**
20  * @class Roo
21  * Roo core utilities and functions.
22  * @singleton
23  */
24 var Roo = {}; 
25 /**
26  * Copies all the properties of config to obj.
27  * @param {Object} obj The receiver of the properties
28  * @param {Object} config The source of the properties
29  * @param {Object} defaults A different object that will also be applied for default values
30  * @return {Object} returns obj
31  * @member Roo apply
32  */
33
34  
35 Roo.apply = function(o, c, defaults){
36     if(defaults){
37         // no "this" reference for friendly out of scope calls
38         Roo.apply(o, defaults);
39     }
40     if(o && c && typeof c == 'object'){
41         for(var p in c){
42             o[p] = c[p];
43         }
44     }
45     return o;
46 };
47
48
49 (function(){
50     var idSeed = 0;
51     var ua = navigator.userAgent.toLowerCase();
52
53     var isStrict = document.compatMode == "CSS1Compat",
54         isOpera = ua.indexOf("opera") > -1,
55         isSafari = (/webkit|khtml/).test(ua),
56         isIE = ua.indexOf("msie") > -1,
57         isIE7 = ua.indexOf("msie 7") > -1,
58         isGecko = !isSafari && ua.indexOf("gecko") > -1,
59         isBorderBox = isIE && !isStrict,
60         isWindows = (ua.indexOf("windows") != -1 || ua.indexOf("win32") != -1),
61         isMac = (ua.indexOf("macintosh") != -1 || ua.indexOf("mac os x") != -1),
62         isLinux = (ua.indexOf("linux") != -1),
63         isSecure = window.location.href.toLowerCase().indexOf("https") === 0,
64         isTouch =  'ontouchstart' in window || window.DocumentTouch && document instanceof DocumentTouch;
65     // remove css image flicker
66         if(isIE && !isIE7){
67         try{
68             document.execCommand("BackgroundImageCache", false, true);
69         }catch(e){}
70     }
71     
72     Roo.apply(Roo, {
73         /**
74          * True if the browser is in strict mode
75          * @type Boolean
76          */
77         isStrict : isStrict,
78         /**
79          * True if the page is running over SSL
80          * @type Boolean
81          */
82         isSecure : isSecure,
83         /**
84          * True when the document is fully initialized and ready for action
85          * @type Boolean
86          */
87         isReady : false,
88         /**
89          * Turn on debugging output (currently only the factory uses this)
90          * @type Boolean
91          */
92         
93         debug: false,
94
95         /**
96          * True to automatically uncache orphaned Roo.Elements periodically (defaults to true)
97          * @type Boolean
98          */
99         enableGarbageCollector : true,
100
101         /**
102          * True to automatically purge event listeners after uncaching an element (defaults to false).
103          * Note: this only happens if enableGarbageCollector is true.
104          * @type Boolean
105          */
106         enableListenerCollection:false,
107
108         /**
109          * URL to a blank file used by Roo when in secure mode for iframe src and onReady src to prevent
110          * the IE insecure content warning (defaults to javascript:false).
111          * @type String
112          */
113         SSL_SECURE_URL : "javascript:false",
114
115         /**
116          * URL to a 1x1 transparent gif image used by Roo to create inline icons with CSS background images. (Defaults to
117          * "http://Roojs.com/s.gif" and you should change this to a URL on your server).
118          * @type String
119          */
120         BLANK_IMAGE_URL : "http:/"+"/localhost/s.gif",
121
122         emptyFn : function(){},
123         
124         /**
125          * Copies all the properties of config to obj if they don't already exist.
126          * @param {Object} obj The receiver of the properties
127          * @param {Object} config The source of the properties
128          * @return {Object} returns obj
129          */
130         applyIf : function(o, c){
131             if(o && c){
132                 for(var p in c){
133                     if(typeof o[p] == "undefined"){ o[p] = c[p]; }
134                 }
135             }
136             return o;
137         },
138
139         /**
140          * Applies event listeners to elements by selectors when the document is ready.
141          * The event name is specified with an @ suffix.
142 <pre><code>
143 Roo.addBehaviors({
144    // add a listener for click on all anchors in element with id foo
145    '#foo a@click' : function(e, t){
146        // do something
147    },
148
149    // add the same listener to multiple selectors (separated by comma BEFORE the @)
150    '#foo a, #bar span.some-class@mouseover' : function(){
151        // do something
152    }
153 });
154 </code></pre>
155          * @param {Object} obj The list of behaviors to apply
156          */
157         addBehaviors : function(o){
158             if(!Roo.isReady){
159                 Roo.onReady(function(){
160                     Roo.addBehaviors(o);
161                 });
162                 return;
163             }
164             var cache = {}; // simple cache for applying multiple behaviors to same selector does query multiple times
165             for(var b in o){
166                 var parts = b.split('@');
167                 if(parts[1]){ // for Object prototype breakers
168                     var s = parts[0];
169                     if(!cache[s]){
170                         cache[s] = Roo.select(s);
171                     }
172                     cache[s].on(parts[1], o[b]);
173                 }
174             }
175             cache = null;
176         },
177
178         /**
179          * Generates unique ids. If the element already has an id, it is unchanged
180          * @param {String/HTMLElement/Element} el (optional) The element to generate an id for
181          * @param {String} prefix (optional) Id prefix (defaults "Roo-gen")
182          * @return {String} The generated Id.
183          */
184         id : function(el, prefix){
185             prefix = prefix || "roo-gen";
186             el = Roo.getDom(el);
187             var id = prefix + (++idSeed);
188             return el ? (el.id ? el.id : (el.id = id)) : id;
189         },
190          
191        
192         /**
193          * Extends one class with another class and optionally overrides members with the passed literal. This class
194          * also adds the function "override()" to the class that can be used to override
195          * members on an instance.
196          * @param {Object} subclass The class inheriting the functionality
197          * @param {Object} superclass The class being extended
198          * @param {Object} overrides (optional) A literal with members
199          * @method extend
200          */
201         extend : function(){
202             // inline overrides
203             var io = function(o){
204                 for(var m in o){
205                     this[m] = o[m];
206                 }
207             };
208             return function(sb, sp, overrides){
209                 if(typeof sp == 'object'){ // eg. prototype, rather than function constructor..
210                     overrides = sp;
211                     sp = sb;
212                     sb = function(){sp.apply(this, arguments);};
213                 }
214                 var F = function(){}, sbp, spp = sp.prototype;
215                 F.prototype = spp;
216                 sbp = sb.prototype = new F();
217                 sbp.constructor=sb;
218                 sb.superclass=spp;
219                 
220                 if(spp.constructor == Object.prototype.constructor){
221                     spp.constructor=sp;
222                    
223                 }
224                 
225                 sb.override = function(o){
226                     Roo.override(sb, o);
227                 };
228                 sbp.override = io;
229                 Roo.override(sb, overrides);
230                 return sb;
231             };
232         }(),
233
234         /**
235          * Adds a list of functions to the prototype of an existing class, overwriting any existing methods with the same name.
236          * Usage:<pre><code>
237 Roo.override(MyClass, {
238     newMethod1: function(){
239         // etc.
240     },
241     newMethod2: function(foo){
242         // etc.
243     }
244 });
245  </code></pre>
246          * @param {Object} origclass The class to override
247          * @param {Object} overrides The list of functions to add to origClass.  This should be specified as an object literal
248          * containing one or more methods.
249          * @method override
250          */
251         override : function(origclass, overrides){
252             if(overrides){
253                 var p = origclass.prototype;
254                 for(var method in overrides){
255                     p[method] = overrides[method];
256                 }
257             }
258         },
259         /**
260          * Creates namespaces to be used for scoping variables and classes so that they are not global.  Usage:
261          * <pre><code>
262 Roo.namespace('Company', 'Company.data');
263 Company.Widget = function() { ... }
264 Company.data.CustomStore = function(config) { ... }
265 </code></pre>
266          * @param {String} namespace1
267          * @param {String} namespace2
268          * @param {String} etc
269          * @method namespace
270          */
271         namespace : function(){
272             var a=arguments, o=null, i, j, d, rt;
273             for (i=0; i<a.length; ++i) {
274                 d=a[i].split(".");
275                 rt = d[0];
276                 /** eval:var:o */
277                 eval('if (typeof ' + rt + ' == "undefined"){' + rt + ' = {};} o = ' + rt + ';');
278                 for (j=1; j<d.length; ++j) {
279                     o[d[j]]=o[d[j]] || {};
280                     o=o[d[j]];
281                 }
282             }
283         },
284         /**
285          * Creates namespaces to be used for scoping variables and classes so that they are not global.  Usage:
286          * <pre><code>
287 Roo.factory({ xns: Roo.data, xtype : 'Store', .....});
288 Roo.factory(conf, Roo.data);
289 </code></pre>
290          * @param {String} classname
291          * @param {String} namespace (optional)
292          * @method factory
293          */
294          
295         factory : function(c, ns)
296         {
297             // no xtype, no ns or c.xns - or forced off by c.xns
298             if (!c.xtype   || (!ns && !c.xns) ||  (c.xns === false)) { // not enough info...
299                 return c;
300             }
301             ns = c.xns ? c.xns : ns; // if c.xns is set, then use that..
302             if (c.constructor == ns[c.xtype]) {// already created...
303                 return c;
304             }
305             if (ns[c.xtype]) {
306                 if (Roo.debug) Roo.log("Roo.Factory(" + c.xtype + ")");
307                 var ret = new ns[c.xtype](c);
308                 ret.xns = false;
309                 return ret;
310             }
311             c.xns = false; // prevent recursion..
312             return c;
313         },
314          /**
315          * Logs to console if it can.
316          *
317          * @param {String|Object} string
318          * @method log
319          */
320         log : function(s)
321         {
322             if ((typeof(console) == 'undefined') || (typeof(console.log) == 'undefined')) {
323                 return; // alerT?
324             }
325             console.log(s);
326             
327         },
328         /**
329          * Takes an object and converts it to an encoded URL. e.g. Roo.urlEncode({foo: 1, bar: 2}); would return "foo=1&bar=2".  Optionally, property values can be arrays, instead of keys and the resulting string that's returned will contain a name/value pair for each array value.
330          * @param {Object} o
331          * @return {String}
332          */
333         urlEncode : function(o){
334             if(!o){
335                 return "";
336             }
337             var buf = [];
338             for(var key in o){
339                 var ov = o[key], k = Roo.encodeURIComponent(key);
340                 var type = typeof ov;
341                 if(type == 'undefined'){
342                     buf.push(k, "=&");
343                 }else if(type != "function" && type != "object"){
344                     buf.push(k, "=", Roo.encodeURIComponent(ov), "&");
345                 }else if(ov instanceof Array){
346                     if (ov.length) {
347                             for(var i = 0, len = ov.length; i < len; i++) {
348                                 buf.push(k, "=", Roo.encodeURIComponent(ov[i] === undefined ? '' : ov[i]), "&");
349                             }
350                         } else {
351                             buf.push(k, "=&");
352                         }
353                 }
354             }
355             buf.pop();
356             return buf.join("");
357         },
358          /**
359          * Safe version of encodeURIComponent
360          * @param {String} data 
361          * @return {String} 
362          */
363         
364         encodeURIComponent : function (data)
365         {
366             try {
367                 return encodeURIComponent(data);
368             } catch(e) {} // should be an uri encode error.
369             
370             if (data == '' || data == null){
371                return '';
372             }
373             // http://stackoverflow.com/questions/2596483/unicode-and-uri-encoding-decoding-and-escaping-in-javascript
374             function nibble_to_hex(nibble){
375                 var chars = '0123456789ABCDEF';
376                 return chars.charAt(nibble);
377             }
378             data = data.toString();
379             var buffer = '';
380             for(var i=0; i<data.length; i++){
381                 var c = data.charCodeAt(i);
382                 var bs = new Array();
383                 if (c > 0x10000){
384                         // 4 bytes
385                     bs[0] = 0xF0 | ((c & 0x1C0000) >>> 18);
386                     bs[1] = 0x80 | ((c & 0x3F000) >>> 12);
387                     bs[2] = 0x80 | ((c & 0xFC0) >>> 6);
388                     bs[3] = 0x80 | (c & 0x3F);
389                 }else if (c > 0x800){
390                          // 3 bytes
391                     bs[0] = 0xE0 | ((c & 0xF000) >>> 12);
392                     bs[1] = 0x80 | ((c & 0xFC0) >>> 6);
393                     bs[2] = 0x80 | (c & 0x3F);
394                 }else if (c > 0x80){
395                        // 2 bytes
396                     bs[0] = 0xC0 | ((c & 0x7C0) >>> 6);
397                     bs[1] = 0x80 | (c & 0x3F);
398                 }else{
399                         // 1 byte
400                     bs[0] = c;
401                 }
402                 for(var j=0; j<bs.length; j++){
403                     var b = bs[j];
404                     var hex = nibble_to_hex((b & 0xF0) >>> 4) 
405                             + nibble_to_hex(b &0x0F);
406                     buffer += '%'+hex;
407                }
408             }
409             return buffer;    
410              
411         },
412
413         /**
414          * Takes an encoded URL and and converts it to an object. e.g. Roo.urlDecode("foo=1&bar=2"); would return {foo: 1, bar: 2} or Roo.urlDecode("foo=1&bar=2&bar=3&bar=4", true); would return {foo: 1, bar: [2, 3, 4]}.
415          * @param {String} string
416          * @param {Boolean} overwrite (optional) Items of the same name will overwrite previous values instead of creating an an array (Defaults to false).
417          * @return {Object} A literal with members
418          */
419         urlDecode : function(string, overwrite){
420             if(!string || !string.length){
421                 return {};
422             }
423             var obj = {};
424             var pairs = string.split('&');
425             var pair, name, value;
426             for(var i = 0, len = pairs.length; i < len; i++){
427                 pair = pairs[i].split('=');
428                 name = decodeURIComponent(pair[0]);
429                 value = decodeURIComponent(pair[1]);
430                 if(overwrite !== true){
431                     if(typeof obj[name] == "undefined"){
432                         obj[name] = value;
433                     }else if(typeof obj[name] == "string"){
434                         obj[name] = [obj[name]];
435                         obj[name].push(value);
436                     }else{
437                         obj[name].push(value);
438                     }
439                 }else{
440                     obj[name] = value;
441                 }
442             }
443             return obj;
444         },
445
446         /**
447          * Iterates an array calling the passed function with each item, stopping if your function returns false. If the
448          * passed array is not really an array, your function is called once with it.
449          * The supplied function is called with (Object item, Number index, Array allItems).
450          * @param {Array/NodeList/Mixed} array
451          * @param {Function} fn
452          * @param {Object} scope
453          */
454         each : function(array, fn, scope){
455             if(typeof array.length == "undefined" || typeof array == "string"){
456                 array = [array];
457             }
458             for(var i = 0, len = array.length; i < len; i++){
459                 if(fn.call(scope || array[i], array[i], i, array) === false){ return i; };
460             }
461         },
462
463         // deprecated
464         combine : function(){
465             var as = arguments, l = as.length, r = [];
466             for(var i = 0; i < l; i++){
467                 var a = as[i];
468                 if(a instanceof Array){
469                     r = r.concat(a);
470                 }else if(a.length !== undefined && !a.substr){
471                     r = r.concat(Array.prototype.slice.call(a, 0));
472                 }else{
473                     r.push(a);
474                 }
475             }
476             return r;
477         },
478
479         /**
480          * Escapes the passed string for use in a regular expression
481          * @param {String} str
482          * @return {String}
483          */
484         escapeRe : function(s) {
485             return s.replace(/([.*+?^${}()|[\]\/\\])/g, "\\$1");
486         },
487
488         // internal
489         callback : function(cb, scope, args, delay){
490             if(typeof cb == "function"){
491                 if(delay){
492                     cb.defer(delay, scope, args || []);
493                 }else{
494                     cb.apply(scope, args || []);
495                 }
496             }
497         },
498
499         /**
500          * Return the dom node for the passed string (id), dom node, or Roo.Element
501          * @param {String/HTMLElement/Roo.Element} el
502          * @return HTMLElement
503          */
504         getDom : function(el){
505             if(!el){
506                 return null;
507             }
508             return el.dom ? el.dom : (typeof el == 'string' ? document.getElementById(el) : el);
509         },
510
511         /**
512         * Shorthand for {@link Roo.ComponentMgr#get}
513         * @param {String} id
514         * @return Roo.Component
515         */
516         getCmp : function(id){
517             return Roo.ComponentMgr.get(id);
518         },
519          
520         num : function(v, defaultValue){
521             if(typeof v != 'number'){
522                 return defaultValue;
523             }
524             return v;
525         },
526
527         destroy : function(){
528             for(var i = 0, a = arguments, len = a.length; i < len; i++) {
529                 var as = a[i];
530                 if(as){
531                     if(as.dom){
532                         as.removeAllListeners();
533                         as.remove();
534                         continue;
535                     }
536                     if(typeof as.purgeListeners == 'function'){
537                         as.purgeListeners();
538                     }
539                     if(typeof as.destroy == 'function'){
540                         as.destroy();
541                     }
542                 }
543             }
544         },
545
546         // inpired by a similar function in mootools library
547         /**
548          * Returns the type of object that is passed in. If the object passed in is null or undefined it
549          * return false otherwise it returns one of the following values:<ul>
550          * <li><b>string</b>: If the object passed is a string</li>
551          * <li><b>number</b>: If the object passed is a number</li>
552          * <li><b>boolean</b>: If the object passed is a boolean value</li>
553          * <li><b>function</b>: If the object passed is a function reference</li>
554          * <li><b>object</b>: If the object passed is an object</li>
555          * <li><b>array</b>: If the object passed is an array</li>
556          * <li><b>regexp</b>: If the object passed is a regular expression</li>
557          * <li><b>element</b>: If the object passed is a DOM Element</li>
558          * <li><b>nodelist</b>: If the object passed is a DOM NodeList</li>
559          * <li><b>textnode</b>: If the object passed is a DOM text node and contains something other than whitespace</li>
560          * <li><b>whitespace</b>: If the object passed is a DOM text node and contains only whitespace</li>
561          * @param {Mixed} object
562          * @return {String}
563          */
564         type : function(o){
565             if(o === undefined || o === null){
566                 return false;
567             }
568             if(o.htmlElement){
569                 return 'element';
570             }
571             var t = typeof o;
572             if(t == 'object' && o.nodeName) {
573                 switch(o.nodeType) {
574                     case 1: return 'element';
575                     case 3: return (/\S/).test(o.nodeValue) ? 'textnode' : 'whitespace';
576                 }
577             }
578             if(t == 'object' || t == 'function') {
579                 switch(o.constructor) {
580                     case Array: return 'array';
581                     case RegExp: return 'regexp';
582                 }
583                 if(typeof o.length == 'number' && typeof o.item == 'function') {
584                     return 'nodelist';
585                 }
586             }
587             return t;
588         },
589
590         /**
591          * Returns true if the passed value is null, undefined or an empty string (optional).
592          * @param {Mixed} value The value to test
593          * @param {Boolean} allowBlank (optional) Pass true if an empty string is not considered empty
594          * @return {Boolean}
595          */
596         isEmpty : function(v, allowBlank){
597             return v === null || v === undefined || (!allowBlank ? v === '' : false);
598         },
599         
600         /** @type Boolean */
601         isOpera : isOpera,
602         /** @type Boolean */
603         isSafari : isSafari,
604         /** @type Boolean */
605         isIE : isIE,
606         /** @type Boolean */
607         isIE7 : isIE7,
608         /** @type Boolean */
609         isGecko : isGecko,
610         /** @type Boolean */
611         isBorderBox : isBorderBox,
612         /** @type Boolean */
613         isWindows : isWindows,
614         /** @type Boolean */
615         isLinux : isLinux,
616         /** @type Boolean */
617         isMac : isMac,
618         /** @type Boolean */
619         isTouch : isTouch,
620
621         /**
622          * By default, Ext intelligently decides whether floating elements should be shimmed. If you are using flash,
623          * you may want to set this to true.
624          * @type Boolean
625          */
626         useShims : ((isIE && !isIE7) || (isGecko && isMac)),
627         
628         
629                 
630         /**
631          * Selects a single element as a Roo Element
632          * This is about as close as you can get to jQuery's $('do crazy stuff')
633          * @param {String} selector The selector/xpath query
634          * @param {Node} root (optional) The start of the query (defaults to document).
635          * @return {Roo.Element}
636          */
637         selectNode : function(selector, root) 
638         {
639             var node = Roo.DomQuery.selectNode(selector,root);
640             return node ? Roo.get(node) : new Roo.Element(false);
641         }
642         
643     });
644
645
646 })();
647
648 Roo.namespace("Roo", "Roo.util", "Roo.grid", "Roo.dd", "Roo.tree", "Roo.data",
649                 "Roo.form", "Roo.menu", "Roo.state", "Roo.lib", "Roo.layout",
650                 "Roo.app", "Roo.ux",
651                 "Roo.bootstrap",
652                 "Roo.bootstrap.dash");
653 /*
654  * Based on:
655  * Ext JS Library 1.1.1
656  * Copyright(c) 2006-2007, Ext JS, LLC.
657  *
658  * Originally Released Under LGPL - original licence link has changed is not relivant.
659  *
660  * Fork - LGPL
661  * <script type="text/javascript">
662  */
663
664 (function() {    
665     // wrappedn so fnCleanup is not in global scope...
666     if(Roo.isIE) {
667         function fnCleanUp() {
668             var p = Function.prototype;
669             delete p.createSequence;
670             delete p.defer;
671             delete p.createDelegate;
672             delete p.createCallback;
673             delete p.createInterceptor;
674
675             window.detachEvent("onunload", fnCleanUp);
676         }
677         window.attachEvent("onunload", fnCleanUp);
678     }
679 })();
680
681
682 /**
683  * @class Function
684  * These functions are available on every Function object (any JavaScript function).
685  */
686 Roo.apply(Function.prototype, {
687      /**
688      * Creates a callback that passes arguments[0], arguments[1], arguments[2], ...
689      * Call directly on any function. Example: <code>myFunction.createCallback(myarg, myarg2)</code>
690      * Will create a function that is bound to those 2 args.
691      * @return {Function} The new function
692     */
693     createCallback : function(/*args...*/){
694         // make args available, in function below
695         var args = arguments;
696         var method = this;
697         return function() {
698             return method.apply(window, args);
699         };
700     },
701
702     /**
703      * Creates a delegate (callback) that sets the scope to obj.
704      * Call directly on any function. Example: <code>this.myFunction.createDelegate(this)</code>
705      * Will create a function that is automatically scoped to this.
706      * @param {Object} obj (optional) The object for which the scope is set
707      * @param {Array} args (optional) Overrides arguments for the call. (Defaults to the arguments passed by the caller)
708      * @param {Boolean/Number} appendArgs (optional) if True args are appended to call args instead of overriding,
709      *                                             if a number the args are inserted at the specified position
710      * @return {Function} The new function
711      */
712     createDelegate : function(obj, args, appendArgs){
713         var method = this;
714         return function() {
715             var callArgs = args || arguments;
716             if(appendArgs === true){
717                 callArgs = Array.prototype.slice.call(arguments, 0);
718                 callArgs = callArgs.concat(args);
719             }else if(typeof appendArgs == "number"){
720                 callArgs = Array.prototype.slice.call(arguments, 0); // copy arguments first
721                 var applyArgs = [appendArgs, 0].concat(args); // create method call params
722                 Array.prototype.splice.apply(callArgs, applyArgs); // splice them in
723             }
724             return method.apply(obj || window, callArgs);
725         };
726     },
727
728     /**
729      * Calls this function after the number of millseconds specified.
730      * @param {Number} millis The number of milliseconds for the setTimeout call (if 0 the function is executed immediately)
731      * @param {Object} obj (optional) The object for which the scope is set
732      * @param {Array} args (optional) Overrides arguments for the call. (Defaults to the arguments passed by the caller)
733      * @param {Boolean/Number} appendArgs (optional) if True args are appended to call args instead of overriding,
734      *                                             if a number the args are inserted at the specified position
735      * @return {Number} The timeout id that can be used with clearTimeout
736      */
737     defer : function(millis, obj, args, appendArgs){
738         var fn = this.createDelegate(obj, args, appendArgs);
739         if(millis){
740             return setTimeout(fn, millis);
741         }
742         fn();
743         return 0;
744     },
745     /**
746      * Create a combined function call sequence of the original function + the passed function.
747      * The resulting function returns the results of the original function.
748      * The passed fcn is called with the parameters of the original function
749      * @param {Function} fcn The function to sequence
750      * @param {Object} scope (optional) The scope of the passed fcn (Defaults to scope of original function or window)
751      * @return {Function} The new function
752      */
753     createSequence : function(fcn, scope){
754         if(typeof fcn != "function"){
755             return this;
756         }
757         var method = this;
758         return function() {
759             var retval = method.apply(this || window, arguments);
760             fcn.apply(scope || this || window, arguments);
761             return retval;
762         };
763     },
764
765     /**
766      * Creates an interceptor function. The passed fcn is called before the original one. If it returns false, the original one is not called.
767      * The resulting function returns the results of the original function.
768      * The passed fcn is called with the parameters of the original function.
769      * @addon
770      * @param {Function} fcn The function to call before the original
771      * @param {Object} scope (optional) The scope of the passed fcn (Defaults to scope of original function or window)
772      * @return {Function} The new function
773      */
774     createInterceptor : function(fcn, scope){
775         if(typeof fcn != "function"){
776             return this;
777         }
778         var method = this;
779         return function() {
780             fcn.target = this;
781             fcn.method = method;
782             if(fcn.apply(scope || this || window, arguments) === false){
783                 return;
784             }
785             return method.apply(this || window, arguments);
786         };
787     }
788 });
789 /*
790  * Based on:
791  * Ext JS Library 1.1.1
792  * Copyright(c) 2006-2007, Ext JS, LLC.
793  *
794  * Originally Released Under LGPL - original licence link has changed is not relivant.
795  *
796  * Fork - LGPL
797  * <script type="text/javascript">
798  */
799
800 Roo.applyIf(String, {
801     
802     /** @scope String */
803     
804     /**
805      * Escapes the passed string for ' and \
806      * @param {String} string The string to escape
807      * @return {String} The escaped string
808      * @static
809      */
810     escape : function(string) {
811         return string.replace(/('|\\)/g, "\\$1");
812     },
813
814     /**
815      * Pads the left side of a string with a specified character.  This is especially useful
816      * for normalizing number and date strings.  Example usage:
817      * <pre><code>
818 var s = String.leftPad('123', 5, '0');
819 // s now contains the string: '00123'
820 </code></pre>
821      * @param {String} string The original string
822      * @param {Number} size The total length of the output string
823      * @param {String} char (optional) The character with which to pad the original string (defaults to empty string " ")
824      * @return {String} The padded string
825      * @static
826      */
827     leftPad : function (val, size, ch) {
828         var result = new String(val);
829         if(ch === null || ch === undefined || ch === '') {
830             ch = " ";
831         }
832         while (result.length < size) {
833             result = ch + result;
834         }
835         return result;
836     },
837
838     /**
839      * Allows you to define a tokenized string and pass an arbitrary number of arguments to replace the tokens.  Each
840      * token must be unique, and must increment in the format {0}, {1}, etc.  Example usage:
841      * <pre><code>
842 var cls = 'my-class', text = 'Some text';
843 var s = String.format('<div class="{0}">{1}</div>', cls, text);
844 // s now contains the string: '<div class="my-class">Some text</div>'
845 </code></pre>
846      * @param {String} string The tokenized string to be formatted
847      * @param {String} value1 The value to replace token {0}
848      * @param {String} value2 Etc...
849      * @return {String} The formatted string
850      * @static
851      */
852     format : function(format){
853         var args = Array.prototype.slice.call(arguments, 1);
854         return format.replace(/\{(\d+)\}/g, function(m, i){
855             return Roo.util.Format.htmlEncode(args[i]);
856         });
857     }
858 });
859
860 /**
861  * Utility function that allows you to easily switch a string between two alternating values.  The passed value
862  * is compared to the current string, and if they are equal, the other value that was passed in is returned.  If
863  * they are already different, the first value passed in is returned.  Note that this method returns the new value
864  * but does not change the current string.
865  * <pre><code>
866 // alternate sort directions
867 sort = sort.toggle('ASC', 'DESC');
868
869 // instead of conditional logic:
870 sort = (sort == 'ASC' ? 'DESC' : 'ASC');
871 </code></pre>
872  * @param {String} value The value to compare to the current string
873  * @param {String} other The new value to use if the string already equals the first value passed in
874  * @return {String} The new value
875  */
876  
877 String.prototype.toggle = function(value, other){
878     return this == value ? other : value;
879 };/*
880  * Based on:
881  * Ext JS Library 1.1.1
882  * Copyright(c) 2006-2007, Ext JS, LLC.
883  *
884  * Originally Released Under LGPL - original licence link has changed is not relivant.
885  *
886  * Fork - LGPL
887  * <script type="text/javascript">
888  */
889
890  /**
891  * @class Number
892  */
893 Roo.applyIf(Number.prototype, {
894     /**
895      * Checks whether or not the current number is within a desired range.  If the number is already within the
896      * range it is returned, otherwise the min or max value is returned depending on which side of the range is
897      * exceeded.  Note that this method returns the constrained value but does not change the current number.
898      * @param {Number} min The minimum number in the range
899      * @param {Number} max The maximum number in the range
900      * @return {Number} The constrained value if outside the range, otherwise the current value
901      */
902     constrain : function(min, max){
903         return Math.min(Math.max(this, min), max);
904     }
905 });/*
906  * Based on:
907  * Ext JS Library 1.1.1
908  * Copyright(c) 2006-2007, Ext JS, LLC.
909  *
910  * Originally Released Under LGPL - original licence link has changed is not relivant.
911  *
912  * Fork - LGPL
913  * <script type="text/javascript">
914  */
915  /**
916  * @class Array
917  */
918 Roo.applyIf(Array.prototype, {
919     /**
920      * Checks whether or not the specified object exists in the array.
921      * @param {Object} o The object to check for
922      * @return {Number} The index of o in the array (or -1 if it is not found)
923      */
924     indexOf : function(o){
925        for (var i = 0, len = this.length; i < len; i++){
926               if(this[i] == o) return i;
927        }
928            return -1;
929     },
930
931     /**
932      * Removes the specified object from the array.  If the object is not found nothing happens.
933      * @param {Object} o The object to remove
934      */
935     remove : function(o){
936        var index = this.indexOf(o);
937        if(index != -1){
938            this.splice(index, 1);
939        }
940     },
941     /**
942      * Map (JS 1.6 compatibility)
943      * @param {Function} function  to call
944      */
945     map : function(fun )
946     {
947         var len = this.length >>> 0;
948         if (typeof fun != "function")
949             throw new TypeError();
950
951         var res = new Array(len);
952         var thisp = arguments[1];
953         for (var i = 0; i < len; i++)
954         {
955             if (i in this)
956                 res[i] = fun.call(thisp, this[i], i, this);
957         }
958
959         return res;
960     }
961     
962 });
963
964
965  /*
966  * Based on:
967  * Ext JS Library 1.1.1
968  * Copyright(c) 2006-2007, Ext JS, LLC.
969  *
970  * Originally Released Under LGPL - original licence link has changed is not relivant.
971  *
972  * Fork - LGPL
973  * <script type="text/javascript">
974  */
975
976 /**
977  * @class Date
978  *
979  * The date parsing and format syntax is a subset of
980  * <a href="http://www.php.net/date">PHP's date() function</a>, and the formats that are
981  * supported will provide results equivalent to their PHP versions.
982  *
983  * Following is the list of all currently supported formats:
984  *<pre>
985 Sample date:
986 'Wed Jan 10 2007 15:05:01 GMT-0600 (Central Standard Time)'
987
988 Format  Output      Description
989 ------  ----------  --------------------------------------------------------------
990   d      10         Day of the month, 2 digits with leading zeros
991   D      Wed        A textual representation of a day, three letters
992   j      10         Day of the month without leading zeros
993   l      Wednesday  A full textual representation of the day of the week
994   S      th         English ordinal day of month suffix, 2 chars (use with j)
995   w      3          Numeric representation of the day of the week
996   z      9          The julian date, or day of the year (0-365)
997   W      01         ISO-8601 2-digit week number of year, weeks starting on Monday (00-52)
998   F      January    A full textual representation of the month
999   m      01         Numeric representation of a month, with leading zeros
1000   M      Jan        Month name abbreviation, three letters
1001   n      1          Numeric representation of a month, without leading zeros
1002   t      31         Number of days in the given month
1003   L      0          Whether it's a leap year (1 if it is a leap year, else 0)
1004   Y      2007       A full numeric representation of a year, 4 digits
1005   y      07         A two digit representation of a year
1006   a      pm         Lowercase Ante meridiem and Post meridiem
1007   A      PM         Uppercase Ante meridiem and Post meridiem
1008   g      3          12-hour format of an hour without leading zeros
1009   G      15         24-hour format of an hour without leading zeros
1010   h      03         12-hour format of an hour with leading zeros
1011   H      15         24-hour format of an hour with leading zeros
1012   i      05         Minutes with leading zeros
1013   s      01         Seconds, with leading zeros
1014   O      -0600      Difference to Greenwich time (GMT) in hours (Allows +08, without minutes)
1015   P      -06:00     Difference to Greenwich time (GMT) with colon between hours and minutes
1016   T      CST        Timezone setting of the machine running the code
1017   Z      -21600     Timezone offset in seconds (negative if west of UTC, positive if east)
1018 </pre>
1019  *
1020  * Example usage (note that you must escape format specifiers with '\\' to render them as character literals):
1021  * <pre><code>
1022 var dt = new Date('1/10/2007 03:05:01 PM GMT-0600');
1023 document.write(dt.format('Y-m-d'));                         //2007-01-10
1024 document.write(dt.format('F j, Y, g:i a'));                 //January 10, 2007, 3:05 pm
1025 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
1026  </code></pre>
1027  *
1028  * Here are some standard date/time patterns that you might find helpful.  They
1029  * are not part of the source of Date.js, but to use them you can simply copy this
1030  * block of code into any script that is included after Date.js and they will also become
1031  * globally available on the Date object.  Feel free to add or remove patterns as needed in your code.
1032  * <pre><code>
1033 Date.patterns = {
1034     ISO8601Long:"Y-m-d H:i:s",
1035     ISO8601Short:"Y-m-d",
1036     ShortDate: "n/j/Y",
1037     LongDate: "l, F d, Y",
1038     FullDateTime: "l, F d, Y g:i:s A",
1039     MonthDay: "F d",
1040     ShortTime: "g:i A",
1041     LongTime: "g:i:s A",
1042     SortableDateTime: "Y-m-d\\TH:i:s",
1043     UniversalSortableDateTime: "Y-m-d H:i:sO",
1044     YearMonth: "F, Y"
1045 };
1046 </code></pre>
1047  *
1048  * Example usage:
1049  * <pre><code>
1050 var dt = new Date();
1051 document.write(dt.format(Date.patterns.ShortDate));
1052  </code></pre>
1053  */
1054
1055 /*
1056  * Most of the date-formatting functions below are the excellent work of Baron Schwartz.
1057  * They generate precompiled functions from date formats instead of parsing and
1058  * processing the pattern every time you format a date.  These functions are available
1059  * on every Date object (any javascript function).
1060  *
1061  * The original article and download are here:
1062  * http://www.xaprb.com/blog/2005/12/12/javascript-closures-for-runtime-efficiency/
1063  *
1064  */
1065  
1066  
1067  // was in core
1068 /**
1069  Returns the number of milliseconds between this date and date
1070  @param {Date} date (optional) Defaults to now
1071  @return {Number} The diff in milliseconds
1072  @member Date getElapsed
1073  */
1074 Date.prototype.getElapsed = function(date) {
1075         return Math.abs((date || new Date()).getTime()-this.getTime());
1076 };
1077 // was in date file..
1078
1079
1080 // private
1081 Date.parseFunctions = {count:0};
1082 // private
1083 Date.parseRegexes = [];
1084 // private
1085 Date.formatFunctions = {count:0};
1086
1087 // private
1088 Date.prototype.dateFormat = function(format) {
1089     if (Date.formatFunctions[format] == null) {
1090         Date.createNewFormat(format);
1091     }
1092     var func = Date.formatFunctions[format];
1093     return this[func]();
1094 };
1095
1096
1097 /**
1098  * Formats a date given the supplied format string
1099  * @param {String} format The format string
1100  * @return {String} The formatted date
1101  * @method
1102  */
1103 Date.prototype.format = Date.prototype.dateFormat;
1104
1105 // private
1106 Date.createNewFormat = function(format) {
1107     var funcName = "format" + Date.formatFunctions.count++;
1108     Date.formatFunctions[format] = funcName;
1109     var code = "Date.prototype." + funcName + " = function(){return ";
1110     var special = false;
1111     var ch = '';
1112     for (var i = 0; i < format.length; ++i) {
1113         ch = format.charAt(i);
1114         if (!special && ch == "\\") {
1115             special = true;
1116         }
1117         else if (special) {
1118             special = false;
1119             code += "'" + String.escape(ch) + "' + ";
1120         }
1121         else {
1122             code += Date.getFormatCode(ch);
1123         }
1124     }
1125     /** eval:var:zzzzzzzzzzzzz */
1126     eval(code.substring(0, code.length - 3) + ";}");
1127 };
1128
1129 // private
1130 Date.getFormatCode = function(character) {
1131     switch (character) {
1132     case "d":
1133         return "String.leftPad(this.getDate(), 2, '0') + ";
1134     case "D":
1135         return "Date.dayNames[this.getDay()].substring(0, 3) + ";
1136     case "j":
1137         return "this.getDate() + ";
1138     case "l":
1139         return "Date.dayNames[this.getDay()] + ";
1140     case "S":
1141         return "this.getSuffix() + ";
1142     case "w":
1143         return "this.getDay() + ";
1144     case "z":
1145         return "this.getDayOfYear() + ";
1146     case "W":
1147         return "this.getWeekOfYear() + ";
1148     case "F":
1149         return "Date.monthNames[this.getMonth()] + ";
1150     case "m":
1151         return "String.leftPad(this.getMonth() + 1, 2, '0') + ";
1152     case "M":
1153         return "Date.monthNames[this.getMonth()].substring(0, 3) + ";
1154     case "n":
1155         return "(this.getMonth() + 1) + ";
1156     case "t":
1157         return "this.getDaysInMonth() + ";
1158     case "L":
1159         return "(this.isLeapYear() ? 1 : 0) + ";
1160     case "Y":
1161         return "this.getFullYear() + ";
1162     case "y":
1163         return "('' + this.getFullYear()).substring(2, 4) + ";
1164     case "a":
1165         return "(this.getHours() < 12 ? 'am' : 'pm') + ";
1166     case "A":
1167         return "(this.getHours() < 12 ? 'AM' : 'PM') + ";
1168     case "g":
1169         return "((this.getHours() % 12) ? this.getHours() % 12 : 12) + ";
1170     case "G":
1171         return "this.getHours() + ";
1172     case "h":
1173         return "String.leftPad((this.getHours() % 12) ? this.getHours() % 12 : 12, 2, '0') + ";
1174     case "H":
1175         return "String.leftPad(this.getHours(), 2, '0') + ";
1176     case "i":
1177         return "String.leftPad(this.getMinutes(), 2, '0') + ";
1178     case "s":
1179         return "String.leftPad(this.getSeconds(), 2, '0') + ";
1180     case "O":
1181         return "this.getGMTOffset() + ";
1182     case "P":
1183         return "this.getGMTColonOffset() + ";
1184     case "T":
1185         return "this.getTimezone() + ";
1186     case "Z":
1187         return "(this.getTimezoneOffset() * -60) + ";
1188     default:
1189         return "'" + String.escape(character) + "' + ";
1190     }
1191 };
1192
1193 /**
1194  * Parses the passed string using the specified format. Note that this function expects dates in normal calendar
1195  * format, meaning that months are 1-based (1 = January) and not zero-based like in JavaScript dates.  Any part of
1196  * the date format that is not specified will default to the current date value for that part.  Time parts can also
1197  * be specified, but default to 0.  Keep in mind that the input date string must precisely match the specified format
1198  * string or the parse operation will fail.
1199  * Example Usage:
1200 <pre><code>
1201 //dt = Fri May 25 2007 (current date)
1202 var dt = new Date();
1203
1204 //dt = Thu May 25 2006 (today's month/day in 2006)
1205 dt = Date.parseDate("2006", "Y");
1206
1207 //dt = Sun Jan 15 2006 (all date parts specified)
1208 dt = Date.parseDate("2006-1-15", "Y-m-d");
1209
1210 //dt = Sun Jan 15 2006 15:20:01 GMT-0600 (CST)
1211 dt = Date.parseDate("2006-1-15 3:20:01 PM", "Y-m-d h:i:s A" );
1212 </code></pre>
1213  * @param {String} input The unparsed date as a string
1214  * @param {String} format The format the date is in
1215  * @return {Date} The parsed date
1216  * @static
1217  */
1218 Date.parseDate = function(input, format) {
1219     if (Date.parseFunctions[format] == null) {
1220         Date.createParser(format);
1221     }
1222     var func = Date.parseFunctions[format];
1223     return Date[func](input);
1224 };
1225 /**
1226  * @private
1227  */
1228 Date.createParser = function(format) {
1229     var funcName = "parse" + Date.parseFunctions.count++;
1230     var regexNum = Date.parseRegexes.length;
1231     var currentGroup = 1;
1232     Date.parseFunctions[format] = funcName;
1233
1234     var code = "Date." + funcName + " = function(input){\n"
1235         + "var y = -1, m = -1, d = -1, h = -1, i = -1, s = -1, o, z, v;\n"
1236         + "var d = new Date();\n"
1237         + "y = d.getFullYear();\n"
1238         + "m = d.getMonth();\n"
1239         + "d = d.getDate();\n"
1240         + "var results = input.match(Date.parseRegexes[" + regexNum + "]);\n"
1241         + "if (results && results.length > 0) {";
1242     var regex = "";
1243
1244     var special = false;
1245     var ch = '';
1246     for (var i = 0; i < format.length; ++i) {
1247         ch = format.charAt(i);
1248         if (!special && ch == "\\") {
1249             special = true;
1250         }
1251         else if (special) {
1252             special = false;
1253             regex += String.escape(ch);
1254         }
1255         else {
1256             var obj = Date.formatCodeToRegex(ch, currentGroup);
1257             currentGroup += obj.g;
1258             regex += obj.s;
1259             if (obj.g && obj.c) {
1260                 code += obj.c;
1261             }
1262         }
1263     }
1264
1265     code += "if (y >= 0 && m >= 0 && d > 0 && h >= 0 && i >= 0 && s >= 0)\n"
1266         + "{v = new Date(y, m, d, h, i, s);}\n"
1267         + "else if (y >= 0 && m >= 0 && d > 0 && h >= 0 && i >= 0)\n"
1268         + "{v = new Date(y, m, d, h, i);}\n"
1269         + "else if (y >= 0 && m >= 0 && d > 0 && h >= 0)\n"
1270         + "{v = new Date(y, m, d, h);}\n"
1271         + "else if (y >= 0 && m >= 0 && d > 0)\n"
1272         + "{v = new Date(y, m, d);}\n"
1273         + "else if (y >= 0 && m >= 0)\n"
1274         + "{v = new Date(y, m);}\n"
1275         + "else if (y >= 0)\n"
1276         + "{v = new Date(y);}\n"
1277         + "}return (v && (z || o))?\n" // favour UTC offset over GMT offset
1278         + "    ((z)? v.add(Date.SECOND, (v.getTimezoneOffset() * 60) + (z*1)) :\n" // reset to UTC, then add offset
1279         + "        v.add(Date.HOUR, (v.getGMTOffset() / 100) + (o / -100))) : v\n" // reset to GMT, then add offset
1280         + ";}";
1281
1282     Date.parseRegexes[regexNum] = new RegExp("^" + regex + "$");
1283     /** eval:var:zzzzzzzzzzzzz */
1284     eval(code);
1285 };
1286
1287 // private
1288 Date.formatCodeToRegex = function(character, currentGroup) {
1289     switch (character) {
1290     case "D":
1291         return {g:0,
1292         c:null,
1293         s:"(?:Sun|Mon|Tue|Wed|Thu|Fri|Sat)"};
1294     case "j":
1295         return {g:1,
1296             c:"d = parseInt(results[" + currentGroup + "], 10);\n",
1297             s:"(\\d{1,2})"}; // day of month without leading zeroes
1298     case "d":
1299         return {g:1,
1300             c:"d = parseInt(results[" + currentGroup + "], 10);\n",
1301             s:"(\\d{2})"}; // day of month with leading zeroes
1302     case "l":
1303         return {g:0,
1304             c:null,
1305             s:"(?:" + Date.dayNames.join("|") + ")"};
1306     case "S":
1307         return {g:0,
1308             c:null,
1309             s:"(?:st|nd|rd|th)"};
1310     case "w":
1311         return {g:0,
1312             c:null,
1313             s:"\\d"};
1314     case "z":
1315         return {g:0,
1316             c:null,
1317             s:"(?:\\d{1,3})"};
1318     case "W":
1319         return {g:0,
1320             c:null,
1321             s:"(?:\\d{2})"};
1322     case "F":
1323         return {g:1,
1324             c:"m = parseInt(Date.monthNumbers[results[" + currentGroup + "].substring(0, 3)], 10);\n",
1325             s:"(" + Date.monthNames.join("|") + ")"};
1326     case "M":
1327         return {g:1,
1328             c:"m = parseInt(Date.monthNumbers[results[" + currentGroup + "]], 10);\n",
1329             s:"(Jan|Feb|Mar|Apr|May|Jun|Jul|Aug|Sep|Oct|Nov|Dec)"};
1330     case "n":
1331         return {g:1,
1332             c:"m = parseInt(results[" + currentGroup + "], 10) - 1;\n",
1333             s:"(\\d{1,2})"}; // Numeric representation of a month, without leading zeros
1334     case "m":
1335         return {g:1,
1336             c:"m = parseInt(results[" + currentGroup + "], 10) - 1;\n",
1337             s:"(\\d{2})"}; // Numeric representation of a month, with leading zeros
1338     case "t":
1339         return {g:0,
1340             c:null,
1341             s:"\\d{1,2}"};
1342     case "L":
1343         return {g:0,
1344             c:null,
1345             s:"(?:1|0)"};
1346     case "Y":
1347         return {g:1,
1348             c:"y = parseInt(results[" + currentGroup + "], 10);\n",
1349             s:"(\\d{4})"};
1350     case "y":
1351         return {g:1,
1352             c:"var ty = parseInt(results[" + currentGroup + "], 10);\n"
1353                 + "y = ty > Date.y2kYear ? 1900 + ty : 2000 + ty;\n",
1354             s:"(\\d{1,2})"};
1355     case "a":
1356         return {g:1,
1357             c:"if (results[" + currentGroup + "] == 'am') {\n"
1358                 + "if (h == 12) { h = 0; }\n"
1359                 + "} else { if (h < 12) { h += 12; }}",
1360             s:"(am|pm)"};
1361     case "A":
1362         return {g:1,
1363             c:"if (results[" + currentGroup + "] == 'AM') {\n"
1364                 + "if (h == 12) { h = 0; }\n"
1365                 + "} else { if (h < 12) { h += 12; }}",
1366             s:"(AM|PM)"};
1367     case "g":
1368     case "G":
1369         return {g:1,
1370             c:"h = parseInt(results[" + currentGroup + "], 10);\n",
1371             s:"(\\d{1,2})"}; // 12/24-hr format  format of an hour without leading zeroes
1372     case "h":
1373     case "H":
1374         return {g:1,
1375             c:"h = parseInt(results[" + currentGroup + "], 10);\n",
1376             s:"(\\d{2})"}; //  12/24-hr format  format of an hour with leading zeroes
1377     case "i":
1378         return {g:1,
1379             c:"i = parseInt(results[" + currentGroup + "], 10);\n",
1380             s:"(\\d{2})"};
1381     case "s":
1382         return {g:1,
1383             c:"s = parseInt(results[" + currentGroup + "], 10);\n",
1384             s:"(\\d{2})"};
1385     case "O":
1386         return {g:1,
1387             c:[
1388                 "o = results[", currentGroup, "];\n",
1389                 "var sn = o.substring(0,1);\n", // get + / - sign
1390                 "var hr = o.substring(1,3)*1 + Math.floor(o.substring(3,5) / 60);\n", // get hours (performs minutes-to-hour conversion also)
1391                 "var mn = o.substring(3,5) % 60;\n", // get minutes
1392                 "o = ((-12 <= (hr*60 + mn)/60) && ((hr*60 + mn)/60 <= 14))?\n", // -12hrs <= GMT offset <= 14hrs
1393                 "    (sn + String.leftPad(hr, 2, 0) + String.leftPad(mn, 2, 0)) : null;\n"
1394             ].join(""),
1395             s:"([+\-]\\d{2,4})"};
1396     
1397     
1398     case "P":
1399         return {g:1,
1400                 c:[
1401                    "o = results[", currentGroup, "];\n",
1402                    "var sn = o.substring(0,1);\n",
1403                    "var hr = o.substring(1,3)*1 + Math.floor(o.substring(4,6) / 60);\n",
1404                    "var mn = o.substring(4,6) % 60;\n",
1405                    "o = ((-12 <= (hr*60 + mn)/60) && ((hr*60 + mn)/60 <= 14))?\n",
1406                         "    (sn + String.leftPad(hr, 2, 0) + String.leftPad(mn, 2, 0)) : null;\n"
1407             ].join(""),
1408             s:"([+\-]\\d{4})"};
1409     case "T":
1410         return {g:0,
1411             c:null,
1412             s:"[A-Z]{1,4}"}; // timezone abbrev. may be between 1 - 4 chars
1413     case "Z":
1414         return {g:1,
1415             c:"z = results[" + currentGroup + "];\n" // -43200 <= UTC offset <= 50400
1416                   + "z = (-43200 <= z*1 && z*1 <= 50400)? z : null;\n",
1417             s:"([+\-]?\\d{1,5})"}; // leading '+' sign is optional for UTC offset
1418     default:
1419         return {g:0,
1420             c:null,
1421             s:String.escape(character)};
1422     }
1423 };
1424
1425 /**
1426  * Get the timezone abbreviation of the current date (equivalent to the format specifier 'T').
1427  * @return {String} The abbreviated timezone name (e.g. 'CST')
1428  */
1429 Date.prototype.getTimezone = function() {
1430     return this.toString().replace(/^.*? ([A-Z]{1,4})[\-+][0-9]{4} .*$/, "$1");
1431 };
1432
1433 /**
1434  * Get the offset from GMT of the current date (equivalent to the format specifier 'O').
1435  * @return {String} The 4-character offset string prefixed with + or - (e.g. '-0600')
1436  */
1437 Date.prototype.getGMTOffset = function() {
1438     return (this.getTimezoneOffset() > 0 ? "-" : "+")
1439         + String.leftPad(Math.abs(Math.floor(this.getTimezoneOffset() / 60)), 2, "0")
1440         + String.leftPad(this.getTimezoneOffset() % 60, 2, "0");
1441 };
1442
1443 /**
1444  * Get the offset from GMT of the current date (equivalent to the format specifier 'P').
1445  * @return {String} 2-characters representing hours and 2-characters representing minutes
1446  * seperated by a colon and prefixed with + or - (e.g. '-06:00')
1447  */
1448 Date.prototype.getGMTColonOffset = function() {
1449         return (this.getTimezoneOffset() > 0 ? "-" : "+")
1450                 + String.leftPad(Math.abs(Math.floor(this.getTimezoneOffset() / 60)), 2, "0")
1451                 + ":"
1452                 + String.leftPad(this.getTimezoneOffset() %60, 2, "0");
1453 }
1454
1455 /**
1456  * Get the numeric day number of the year, adjusted for leap year.
1457  * @return {Number} 0 through 364 (365 in leap years)
1458  */
1459 Date.prototype.getDayOfYear = function() {
1460     var num = 0;
1461     Date.daysInMonth[1] = this.isLeapYear() ? 29 : 28;
1462     for (var i = 0; i < this.getMonth(); ++i) {
1463         num += Date.daysInMonth[i];
1464     }
1465     return num + this.getDate() - 1;
1466 };
1467
1468 /**
1469  * Get the string representation of the numeric week number of the year
1470  * (equivalent to the format specifier 'W').
1471  * @return {String} '00' through '52'
1472  */
1473 Date.prototype.getWeekOfYear = function() {
1474     // Skip to Thursday of this week
1475     var now = this.getDayOfYear() + (4 - this.getDay());
1476     // Find the first Thursday of the year
1477     var jan1 = new Date(this.getFullYear(), 0, 1);
1478     var then = (7 - jan1.getDay() + 4);
1479     return String.leftPad(((now - then) / 7) + 1, 2, "0");
1480 };
1481
1482 /**
1483  * Whether or not the current date is in a leap year.
1484  * @return {Boolean} True if the current date is in a leap year, else false
1485  */
1486 Date.prototype.isLeapYear = function() {
1487     var year = this.getFullYear();
1488     return ((year & 3) == 0 && (year % 100 || (year % 400 == 0 && year)));
1489 };
1490
1491 /**
1492  * Get the first day of the current month, adjusted for leap year.  The returned value
1493  * is the numeric day index within the week (0-6) which can be used in conjunction with
1494  * the {@link #monthNames} array to retrieve the textual day name.
1495  * Example:
1496  *<pre><code>
1497 var dt = new Date('1/10/2007');
1498 document.write(Date.dayNames[dt.getFirstDayOfMonth()]); //output: 'Monday'
1499 </code></pre>
1500  * @return {Number} The day number (0-6)
1501  */
1502 Date.prototype.getFirstDayOfMonth = function() {
1503     var day = (this.getDay() - (this.getDate() - 1)) % 7;
1504     return (day < 0) ? (day + 7) : day;
1505 };
1506
1507 /**
1508  * Get the last day of the current month, adjusted for leap year.  The returned value
1509  * is the numeric day index within the week (0-6) which can be used in conjunction with
1510  * the {@link #monthNames} array to retrieve the textual day name.
1511  * Example:
1512  *<pre><code>
1513 var dt = new Date('1/10/2007');
1514 document.write(Date.dayNames[dt.getLastDayOfMonth()]); //output: 'Wednesday'
1515 </code></pre>
1516  * @return {Number} The day number (0-6)
1517  */
1518 Date.prototype.getLastDayOfMonth = function() {
1519     var day = (this.getDay() + (Date.daysInMonth[this.getMonth()] - this.getDate())) % 7;
1520     return (day < 0) ? (day + 7) : day;
1521 };
1522
1523
1524 /**
1525  * Get the first date of this date's month
1526  * @return {Date}
1527  */
1528 Date.prototype.getFirstDateOfMonth = function() {
1529     return new Date(this.getFullYear(), this.getMonth(), 1);
1530 };
1531
1532 /**
1533  * Get the last date of this date's month
1534  * @return {Date}
1535  */
1536 Date.prototype.getLastDateOfMonth = function() {
1537     return new Date(this.getFullYear(), this.getMonth(), this.getDaysInMonth());
1538 };
1539 /**
1540  * Get the number of days in the current month, adjusted for leap year.
1541  * @return {Number} The number of days in the month
1542  */
1543 Date.prototype.getDaysInMonth = function() {
1544     Date.daysInMonth[1] = this.isLeapYear() ? 29 : 28;
1545     return Date.daysInMonth[this.getMonth()];
1546 };
1547
1548 /**
1549  * Get the English ordinal suffix of the current day (equivalent to the format specifier 'S').
1550  * @return {String} 'st, 'nd', 'rd' or 'th'
1551  */
1552 Date.prototype.getSuffix = function() {
1553     switch (this.getDate()) {
1554         case 1:
1555         case 21:
1556         case 31:
1557             return "st";
1558         case 2:
1559         case 22:
1560             return "nd";
1561         case 3:
1562         case 23:
1563             return "rd";
1564         default:
1565             return "th";
1566     }
1567 };
1568
1569 // private
1570 Date.daysInMonth = [31,28,31,30,31,30,31,31,30,31,30,31];
1571
1572 /**
1573  * An array of textual month names.
1574  * Override these values for international dates, for example...
1575  * Date.monthNames = ['JanInYourLang', 'FebInYourLang', ...];
1576  * @type Array
1577  * @static
1578  */
1579 Date.monthNames =
1580    ["January",
1581     "February",
1582     "March",
1583     "April",
1584     "May",
1585     "June",
1586     "July",
1587     "August",
1588     "September",
1589     "October",
1590     "November",
1591     "December"];
1592
1593 /**
1594  * An array of textual day names.
1595  * Override these values for international dates, for example...
1596  * Date.dayNames = ['SundayInYourLang', 'MondayInYourLang', ...];
1597  * @type Array
1598  * @static
1599  */
1600 Date.dayNames =
1601    ["Sunday",
1602     "Monday",
1603     "Tuesday",
1604     "Wednesday",
1605     "Thursday",
1606     "Friday",
1607     "Saturday"];
1608
1609 // private
1610 Date.y2kYear = 50;
1611 // private
1612 Date.monthNumbers = {
1613     Jan:0,
1614     Feb:1,
1615     Mar:2,
1616     Apr:3,
1617     May:4,
1618     Jun:5,
1619     Jul:6,
1620     Aug:7,
1621     Sep:8,
1622     Oct:9,
1623     Nov:10,
1624     Dec:11};
1625
1626 /**
1627  * Creates and returns a new Date instance with the exact same date value as the called instance.
1628  * Dates are copied and passed by reference, so if a copied date variable is modified later, the original
1629  * variable will also be changed.  When the intention is to create a new variable that will not
1630  * modify the original instance, you should create a clone.
1631  *
1632  * Example of correctly cloning a date:
1633  * <pre><code>
1634 //wrong way:
1635 var orig = new Date('10/1/2006');
1636 var copy = orig;
1637 copy.setDate(5);
1638 document.write(orig);  //returns 'Thu Oct 05 2006'!
1639
1640 //correct way:
1641 var orig = new Date('10/1/2006');
1642 var copy = orig.clone();
1643 copy.setDate(5);
1644 document.write(orig);  //returns 'Thu Oct 01 2006'
1645 </code></pre>
1646  * @return {Date} The new Date instance
1647  */
1648 Date.prototype.clone = function() {
1649         return new Date(this.getTime());
1650 };
1651
1652 /**
1653  * Clears any time information from this date
1654  @param {Boolean} clone true to create a clone of this date, clear the time and return it
1655  @return {Date} this or the clone
1656  */
1657 Date.prototype.clearTime = function(clone){
1658     if(clone){
1659         return this.clone().clearTime();
1660     }
1661     this.setHours(0);
1662     this.setMinutes(0);
1663     this.setSeconds(0);
1664     this.setMilliseconds(0);
1665     return this;
1666 };
1667
1668 // private
1669 // safari setMonth is broken
1670 if(Roo.isSafari){
1671     Date.brokenSetMonth = Date.prototype.setMonth;
1672         Date.prototype.setMonth = function(num){
1673                 if(num <= -1){
1674                         var n = Math.ceil(-num);
1675                         var back_year = Math.ceil(n/12);
1676                         var month = (n % 12) ? 12 - n % 12 : 0 ;
1677                         this.setFullYear(this.getFullYear() - back_year);
1678                         return Date.brokenSetMonth.call(this, month);
1679                 } else {
1680                         return Date.brokenSetMonth.apply(this, arguments);
1681                 }
1682         };
1683 }
1684
1685 /** Date interval constant 
1686 * @static 
1687 * @type String */
1688 Date.MILLI = "ms";
1689 /** Date interval constant 
1690 * @static 
1691 * @type String */
1692 Date.SECOND = "s";
1693 /** Date interval constant 
1694 * @static 
1695 * @type String */
1696 Date.MINUTE = "mi";
1697 /** Date interval constant 
1698 * @static 
1699 * @type String */
1700 Date.HOUR = "h";
1701 /** Date interval constant 
1702 * @static 
1703 * @type String */
1704 Date.DAY = "d";
1705 /** Date interval constant 
1706 * @static 
1707 * @type String */
1708 Date.MONTH = "mo";
1709 /** Date interval constant 
1710 * @static 
1711 * @type String */
1712 Date.YEAR = "y";
1713
1714 /**
1715  * Provides a convenient method of performing basic date arithmetic.  This method
1716  * does not modify the Date instance being called - it creates and returns
1717  * a new Date instance containing the resulting date value.
1718  *
1719  * Examples:
1720  * <pre><code>
1721 //Basic usage:
1722 var dt = new Date('10/29/2006').add(Date.DAY, 5);
1723 document.write(dt); //returns 'Fri Oct 06 2006 00:00:00'
1724
1725 //Negative values will subtract correctly:
1726 var dt2 = new Date('10/1/2006').add(Date.DAY, -5);
1727 document.write(dt2); //returns 'Tue Sep 26 2006 00:00:00'
1728
1729 //You can even chain several calls together in one line!
1730 var dt3 = new Date('10/1/2006').add(Date.DAY, 5).add(Date.HOUR, 8).add(Date.MINUTE, -30);
1731 document.write(dt3); //returns 'Fri Oct 06 2006 07:30:00'
1732  </code></pre>
1733  *
1734  * @param {String} interval   A valid date interval enum value
1735  * @param {Number} value      The amount to add to the current date
1736  * @return {Date} The new Date instance
1737  */
1738 Date.prototype.add = function(interval, value){
1739   var d = this.clone();
1740   if (!interval || value === 0) return d;
1741   switch(interval.toLowerCase()){
1742     case Date.MILLI:
1743       d.setMilliseconds(this.getMilliseconds() + value);
1744       break;
1745     case Date.SECOND:
1746       d.setSeconds(this.getSeconds() + value);
1747       break;
1748     case Date.MINUTE:
1749       d.setMinutes(this.getMinutes() + value);
1750       break;
1751     case Date.HOUR:
1752       d.setHours(this.getHours() + value);
1753       break;
1754     case Date.DAY:
1755       d.setDate(this.getDate() + value);
1756       break;
1757     case Date.MONTH:
1758       var day = this.getDate();
1759       if(day > 28){
1760           day = Math.min(day, this.getFirstDateOfMonth().add('mo', value).getLastDateOfMonth().getDate());
1761       }
1762       d.setDate(day);
1763       d.setMonth(this.getMonth() + value);
1764       break;
1765     case Date.YEAR:
1766       d.setFullYear(this.getFullYear() + value);
1767       break;
1768   }
1769   return d;
1770 };
1771 /*
1772  * Based on:
1773  * Ext JS Library 1.1.1
1774  * Copyright(c) 2006-2007, Ext JS, LLC.
1775  *
1776  * Originally Released Under LGPL - original licence link has changed is not relivant.
1777  *
1778  * Fork - LGPL
1779  * <script type="text/javascript">
1780  */
1781
1782 /**
1783  * @class Roo.lib.Dom
1784  * @static
1785  * 
1786  * Dom utils (from YIU afaik)
1787  * 
1788  **/
1789 Roo.lib.Dom = {
1790     /**
1791      * Get the view width
1792      * @param {Boolean} full True will get the full document, otherwise it's the view width
1793      * @return {Number} The width
1794      */
1795      
1796     getViewWidth : function(full) {
1797         return full ? this.getDocumentWidth() : this.getViewportWidth();
1798     },
1799     /**
1800      * Get the view height
1801      * @param {Boolean} full True will get the full document, otherwise it's the view height
1802      * @return {Number} The height
1803      */
1804     getViewHeight : function(full) {
1805         return full ? this.getDocumentHeight() : this.getViewportHeight();
1806     },
1807
1808     getDocumentHeight: function() {
1809         var scrollHeight = (document.compatMode != "CSS1Compat") ? document.body.scrollHeight : document.documentElement.scrollHeight;
1810         return Math.max(scrollHeight, this.getViewportHeight());
1811     },
1812
1813     getDocumentWidth: function() {
1814         var scrollWidth = (document.compatMode != "CSS1Compat") ? document.body.scrollWidth : document.documentElement.scrollWidth;
1815         return Math.max(scrollWidth, this.getViewportWidth());
1816     },
1817
1818     getViewportHeight: function() {
1819         var height = self.innerHeight;
1820         var mode = document.compatMode;
1821
1822         if ((mode || Roo.isIE) && !Roo.isOpera) {
1823             height = (mode == "CSS1Compat") ?
1824                      document.documentElement.clientHeight :
1825                      document.body.clientHeight;
1826         }
1827
1828         return height;
1829     },
1830
1831     getViewportWidth: function() {
1832         var width = self.innerWidth;
1833         var mode = document.compatMode;
1834
1835         if (mode || Roo.isIE) {
1836             width = (mode == "CSS1Compat") ?
1837                     document.documentElement.clientWidth :
1838                     document.body.clientWidth;
1839         }
1840         return width;
1841     },
1842
1843     isAncestor : function(p, c) {
1844         p = Roo.getDom(p);
1845         c = Roo.getDom(c);
1846         if (!p || !c) {
1847             return false;
1848         }
1849
1850         if (p.contains && !Roo.isSafari) {
1851             return p.contains(c);
1852         } else if (p.compareDocumentPosition) {
1853             return !!(p.compareDocumentPosition(c) & 16);
1854         } else {
1855             var parent = c.parentNode;
1856             while (parent) {
1857                 if (parent == p) {
1858                     return true;
1859                 }
1860                 else if (!parent.tagName || parent.tagName.toUpperCase() == "HTML") {
1861                     return false;
1862                 }
1863                 parent = parent.parentNode;
1864             }
1865             return false;
1866         }
1867     },
1868
1869     getRegion : function(el) {
1870         return Roo.lib.Region.getRegion(el);
1871     },
1872
1873     getY : function(el) {
1874         return this.getXY(el)[1];
1875     },
1876
1877     getX : function(el) {
1878         return this.getXY(el)[0];
1879     },
1880
1881     getXY : function(el) {
1882         var p, pe, b, scroll, bd = document.body;
1883         el = Roo.getDom(el);
1884         var fly = Roo.lib.AnimBase.fly;
1885         if (el.getBoundingClientRect) {
1886             b = el.getBoundingClientRect();
1887             scroll = fly(document).getScroll();
1888             return [b.left + scroll.left, b.top + scroll.top];
1889         }
1890         var x = 0, y = 0;
1891
1892         p = el;
1893
1894         var hasAbsolute = fly(el).getStyle("position") == "absolute";
1895
1896         while (p) {
1897
1898             x += p.offsetLeft;
1899             y += p.offsetTop;
1900
1901             if (!hasAbsolute && fly(p).getStyle("position") == "absolute") {
1902                 hasAbsolute = true;
1903             }
1904
1905             if (Roo.isGecko) {
1906                 pe = fly(p);
1907
1908                 var bt = parseInt(pe.getStyle("borderTopWidth"), 10) || 0;
1909                 var bl = parseInt(pe.getStyle("borderLeftWidth"), 10) || 0;
1910
1911
1912                 x += bl;
1913                 y += bt;
1914
1915
1916                 if (p != el && pe.getStyle('overflow') != 'visible') {
1917                     x += bl;
1918                     y += bt;
1919                 }
1920             }
1921             p = p.offsetParent;
1922         }
1923
1924         if (Roo.isSafari && hasAbsolute) {
1925             x -= bd.offsetLeft;
1926             y -= bd.offsetTop;
1927         }
1928
1929         if (Roo.isGecko && !hasAbsolute) {
1930             var dbd = fly(bd);
1931             x += parseInt(dbd.getStyle("borderLeftWidth"), 10) || 0;
1932             y += parseInt(dbd.getStyle("borderTopWidth"), 10) || 0;
1933         }
1934
1935         p = el.parentNode;
1936         while (p && p != bd) {
1937             if (!Roo.isOpera || (p.tagName != 'TR' && fly(p).getStyle("display") != "inline")) {
1938                 x -= p.scrollLeft;
1939                 y -= p.scrollTop;
1940             }
1941             p = p.parentNode;
1942         }
1943         return [x, y];
1944     },
1945  
1946   
1947
1948
1949     setXY : function(el, xy) {
1950         el = Roo.fly(el, '_setXY');
1951         el.position();
1952         var pts = el.translatePoints(xy);
1953         if (xy[0] !== false) {
1954             el.dom.style.left = pts.left + "px";
1955         }
1956         if (xy[1] !== false) {
1957             el.dom.style.top = pts.top + "px";
1958         }
1959     },
1960
1961     setX : function(el, x) {
1962         this.setXY(el, [x, false]);
1963     },
1964
1965     setY : function(el, y) {
1966         this.setXY(el, [false, y]);
1967     }
1968 };
1969 /*
1970  * Portions of this file are based on pieces of Yahoo User Interface Library
1971  * Copyright (c) 2007, Yahoo! Inc. All rights reserved.
1972  * YUI licensed under the BSD License:
1973  * http://developer.yahoo.net/yui/license.txt
1974  * <script type="text/javascript">
1975  *
1976  */
1977
1978 Roo.lib.Event = function() {
1979     var loadComplete = false;
1980     var listeners = [];
1981     var unloadListeners = [];
1982     var retryCount = 0;
1983     var onAvailStack = [];
1984     var counter = 0;
1985     var lastError = null;
1986
1987     return {
1988         POLL_RETRYS: 200,
1989         POLL_INTERVAL: 20,
1990         EL: 0,
1991         TYPE: 1,
1992         FN: 2,
1993         WFN: 3,
1994         OBJ: 3,
1995         ADJ_SCOPE: 4,
1996         _interval: null,
1997
1998         startInterval: function() {
1999             if (!this._interval) {
2000                 var self = this;
2001                 var callback = function() {
2002                     self._tryPreloadAttach();
2003                 };
2004                 this._interval = setInterval(callback, this.POLL_INTERVAL);
2005
2006             }
2007         },
2008
2009         onAvailable: function(p_id, p_fn, p_obj, p_override) {
2010             onAvailStack.push({ id:         p_id,
2011                 fn:         p_fn,
2012                 obj:        p_obj,
2013                 override:   p_override,
2014                 checkReady: false    });
2015
2016             retryCount = this.POLL_RETRYS;
2017             this.startInterval();
2018         },
2019
2020
2021         addListener: function(el, eventName, fn) {
2022             el = Roo.getDom(el);
2023             if (!el || !fn) {
2024                 return false;
2025             }
2026
2027             if ("unload" == eventName) {
2028                 unloadListeners[unloadListeners.length] =
2029                 [el, eventName, fn];
2030                 return true;
2031             }
2032
2033             var wrappedFn = function(e) {
2034                 return fn(Roo.lib.Event.getEvent(e));
2035             };
2036
2037             var li = [el, eventName, fn, wrappedFn];
2038
2039             var index = listeners.length;
2040             listeners[index] = li;
2041
2042             this.doAdd(el, eventName, wrappedFn, false);
2043             return true;
2044
2045         },
2046
2047
2048         removeListener: function(el, eventName, fn) {
2049             var i, len;
2050
2051             el = Roo.getDom(el);
2052
2053             if(!fn) {
2054                 return this.purgeElement(el, false, eventName);
2055             }
2056
2057
2058             if ("unload" == eventName) {
2059
2060                 for (i = 0,len = unloadListeners.length; i < len; i++) {
2061                     var li = unloadListeners[i];
2062                     if (li &&
2063                         li[0] == el &&
2064                         li[1] == eventName &&
2065                         li[2] == fn) {
2066                         unloadListeners.splice(i, 1);
2067                         return true;
2068                     }
2069                 }
2070
2071                 return false;
2072             }
2073
2074             var cacheItem = null;
2075
2076
2077             var index = arguments[3];
2078
2079             if ("undefined" == typeof index) {
2080                 index = this._getCacheIndex(el, eventName, fn);
2081             }
2082
2083             if (index >= 0) {
2084                 cacheItem = listeners[index];
2085             }
2086
2087             if (!el || !cacheItem) {
2088                 return false;
2089             }
2090
2091             this.doRemove(el, eventName, cacheItem[this.WFN], false);
2092
2093             delete listeners[index][this.WFN];
2094             delete listeners[index][this.FN];
2095             listeners.splice(index, 1);
2096
2097             return true;
2098
2099         },
2100
2101
2102         getTarget: function(ev, resolveTextNode) {
2103             ev = ev.browserEvent || ev;
2104             ev = ev.touches ? (ev.touches[0] || ev.changedTouches[0] || ev )  : ev;
2105             var t = ev.target || ev.srcElement;
2106             return this.resolveTextNode(t);
2107         },
2108
2109
2110         resolveTextNode: function(node) {
2111             if (Roo.isSafari && node && 3 == node.nodeType) {
2112                 return node.parentNode;
2113             } else {
2114                 return node;
2115             }
2116         },
2117
2118
2119         getPageX: function(ev) {
2120             ev = ev.browserEvent || ev;
2121             ev = ev.touches ? (ev.touches[0] || ev.changedTouches[0] || ev )  : ev;
2122             var x = ev.pageX;
2123             if (!x && 0 !== x) {
2124                 x = ev.clientX || 0;
2125
2126                 if (Roo.isIE) {
2127                     x += this.getScroll()[1];
2128                 }
2129             }
2130
2131             return x;
2132         },
2133
2134
2135         getPageY: function(ev) {
2136             ev = ev.browserEvent || ev;
2137             ev = ev.touches ? (ev.touches[0] || ev.changedTouches[0] || ev )  : ev;
2138             var y = ev.pageY;
2139             if (!y && 0 !== y) {
2140                 y = ev.clientY || 0;
2141
2142                 if (Roo.isIE) {
2143                     y += this.getScroll()[0];
2144                 }
2145             }
2146
2147
2148             return y;
2149         },
2150
2151
2152         getXY: function(ev) {
2153             ev = ev.browserEvent || ev;
2154             ev = ev.touches ? (ev.touches[0] || ev.changedTouches[0] || ev )  : ev;
2155             return [this.getPageX(ev), this.getPageY(ev)];
2156         },
2157
2158
2159         getRelatedTarget: function(ev) {
2160             ev = ev.browserEvent || ev;
2161             ev = ev.touches ? (ev.touches[0] || ev.changedTouches[0] || ev )  : ev;
2162             var t = ev.relatedTarget;
2163             if (!t) {
2164                 if (ev.type == "mouseout") {
2165                     t = ev.toElement;
2166                 } else if (ev.type == "mouseover") {
2167                     t = ev.fromElement;
2168                 }
2169             }
2170
2171             return this.resolveTextNode(t);
2172         },
2173
2174
2175         getTime: function(ev) {
2176             ev = ev.browserEvent || ev;
2177             ev = ev.touches ? (ev.touches[0] || ev.changedTouches[0] || ev )  : ev;
2178             if (!ev.time) {
2179                 var t = new Date().getTime();
2180                 try {
2181                     ev.time = t;
2182                 } catch(ex) {
2183                     this.lastError = ex;
2184                     return t;
2185                 }
2186             }
2187
2188             return ev.time;
2189         },
2190
2191
2192         stopEvent: function(ev) {
2193             this.stopPropagation(ev);
2194             this.preventDefault(ev);
2195         },
2196
2197
2198         stopPropagation: function(ev) {
2199             ev = ev.browserEvent || ev;
2200             if (ev.stopPropagation) {
2201                 ev.stopPropagation();
2202             } else {
2203                 ev.cancelBubble = true;
2204             }
2205         },
2206
2207
2208         preventDefault: function(ev) {
2209             ev = ev.browserEvent || ev;
2210             if(ev.preventDefault) {
2211                 ev.preventDefault();
2212             } else {
2213                 ev.returnValue = false;
2214             }
2215         },
2216
2217
2218         getEvent: function(e) {
2219             var ev = e || window.event;
2220             if (!ev) {
2221                 var c = this.getEvent.caller;
2222                 while (c) {
2223                     ev = c.arguments[0];
2224                     if (ev && Event == ev.constructor) {
2225                         break;
2226                     }
2227                     c = c.caller;
2228                 }
2229             }
2230             return ev;
2231         },
2232
2233
2234         getCharCode: function(ev) {
2235             ev = ev.browserEvent || ev;
2236             return ev.charCode || ev.keyCode || 0;
2237         },
2238
2239
2240         _getCacheIndex: function(el, eventName, fn) {
2241             for (var i = 0,len = listeners.length; i < len; ++i) {
2242                 var li = listeners[i];
2243                 if (li &&
2244                     li[this.FN] == fn &&
2245                     li[this.EL] == el &&
2246                     li[this.TYPE] == eventName) {
2247                     return i;
2248                 }
2249             }
2250
2251             return -1;
2252         },
2253
2254
2255         elCache: {},
2256
2257
2258         getEl: function(id) {
2259             return document.getElementById(id);
2260         },
2261
2262
2263         clearCache: function() {
2264         },
2265
2266
2267         _load: function(e) {
2268             loadComplete = true;
2269             var EU = Roo.lib.Event;
2270
2271
2272             if (Roo.isIE) {
2273                 EU.doRemove(window, "load", EU._load);
2274             }
2275         },
2276
2277
2278         _tryPreloadAttach: function() {
2279
2280             if (this.locked) {
2281                 return false;
2282             }
2283
2284             this.locked = true;
2285
2286
2287             var tryAgain = !loadComplete;
2288             if (!tryAgain) {
2289                 tryAgain = (retryCount > 0);
2290             }
2291
2292
2293             var notAvail = [];
2294             for (var i = 0,len = onAvailStack.length; i < len; ++i) {
2295                 var item = onAvailStack[i];
2296                 if (item) {
2297                     var el = this.getEl(item.id);
2298
2299                     if (el) {
2300                         if (!item.checkReady ||
2301                             loadComplete ||
2302                             el.nextSibling ||
2303                             (document && document.body)) {
2304
2305                             var scope = el;
2306                             if (item.override) {
2307                                 if (item.override === true) {
2308                                     scope = item.obj;
2309                                 } else {
2310                                     scope = item.override;
2311                                 }
2312                             }
2313                             item.fn.call(scope, item.obj);
2314                             onAvailStack[i] = null;
2315                         }
2316                     } else {
2317                         notAvail.push(item);
2318                     }
2319                 }
2320             }
2321
2322             retryCount = (notAvail.length === 0) ? 0 : retryCount - 1;
2323
2324             if (tryAgain) {
2325
2326                 this.startInterval();
2327             } else {
2328                 clearInterval(this._interval);
2329                 this._interval = null;
2330             }
2331
2332             this.locked = false;
2333
2334             return true;
2335
2336         },
2337
2338
2339         purgeElement: function(el, recurse, eventName) {
2340             var elListeners = this.getListeners(el, eventName);
2341             if (elListeners) {
2342                 for (var i = 0,len = elListeners.length; i < len; ++i) {
2343                     var l = elListeners[i];
2344                     this.removeListener(el, l.type, l.fn);
2345                 }
2346             }
2347
2348             if (recurse && el && el.childNodes) {
2349                 for (i = 0,len = el.childNodes.length; i < len; ++i) {
2350                     this.purgeElement(el.childNodes[i], recurse, eventName);
2351                 }
2352             }
2353         },
2354
2355
2356         getListeners: function(el, eventName) {
2357             var results = [], searchLists;
2358             if (!eventName) {
2359                 searchLists = [listeners, unloadListeners];
2360             } else if (eventName == "unload") {
2361                 searchLists = [unloadListeners];
2362             } else {
2363                 searchLists = [listeners];
2364             }
2365
2366             for (var j = 0; j < searchLists.length; ++j) {
2367                 var searchList = searchLists[j];
2368                 if (searchList && searchList.length > 0) {
2369                     for (var i = 0,len = searchList.length; i < len; ++i) {
2370                         var l = searchList[i];
2371                         if (l && l[this.EL] === el &&
2372                             (!eventName || eventName === l[this.TYPE])) {
2373                             results.push({
2374                                 type:   l[this.TYPE],
2375                                 fn:     l[this.FN],
2376                                 obj:    l[this.OBJ],
2377                                 adjust: l[this.ADJ_SCOPE],
2378                                 index:  i
2379                             });
2380                         }
2381                     }
2382                 }
2383             }
2384
2385             return (results.length) ? results : null;
2386         },
2387
2388
2389         _unload: function(e) {
2390
2391             var EU = Roo.lib.Event, i, j, l, len, index;
2392
2393             for (i = 0,len = unloadListeners.length; i < len; ++i) {
2394                 l = unloadListeners[i];
2395                 if (l) {
2396                     var scope = window;
2397                     if (l[EU.ADJ_SCOPE]) {
2398                         if (l[EU.ADJ_SCOPE] === true) {
2399                             scope = l[EU.OBJ];
2400                         } else {
2401                             scope = l[EU.ADJ_SCOPE];
2402                         }
2403                     }
2404                     l[EU.FN].call(scope, EU.getEvent(e), l[EU.OBJ]);
2405                     unloadListeners[i] = null;
2406                     l = null;
2407                     scope = null;
2408                 }
2409             }
2410
2411             unloadListeners = null;
2412
2413             if (listeners && listeners.length > 0) {
2414                 j = listeners.length;
2415                 while (j) {
2416                     index = j - 1;
2417                     l = listeners[index];
2418                     if (l) {
2419                         EU.removeListener(l[EU.EL], l[EU.TYPE],
2420                                 l[EU.FN], index);
2421                     }
2422                     j = j - 1;
2423                 }
2424                 l = null;
2425
2426                 EU.clearCache();
2427             }
2428
2429             EU.doRemove(window, "unload", EU._unload);
2430
2431         },
2432
2433
2434         getScroll: function() {
2435             var dd = document.documentElement, db = document.body;
2436             if (dd && (dd.scrollTop || dd.scrollLeft)) {
2437                 return [dd.scrollTop, dd.scrollLeft];
2438             } else if (db) {
2439                 return [db.scrollTop, db.scrollLeft];
2440             } else {
2441                 return [0, 0];
2442             }
2443         },
2444
2445
2446         doAdd: function () {
2447             if (window.addEventListener) {
2448                 return function(el, eventName, fn, capture) {
2449                     el.addEventListener(eventName, fn, (capture));
2450                 };
2451             } else if (window.attachEvent) {
2452                 return function(el, eventName, fn, capture) {
2453                     el.attachEvent("on" + eventName, fn);
2454                 };
2455             } else {
2456                 return function() {
2457                 };
2458             }
2459         }(),
2460
2461
2462         doRemove: function() {
2463             if (window.removeEventListener) {
2464                 return function (el, eventName, fn, capture) {
2465                     el.removeEventListener(eventName, fn, (capture));
2466                 };
2467             } else if (window.detachEvent) {
2468                 return function (el, eventName, fn) {
2469                     el.detachEvent("on" + eventName, fn);
2470                 };
2471             } else {
2472                 return function() {
2473                 };
2474             }
2475         }()
2476     };
2477     
2478 }();
2479 (function() {     
2480    
2481     var E = Roo.lib.Event;
2482     E.on = E.addListener;
2483     E.un = E.removeListener;
2484
2485     if (document && document.body) {
2486         E._load();
2487     } else {
2488         E.doAdd(window, "load", E._load);
2489     }
2490     E.doAdd(window, "unload", E._unload);
2491     E._tryPreloadAttach();
2492 })();
2493
2494 /*
2495  * Portions of this file are based on pieces of Yahoo User Interface Library
2496  * Copyright (c) 2007, Yahoo! Inc. All rights reserved.
2497  * YUI licensed under the BSD License:
2498  * http://developer.yahoo.net/yui/license.txt
2499  * <script type="text/javascript">
2500  *
2501  */
2502
2503 (function() {
2504     /**
2505      * @class Roo.lib.Ajax
2506      *
2507      */
2508     Roo.lib.Ajax = {
2509         /**
2510          * @static 
2511          */
2512         request : function(method, uri, cb, data, options) {
2513             if(options){
2514                 var hs = options.headers;
2515                 if(hs){
2516                     for(var h in hs){
2517                         if(hs.hasOwnProperty(h)){
2518                             this.initHeader(h, hs[h], false);
2519                         }
2520                     }
2521                 }
2522                 if(options.xmlData){
2523                     this.initHeader('Content-Type', 'text/xml', false);
2524                     method = 'POST';
2525                     data = options.xmlData;
2526                 }
2527             }
2528
2529             return this.asyncRequest(method, uri, cb, data);
2530         },
2531
2532         serializeForm : function(form) {
2533             if(typeof form == 'string') {
2534                 form = (document.getElementById(form) || document.forms[form]);
2535             }
2536
2537             var el, name, val, disabled, data = '', hasSubmit = false;
2538             for (var i = 0; i < form.elements.length; i++) {
2539                 el = form.elements[i];
2540                 disabled = form.elements[i].disabled;
2541                 name = form.elements[i].name;
2542                 val = form.elements[i].value;
2543
2544                 if (!disabled && name){
2545                     switch (el.type)
2546                             {
2547                         case 'select-one':
2548                         case 'select-multiple':
2549                             for (var j = 0; j < el.options.length; j++) {
2550                                 if (el.options[j].selected) {
2551                                     if (Roo.isIE) {
2552                                         data += Roo.encodeURIComponent(name) + '=' + Roo.encodeURIComponent(el.options[j].attributes['value'].specified ? el.options[j].value : el.options[j].text) + '&';
2553                                     }
2554                                     else {
2555                                         data += Roo.encodeURIComponent(name) + '=' + Roo.encodeURIComponent(el.options[j].hasAttribute('value') ? el.options[j].value : el.options[j].text) + '&';
2556                                     }
2557                                 }
2558                             }
2559                             break;
2560                         case 'radio':
2561                         case 'checkbox':
2562                             if (el.checked) {
2563                                 data += Roo.encodeURIComponent(name) + '=' + Roo.encodeURIComponent(val) + '&';
2564                             }
2565                             break;
2566                         case 'file':
2567
2568                         case undefined:
2569
2570                         case 'reset':
2571
2572                         case 'button':
2573
2574                             break;
2575                         case 'submit':
2576                             if(hasSubmit == false) {
2577                                 data += Roo.encodeURIComponent(name) + '=' + Roo.encodeURIComponent(val) + '&';
2578                                 hasSubmit = true;
2579                             }
2580                             break;
2581                         default:
2582                             data += Roo.encodeURIComponent(name) + '=' + Roo.encodeURIComponent(val) + '&';
2583                             break;
2584                     }
2585                 }
2586             }
2587             data = data.substr(0, data.length - 1);
2588             return data;
2589         },
2590
2591         headers:{},
2592
2593         hasHeaders:false,
2594
2595         useDefaultHeader:true,
2596
2597         defaultPostHeader:'application/x-www-form-urlencoded',
2598
2599         useDefaultXhrHeader:true,
2600
2601         defaultXhrHeader:'XMLHttpRequest',
2602
2603         hasDefaultHeaders:true,
2604
2605         defaultHeaders:{},
2606
2607         poll:{},
2608
2609         timeout:{},
2610
2611         pollInterval:50,
2612
2613         transactionId:0,
2614
2615         setProgId:function(id)
2616         {
2617             this.activeX.unshift(id);
2618         },
2619
2620         setDefaultPostHeader:function(b)
2621         {
2622             this.useDefaultHeader = b;
2623         },
2624
2625         setDefaultXhrHeader:function(b)
2626         {
2627             this.useDefaultXhrHeader = b;
2628         },
2629
2630         setPollingInterval:function(i)
2631         {
2632             if (typeof i == 'number' && isFinite(i)) {
2633                 this.pollInterval = i;
2634             }
2635         },
2636
2637         createXhrObject:function(transactionId)
2638         {
2639             var obj,http;
2640             try
2641             {
2642
2643                 http = new XMLHttpRequest();
2644
2645                 obj = { conn:http, tId:transactionId };
2646             }
2647             catch(e)
2648             {
2649                 for (var i = 0; i < this.activeX.length; ++i) {
2650                     try
2651                     {
2652
2653                         http = new ActiveXObject(this.activeX[i]);
2654
2655                         obj = { conn:http, tId:transactionId };
2656                         break;
2657                     }
2658                     catch(e) {
2659                     }
2660                 }
2661             }
2662             finally
2663             {
2664                 return obj;
2665             }
2666         },
2667
2668         getConnectionObject:function()
2669         {
2670             var o;
2671             var tId = this.transactionId;
2672
2673             try
2674             {
2675                 o = this.createXhrObject(tId);
2676                 if (o) {
2677                     this.transactionId++;
2678                 }
2679             }
2680             catch(e) {
2681             }
2682             finally
2683             {
2684                 return o;
2685             }
2686         },
2687
2688         asyncRequest:function(method, uri, callback, postData)
2689         {
2690             var o = this.getConnectionObject();
2691
2692             if (!o) {
2693                 return null;
2694             }
2695             else {
2696                 o.conn.open(method, uri, true);
2697
2698                 if (this.useDefaultXhrHeader) {
2699                     if (!this.defaultHeaders['X-Requested-With']) {
2700                         this.initHeader('X-Requested-With', this.defaultXhrHeader, true);
2701                     }
2702                 }
2703
2704                 if(postData && this.useDefaultHeader){
2705                     this.initHeader('Content-Type', this.defaultPostHeader);
2706                 }
2707
2708                  if (this.hasDefaultHeaders || this.hasHeaders) {
2709                     this.setHeader(o);
2710                 }
2711
2712                 this.handleReadyState(o, callback);
2713                 o.conn.send(postData || null);
2714
2715                 return o;
2716             }
2717         },
2718
2719         handleReadyState:function(o, callback)
2720         {
2721             var oConn = this;
2722
2723             if (callback && callback.timeout) {
2724                 
2725                 this.timeout[o.tId] = window.setTimeout(function() {
2726                     oConn.abort(o, callback, true);
2727                 }, callback.timeout);
2728             }
2729
2730             this.poll[o.tId] = window.setInterval(
2731                     function() {
2732                         if (o.conn && o.conn.readyState == 4) {
2733                             window.clearInterval(oConn.poll[o.tId]);
2734                             delete oConn.poll[o.tId];
2735
2736                             if(callback && callback.timeout) {
2737                                 window.clearTimeout(oConn.timeout[o.tId]);
2738                                 delete oConn.timeout[o.tId];
2739                             }
2740
2741                             oConn.handleTransactionResponse(o, callback);
2742                         }
2743                     }
2744                     , this.pollInterval);
2745         },
2746
2747         handleTransactionResponse:function(o, callback, isAbort)
2748         {
2749
2750             if (!callback) {
2751                 this.releaseObject(o);
2752                 return;
2753             }
2754
2755             var httpStatus, responseObject;
2756
2757             try
2758             {
2759                 if (o.conn.status !== undefined && o.conn.status != 0) {
2760                     httpStatus = o.conn.status;
2761                 }
2762                 else {
2763                     httpStatus = 13030;
2764                 }
2765             }
2766             catch(e) {
2767
2768
2769                 httpStatus = 13030;
2770             }
2771
2772             if (httpStatus >= 200 && httpStatus < 300) {
2773                 responseObject = this.createResponseObject(o, callback.argument);
2774                 if (callback.success) {
2775                     if (!callback.scope) {
2776                         callback.success(responseObject);
2777                     }
2778                     else {
2779
2780
2781                         callback.success.apply(callback.scope, [responseObject]);
2782                     }
2783                 }
2784             }
2785             else {
2786                 switch (httpStatus) {
2787
2788                     case 12002:
2789                     case 12029:
2790                     case 12030:
2791                     case 12031:
2792                     case 12152:
2793                     case 13030:
2794                         responseObject = this.createExceptionObject(o.tId, callback.argument, (isAbort ? isAbort : false));
2795                         if (callback.failure) {
2796                             if (!callback.scope) {
2797                                 callback.failure(responseObject);
2798                             }
2799                             else {
2800                                 callback.failure.apply(callback.scope, [responseObject]);
2801                             }
2802                         }
2803                         break;
2804                     default:
2805                         responseObject = this.createResponseObject(o, callback.argument);
2806                         if (callback.failure) {
2807                             if (!callback.scope) {
2808                                 callback.failure(responseObject);
2809                             }
2810                             else {
2811                                 callback.failure.apply(callback.scope, [responseObject]);
2812                             }
2813                         }
2814                 }
2815             }
2816
2817             this.releaseObject(o);
2818             responseObject = null;
2819         },
2820
2821         createResponseObject:function(o, callbackArg)
2822         {
2823             var obj = {};
2824             var headerObj = {};
2825
2826             try
2827             {
2828                 var headerStr = o.conn.getAllResponseHeaders();
2829                 var header = headerStr.split('\n');
2830                 for (var i = 0; i < header.length; i++) {
2831                     var delimitPos = header[i].indexOf(':');
2832                     if (delimitPos != -1) {
2833                         headerObj[header[i].substring(0, delimitPos)] = header[i].substring(delimitPos + 2);
2834                     }
2835                 }
2836             }
2837             catch(e) {
2838             }
2839
2840             obj.tId = o.tId;
2841             obj.status = o.conn.status;
2842             obj.statusText = o.conn.statusText;
2843             obj.getResponseHeader = headerObj;
2844             obj.getAllResponseHeaders = headerStr;
2845             obj.responseText = o.conn.responseText;
2846             obj.responseXML = o.conn.responseXML;
2847
2848             if (typeof callbackArg !== undefined) {
2849                 obj.argument = callbackArg;
2850             }
2851
2852             return obj;
2853         },
2854
2855         createExceptionObject:function(tId, callbackArg, isAbort)
2856         {
2857             var COMM_CODE = 0;
2858             var COMM_ERROR = 'communication failure';
2859             var ABORT_CODE = -1;
2860             var ABORT_ERROR = 'transaction aborted';
2861
2862             var obj = {};
2863
2864             obj.tId = tId;
2865             if (isAbort) {
2866                 obj.status = ABORT_CODE;
2867                 obj.statusText = ABORT_ERROR;
2868             }
2869             else {
2870                 obj.status = COMM_CODE;
2871                 obj.statusText = COMM_ERROR;
2872             }
2873
2874             if (callbackArg) {
2875                 obj.argument = callbackArg;
2876             }
2877
2878             return obj;
2879         },
2880
2881         initHeader:function(label, value, isDefault)
2882         {
2883             var headerObj = (isDefault) ? this.defaultHeaders : this.headers;
2884
2885             if (headerObj[label] === undefined) {
2886                 headerObj[label] = value;
2887             }
2888             else {
2889
2890
2891                 headerObj[label] = value + "," + headerObj[label];
2892             }
2893
2894             if (isDefault) {
2895                 this.hasDefaultHeaders = true;
2896             }
2897             else {
2898                 this.hasHeaders = true;
2899             }
2900         },
2901
2902
2903         setHeader:function(o)
2904         {
2905             if (this.hasDefaultHeaders) {
2906                 for (var prop in this.defaultHeaders) {
2907                     if (this.defaultHeaders.hasOwnProperty(prop)) {
2908                         o.conn.setRequestHeader(prop, this.defaultHeaders[prop]);
2909                     }
2910                 }
2911             }
2912
2913             if (this.hasHeaders) {
2914                 for (var prop in this.headers) {
2915                     if (this.headers.hasOwnProperty(prop)) {
2916                         o.conn.setRequestHeader(prop, this.headers[prop]);
2917                     }
2918                 }
2919                 this.headers = {};
2920                 this.hasHeaders = false;
2921             }
2922         },
2923
2924         resetDefaultHeaders:function() {
2925             delete this.defaultHeaders;
2926             this.defaultHeaders = {};
2927             this.hasDefaultHeaders = false;
2928         },
2929
2930         abort:function(o, callback, isTimeout)
2931         {
2932             if(this.isCallInProgress(o)) {
2933                 o.conn.abort();
2934                 window.clearInterval(this.poll[o.tId]);
2935                 delete this.poll[o.tId];
2936                 if (isTimeout) {
2937                     delete this.timeout[o.tId];
2938                 }
2939
2940                 this.handleTransactionResponse(o, callback, true);
2941
2942                 return true;
2943             }
2944             else {
2945                 return false;
2946             }
2947         },
2948
2949
2950         isCallInProgress:function(o)
2951         {
2952             if (o && o.conn) {
2953                 return o.conn.readyState != 4 && o.conn.readyState != 0;
2954             }
2955             else {
2956
2957                 return false;
2958             }
2959         },
2960
2961
2962         releaseObject:function(o)
2963         {
2964
2965             o.conn = null;
2966
2967             o = null;
2968         },
2969
2970         activeX:[
2971         'MSXML2.XMLHTTP.3.0',
2972         'MSXML2.XMLHTTP',
2973         'Microsoft.XMLHTTP'
2974         ]
2975
2976
2977     };
2978 })();/*
2979  * Portions of this file are based on pieces of Yahoo User Interface Library
2980  * Copyright (c) 2007, Yahoo! Inc. All rights reserved.
2981  * YUI licensed under the BSD License:
2982  * http://developer.yahoo.net/yui/license.txt
2983  * <script type="text/javascript">
2984  *
2985  */
2986
2987 Roo.lib.Region = function(t, r, b, l) {
2988     this.top = t;
2989     this[1] = t;
2990     this.right = r;
2991     this.bottom = b;
2992     this.left = l;
2993     this[0] = l;
2994 };
2995
2996
2997 Roo.lib.Region.prototype = {
2998     contains : function(region) {
2999         return ( region.left >= this.left &&
3000                  region.right <= this.right &&
3001                  region.top >= this.top &&
3002                  region.bottom <= this.bottom    );
3003
3004     },
3005
3006     getArea : function() {
3007         return ( (this.bottom - this.top) * (this.right - this.left) );
3008     },
3009
3010     intersect : function(region) {
3011         var t = Math.max(this.top, region.top);
3012         var r = Math.min(this.right, region.right);
3013         var b = Math.min(this.bottom, region.bottom);
3014         var l = Math.max(this.left, region.left);
3015
3016         if (b >= t && r >= l) {
3017             return new Roo.lib.Region(t, r, b, l);
3018         } else {
3019             return null;
3020         }
3021     },
3022     union : function(region) {
3023         var t = Math.min(this.top, region.top);
3024         var r = Math.max(this.right, region.right);
3025         var b = Math.max(this.bottom, region.bottom);
3026         var l = Math.min(this.left, region.left);
3027
3028         return new Roo.lib.Region(t, r, b, l);
3029     },
3030
3031     adjust : function(t, l, b, r) {
3032         this.top += t;
3033         this.left += l;
3034         this.right += r;
3035         this.bottom += b;
3036         return this;
3037     }
3038 };
3039
3040 Roo.lib.Region.getRegion = function(el) {
3041     var p = Roo.lib.Dom.getXY(el);
3042
3043     var t = p[1];
3044     var r = p[0] + el.offsetWidth;
3045     var b = p[1] + el.offsetHeight;
3046     var l = p[0];
3047
3048     return new Roo.lib.Region(t, r, b, l);
3049 };
3050 /*
3051  * Portions of this file are based on pieces of Yahoo User Interface Library
3052  * Copyright (c) 2007, Yahoo! Inc. All rights reserved.
3053  * YUI licensed under the BSD License:
3054  * http://developer.yahoo.net/yui/license.txt
3055  * <script type="text/javascript">
3056  *
3057  */
3058 //@@dep Roo.lib.Region
3059
3060
3061 Roo.lib.Point = function(x, y) {
3062     if (x instanceof Array) {
3063         y = x[1];
3064         x = x[0];
3065     }
3066     this.x = this.right = this.left = this[0] = x;
3067     this.y = this.top = this.bottom = this[1] = y;
3068 };
3069
3070 Roo.lib.Point.prototype = new Roo.lib.Region();
3071 /*
3072  * Portions of this file are based on pieces of Yahoo User Interface Library
3073  * Copyright (c) 2007, Yahoo! Inc. All rights reserved.
3074  * YUI licensed under the BSD License:
3075  * http://developer.yahoo.net/yui/license.txt
3076  * <script type="text/javascript">
3077  *
3078  */
3079  
3080 (function() {   
3081
3082     Roo.lib.Anim = {
3083         scroll : function(el, args, duration, easing, cb, scope) {
3084             this.run(el, args, duration, easing, cb, scope, Roo.lib.Scroll);
3085         },
3086
3087         motion : function(el, args, duration, easing, cb, scope) {
3088             this.run(el, args, duration, easing, cb, scope, Roo.lib.Motion);
3089         },
3090
3091         color : function(el, args, duration, easing, cb, scope) {
3092             this.run(el, args, duration, easing, cb, scope, Roo.lib.ColorAnim);
3093         },
3094
3095         run : function(el, args, duration, easing, cb, scope, type) {
3096             type = type || Roo.lib.AnimBase;
3097             if (typeof easing == "string") {
3098                 easing = Roo.lib.Easing[easing];
3099             }
3100             var anim = new type(el, args, duration, easing);
3101             anim.animateX(function() {
3102                 Roo.callback(cb, scope);
3103             });
3104             return anim;
3105         }
3106     };
3107 })();/*
3108  * Portions of this file are based on pieces of Yahoo User Interface Library
3109  * Copyright (c) 2007, Yahoo! Inc. All rights reserved.
3110  * YUI licensed under the BSD License:
3111  * http://developer.yahoo.net/yui/license.txt
3112  * <script type="text/javascript">
3113  *
3114  */
3115
3116 (function() {    
3117     var libFlyweight;
3118     
3119     function fly(el) {
3120         if (!libFlyweight) {
3121             libFlyweight = new Roo.Element.Flyweight();
3122         }
3123         libFlyweight.dom = el;
3124         return libFlyweight;
3125     }
3126
3127     // since this uses fly! - it cant be in DOM (which does not have fly yet..)
3128     
3129    
3130     
3131     Roo.lib.AnimBase = function(el, attributes, duration, method) {
3132         if (el) {
3133             this.init(el, attributes, duration, method);
3134         }
3135     };
3136
3137     Roo.lib.AnimBase.fly = fly;
3138     
3139     
3140     
3141     Roo.lib.AnimBase.prototype = {
3142
3143         toString: function() {
3144             var el = this.getEl();
3145             var id = el.id || el.tagName;
3146             return ("Anim " + id);
3147         },
3148
3149         patterns: {
3150             noNegatives:        /width|height|opacity|padding/i,
3151             offsetAttribute:  /^((width|height)|(top|left))$/,
3152             defaultUnit:        /width|height|top$|bottom$|left$|right$/i,
3153             offsetUnit:         /\d+(em|%|en|ex|pt|in|cm|mm|pc)$/i
3154         },
3155
3156
3157         doMethod: function(attr, start, end) {
3158             return this.method(this.currentFrame, start, end - start, this.totalFrames);
3159         },
3160
3161
3162         setAttribute: function(attr, val, unit) {
3163             if (this.patterns.noNegatives.test(attr)) {
3164                 val = (val > 0) ? val : 0;
3165             }
3166
3167             Roo.fly(this.getEl(), '_anim').setStyle(attr, val + unit);
3168         },
3169
3170
3171         getAttribute: function(attr) {
3172             var el = this.getEl();
3173             var val = fly(el).getStyle(attr);
3174
3175             if (val !== 'auto' && !this.patterns.offsetUnit.test(val)) {
3176                 return parseFloat(val);
3177             }
3178
3179             var a = this.patterns.offsetAttribute.exec(attr) || [];
3180             var pos = !!( a[3] );
3181             var box = !!( a[2] );
3182
3183
3184             if (box || (fly(el).getStyle('position') == 'absolute' && pos)) {
3185                 val = el['offset' + a[0].charAt(0).toUpperCase() + a[0].substr(1)];
3186             } else {
3187                 val = 0;
3188             }
3189
3190             return val;
3191         },
3192
3193
3194         getDefaultUnit: function(attr) {
3195             if (this.patterns.defaultUnit.test(attr)) {
3196                 return 'px';
3197             }
3198
3199             return '';
3200         },
3201
3202         animateX : function(callback, scope) {
3203             var f = function() {
3204                 this.onComplete.removeListener(f);
3205                 if (typeof callback == "function") {
3206                     callback.call(scope || this, this);
3207                 }
3208             };
3209             this.onComplete.addListener(f, this);
3210             this.animate();
3211         },
3212
3213
3214         setRuntimeAttribute: function(attr) {
3215             var start;
3216             var end;
3217             var attributes = this.attributes;
3218
3219             this.runtimeAttributes[attr] = {};
3220
3221             var isset = function(prop) {
3222                 return (typeof prop !== 'undefined');
3223             };
3224
3225             if (!isset(attributes[attr]['to']) && !isset(attributes[attr]['by'])) {
3226                 return false;
3227             }
3228
3229             start = ( isset(attributes[attr]['from']) ) ? attributes[attr]['from'] : this.getAttribute(attr);
3230
3231
3232             if (isset(attributes[attr]['to'])) {
3233                 end = attributes[attr]['to'];
3234             } else if (isset(attributes[attr]['by'])) {
3235                 if (start.constructor == Array) {
3236                     end = [];
3237                     for (var i = 0, len = start.length; i < len; ++i) {
3238                         end[i] = start[i] + attributes[attr]['by'][i];
3239                     }
3240                 } else {
3241                     end = start + attributes[attr]['by'];
3242                 }
3243             }
3244
3245             this.runtimeAttributes[attr].start = start;
3246             this.runtimeAttributes[attr].end = end;
3247
3248
3249             this.runtimeAttributes[attr].unit = ( isset(attributes[attr].unit) ) ? attributes[attr]['unit'] : this.getDefaultUnit(attr);
3250         },
3251
3252
3253         init: function(el, attributes, duration, method) {
3254
3255             var isAnimated = false;
3256
3257
3258             var startTime = null;
3259
3260
3261             var actualFrames = 0;
3262
3263
3264             el = Roo.getDom(el);
3265
3266
3267             this.attributes = attributes || {};
3268
3269
3270             this.duration = duration || 1;
3271
3272
3273             this.method = method || Roo.lib.Easing.easeNone;
3274
3275
3276             this.useSeconds = true;
3277
3278
3279             this.currentFrame = 0;
3280
3281
3282             this.totalFrames = Roo.lib.AnimMgr.fps;
3283
3284
3285             this.getEl = function() {
3286                 return el;
3287             };
3288
3289
3290             this.isAnimated = function() {
3291                 return isAnimated;
3292             };
3293
3294
3295             this.getStartTime = function() {
3296                 return startTime;
3297             };
3298
3299             this.runtimeAttributes = {};
3300
3301
3302             this.animate = function() {
3303                 if (this.isAnimated()) {
3304                     return false;
3305                 }
3306
3307                 this.currentFrame = 0;
3308
3309                 this.totalFrames = ( this.useSeconds ) ? Math.ceil(Roo.lib.AnimMgr.fps * this.duration) : this.duration;
3310
3311                 Roo.lib.AnimMgr.registerElement(this);
3312             };
3313
3314
3315             this.stop = function(finish) {
3316                 if (finish) {
3317                     this.currentFrame = this.totalFrames;
3318                     this._onTween.fire();
3319                 }
3320                 Roo.lib.AnimMgr.stop(this);
3321             };
3322
3323             var onStart = function() {
3324                 this.onStart.fire();
3325
3326                 this.runtimeAttributes = {};
3327                 for (var attr in this.attributes) {
3328                     this.setRuntimeAttribute(attr);
3329                 }
3330
3331                 isAnimated = true;
3332                 actualFrames = 0;
3333                 startTime = new Date();
3334             };
3335
3336
3337             var onTween = function() {
3338                 var data = {
3339                     duration: new Date() - this.getStartTime(),
3340                     currentFrame: this.currentFrame
3341                 };
3342
3343                 data.toString = function() {
3344                     return (
3345                             'duration: ' + data.duration +
3346                             ', currentFrame: ' + data.currentFrame
3347                             );
3348                 };
3349
3350                 this.onTween.fire(data);
3351
3352                 var runtimeAttributes = this.runtimeAttributes;
3353
3354                 for (var attr in runtimeAttributes) {
3355                     this.setAttribute(attr, this.doMethod(attr, runtimeAttributes[attr].start, runtimeAttributes[attr].end), runtimeAttributes[attr].unit);
3356                 }
3357
3358                 actualFrames += 1;
3359             };
3360
3361             var onComplete = function() {
3362                 var actual_duration = (new Date() - startTime) / 1000 ;
3363
3364                 var data = {
3365                     duration: actual_duration,
3366                     frames: actualFrames,
3367                     fps: actualFrames / actual_duration
3368                 };
3369
3370                 data.toString = function() {
3371                     return (
3372                             'duration: ' + data.duration +
3373                             ', frames: ' + data.frames +
3374                             ', fps: ' + data.fps
3375                             );
3376                 };
3377
3378                 isAnimated = false;
3379                 actualFrames = 0;
3380                 this.onComplete.fire(data);
3381             };
3382
3383
3384             this._onStart = new Roo.util.Event(this);
3385             this.onStart = new Roo.util.Event(this);
3386             this.onTween = new Roo.util.Event(this);
3387             this._onTween = new Roo.util.Event(this);
3388             this.onComplete = new Roo.util.Event(this);
3389             this._onComplete = new Roo.util.Event(this);
3390             this._onStart.addListener(onStart);
3391             this._onTween.addListener(onTween);
3392             this._onComplete.addListener(onComplete);
3393         }
3394     };
3395 })();
3396 /*
3397  * Portions of this file are based on pieces of Yahoo User Interface Library
3398  * Copyright (c) 2007, Yahoo! Inc. All rights reserved.
3399  * YUI licensed under the BSD License:
3400  * http://developer.yahoo.net/yui/license.txt
3401  * <script type="text/javascript">
3402  *
3403  */
3404
3405 Roo.lib.AnimMgr = new function() {
3406
3407     var thread = null;
3408
3409
3410     var queue = [];
3411
3412
3413     var tweenCount = 0;
3414
3415
3416     this.fps = 1000;
3417
3418
3419     this.delay = 1;
3420
3421
3422     this.registerElement = function(tween) {
3423         queue[queue.length] = tween;
3424         tweenCount += 1;
3425         tween._onStart.fire();
3426         this.start();
3427     };
3428
3429
3430     this.unRegister = function(tween, index) {
3431         tween._onComplete.fire();
3432         index = index || getIndex(tween);
3433         if (index != -1) {
3434             queue.splice(index, 1);
3435         }
3436
3437         tweenCount -= 1;
3438         if (tweenCount <= 0) {
3439             this.stop();
3440         }
3441     };
3442
3443
3444     this.start = function() {
3445         if (thread === null) {
3446             thread = setInterval(this.run, this.delay);
3447         }
3448     };
3449
3450
3451     this.stop = function(tween) {
3452         if (!tween) {
3453             clearInterval(thread);
3454
3455             for (var i = 0, len = queue.length; i < len; ++i) {
3456                 if (queue[0].isAnimated()) {
3457                     this.unRegister(queue[0], 0);
3458                 }
3459             }
3460
3461             queue = [];
3462             thread = null;
3463             tweenCount = 0;
3464         }
3465         else {
3466             this.unRegister(tween);
3467         }
3468     };
3469
3470
3471     this.run = function() {
3472         for (var i = 0, len = queue.length; i < len; ++i) {
3473             var tween = queue[i];
3474             if (!tween || !tween.isAnimated()) {
3475                 continue;
3476             }
3477
3478             if (tween.currentFrame < tween.totalFrames || tween.totalFrames === null)
3479             {
3480                 tween.currentFrame += 1;
3481
3482                 if (tween.useSeconds) {
3483                     correctFrame(tween);
3484                 }
3485                 tween._onTween.fire();
3486             }
3487             else {
3488                 Roo.lib.AnimMgr.stop(tween, i);
3489             }
3490         }
3491     };
3492
3493     var getIndex = function(anim) {
3494         for (var i = 0, len = queue.length; i < len; ++i) {
3495             if (queue[i] == anim) {
3496                 return i;
3497             }
3498         }
3499         return -1;
3500     };
3501
3502
3503     var correctFrame = function(tween) {
3504         var frames = tween.totalFrames;
3505         var frame = tween.currentFrame;
3506         var expected = (tween.currentFrame * tween.duration * 1000 / tween.totalFrames);
3507         var elapsed = (new Date() - tween.getStartTime());
3508         var tweak = 0;
3509
3510         if (elapsed < tween.duration * 1000) {
3511             tweak = Math.round((elapsed / expected - 1) * tween.currentFrame);
3512         } else {
3513             tweak = frames - (frame + 1);
3514         }
3515         if (tweak > 0 && isFinite(tweak)) {
3516             if (tween.currentFrame + tweak >= frames) {
3517                 tweak = frames - (frame + 1);
3518             }
3519
3520             tween.currentFrame += tweak;
3521         }
3522     };
3523 };
3524
3525     /*
3526  * Portions of this file are based on pieces of Yahoo User Interface Library
3527  * Copyright (c) 2007, Yahoo! Inc. All rights reserved.
3528  * YUI licensed under the BSD License:
3529  * http://developer.yahoo.net/yui/license.txt
3530  * <script type="text/javascript">
3531  *
3532  */
3533 Roo.lib.Bezier = new function() {
3534
3535         this.getPosition = function(points, t) {
3536             var n = points.length;
3537             var tmp = [];
3538
3539             for (var i = 0; i < n; ++i) {
3540                 tmp[i] = [points[i][0], points[i][1]];
3541             }
3542
3543             for (var j = 1; j < n; ++j) {
3544                 for (i = 0; i < n - j; ++i) {
3545                     tmp[i][0] = (1 - t) * tmp[i][0] + t * tmp[parseInt(i + 1, 10)][0];
3546                     tmp[i][1] = (1 - t) * tmp[i][1] + t * tmp[parseInt(i + 1, 10)][1];
3547                 }
3548             }
3549
3550             return [ tmp[0][0], tmp[0][1] ];
3551
3552         };
3553     };/*
3554  * Portions of this file are based on pieces of Yahoo User Interface Library
3555  * Copyright (c) 2007, Yahoo! Inc. All rights reserved.
3556  * YUI licensed under the BSD License:
3557  * http://developer.yahoo.net/yui/license.txt
3558  * <script type="text/javascript">
3559  *
3560  */
3561 (function() {
3562
3563     Roo.lib.ColorAnim = function(el, attributes, duration, method) {
3564         Roo.lib.ColorAnim.superclass.constructor.call(this, el, attributes, duration, method);
3565     };
3566
3567     Roo.extend(Roo.lib.ColorAnim, Roo.lib.AnimBase);
3568
3569     var fly = Roo.lib.AnimBase.fly;
3570     var Y = Roo.lib;
3571     var superclass = Y.ColorAnim.superclass;
3572     var proto = Y.ColorAnim.prototype;
3573
3574     proto.toString = function() {
3575         var el = this.getEl();
3576         var id = el.id || el.tagName;
3577         return ("ColorAnim " + id);
3578     };
3579
3580     proto.patterns.color = /color$/i;
3581     proto.patterns.rgb = /^rgb\(([0-9]+)\s*,\s*([0-9]+)\s*,\s*([0-9]+)\)$/i;
3582     proto.patterns.hex = /^#?([0-9A-F]{2})([0-9A-F]{2})([0-9A-F]{2})$/i;
3583     proto.patterns.hex3 = /^#?([0-9A-F]{1})([0-9A-F]{1})([0-9A-F]{1})$/i;
3584     proto.patterns.transparent = /^transparent|rgba\(0, 0, 0, 0\)$/;
3585
3586
3587     proto.parseColor = function(s) {
3588         if (s.length == 3) {
3589             return s;
3590         }
3591
3592         var c = this.patterns.hex.exec(s);
3593         if (c && c.length == 4) {
3594             return [ parseInt(c[1], 16), parseInt(c[2], 16), parseInt(c[3], 16) ];
3595         }
3596
3597         c = this.patterns.rgb.exec(s);
3598         if (c && c.length == 4) {
3599             return [ parseInt(c[1], 10), parseInt(c[2], 10), parseInt(c[3], 10) ];
3600         }
3601
3602         c = this.patterns.hex3.exec(s);
3603         if (c && c.length == 4) {
3604             return [ parseInt(c[1] + c[1], 16), parseInt(c[2] + c[2], 16), parseInt(c[3] + c[3], 16) ];
3605         }
3606
3607         return null;
3608     };
3609     // since this uses fly! - it cant be in ColorAnim (which does not have fly yet..)
3610     proto.getAttribute = function(attr) {
3611         var el = this.getEl();
3612         if (this.patterns.color.test(attr)) {
3613             var val = fly(el).getStyle(attr);
3614
3615             if (this.patterns.transparent.test(val)) {
3616                 var parent = el.parentNode;
3617                 val = fly(parent).getStyle(attr);
3618
3619                 while (parent && this.patterns.transparent.test(val)) {
3620                     parent = parent.parentNode;
3621                     val = fly(parent).getStyle(attr);
3622                     if (parent.tagName.toUpperCase() == 'HTML') {
3623                         val = '#fff';
3624                     }
3625                 }
3626             }
3627         } else {
3628             val = superclass.getAttribute.call(this, attr);
3629         }
3630
3631         return val;
3632     };
3633     proto.getAttribute = function(attr) {
3634         var el = this.getEl();
3635         if (this.patterns.color.test(attr)) {
3636             var val = fly(el).getStyle(attr);
3637
3638             if (this.patterns.transparent.test(val)) {
3639                 var parent = el.parentNode;
3640                 val = fly(parent).getStyle(attr);
3641
3642                 while (parent && this.patterns.transparent.test(val)) {
3643                     parent = parent.parentNode;
3644                     val = fly(parent).getStyle(attr);
3645                     if (parent.tagName.toUpperCase() == 'HTML') {
3646                         val = '#fff';
3647                     }
3648                 }
3649             }
3650         } else {
3651             val = superclass.getAttribute.call(this, attr);
3652         }
3653
3654         return val;
3655     };
3656
3657     proto.doMethod = function(attr, start, end) {
3658         var val;
3659
3660         if (this.patterns.color.test(attr)) {
3661             val = [];
3662             for (var i = 0, len = start.length; i < len; ++i) {
3663                 val[i] = superclass.doMethod.call(this, attr, start[i], end[i]);
3664             }
3665
3666             val = 'rgb(' + Math.floor(val[0]) + ',' + Math.floor(val[1]) + ',' + Math.floor(val[2]) + ')';
3667         }
3668         else {
3669             val = superclass.doMethod.call(this, attr, start, end);
3670         }
3671
3672         return val;
3673     };
3674
3675     proto.setRuntimeAttribute = function(attr) {
3676         superclass.setRuntimeAttribute.call(this, attr);
3677
3678         if (this.patterns.color.test(attr)) {
3679             var attributes = this.attributes;
3680             var start = this.parseColor(this.runtimeAttributes[attr].start);
3681             var end = this.parseColor(this.runtimeAttributes[attr].end);
3682
3683             if (typeof attributes[attr]['to'] === 'undefined' && typeof attributes[attr]['by'] !== 'undefined') {
3684                 end = this.parseColor(attributes[attr].by);
3685
3686                 for (var i = 0, len = start.length; i < len; ++i) {
3687                     end[i] = start[i] + end[i];
3688                 }
3689             }
3690
3691             this.runtimeAttributes[attr].start = start;
3692             this.runtimeAttributes[attr].end = end;
3693         }
3694     };
3695 })();
3696
3697 /*
3698  * Portions of this file are based on pieces of Yahoo User Interface Library
3699  * Copyright (c) 2007, Yahoo! Inc. All rights reserved.
3700  * YUI licensed under the BSD License:
3701  * http://developer.yahoo.net/yui/license.txt
3702  * <script type="text/javascript">
3703  *
3704  */
3705 Roo.lib.Easing = {
3706
3707
3708     easeNone: function (t, b, c, d) {
3709         return c * t / d + b;
3710     },
3711
3712
3713     easeIn: function (t, b, c, d) {
3714         return c * (t /= d) * t + b;
3715     },
3716
3717
3718     easeOut: function (t, b, c, d) {
3719         return -c * (t /= d) * (t - 2) + b;
3720     },
3721
3722
3723     easeBoth: function (t, b, c, d) {
3724         if ((t /= d / 2) < 1) {
3725             return c / 2 * t * t + b;
3726         }
3727
3728         return -c / 2 * ((--t) * (t - 2) - 1) + b;
3729     },
3730
3731
3732     easeInStrong: function (t, b, c, d) {
3733         return c * (t /= d) * t * t * t + b;
3734     },
3735
3736
3737     easeOutStrong: function (t, b, c, d) {
3738         return -c * ((t = t / d - 1) * t * t * t - 1) + b;
3739     },
3740
3741
3742     easeBothStrong: function (t, b, c, d) {
3743         if ((t /= d / 2) < 1) {
3744             return c / 2 * t * t * t * t + b;
3745         }
3746
3747         return -c / 2 * ((t -= 2) * t * t * t - 2) + b;
3748     },
3749
3750
3751
3752     elasticIn: function (t, b, c, d, a, p) {
3753         if (t == 0) {
3754             return b;
3755         }
3756         if ((t /= d) == 1) {
3757             return b + c;
3758         }
3759         if (!p) {
3760             p = d * .3;
3761         }
3762
3763         if (!a || a < Math.abs(c)) {
3764             a = c;
3765             var s = p / 4;
3766         }
3767         else {
3768             var s = p / (2 * Math.PI) * Math.asin(c / a);
3769         }
3770
3771         return -(a * Math.pow(2, 10 * (t -= 1)) * Math.sin((t * d - s) * (2 * Math.PI) / p)) + b;
3772     },
3773
3774
3775     elasticOut: function (t, b, c, d, a, p) {
3776         if (t == 0) {
3777             return b;
3778         }
3779         if ((t /= d) == 1) {
3780             return b + c;
3781         }
3782         if (!p) {
3783             p = d * .3;
3784         }
3785
3786         if (!a || a < Math.abs(c)) {
3787             a = c;
3788             var s = p / 4;
3789         }
3790         else {
3791             var s = p / (2 * Math.PI) * Math.asin(c / a);
3792         }
3793
3794         return a * Math.pow(2, -10 * t) * Math.sin((t * d - s) * (2 * Math.PI) / p) + c + b;
3795     },
3796
3797
3798     elasticBoth: function (t, b, c, d, a, p) {
3799         if (t == 0) {
3800             return b;
3801         }
3802
3803         if ((t /= d / 2) == 2) {
3804             return b + c;
3805         }
3806
3807         if (!p) {
3808             p = d * (.3 * 1.5);
3809         }
3810
3811         if (!a || a < Math.abs(c)) {
3812             a = c;
3813             var s = p / 4;
3814         }
3815         else {
3816             var s = p / (2 * Math.PI) * Math.asin(c / a);
3817         }
3818
3819         if (t < 1) {
3820             return -.5 * (a * Math.pow(2, 10 * (t -= 1)) *
3821                           Math.sin((t * d - s) * (2 * Math.PI) / p)) + b;
3822         }
3823         return a * Math.pow(2, -10 * (t -= 1)) *
3824                Math.sin((t * d - s) * (2 * Math.PI) / p) * .5 + c + b;
3825     },
3826
3827
3828
3829     backIn: function (t, b, c, d, s) {
3830         if (typeof s == 'undefined') {
3831             s = 1.70158;
3832         }
3833         return c * (t /= d) * t * ((s + 1) * t - s) + b;
3834     },
3835
3836
3837     backOut: function (t, b, c, d, s) {
3838         if (typeof s == 'undefined') {
3839             s = 1.70158;
3840         }
3841         return c * ((t = t / d - 1) * t * ((s + 1) * t + s) + 1) + b;
3842     },
3843
3844
3845     backBoth: function (t, b, c, d, s) {
3846         if (typeof s == 'undefined') {
3847             s = 1.70158;
3848         }
3849
3850         if ((t /= d / 2 ) < 1) {
3851             return c / 2 * (t * t * (((s *= (1.525)) + 1) * t - s)) + b;
3852         }
3853         return c / 2 * ((t -= 2) * t * (((s *= (1.525)) + 1) * t + s) + 2) + b;
3854     },
3855
3856
3857     bounceIn: function (t, b, c, d) {
3858         return c - Roo.lib.Easing.bounceOut(d - t, 0, c, d) + b;
3859     },
3860
3861
3862     bounceOut: function (t, b, c, d) {
3863         if ((t /= d) < (1 / 2.75)) {
3864             return c * (7.5625 * t * t) + b;
3865         } else if (t < (2 / 2.75)) {
3866             return c * (7.5625 * (t -= (1.5 / 2.75)) * t + .75) + b;
3867         } else if (t < (2.5 / 2.75)) {
3868             return c * (7.5625 * (t -= (2.25 / 2.75)) * t + .9375) + b;
3869         }
3870         return c * (7.5625 * (t -= (2.625 / 2.75)) * t + .984375) + b;
3871     },
3872
3873
3874     bounceBoth: function (t, b, c, d) {
3875         if (t < d / 2) {
3876             return Roo.lib.Easing.bounceIn(t * 2, 0, c, d) * .5 + b;
3877         }
3878         return Roo.lib.Easing.bounceOut(t * 2 - d, 0, c, d) * .5 + c * .5 + b;
3879     }
3880 };/*
3881  * Portions of this file are based on pieces of Yahoo User Interface Library
3882  * Copyright (c) 2007, Yahoo! Inc. All rights reserved.
3883  * YUI licensed under the BSD License:
3884  * http://developer.yahoo.net/yui/license.txt
3885  * <script type="text/javascript">
3886  *
3887  */
3888     (function() {
3889         Roo.lib.Motion = function(el, attributes, duration, method) {
3890             if (el) {
3891                 Roo.lib.Motion.superclass.constructor.call(this, el, attributes, duration, method);
3892             }
3893         };
3894
3895         Roo.extend(Roo.lib.Motion, Roo.lib.ColorAnim);
3896
3897
3898         var Y = Roo.lib;
3899         var superclass = Y.Motion.superclass;
3900         var proto = Y.Motion.prototype;
3901
3902         proto.toString = function() {
3903             var el = this.getEl();
3904             var id = el.id || el.tagName;
3905             return ("Motion " + id);
3906         };
3907
3908         proto.patterns.points = /^points$/i;
3909
3910         proto.setAttribute = function(attr, val, unit) {
3911             if (this.patterns.points.test(attr)) {
3912                 unit = unit || 'px';
3913                 superclass.setAttribute.call(this, 'left', val[0], unit);
3914                 superclass.setAttribute.call(this, 'top', val[1], unit);
3915             } else {
3916                 superclass.setAttribute.call(this, attr, val, unit);
3917             }
3918         };
3919
3920         proto.getAttribute = function(attr) {
3921             if (this.patterns.points.test(attr)) {
3922                 var val = [
3923                         superclass.getAttribute.call(this, 'left'),
3924                         superclass.getAttribute.call(this, 'top')
3925                         ];
3926             } else {
3927                 val = superclass.getAttribute.call(this, attr);
3928             }
3929
3930             return val;
3931         };
3932
3933         proto.doMethod = function(attr, start, end) {
3934             var val = null;
3935
3936             if (this.patterns.points.test(attr)) {
3937                 var t = this.method(this.currentFrame, 0, 100, this.totalFrames) / 100;
3938                 val = Y.Bezier.getPosition(this.runtimeAttributes[attr], t);
3939             } else {
3940                 val = superclass.doMethod.call(this, attr, start, end);
3941             }
3942             return val;
3943         };
3944
3945         proto.setRuntimeAttribute = function(attr) {
3946             if (this.patterns.points.test(attr)) {
3947                 var el = this.getEl();
3948                 var attributes = this.attributes;
3949                 var start;
3950                 var control = attributes['points']['control'] || [];
3951                 var end;
3952                 var i, len;
3953
3954                 if (control.length > 0 && !(control[0] instanceof Array)) {
3955                     control = [control];
3956                 } else {
3957                     var tmp = [];
3958                     for (i = 0,len = control.length; i < len; ++i) {
3959                         tmp[i] = control[i];
3960                     }
3961                     control = tmp;
3962                 }
3963
3964                 Roo.fly(el).position();
3965
3966                 if (isset(attributes['points']['from'])) {
3967                     Roo.lib.Dom.setXY(el, attributes['points']['from']);
3968                 }
3969                 else {
3970                     Roo.lib.Dom.setXY(el, Roo.lib.Dom.getXY(el));
3971                 }
3972
3973                 start = this.getAttribute('points');
3974
3975
3976                 if (isset(attributes['points']['to'])) {
3977                     end = translateValues.call(this, attributes['points']['to'], start);
3978
3979                     var pageXY = Roo.lib.Dom.getXY(this.getEl());
3980                     for (i = 0,len = control.length; i < len; ++i) {
3981                         control[i] = translateValues.call(this, control[i], start);
3982                     }
3983
3984
3985                 } else if (isset(attributes['points']['by'])) {
3986                     end = [ start[0] + attributes['points']['by'][0], start[1] + attributes['points']['by'][1] ];
3987
3988                     for (i = 0,len = control.length; i < len; ++i) {
3989                         control[i] = [ start[0] + control[i][0], start[1] + control[i][1] ];
3990                     }
3991                 }
3992
3993                 this.runtimeAttributes[attr] = [start];
3994
3995                 if (control.length > 0) {
3996                     this.runtimeAttributes[attr] = this.runtimeAttributes[attr].concat(control);
3997                 }
3998
3999                 this.runtimeAttributes[attr][this.runtimeAttributes[attr].length] = end;
4000             }
4001             else {
4002                 superclass.setRuntimeAttribute.call(this, attr);
4003             }
4004         };
4005
4006         var translateValues = function(val, start) {
4007             var pageXY = Roo.lib.Dom.getXY(this.getEl());
4008             val = [ val[0] - pageXY[0] + start[0], val[1] - pageXY[1] + start[1] ];
4009
4010             return val;
4011         };
4012
4013         var isset = function(prop) {
4014             return (typeof prop !== 'undefined');
4015         };
4016     })();
4017 /*
4018  * Portions of this file are based on pieces of Yahoo User Interface Library
4019  * Copyright (c) 2007, Yahoo! Inc. All rights reserved.
4020  * YUI licensed under the BSD License:
4021  * http://developer.yahoo.net/yui/license.txt
4022  * <script type="text/javascript">
4023  *
4024  */
4025     (function() {
4026         Roo.lib.Scroll = function(el, attributes, duration, method) {
4027             if (el) {
4028                 Roo.lib.Scroll.superclass.constructor.call(this, el, attributes, duration, method);
4029             }
4030         };
4031
4032         Roo.extend(Roo.lib.Scroll, Roo.lib.ColorAnim);
4033
4034
4035         var Y = Roo.lib;
4036         var superclass = Y.Scroll.superclass;
4037         var proto = Y.Scroll.prototype;
4038
4039         proto.toString = function() {
4040             var el = this.getEl();
4041             var id = el.id || el.tagName;
4042             return ("Scroll " + id);
4043         };
4044
4045         proto.doMethod = function(attr, start, end) {
4046             var val = null;
4047
4048             if (attr == 'scroll') {
4049                 val = [
4050                         this.method(this.currentFrame, start[0], end[0] - start[0], this.totalFrames),
4051                         this.method(this.currentFrame, start[1], end[1] - start[1], this.totalFrames)
4052                         ];
4053
4054             } else {
4055                 val = superclass.doMethod.call(this, attr, start, end);
4056             }
4057             return val;
4058         };
4059
4060         proto.getAttribute = function(attr) {
4061             var val = null;
4062             var el = this.getEl();
4063
4064             if (attr == 'scroll') {
4065                 val = [ el.scrollLeft, el.scrollTop ];
4066             } else {
4067                 val = superclass.getAttribute.call(this, attr);
4068             }
4069
4070             return val;
4071         };
4072
4073         proto.setAttribute = function(attr, val, unit) {
4074             var el = this.getEl();
4075
4076             if (attr == 'scroll') {
4077                 el.scrollLeft = val[0];
4078                 el.scrollTop = val[1];
4079             } else {
4080                 superclass.setAttribute.call(this, attr, val, unit);
4081             }
4082         };
4083     })();
4084 /*
4085  * Based on:
4086  * Ext JS Library 1.1.1
4087  * Copyright(c) 2006-2007, Ext JS, LLC.
4088  *
4089  * Originally Released Under LGPL - original licence link has changed is not relivant.
4090  *
4091  * Fork - LGPL
4092  * <script type="text/javascript">
4093  */
4094
4095
4096 // nasty IE9 hack - what a pile of crap that is..
4097
4098  if (typeof Range != "undefined" && typeof Range.prototype.createContextualFragment == "undefined") {
4099     Range.prototype.createContextualFragment = function (html) {
4100         var doc = window.document;
4101         var container = doc.createElement("div");
4102         container.innerHTML = html;
4103         var frag = doc.createDocumentFragment(), n;
4104         while ((n = container.firstChild)) {
4105             frag.appendChild(n);
4106         }
4107         return frag;
4108     };
4109 }
4110
4111 /**
4112  * @class Roo.DomHelper
4113  * Utility class for working with DOM and/or Templates. It transparently supports using HTML fragments or DOM.
4114  * 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>.
4115  * @singleton
4116  */
4117 Roo.DomHelper = function(){
4118     var tempTableEl = null;
4119     var emptyTags = /^(?:br|frame|hr|img|input|link|meta|range|spacer|wbr|area|param|col)$/i;
4120     var tableRe = /^table|tbody|tr|td$/i;
4121     var xmlns = {};
4122     // build as innerHTML where available
4123     /** @ignore */
4124     var createHtml = function(o){
4125         if(typeof o == 'string'){
4126             return o;
4127         }
4128         var b = "";
4129         if(!o.tag){
4130             o.tag = "div";
4131         }
4132         b += "<" + o.tag;
4133         for(var attr in o){
4134             if(attr == "tag" || attr == "children" || attr == "cn" || attr == "html" || typeof o[attr] == "function") continue;
4135             if(attr == "style"){
4136                 var s = o["style"];
4137                 if(typeof s == "function"){
4138                     s = s.call();
4139                 }
4140                 if(typeof s == "string"){
4141                     b += ' style="' + s + '"';
4142                 }else if(typeof s == "object"){
4143                     b += ' style="';
4144                     for(var key in s){
4145                         if(typeof s[key] != "function"){
4146                             b += key + ":" + s[key] + ";";
4147                         }
4148                     }
4149                     b += '"';
4150                 }
4151             }else{
4152                 if(attr == "cls"){
4153                     b += ' class="' + o["cls"] + '"';
4154                 }else if(attr == "htmlFor"){
4155                     b += ' for="' + o["htmlFor"] + '"';
4156                 }else{
4157                     b += " " + attr + '="' + o[attr] + '"';
4158                 }
4159             }
4160         }
4161         if(emptyTags.test(o.tag)){
4162             b += "/>";
4163         }else{
4164             b += ">";
4165             var cn = o.children || o.cn;
4166             if(cn){
4167                 //http://bugs.kde.org/show_bug.cgi?id=71506
4168                 if((cn instanceof Array) || (Roo.isSafari && typeof(cn.join) == "function")){
4169                     for(var i = 0, len = cn.length; i < len; i++) {
4170                         b += createHtml(cn[i], b);
4171                     }
4172                 }else{
4173                     b += createHtml(cn, b);
4174                 }
4175             }
4176             if(o.html){
4177                 b += o.html;
4178             }
4179             b += "</" + o.tag + ">";
4180         }
4181         return b;
4182     };
4183
4184     // build as dom
4185     /** @ignore */
4186     var createDom = function(o, parentNode){
4187          
4188         // defininition craeted..
4189         var ns = false;
4190         if (o.ns && o.ns != 'html') {
4191                
4192             if (o.xmlns && typeof(xmlns[o.ns]) == 'undefined') {
4193                 xmlns[o.ns] = o.xmlns;
4194                 ns = o.xmlns;
4195             }
4196             if (typeof(xmlns[o.ns]) == 'undefined') {
4197                 console.log("Trying to create namespace element " + o.ns + ", however no xmlns was sent to builder previously");
4198             }
4199             ns = xmlns[o.ns];
4200         }
4201         
4202         
4203         if (typeof(o) == 'string') {
4204             return parentNode.appendChild(document.createTextNode(o));
4205         }
4206         o.tag = o.tag || div;
4207         if (o.ns && Roo.isIE) {
4208             ns = false;
4209             o.tag = o.ns + ':' + o.tag;
4210             
4211         }
4212         var el = ns ? document.createElementNS( ns, o.tag||'div') :  document.createElement(o.tag||'div');
4213         var useSet = el.setAttribute ? true : false; // In IE some elements don't have setAttribute
4214         for(var attr in o){
4215             
4216             if(attr == "tag" || attr == "ns" ||attr == "xmlns" ||attr == "children" || attr == "cn" || attr == "html" || 
4217                     attr == "style" || typeof o[attr] == "function") continue;
4218                     
4219             if(attr=="cls" && Roo.isIE){
4220                 el.className = o["cls"];
4221             }else{
4222                 if(useSet) el.setAttribute(attr=="cls" ? 'class' : attr, o[attr]);
4223                 else el[attr] = o[attr];
4224             }
4225         }
4226         Roo.DomHelper.applyStyles(el, o.style);
4227         var cn = o.children || o.cn;
4228         if(cn){
4229             //http://bugs.kde.org/show_bug.cgi?id=71506
4230              if((cn instanceof Array) || (Roo.isSafari && typeof(cn.join) == "function")){
4231                 for(var i = 0, len = cn.length; i < len; i++) {
4232                     createDom(cn[i], el);
4233                 }
4234             }else{
4235                 createDom(cn, el);
4236             }
4237         }
4238         if(o.html){
4239             el.innerHTML = o.html;
4240         }
4241         if(parentNode){
4242            parentNode.appendChild(el);
4243         }
4244         return el;
4245     };
4246
4247     var ieTable = function(depth, s, h, e){
4248         tempTableEl.innerHTML = [s, h, e].join('');
4249         var i = -1, el = tempTableEl;
4250         while(++i < depth){
4251             el = el.firstChild;
4252         }
4253         return el;
4254     };
4255
4256     // kill repeat to save bytes
4257     var ts = '<table>',
4258         te = '</table>',
4259         tbs = ts+'<tbody>',
4260         tbe = '</tbody>'+te,
4261         trs = tbs + '<tr>',
4262         tre = '</tr>'+tbe;
4263
4264     /**
4265      * @ignore
4266      * Nasty code for IE's broken table implementation
4267      */
4268     var insertIntoTable = function(tag, where, el, html){
4269         if(!tempTableEl){
4270             tempTableEl = document.createElement('div');
4271         }
4272         var node;
4273         var before = null;
4274         if(tag == 'td'){
4275             if(where == 'afterbegin' || where == 'beforeend'){ // INTO a TD
4276                 return;
4277             }
4278             if(where == 'beforebegin'){
4279                 before = el;
4280                 el = el.parentNode;
4281             } else{
4282                 before = el.nextSibling;
4283                 el = el.parentNode;
4284             }
4285             node = ieTable(4, trs, html, tre);
4286         }
4287         else if(tag == 'tr'){
4288             if(where == 'beforebegin'){
4289                 before = el;
4290                 el = el.parentNode;
4291                 node = ieTable(3, tbs, html, tbe);
4292             } else if(where == 'afterend'){
4293                 before = el.nextSibling;
4294                 el = el.parentNode;
4295                 node = ieTable(3, tbs, html, tbe);
4296             } else{ // INTO a TR
4297                 if(where == 'afterbegin'){
4298                     before = el.firstChild;
4299                 }
4300                 node = ieTable(4, trs, html, tre);
4301             }
4302         } else if(tag == 'tbody'){
4303             if(where == 'beforebegin'){
4304                 before = el;
4305                 el = el.parentNode;
4306                 node = ieTable(2, ts, html, te);
4307             } else if(where == 'afterend'){
4308                 before = el.nextSibling;
4309                 el = el.parentNode;
4310                 node = ieTable(2, ts, html, te);
4311             } else{
4312                 if(where == 'afterbegin'){
4313                     before = el.firstChild;
4314                 }
4315                 node = ieTable(3, tbs, html, tbe);
4316             }
4317         } else{ // TABLE
4318             if(where == 'beforebegin' || where == 'afterend'){ // OUTSIDE the table
4319                 return;
4320             }
4321             if(where == 'afterbegin'){
4322                 before = el.firstChild;
4323             }
4324             node = ieTable(2, ts, html, te);
4325         }
4326         el.insertBefore(node, before);
4327         return node;
4328     };
4329
4330     return {
4331     /** True to force the use of DOM instead of html fragments @type Boolean */
4332     useDom : false,
4333
4334     /**
4335      * Returns the markup for the passed Element(s) config
4336      * @param {Object} o The Dom object spec (and children)
4337      * @return {String}
4338      */
4339     markup : function(o){
4340         return createHtml(o);
4341     },
4342
4343     /**
4344      * Applies a style specification to an element
4345      * @param {String/HTMLElement} el The element to apply styles to
4346      * @param {String/Object/Function} styles A style specification string eg "width:100px", or object in the form {width:"100px"}, or
4347      * a function which returns such a specification.
4348      */
4349     applyStyles : function(el, styles){
4350         if(styles){
4351            el = Roo.fly(el);
4352            if(typeof styles == "string"){
4353                var re = /\s?([a-z\-]*)\:\s?([^;]*);?/gi;
4354                var matches;
4355                while ((matches = re.exec(styles)) != null){
4356                    el.setStyle(matches[1], matches[2]);
4357                }
4358            }else if (typeof styles == "object"){
4359                for (var style in styles){
4360                   el.setStyle(style, styles[style]);
4361                }
4362            }else if (typeof styles == "function"){
4363                 Roo.DomHelper.applyStyles(el, styles.call());
4364            }
4365         }
4366     },
4367
4368     /**
4369      * Inserts an HTML fragment into the Dom
4370      * @param {String} where Where to insert the html in relation to el - beforeBegin, afterBegin, beforeEnd, afterEnd.
4371      * @param {HTMLElement} el The context element
4372      * @param {String} html The HTML fragmenet
4373      * @return {HTMLElement} The new node
4374      */
4375     insertHtml : function(where, el, html){
4376         where = where.toLowerCase();
4377         if(el.insertAdjacentHTML){
4378             if(tableRe.test(el.tagName)){
4379                 var rs;
4380                 if(rs = insertIntoTable(el.tagName.toLowerCase(), where, el, html)){
4381                     return rs;
4382                 }
4383             }
4384             switch(where){
4385                 case "beforebegin":
4386                     el.insertAdjacentHTML('BeforeBegin', html);
4387                     return el.previousSibling;
4388                 case "afterbegin":
4389                     el.insertAdjacentHTML('AfterBegin', html);
4390                     return el.firstChild;
4391                 case "beforeend":
4392                     el.insertAdjacentHTML('BeforeEnd', html);
4393                     return el.lastChild;
4394                 case "afterend":
4395                     el.insertAdjacentHTML('AfterEnd', html);
4396                     return el.nextSibling;
4397             }
4398             throw 'Illegal insertion point -> "' + where + '"';
4399         }
4400         var range = el.ownerDocument.createRange();
4401         var frag;
4402         switch(where){
4403              case "beforebegin":
4404                 range.setStartBefore(el);
4405                 frag = range.createContextualFragment(html);
4406                 el.parentNode.insertBefore(frag, el);
4407                 return el.previousSibling;
4408              case "afterbegin":
4409                 if(el.firstChild){
4410                     range.setStartBefore(el.firstChild);
4411                     frag = range.createContextualFragment(html);
4412                     el.insertBefore(frag, el.firstChild);
4413                     return el.firstChild;
4414                 }else{
4415                     el.innerHTML = html;
4416                     return el.firstChild;
4417                 }
4418             case "beforeend":
4419                 if(el.lastChild){
4420                     range.setStartAfter(el.lastChild);
4421                     frag = range.createContextualFragment(html);
4422                     el.appendChild(frag);
4423                     return el.lastChild;
4424                 }else{
4425                     el.innerHTML = html;
4426                     return el.lastChild;
4427                 }
4428             case "afterend":
4429                 range.setStartAfter(el);
4430                 frag = range.createContextualFragment(html);
4431                 el.parentNode.insertBefore(frag, el.nextSibling);
4432                 return el.nextSibling;
4433             }
4434             throw 'Illegal insertion point -> "' + where + '"';
4435     },
4436
4437     /**
4438      * Creates new Dom element(s) and inserts them before el
4439      * @param {String/HTMLElement/Element} el The context element
4440      * @param {Object/String} o The Dom object spec (and children) or raw HTML blob
4441      * @param {Boolean} returnElement (optional) true to return a Roo.Element
4442      * @return {HTMLElement/Roo.Element} The new node
4443      */
4444     insertBefore : function(el, o, returnElement){
4445         return this.doInsert(el, o, returnElement, "beforeBegin");
4446     },
4447
4448     /**
4449      * Creates new Dom element(s) and inserts them after el
4450      * @param {String/HTMLElement/Element} el The context element
4451      * @param {Object} o The Dom object spec (and children)
4452      * @param {Boolean} returnElement (optional) true to return a Roo.Element
4453      * @return {HTMLElement/Roo.Element} The new node
4454      */
4455     insertAfter : function(el, o, returnElement){
4456         return this.doInsert(el, o, returnElement, "afterEnd", "nextSibling");
4457     },
4458
4459     /**
4460      * Creates new Dom element(s) and inserts them as the first child of el
4461      * @param {String/HTMLElement/Element} el The context element
4462      * @param {Object/String} o The Dom object spec (and children) or raw HTML blob
4463      * @param {Boolean} returnElement (optional) true to return a Roo.Element
4464      * @return {HTMLElement/Roo.Element} The new node
4465      */
4466     insertFirst : function(el, o, returnElement){
4467         return this.doInsert(el, o, returnElement, "afterBegin");
4468     },
4469
4470     // private
4471     doInsert : function(el, o, returnElement, pos, sibling){
4472         el = Roo.getDom(el);
4473         var newNode;
4474         if(this.useDom || o.ns){
4475             newNode = createDom(o, null);
4476             el.parentNode.insertBefore(newNode, sibling ? el[sibling] : el);
4477         }else{
4478             var html = createHtml(o);
4479             newNode = this.insertHtml(pos, el, html);
4480         }
4481         return returnElement ? Roo.get(newNode, true) : newNode;
4482     },
4483
4484     /**
4485      * Creates new Dom element(s) and appends them to el
4486      * @param {String/HTMLElement/Element} el The context element
4487      * @param {Object/String} o The Dom object spec (and children) or raw HTML blob
4488      * @param {Boolean} returnElement (optional) true to return a Roo.Element
4489      * @return {HTMLElement/Roo.Element} The new node
4490      */
4491     append : function(el, o, returnElement){
4492         el = Roo.getDom(el);
4493         var newNode;
4494         if(this.useDom || o.ns){
4495             newNode = createDom(o, null);
4496             el.appendChild(newNode);
4497         }else{
4498             var html = createHtml(o);
4499             newNode = this.insertHtml("beforeEnd", el, html);
4500         }
4501         return returnElement ? Roo.get(newNode, true) : newNode;
4502     },
4503
4504     /**
4505      * Creates new Dom element(s) and overwrites the contents of el with them
4506      * @param {String/HTMLElement/Element} el The context element
4507      * @param {Object/String} o The Dom object spec (and children) or raw HTML blob
4508      * @param {Boolean} returnElement (optional) true to return a Roo.Element
4509      * @return {HTMLElement/Roo.Element} The new node
4510      */
4511     overwrite : function(el, o, returnElement){
4512         el = Roo.getDom(el);
4513         if (o.ns) {
4514           
4515             while (el.childNodes.length) {
4516                 el.removeChild(el.firstChild);
4517             }
4518             createDom(o, el);
4519         } else {
4520             el.innerHTML = createHtml(o);   
4521         }
4522         
4523         return returnElement ? Roo.get(el.firstChild, true) : el.firstChild;
4524     },
4525
4526     /**
4527      * Creates a new Roo.DomHelper.Template from the Dom object spec
4528      * @param {Object} o The Dom object spec (and children)
4529      * @return {Roo.DomHelper.Template} The new template
4530      */
4531     createTemplate : function(o){
4532         var html = createHtml(o);
4533         return new Roo.Template(html);
4534     }
4535     };
4536 }();
4537 /*
4538  * Based on:
4539  * Ext JS Library 1.1.1
4540  * Copyright(c) 2006-2007, Ext JS, LLC.
4541  *
4542  * Originally Released Under LGPL - original licence link has changed is not relivant.
4543  *
4544  * Fork - LGPL
4545  * <script type="text/javascript">
4546  */
4547  
4548 /**
4549 * @class Roo.Template
4550 * Represents an HTML fragment template. Templates can be precompiled for greater performance.
4551 * For a list of available format functions, see {@link Roo.util.Format}.<br />
4552 * Usage:
4553 <pre><code>
4554 var t = new Roo.Template({
4555     html :  '&lt;div name="{id}"&gt;' + 
4556         '&lt;span class="{cls}"&gt;{name:trim} {someval:this.myformat}{value:ellipsis(10)}&lt;/span&gt;' +
4557         '&lt;/div&gt;',
4558     myformat: function (value, allValues) {
4559         return 'XX' + value;
4560     }
4561 });
4562 t.append('some-element', {id: 'myid', cls: 'myclass', name: 'foo', value: 'bar'});
4563 </code></pre>
4564 * For more information see this blog post with examples:
4565 *  <a href="http://www.cnitblog.com/seeyeah/archive/2011/12/30/38728.html/">DomHelper
4566      - Create Elements using DOM, HTML fragments and Templates</a>. 
4567 * @constructor
4568 * @param {Object} cfg - Configuration object.
4569 */
4570 Roo.Template = function(cfg){
4571     // BC!
4572     if(cfg instanceof Array){
4573         cfg = cfg.join("");
4574     }else if(arguments.length > 1){
4575         cfg = Array.prototype.join.call(arguments, "");
4576     }
4577     
4578     
4579     if (typeof(cfg) == 'object') {
4580         Roo.apply(this,cfg)
4581     } else {
4582         // bc
4583         this.html = cfg;
4584     }
4585     if (this.url) {
4586         this.load();
4587     }
4588     
4589 };
4590 Roo.Template.prototype = {
4591     
4592     /**
4593      * @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..
4594      *                    it should be fixed so that template is observable...
4595      */
4596     url : false,
4597     /**
4598      * @cfg {String} html  The HTML fragment or an array of fragments to join("") or multiple arguments to join("")
4599      */
4600     html : '',
4601     /**
4602      * Returns an HTML fragment of this template with the specified values applied.
4603      * @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'})
4604      * @return {String} The HTML fragment
4605      */
4606     applyTemplate : function(values){
4607         try {
4608            
4609             if(this.compiled){
4610                 return this.compiled(values);
4611             }
4612             var useF = this.disableFormats !== true;
4613             var fm = Roo.util.Format, tpl = this;
4614             var fn = function(m, name, format, args){
4615                 if(format && useF){
4616                     if(format.substr(0, 5) == "this."){
4617                         return tpl.call(format.substr(5), values[name], values);
4618                     }else{
4619                         if(args){
4620                             // quoted values are required for strings in compiled templates, 
4621                             // but for non compiled we need to strip them
4622                             // quoted reversed for jsmin
4623                             var re = /^\s*['"](.*)["']\s*$/;
4624                             args = args.split(',');
4625                             for(var i = 0, len = args.length; i < len; i++){
4626                                 args[i] = args[i].replace(re, "$1");
4627                             }
4628                             args = [values[name]].concat(args);
4629                         }else{
4630                             args = [values[name]];
4631                         }
4632                         return fm[format].apply(fm, args);
4633                     }
4634                 }else{
4635                     return values[name] !== undefined ? values[name] : "";
4636                 }
4637             };
4638             return this.html.replace(this.re, fn);
4639         } catch (e) {
4640             Roo.log(e);
4641             throw e;
4642         }
4643          
4644     },
4645     
4646     loading : false,
4647       
4648     load : function ()
4649     {
4650          
4651         if (this.loading) {
4652             return;
4653         }
4654         var _t = this;
4655         
4656         this.loading = true;
4657         this.compiled = false;
4658         
4659         var cx = new Roo.data.Connection();
4660         cx.request({
4661             url : this.url,
4662             method : 'GET',
4663             success : function (response) {
4664                 _t.loading = false;
4665                 _t.html = response.responseText;
4666                 _t.url = false;
4667                 _t.compile();
4668              },
4669             failure : function(response) {
4670                 Roo.log("Template failed to load from " + _t.url);
4671                 _t.loading = false;
4672             }
4673         });
4674     },
4675
4676     /**
4677      * Sets the HTML used as the template and optionally compiles it.
4678      * @param {String} html
4679      * @param {Boolean} compile (optional) True to compile the template (defaults to undefined)
4680      * @return {Roo.Template} this
4681      */
4682     set : function(html, compile){
4683         this.html = html;
4684         this.compiled = null;
4685         if(compile){
4686             this.compile();
4687         }
4688         return this;
4689     },
4690     
4691     /**
4692      * True to disable format functions (defaults to false)
4693      * @type Boolean
4694      */
4695     disableFormats : false,
4696     
4697     /**
4698     * The regular expression used to match template variables 
4699     * @type RegExp
4700     * @property 
4701     */
4702     re : /\{([\w-]+)(?:\:([\w\.]*)(?:\((.*?)?\))?)?\}/g,
4703     
4704     /**
4705      * Compiles the template into an internal function, eliminating the RegEx overhead.
4706      * @return {Roo.Template} this
4707      */
4708     compile : function(){
4709         var fm = Roo.util.Format;
4710         var useF = this.disableFormats !== true;
4711         var sep = Roo.isGecko ? "+" : ",";
4712         var fn = function(m, name, format, args){
4713             if(format && useF){
4714                 args = args ? ',' + args : "";
4715                 if(format.substr(0, 5) != "this."){
4716                     format = "fm." + format + '(';
4717                 }else{
4718                     format = 'this.call("'+ format.substr(5) + '", ';
4719                     args = ", values";
4720                 }
4721             }else{
4722                 args= ''; format = "(values['" + name + "'] == undefined ? '' : ";
4723             }
4724             return "'"+ sep + format + "values['" + name + "']" + args + ")"+sep+"'";
4725         };
4726         var body;
4727         // branched to use + in gecko and [].join() in others
4728         if(Roo.isGecko){
4729             body = "this.compiled = function(values){ return '" +
4730                    this.html.replace(/\\/g, '\\\\').replace(/(\r\n|\n)/g, '\\n').replace(/'/g, "\\'").replace(this.re, fn) +
4731                     "';};";
4732         }else{
4733             body = ["this.compiled = function(values){ return ['"];
4734             body.push(this.html.replace(/\\/g, '\\\\').replace(/(\r\n|\n)/g, '\\n').replace(/'/g, "\\'").replace(this.re, fn));
4735             body.push("'].join('');};");
4736             body = body.join('');
4737         }
4738         /**
4739          * eval:var:values
4740          * eval:var:fm
4741          */
4742         eval(body);
4743         return this;
4744     },
4745     
4746     // private function used to call members
4747     call : function(fnName, value, allValues){
4748         return this[fnName](value, allValues);
4749     },
4750     
4751     /**
4752      * Applies the supplied values to the template and inserts the new node(s) as the first child of el.
4753      * @param {String/HTMLElement/Roo.Element} el The context element
4754      * @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'})
4755      * @param {Boolean} returnElement (optional) true to return a Roo.Element (defaults to undefined)
4756      * @return {HTMLElement/Roo.Element} The new node or Element
4757      */
4758     insertFirst: function(el, values, returnElement){
4759         return this.doInsert('afterBegin', el, values, returnElement);
4760     },
4761
4762     /**
4763      * Applies the supplied values to the template and inserts the new node(s) before el.
4764      * @param {String/HTMLElement/Roo.Element} el The context element
4765      * @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'})
4766      * @param {Boolean} returnElement (optional) true to return a Roo.Element (defaults to undefined)
4767      * @return {HTMLElement/Roo.Element} The new node or Element
4768      */
4769     insertBefore: function(el, values, returnElement){
4770         return this.doInsert('beforeBegin', el, values, returnElement);
4771     },
4772
4773     /**
4774      * Applies the supplied values to the template and inserts the new node(s) after el.
4775      * @param {String/HTMLElement/Roo.Element} el The context element
4776      * @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'})
4777      * @param {Boolean} returnElement (optional) true to return a Roo.Element (defaults to undefined)
4778      * @return {HTMLElement/Roo.Element} The new node or Element
4779      */
4780     insertAfter : function(el, values, returnElement){
4781         return this.doInsert('afterEnd', el, values, returnElement);
4782     },
4783     
4784     /**
4785      * Applies the supplied values to the template and appends the new node(s) to el.
4786      * @param {String/HTMLElement/Roo.Element} el The context element
4787      * @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'})
4788      * @param {Boolean} returnElement (optional) true to return a Roo.Element (defaults to undefined)
4789      * @return {HTMLElement/Roo.Element} The new node or Element
4790      */
4791     append : function(el, values, returnElement){
4792         return this.doInsert('beforeEnd', el, values, returnElement);
4793     },
4794
4795     doInsert : function(where, el, values, returnEl){
4796         el = Roo.getDom(el);
4797         var newNode = Roo.DomHelper.insertHtml(where, el, this.applyTemplate(values));
4798         return returnEl ? Roo.get(newNode, true) : newNode;
4799     },
4800
4801     /**
4802      * Applies the supplied values to the template and overwrites the content of el with the new node(s).
4803      * @param {String/HTMLElement/Roo.Element} el The context element
4804      * @param {Object} values The template values. Can be an array if your params are numeric (i.e. {0}) or an object (i.e. {foo: 'bar'})
4805      * @param {Boolean} returnElement (optional) true to return a Roo.Element (defaults to undefined)
4806      * @return {HTMLElement/Roo.Element} The new node or Element
4807      */
4808     overwrite : function(el, values, returnElement){
4809         el = Roo.getDom(el);
4810         el.innerHTML = this.applyTemplate(values);
4811         return returnElement ? Roo.get(el.firstChild, true) : el.firstChild;
4812     }
4813 };
4814 /**
4815  * Alias for {@link #applyTemplate}
4816  * @method
4817  */
4818 Roo.Template.prototype.apply = Roo.Template.prototype.applyTemplate;
4819
4820 // backwards compat
4821 Roo.DomHelper.Template = Roo.Template;
4822
4823 /**
4824  * Creates a template from the passed element's value (<i>display:none</i> textarea, preferred) or innerHTML.
4825  * @param {String/HTMLElement} el A DOM element or its id
4826  * @returns {Roo.Template} The created template
4827  * @static
4828  */
4829 Roo.Template.from = function(el){
4830     el = Roo.getDom(el);
4831     return new Roo.Template(el.value || el.innerHTML);
4832 };/*
4833  * Based on:
4834  * Ext JS Library 1.1.1
4835  * Copyright(c) 2006-2007, Ext JS, LLC.
4836  *
4837  * Originally Released Under LGPL - original licence link has changed is not relivant.
4838  *
4839  * Fork - LGPL
4840  * <script type="text/javascript">
4841  */
4842  
4843
4844 /*
4845  * This is code is also distributed under MIT license for use
4846  * with jQuery and prototype JavaScript libraries.
4847  */
4848 /**
4849  * @class Roo.DomQuery
4850 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).
4851 <p>
4852 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>
4853
4854 <p>
4855 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.
4856 </p>
4857 <h4>Element Selectors:</h4>
4858 <ul class="list">
4859     <li> <b>*</b> any element</li>
4860     <li> <b>E</b> an element with the tag E</li>
4861     <li> <b>E F</b> All descendent elements of E that have the tag F</li>
4862     <li> <b>E > F</b> or <b>E/F</b> all direct children elements of E that have the tag F</li>
4863     <li> <b>E + F</b> all elements with the tag F that are immediately preceded by an element with the tag E</li>
4864     <li> <b>E ~ F</b> all elements with the tag F that are preceded by a sibling element with the tag E</li>
4865 </ul>
4866 <h4>Attribute Selectors:</h4>
4867 <p>The use of @ and quotes are optional. For example, div[@foo='bar'] is also a valid attribute selector.</p>
4868 <ul class="list">
4869     <li> <b>E[foo]</b> has an attribute "foo"</li>
4870     <li> <b>E[foo=bar]</b> has an attribute "foo" that equals "bar"</li>
4871     <li> <b>E[foo^=bar]</b> has an attribute "foo" that starts with "bar"</li>
4872     <li> <b>E[foo$=bar]</b> has an attribute "foo" that ends with "bar"</li>
4873     <li> <b>E[foo*=bar]</b> has an attribute "foo" that contains the substring "bar"</li>
4874     <li> <b>E[foo%=2]</b> has an attribute "foo" that is evenly divisible by 2</li>
4875     <li> <b>E[foo!=bar]</b> has an attribute "foo" that does not equal "bar"</li>
4876 </ul>
4877 <h4>Pseudo Classes:</h4>
4878 <ul class="list">
4879     <li> <b>E:first-child</b> E is the first child of its parent</li>
4880     <li> <b>E:last-child</b> E is the last child of its parent</li>
4881     <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>
4882     <li> <b>E:nth-child(odd)</b> E is an odd child of its parent</li>
4883     <li> <b>E:nth-child(even)</b> E is an even child of its parent</li>
4884     <li> <b>E:only-child</b> E is the only child of its parent</li>
4885     <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>
4886     <li> <b>E:first</b> the first E in the resultset</li>
4887     <li> <b>E:last</b> the last E in the resultset</li>
4888     <li> <b>E:nth(<i>n</i>)</b> the <i>n</i>th E in the resultset (1 based)</li>
4889     <li> <b>E:odd</b> shortcut for :nth-child(odd)</li>
4890     <li> <b>E:even</b> shortcut for :nth-child(even)</li>
4891     <li> <b>E:contains(foo)</b> E's innerHTML contains the substring "foo"</li>
4892     <li> <b>E:nodeValue(foo)</b> E contains a textNode with a nodeValue that equals "foo"</li>
4893     <li> <b>E:not(S)</b> an E element that does not match simple selector S</li>
4894     <li> <b>E:has(S)</b> an E element that has a descendent that matches simple selector S</li>
4895     <li> <b>E:next(S)</b> an E element whose next sibling matches simple selector S</li>
4896     <li> <b>E:prev(S)</b> an E element whose previous sibling matches simple selector S</li>
4897 </ul>
4898 <h4>CSS Value Selectors:</h4>
4899 <ul class="list">
4900     <li> <b>E{display=none}</b> css value "display" that equals "none"</li>
4901     <li> <b>E{display^=none}</b> css value "display" that starts with "none"</li>
4902     <li> <b>E{display$=none}</b> css value "display" that ends with "none"</li>
4903     <li> <b>E{display*=none}</b> css value "display" that contains the substring "none"</li>
4904     <li> <b>E{display%=2}</b> css value "display" that is evenly divisible by 2</li>
4905     <li> <b>E{display!=none}</b> css value "display" that does not equal "none"</li>
4906 </ul>
4907  * @singleton
4908  */
4909 Roo.DomQuery = function(){
4910     var cache = {}, simpleCache = {}, valueCache = {};
4911     var nonSpace = /\S/;
4912     var trimRe = /^\s+|\s+$/g;
4913     var tplRe = /\{(\d+)\}/g;
4914     var modeRe = /^(\s?[\/>+~]\s?|\s|$)/;
4915     var tagTokenRe = /^(#)?([\w-\*]+)/;
4916     var nthRe = /(\d*)n\+?(\d*)/, nthRe2 = /\D/;
4917
4918     function child(p, index){
4919         var i = 0;
4920         var n = p.firstChild;
4921         while(n){
4922             if(n.nodeType == 1){
4923                if(++i == index){
4924                    return n;
4925                }
4926             }
4927             n = n.nextSibling;
4928         }
4929         return null;
4930     };
4931
4932     function next(n){
4933         while((n = n.nextSibling) && n.nodeType != 1);
4934         return n;
4935     };
4936
4937     function prev(n){
4938         while((n = n.previousSibling) && n.nodeType != 1);
4939         return n;
4940     };
4941
4942     function children(d){
4943         var n = d.firstChild, ni = -1;
4944             while(n){
4945                 var nx = n.nextSibling;
4946                 if(n.nodeType == 3 && !nonSpace.test(n.nodeValue)){
4947                     d.removeChild(n);
4948                 }else{
4949                     n.nodeIndex = ++ni;
4950                 }
4951                 n = nx;
4952             }
4953             return this;
4954         };
4955
4956     function byClassName(c, a, v){
4957         if(!v){
4958             return c;
4959         }
4960         var r = [], ri = -1, cn;
4961         for(var i = 0, ci; ci = c[i]; i++){
4962             if((' '+ci.className+' ').indexOf(v) != -1){
4963                 r[++ri] = ci;
4964             }
4965         }
4966         return r;
4967     };
4968
4969     function attrValue(n, attr){
4970         if(!n.tagName && typeof n.length != "undefined"){
4971             n = n[0];
4972         }
4973         if(!n){
4974             return null;
4975         }
4976         if(attr == "for"){
4977             return n.htmlFor;
4978         }
4979         if(attr == "class" || attr == "className"){
4980             return n.className;
4981         }
4982         return n.getAttribute(attr) || n[attr];
4983
4984     };
4985
4986     function getNodes(ns, mode, tagName){
4987         var result = [], ri = -1, cs;
4988         if(!ns){
4989             return result;
4990         }
4991         tagName = tagName || "*";
4992         if(typeof ns.getElementsByTagName != "undefined"){
4993             ns = [ns];
4994         }
4995         if(!mode){
4996             for(var i = 0, ni; ni = ns[i]; i++){
4997                 cs = ni.getElementsByTagName(tagName);
4998                 for(var j = 0, ci; ci = cs[j]; j++){
4999                     result[++ri] = ci;
5000                 }
5001             }
5002         }else if(mode == "/" || mode == ">"){
5003             var utag = tagName.toUpperCase();
5004             for(var i = 0, ni, cn; ni = ns[i]; i++){
5005                 cn = ni.children || ni.childNodes;
5006                 for(var j = 0, cj; cj = cn[j]; j++){
5007                     if(cj.nodeName == utag || cj.nodeName == tagName  || tagName == '*'){
5008                         result[++ri] = cj;
5009                     }
5010                 }
5011             }
5012         }else if(mode == "+"){
5013             var utag = tagName.toUpperCase();
5014             for(var i = 0, n; n = ns[i]; i++){
5015                 while((n = n.nextSibling) && n.nodeType != 1);
5016                 if(n && (n.nodeName == utag || n.nodeName == tagName || tagName == '*')){
5017                     result[++ri] = n;
5018                 }
5019             }
5020         }else if(mode == "~"){
5021             for(var i = 0, n; n = ns[i]; i++){
5022                 while((n = n.nextSibling) && (n.nodeType != 1 || (tagName == '*' || n.tagName.toLowerCase()!=tagName)));
5023                 if(n){
5024                     result[++ri] = n;
5025                 }
5026             }
5027         }
5028         return result;
5029     };
5030
5031     function concat(a, b){
5032         if(b.slice){
5033             return a.concat(b);
5034         }
5035         for(var i = 0, l = b.length; i < l; i++){
5036             a[a.length] = b[i];
5037         }
5038         return a;
5039     }
5040
5041     function byTag(cs, tagName){
5042         if(cs.tagName || cs == document){
5043             cs = [cs];
5044         }
5045         if(!tagName){
5046             return cs;
5047         }
5048         var r = [], ri = -1;
5049         tagName = tagName.toLowerCase();
5050         for(var i = 0, ci; ci = cs[i]; i++){
5051             if(ci.nodeType == 1 && ci.tagName.toLowerCase()==tagName){
5052                 r[++ri] = ci;
5053             }
5054         }
5055         return r;
5056     };
5057
5058     function byId(cs, attr, id){
5059         if(cs.tagName || cs == document){
5060             cs = [cs];
5061         }
5062         if(!id){
5063             return cs;
5064         }
5065         var r = [], ri = -1;
5066         for(var i = 0,ci; ci = cs[i]; i++){
5067             if(ci && ci.id == id){
5068                 r[++ri] = ci;
5069                 return r;
5070             }
5071         }
5072         return r;
5073     };
5074
5075     function byAttribute(cs, attr, value, op, custom){
5076         var r = [], ri = -1, st = custom=="{";
5077         var f = Roo.DomQuery.operators[op];
5078         for(var i = 0, ci; ci = cs[i]; i++){
5079             var a;
5080             if(st){
5081                 a = Roo.DomQuery.getStyle(ci, attr);
5082             }
5083             else if(attr == "class" || attr == "className"){
5084                 a = ci.className;
5085             }else if(attr == "for"){
5086                 a = ci.htmlFor;
5087             }else if(attr == "href"){
5088                 a = ci.getAttribute("href", 2);
5089             }else{
5090                 a = ci.getAttribute(attr);
5091             }
5092             if((f && f(a, value)) || (!f && a)){
5093                 r[++ri] = ci;
5094             }
5095         }
5096         return r;
5097     };
5098
5099     function byPseudo(cs, name, value){
5100         return Roo.DomQuery.pseudos[name](cs, value);
5101     };
5102
5103     // This is for IE MSXML which does not support expandos.
5104     // IE runs the same speed using setAttribute, however FF slows way down
5105     // and Safari completely fails so they need to continue to use expandos.
5106     var isIE = window.ActiveXObject ? true : false;
5107
5108     // this eval is stop the compressor from
5109     // renaming the variable to something shorter
5110     
5111     /** eval:var:batch */
5112     var batch = 30803; 
5113
5114     var key = 30803;
5115
5116     function nodupIEXml(cs){
5117         var d = ++key;
5118         cs[0].setAttribute("_nodup", d);
5119         var r = [cs[0]];
5120         for(var i = 1, len = cs.length; i < len; i++){
5121             var c = cs[i];
5122             if(!c.getAttribute("_nodup") != d){
5123                 c.setAttribute("_nodup", d);
5124                 r[r.length] = c;
5125             }
5126         }
5127         for(var i = 0, len = cs.length; i < len; i++){
5128             cs[i].removeAttribute("_nodup");
5129         }
5130         return r;
5131     }
5132
5133     function nodup(cs){
5134         if(!cs){
5135             return [];
5136         }
5137         var len = cs.length, c, i, r = cs, cj, ri = -1;
5138         if(!len || typeof cs.nodeType != "undefined" || len == 1){
5139             return cs;
5140         }
5141         if(isIE && typeof cs[0].selectSingleNode != "undefined"){
5142             return nodupIEXml(cs);
5143         }
5144         var d = ++key;
5145         cs[0]._nodup = d;
5146         for(i = 1; c = cs[i]; i++){
5147             if(c._nodup != d){
5148                 c._nodup = d;
5149             }else{
5150                 r = [];
5151                 for(var j = 0; j < i; j++){
5152                     r[++ri] = cs[j];
5153                 }
5154                 for(j = i+1; cj = cs[j]; j++){
5155                     if(cj._nodup != d){
5156                         cj._nodup = d;
5157                         r[++ri] = cj;
5158                     }
5159                 }
5160                 return r;
5161             }
5162         }
5163         return r;
5164     }
5165
5166     function quickDiffIEXml(c1, c2){
5167         var d = ++key;
5168         for(var i = 0, len = c1.length; i < len; i++){
5169             c1[i].setAttribute("_qdiff", d);
5170         }
5171         var r = [];
5172         for(var i = 0, len = c2.length; i < len; i++){
5173             if(c2[i].getAttribute("_qdiff") != d){
5174                 r[r.length] = c2[i];
5175             }
5176         }
5177         for(var i = 0, len = c1.length; i < len; i++){
5178            c1[i].removeAttribute("_qdiff");
5179         }
5180         return r;
5181     }
5182
5183     function quickDiff(c1, c2){
5184         var len1 = c1.length;
5185         if(!len1){
5186             return c2;
5187         }
5188         if(isIE && c1[0].selectSingleNode){
5189             return quickDiffIEXml(c1, c2);
5190         }
5191         var d = ++key;
5192         for(var i = 0; i < len1; i++){
5193             c1[i]._qdiff = d;
5194         }
5195         var r = [];
5196         for(var i = 0, len = c2.length; i < len; i++){
5197             if(c2[i]._qdiff != d){
5198                 r[r.length] = c2[i];
5199             }
5200         }
5201         return r;
5202     }
5203
5204     function quickId(ns, mode, root, id){
5205         if(ns == root){
5206            var d = root.ownerDocument || root;
5207            return d.getElementById(id);
5208         }
5209         ns = getNodes(ns, mode, "*");
5210         return byId(ns, null, id);
5211     }
5212
5213     return {
5214         getStyle : function(el, name){
5215             return Roo.fly(el).getStyle(name);
5216         },
5217         /**
5218          * Compiles a selector/xpath query into a reusable function. The returned function
5219          * takes one parameter "root" (optional), which is the context node from where the query should start.
5220          * @param {String} selector The selector/xpath query
5221          * @param {String} type (optional) Either "select" (the default) or "simple" for a simple selector match
5222          * @return {Function}
5223          */
5224         compile : function(path, type){
5225             type = type || "select";
5226             
5227             var fn = ["var f = function(root){\n var mode; ++batch; var n = root || document;\n"];
5228             var q = path, mode, lq;
5229             var tk = Roo.DomQuery.matchers;
5230             var tklen = tk.length;
5231             var mm;
5232
5233             // accept leading mode switch
5234             var lmode = q.match(modeRe);
5235             if(lmode && lmode[1]){
5236                 fn[fn.length] = 'mode="'+lmode[1].replace(trimRe, "")+'";';
5237                 q = q.replace(lmode[1], "");
5238             }
5239             // strip leading slashes
5240             while(path.substr(0, 1)=="/"){
5241                 path = path.substr(1);
5242             }
5243
5244             while(q && lq != q){
5245                 lq = q;
5246                 var tm = q.match(tagTokenRe);
5247                 if(type == "select"){
5248                     if(tm){
5249                         if(tm[1] == "#"){
5250                             fn[fn.length] = 'n = quickId(n, mode, root, "'+tm[2]+'");';
5251                         }else{
5252                             fn[fn.length] = 'n = getNodes(n, mode, "'+tm[2]+'");';
5253                         }
5254                         q = q.replace(tm[0], "");
5255                     }else if(q.substr(0, 1) != '@'){
5256                         fn[fn.length] = 'n = getNodes(n, mode, "*");';
5257                     }
5258                 }else{
5259                     if(tm){
5260                         if(tm[1] == "#"){
5261                             fn[fn.length] = 'n = byId(n, null, "'+tm[2]+'");';
5262                         }else{
5263                             fn[fn.length] = 'n = byTag(n, "'+tm[2]+'");';
5264                         }
5265                         q = q.replace(tm[0], "");
5266                     }
5267                 }
5268                 while(!(mm = q.match(modeRe))){
5269                     var matched = false;
5270                     for(var j = 0; j < tklen; j++){
5271                         var t = tk[j];
5272                         var m = q.match(t.re);
5273                         if(m){
5274                             fn[fn.length] = t.select.replace(tplRe, function(x, i){
5275                                                     return m[i];
5276                                                 });
5277                             q = q.replace(m[0], "");
5278                             matched = true;
5279                             break;
5280                         }
5281                     }
5282                     // prevent infinite loop on bad selector
5283                     if(!matched){
5284                         throw 'Error parsing selector, parsing failed at "' + q + '"';
5285                     }
5286                 }
5287                 if(mm[1]){
5288                     fn[fn.length] = 'mode="'+mm[1].replace(trimRe, "")+'";';
5289                     q = q.replace(mm[1], "");
5290                 }
5291             }
5292             fn[fn.length] = "return nodup(n);\n}";
5293             
5294              /** 
5295               * list of variables that need from compression as they are used by eval.
5296              *  eval:var:batch 
5297              *  eval:var:nodup
5298              *  eval:var:byTag
5299              *  eval:var:ById
5300              *  eval:var:getNodes
5301              *  eval:var:quickId
5302              *  eval:var:mode
5303              *  eval:var:root
5304              *  eval:var:n
5305              *  eval:var:byClassName
5306              *  eval:var:byPseudo
5307              *  eval:var:byAttribute
5308              *  eval:var:attrValue
5309              * 
5310              **/ 
5311             eval(fn.join(""));
5312             return f;
5313         },
5314
5315         /**
5316          * Selects a group of elements.
5317          * @param {String} selector The selector/xpath query (can be a comma separated list of selectors)
5318          * @param {Node} root (optional) The start of the query (defaults to document).
5319          * @return {Array}
5320          */
5321         select : function(path, root, type){
5322             if(!root || root == document){
5323                 root = document;
5324             }
5325             if(typeof root == "string"){
5326                 root = document.getElementById(root);
5327             }
5328             var paths = path.split(",");
5329             var results = [];
5330             for(var i = 0, len = paths.length; i < len; i++){
5331                 var p = paths[i].replace(trimRe, "");
5332                 if(!cache[p]){
5333                     cache[p] = Roo.DomQuery.compile(p);
5334                     if(!cache[p]){
5335                         throw p + " is not a valid selector";
5336                     }
5337                 }
5338                 var result = cache[p](root);
5339                 if(result && result != document){
5340                     results = results.concat(result);
5341                 }
5342             }
5343             if(paths.length > 1){
5344                 return nodup(results);
5345             }
5346             return results;
5347         },
5348
5349         /**
5350          * Selects a single element.
5351          * @param {String} selector The selector/xpath query
5352          * @param {Node} root (optional) The start of the query (defaults to document).
5353          * @return {Element}
5354          */
5355         selectNode : function(path, root){
5356             return Roo.DomQuery.select(path, root)[0];
5357         },
5358
5359         /**
5360          * Selects the value of a node, optionally replacing null with the defaultValue.
5361          * @param {String} selector The selector/xpath query
5362          * @param {Node} root (optional) The start of the query (defaults to document).
5363          * @param {String} defaultValue
5364          */
5365         selectValue : function(path, root, defaultValue){
5366             path = path.replace(trimRe, "");
5367             if(!valueCache[path]){
5368                 valueCache[path] = Roo.DomQuery.compile(path, "select");
5369             }
5370             var n = valueCache[path](root);
5371             n = n[0] ? n[0] : n;
5372             var v = (n && n.firstChild ? n.firstChild.nodeValue : null);
5373             return ((v === null||v === undefined||v==='') ? defaultValue : v);
5374         },
5375
5376         /**
5377          * Selects the value of a node, parsing integers and floats.
5378          * @param {String} selector The selector/xpath query
5379          * @param {Node} root (optional) The start of the query (defaults to document).
5380          * @param {Number} defaultValue
5381          * @return {Number}
5382          */
5383         selectNumber : function(path, root, defaultValue){
5384             var v = Roo.DomQuery.selectValue(path, root, defaultValue || 0);
5385             return parseFloat(v);
5386         },
5387
5388         /**
5389          * Returns true if the passed element(s) match the passed simple selector (e.g. div.some-class or span:first-child)
5390          * @param {String/HTMLElement/Array} el An element id, element or array of elements
5391          * @param {String} selector The simple selector to test
5392          * @return {Boolean}
5393          */
5394         is : function(el, ss){
5395             if(typeof el == "string"){
5396                 el = document.getElementById(el);
5397             }
5398             var isArray = (el instanceof Array);
5399             var result = Roo.DomQuery.filter(isArray ? el : [el], ss);
5400             return isArray ? (result.length == el.length) : (result.length > 0);
5401         },
5402
5403         /**
5404          * Filters an array of elements to only include matches of a simple selector (e.g. div.some-class or span:first-child)
5405          * @param {Array} el An array of elements to filter
5406          * @param {String} selector The simple selector to test
5407          * @param {Boolean} nonMatches If true, it returns the elements that DON'T match
5408          * the selector instead of the ones that match
5409          * @return {Array}
5410          */
5411         filter : function(els, ss, nonMatches){
5412             ss = ss.replace(trimRe, "");
5413             if(!simpleCache[ss]){
5414                 simpleCache[ss] = Roo.DomQuery.compile(ss, "simple");
5415             }
5416             var result = simpleCache[ss](els);
5417             return nonMatches ? quickDiff(result, els) : result;
5418         },
5419
5420         /**
5421          * Collection of matching regular expressions and code snippets.
5422          */
5423         matchers : [{
5424                 re: /^\.([\w-]+)/,
5425                 select: 'n = byClassName(n, null, " {1} ");'
5426             }, {
5427                 re: /^\:([\w-]+)(?:\(((?:[^\s>\/]*|.*?))\))?/,
5428                 select: 'n = byPseudo(n, "{1}", "{2}");'
5429             },{
5430                 re: /^(?:([\[\{])(?:@)?([\w-]+)\s?(?:(=|.=)\s?['"]?(.*?)["']?)?[\]\}])/,
5431                 select: 'n = byAttribute(n, "{2}", "{4}", "{3}", "{1}");'
5432             }, {
5433                 re: /^#([\w-]+)/,
5434                 select: 'n = byId(n, null, "{1}");'
5435             },{
5436                 re: /^@([\w-]+)/,
5437                 select: 'return {firstChild:{nodeValue:attrValue(n, "{1}")}};'
5438             }
5439         ],
5440
5441         /**
5442          * Collection of operator comparison functions. The default operators are =, !=, ^=, $=, *=, %=, |= and ~=.
5443          * 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;.
5444          */
5445         operators : {
5446             "=" : function(a, v){
5447                 return a == v;
5448             },
5449             "!=" : function(a, v){
5450                 return a != v;
5451             },
5452             "^=" : function(a, v){
5453                 return a && a.substr(0, v.length) == v;
5454             },
5455             "$=" : function(a, v){
5456                 return a && a.substr(a.length-v.length) == v;
5457             },
5458             "*=" : function(a, v){
5459                 return a && a.indexOf(v) !== -1;
5460             },
5461             "%=" : function(a, v){
5462                 return (a % v) == 0;
5463             },
5464             "|=" : function(a, v){
5465                 return a && (a == v || a.substr(0, v.length+1) == v+'-');
5466             },
5467             "~=" : function(a, v){
5468                 return a && (' '+a+' ').indexOf(' '+v+' ') != -1;
5469             }
5470         },
5471
5472         /**
5473          * Collection of "pseudo class" processors. Each processor is passed the current nodeset (array)
5474          * and the argument (if any) supplied in the selector.
5475          */
5476         pseudos : {
5477             "first-child" : function(c){
5478                 var r = [], ri = -1, n;
5479                 for(var i = 0, ci; ci = n = c[i]; i++){
5480                     while((n = n.previousSibling) && n.nodeType != 1);
5481                     if(!n){
5482                         r[++ri] = ci;
5483                     }
5484                 }
5485                 return r;
5486             },
5487
5488             "last-child" : function(c){
5489                 var r = [], ri = -1, n;
5490                 for(var i = 0, ci; ci = n = c[i]; i++){
5491                     while((n = n.nextSibling) && n.nodeType != 1);
5492                     if(!n){
5493                         r[++ri] = ci;
5494                     }
5495                 }
5496                 return r;
5497             },
5498
5499             "nth-child" : function(c, a) {
5500                 var r = [], ri = -1;
5501                 var m = nthRe.exec(a == "even" && "2n" || a == "odd" && "2n+1" || !nthRe2.test(a) && "n+" + a || a);
5502                 var f = (m[1] || 1) - 0, l = m[2] - 0;
5503                 for(var i = 0, n; n = c[i]; i++){
5504                     var pn = n.parentNode;
5505                     if (batch != pn._batch) {
5506                         var j = 0;
5507                         for(var cn = pn.firstChild; cn; cn = cn.nextSibling){
5508                             if(cn.nodeType == 1){
5509                                cn.nodeIndex = ++j;
5510                             }
5511                         }
5512                         pn._batch = batch;
5513                     }
5514                     if (f == 1) {
5515                         if (l == 0 || n.nodeIndex == l){
5516                             r[++ri] = n;
5517                         }
5518                     } else if ((n.nodeIndex + l) % f == 0){
5519                         r[++ri] = n;
5520                     }
5521                 }
5522
5523                 return r;
5524             },
5525
5526             "only-child" : function(c){
5527                 var r = [], ri = -1;;
5528                 for(var i = 0, ci; ci = c[i]; i++){
5529                     if(!prev(ci) && !next(ci)){
5530                         r[++ri] = ci;
5531                     }
5532                 }
5533                 return r;
5534             },
5535
5536             "empty" : function(c){
5537                 var r = [], ri = -1;
5538                 for(var i = 0, ci; ci = c[i]; i++){
5539                     var cns = ci.childNodes, j = 0, cn, empty = true;
5540                     while(cn = cns[j]){
5541                         ++j;
5542                         if(cn.nodeType == 1 || cn.nodeType == 3){
5543                             empty = false;
5544                             break;
5545                         }
5546                     }
5547                     if(empty){
5548                         r[++ri] = ci;
5549                     }
5550                 }
5551                 return r;
5552             },
5553
5554             "contains" : function(c, v){
5555                 var r = [], ri = -1;
5556                 for(var i = 0, ci; ci = c[i]; i++){
5557                     if((ci.textContent||ci.innerText||'').indexOf(v) != -1){
5558                         r[++ri] = ci;
5559                     }
5560                 }
5561                 return r;
5562             },
5563
5564             "nodeValue" : function(c, v){
5565                 var r = [], ri = -1;
5566                 for(var i = 0, ci; ci = c[i]; i++){
5567                     if(ci.firstChild && ci.firstChild.nodeValue == v){
5568                         r[++ri] = ci;
5569                     }
5570                 }
5571                 return r;
5572             },
5573
5574             "checked" : function(c){
5575                 var r = [], ri = -1;
5576                 for(var i = 0, ci; ci = c[i]; i++){
5577                     if(ci.checked == true){
5578                         r[++ri] = ci;
5579                     }
5580                 }
5581                 return r;
5582             },
5583
5584             "not" : function(c, ss){
5585                 return Roo.DomQuery.filter(c, ss, true);
5586             },
5587
5588             "odd" : function(c){
5589                 return this["nth-child"](c, "odd");
5590             },
5591
5592             "even" : function(c){
5593                 return this["nth-child"](c, "even");
5594             },
5595
5596             "nth" : function(c, a){
5597                 return c[a-1] || [];
5598             },
5599
5600             "first" : function(c){
5601                 return c[0] || [];
5602             },
5603
5604             "last" : function(c){
5605                 return c[c.length-1] || [];
5606             },
5607
5608             "has" : function(c, ss){
5609                 var s = Roo.DomQuery.select;
5610                 var r = [], ri = -1;
5611                 for(var i = 0, ci; ci = c[i]; i++){
5612                     if(s(ss, ci).length > 0){
5613                         r[++ri] = ci;
5614                     }
5615                 }
5616                 return r;
5617             },
5618
5619             "next" : function(c, ss){
5620                 var is = Roo.DomQuery.is;
5621                 var r = [], ri = -1;
5622                 for(var i = 0, ci; ci = c[i]; i++){
5623                     var n = next(ci);
5624                     if(n && is(n, ss)){
5625                         r[++ri] = ci;
5626                     }
5627                 }
5628                 return r;
5629             },
5630
5631             "prev" : function(c, ss){
5632                 var is = Roo.DomQuery.is;
5633                 var r = [], ri = -1;
5634                 for(var i = 0, ci; ci = c[i]; i++){
5635                     var n = prev(ci);
5636                     if(n && is(n, ss)){
5637                         r[++ri] = ci;
5638                     }
5639                 }
5640                 return r;
5641             }
5642         }
5643     };
5644 }();
5645
5646 /**
5647  * Selects an array of DOM nodes by CSS/XPath selector. Shorthand of {@link Roo.DomQuery#select}
5648  * @param {String} path The selector/xpath query
5649  * @param {Node} root (optional) The start of the query (defaults to document).
5650  * @return {Array}
5651  * @member Roo
5652  * @method query
5653  */
5654 Roo.query = Roo.DomQuery.select;
5655 /*
5656  * Based on:
5657  * Ext JS Library 1.1.1
5658  * Copyright(c) 2006-2007, Ext JS, LLC.
5659  *
5660  * Originally Released Under LGPL - original licence link has changed is not relivant.
5661  *
5662  * Fork - LGPL
5663  * <script type="text/javascript">
5664  */
5665
5666 /**
5667  * @class Roo.util.Observable
5668  * Base class that provides a common interface for publishing events. Subclasses are expected to
5669  * to have a property "events" with all the events defined.<br>
5670  * For example:
5671  * <pre><code>
5672  Employee = function(name){
5673     this.name = name;
5674     this.addEvents({
5675         "fired" : true,
5676         "quit" : true
5677     });
5678  }
5679  Roo.extend(Employee, Roo.util.Observable);
5680 </code></pre>
5681  * @param {Object} config properties to use (incuding events / listeners)
5682  */
5683
5684 Roo.util.Observable = function(cfg){
5685     
5686     cfg = cfg|| {};
5687     this.addEvents(cfg.events || {});
5688     if (cfg.events) {
5689         delete cfg.events; // make sure
5690     }
5691      
5692     Roo.apply(this, cfg);
5693     
5694     if(this.listeners){
5695         this.on(this.listeners);
5696         delete this.listeners;
5697     }
5698 };
5699 Roo.util.Observable.prototype = {
5700     /** 
5701  * @cfg {Object} listeners  list of events and functions to call for this object, 
5702  * For example :
5703  * <pre><code>
5704     listeners :  { 
5705        'click' : function(e) {
5706            ..... 
5707         } ,
5708         .... 
5709     } 
5710   </code></pre>
5711  */
5712     
5713     
5714     /**
5715      * Fires the specified event with the passed parameters (minus the event name).
5716      * @param {String} eventName
5717      * @param {Object...} args Variable number of parameters are passed to handlers
5718      * @return {Boolean} returns false if any of the handlers return false otherwise it returns true
5719      */
5720     fireEvent : function(){
5721         var ce = this.events[arguments[0].toLowerCase()];
5722         if(typeof ce == "object"){
5723             return ce.fire.apply(ce, Array.prototype.slice.call(arguments, 1));
5724         }else{
5725             return true;
5726         }
5727     },
5728
5729     // private
5730     filterOptRe : /^(?:scope|delay|buffer|single)$/,
5731
5732     /**
5733      * Appends an event handler to this component
5734      * @param {String}   eventName The type of event to listen for
5735      * @param {Function} handler The method the event invokes
5736      * @param {Object}   scope (optional) The scope in which to execute the handler
5737      * function. The handler function's "this" context.
5738      * @param {Object}   options (optional) An object containing handler configuration
5739      * properties. This may contain any of the following properties:<ul>
5740      * <li>scope {Object} The scope in which to execute the handler function. The handler function's "this" context.</li>
5741      * <li>delay {Number} The number of milliseconds to delay the invocation of the handler after te event fires.</li>
5742      * <li>single {Boolean} True to add a handler to handle just the next firing of the event, and then remove itself.</li>
5743      * <li>buffer {Number} Causes the handler to be scheduled to run in an {@link Roo.util.DelayedTask} delayed
5744      * by the specified number of milliseconds. If the event fires again within that time, the original
5745      * handler is <em>not</em> invoked, but the new handler is scheduled in its place.</li>
5746      * </ul><br>
5747      * <p>
5748      * <b>Combining Options</b><br>
5749      * Using the options argument, it is possible to combine different types of listeners:<br>
5750      * <br>
5751      * A normalized, delayed, one-time listener that auto stops the event and passes a custom argument (forumId)
5752                 <pre><code>
5753                 el.on('click', this.onClick, this, {
5754                         single: true,
5755                 delay: 100,
5756                 forumId: 4
5757                 });
5758                 </code></pre>
5759      * <p>
5760      * <b>Attaching multiple handlers in 1 call</b><br>
5761      * The method also allows for a single argument to be passed which is a config object containing properties
5762      * which specify multiple handlers.
5763      * <pre><code>
5764                 el.on({
5765                         'click': {
5766                         fn: this.onClick,
5767                         scope: this,
5768                         delay: 100
5769                 }, 
5770                 'mouseover': {
5771                         fn: this.onMouseOver,
5772                         scope: this
5773                 },
5774                 'mouseout': {
5775                         fn: this.onMouseOut,
5776                         scope: this
5777                 }
5778                 });
5779                 </code></pre>
5780      * <p>
5781      * Or a shorthand syntax which passes the same scope object to all handlers:
5782         <pre><code>
5783                 el.on({
5784                         'click': this.onClick,
5785                 'mouseover': this.onMouseOver,
5786                 'mouseout': this.onMouseOut,
5787                 scope: this
5788                 });
5789                 </code></pre>
5790      */
5791     addListener : function(eventName, fn, scope, o){
5792         if(typeof eventName == "object"){
5793             o = eventName;
5794             for(var e in o){
5795                 if(this.filterOptRe.test(e)){
5796                     continue;
5797                 }
5798                 if(typeof o[e] == "function"){
5799                     // shared options
5800                     this.addListener(e, o[e], o.scope,  o);
5801                 }else{
5802                     // individual options
5803                     this.addListener(e, o[e].fn, o[e].scope, o[e]);
5804                 }
5805             }
5806             return;
5807         }
5808         o = (!o || typeof o == "boolean") ? {} : o;
5809         eventName = eventName.toLowerCase();
5810         var ce = this.events[eventName] || true;
5811         if(typeof ce == "boolean"){
5812             ce = new Roo.util.Event(this, eventName);
5813             this.events[eventName] = ce;
5814         }
5815         ce.addListener(fn, scope, o);
5816     },
5817
5818     /**
5819      * Removes a listener
5820      * @param {String}   eventName     The type of event to listen for
5821      * @param {Function} handler        The handler to remove
5822      * @param {Object}   scope  (optional) The scope (this object) for the handler
5823      */
5824     removeListener : function(eventName, fn, scope){
5825         var ce = this.events[eventName.toLowerCase()];
5826         if(typeof ce == "object"){
5827             ce.removeListener(fn, scope);
5828         }
5829     },
5830
5831     /**
5832      * Removes all listeners for this object
5833      */
5834     purgeListeners : function(){
5835         for(var evt in this.events){
5836             if(typeof this.events[evt] == "object"){
5837                  this.events[evt].clearListeners();
5838             }
5839         }
5840     },
5841
5842     relayEvents : function(o, events){
5843         var createHandler = function(ename){
5844             return function(){
5845                 return this.fireEvent.apply(this, Roo.combine(ename, Array.prototype.slice.call(arguments, 0)));
5846             };
5847         };
5848         for(var i = 0, len = events.length; i < len; i++){
5849             var ename = events[i];
5850             if(!this.events[ename]){ this.events[ename] = true; };
5851             o.on(ename, createHandler(ename), this);
5852         }
5853     },
5854
5855     /**
5856      * Used to define events on this Observable
5857      * @param {Object} object The object with the events defined
5858      */
5859     addEvents : function(o){
5860         if(!this.events){
5861             this.events = {};
5862         }
5863         Roo.applyIf(this.events, o);
5864     },
5865
5866     /**
5867      * Checks to see if this object has any listeners for a specified event
5868      * @param {String} eventName The name of the event to check for
5869      * @return {Boolean} True if the event is being listened for, else false
5870      */
5871     hasListener : function(eventName){
5872         var e = this.events[eventName];
5873         return typeof e == "object" && e.listeners.length > 0;
5874     }
5875 };
5876 /**
5877  * Appends an event handler to this element (shorthand for addListener)
5878  * @param {String}   eventName     The type of event to listen for
5879  * @param {Function} handler        The method the event invokes
5880  * @param {Object}   scope (optional) The scope in which to execute the handler
5881  * function. The handler function's "this" context.
5882  * @param {Object}   options  (optional)
5883  * @method
5884  */
5885 Roo.util.Observable.prototype.on = Roo.util.Observable.prototype.addListener;
5886 /**
5887  * Removes a listener (shorthand for removeListener)
5888  * @param {String}   eventName     The type of event to listen for
5889  * @param {Function} handler        The handler to remove
5890  * @param {Object}   scope  (optional) The scope (this object) for the handler
5891  * @method
5892  */
5893 Roo.util.Observable.prototype.un = Roo.util.Observable.prototype.removeListener;
5894
5895 /**
5896  * Starts capture on the specified Observable. All events will be passed
5897  * to the supplied function with the event name + standard signature of the event
5898  * <b>before</b> the event is fired. If the supplied function returns false,
5899  * the event will not fire.
5900  * @param {Observable} o The Observable to capture
5901  * @param {Function} fn The function to call
5902  * @param {Object} scope (optional) The scope (this object) for the fn
5903  * @static
5904  */
5905 Roo.util.Observable.capture = function(o, fn, scope){
5906     o.fireEvent = o.fireEvent.createInterceptor(fn, scope);
5907 };
5908
5909 /**
5910  * Removes <b>all</b> added captures from the Observable.
5911  * @param {Observable} o The Observable to release
5912  * @static
5913  */
5914 Roo.util.Observable.releaseCapture = function(o){
5915     o.fireEvent = Roo.util.Observable.prototype.fireEvent;
5916 };
5917
5918 (function(){
5919
5920     var createBuffered = function(h, o, scope){
5921         var task = new Roo.util.DelayedTask();
5922         return function(){
5923             task.delay(o.buffer, h, scope, Array.prototype.slice.call(arguments, 0));
5924         };
5925     };
5926
5927     var createSingle = function(h, e, fn, scope){
5928         return function(){
5929             e.removeListener(fn, scope);
5930             return h.apply(scope, arguments);
5931         };
5932     };
5933
5934     var createDelayed = function(h, o, scope){
5935         return function(){
5936             var args = Array.prototype.slice.call(arguments, 0);
5937             setTimeout(function(){
5938                 h.apply(scope, args);
5939             }, o.delay || 10);
5940         };
5941     };
5942
5943     Roo.util.Event = function(obj, name){
5944         this.name = name;
5945         this.obj = obj;
5946         this.listeners = [];
5947     };
5948
5949     Roo.util.Event.prototype = {
5950         addListener : function(fn, scope, options){
5951             var o = options || {};
5952             scope = scope || this.obj;
5953             if(!this.isListening(fn, scope)){
5954                 var l = {fn: fn, scope: scope, options: o};
5955                 var h = fn;
5956                 if(o.delay){
5957                     h = createDelayed(h, o, scope);
5958                 }
5959                 if(o.single){
5960                     h = createSingle(h, this, fn, scope);
5961                 }
5962                 if(o.buffer){
5963                     h = createBuffered(h, o, scope);
5964                 }
5965                 l.fireFn = h;
5966                 if(!this.firing){ // if we are currently firing this event, don't disturb the listener loop
5967                     this.listeners.push(l);
5968                 }else{
5969                     this.listeners = this.listeners.slice(0);
5970                     this.listeners.push(l);
5971                 }
5972             }
5973         },
5974
5975         findListener : function(fn, scope){
5976             scope = scope || this.obj;
5977             var ls = this.listeners;
5978             for(var i = 0, len = ls.length; i < len; i++){
5979                 var l = ls[i];
5980                 if(l.fn == fn && l.scope == scope){
5981                     return i;
5982                 }
5983             }
5984             return -1;
5985         },
5986
5987         isListening : function(fn, scope){
5988             return this.findListener(fn, scope) != -1;
5989         },
5990
5991         removeListener : function(fn, scope){
5992             var index;
5993             if((index = this.findListener(fn, scope)) != -1){
5994                 if(!this.firing){
5995                     this.listeners.splice(index, 1);
5996                 }else{
5997                     this.listeners = this.listeners.slice(0);
5998                     this.listeners.splice(index, 1);
5999                 }
6000                 return true;
6001             }
6002             return false;
6003         },
6004
6005         clearListeners : function(){
6006             this.listeners = [];
6007         },
6008
6009         fire : function(){
6010             var ls = this.listeners, scope, len = ls.length;
6011             if(len > 0){
6012                 this.firing = true;
6013                 var args = Array.prototype.slice.call(arguments, 0);
6014                 for(var i = 0; i < len; i++){
6015                     var l = ls[i];
6016                     if(l.fireFn.apply(l.scope||this.obj||window, arguments) === false){
6017                         this.firing = false;
6018                         return false;
6019                     }
6020                 }
6021                 this.firing = false;
6022             }
6023             return true;
6024         }
6025     };
6026 })();/*
6027  * Based on:
6028  * Ext JS Library 1.1.1
6029  * Copyright(c) 2006-2007, Ext JS, LLC.
6030  *
6031  * Originally Released Under LGPL - original licence link has changed is not relivant.
6032  *
6033  * Fork - LGPL
6034  * <script type="text/javascript">
6035  */
6036
6037 /**
6038  * @class Roo.EventManager
6039  * Registers event handlers that want to receive a normalized EventObject instead of the standard browser event and provides 
6040  * several useful events directly.
6041  * See {@link Roo.EventObject} for more details on normalized event objects.
6042  * @singleton
6043  */
6044 Roo.EventManager = function(){
6045     var docReadyEvent, docReadyProcId, docReadyState = false;
6046     var resizeEvent, resizeTask, textEvent, textSize;
6047     var E = Roo.lib.Event;
6048     var D = Roo.lib.Dom;
6049
6050     
6051     
6052
6053     var fireDocReady = function(){
6054         if(!docReadyState){
6055             docReadyState = true;
6056             Roo.isReady = true;
6057             if(docReadyProcId){
6058                 clearInterval(docReadyProcId);
6059             }
6060             if(Roo.isGecko || Roo.isOpera) {
6061                 document.removeEventListener("DOMContentLoaded", fireDocReady, false);
6062             }
6063             if(Roo.isIE){
6064                 var defer = document.getElementById("ie-deferred-loader");
6065                 if(defer){
6066                     defer.onreadystatechange = null;
6067                     defer.parentNode.removeChild(defer);
6068                 }
6069             }
6070             if(docReadyEvent){
6071                 docReadyEvent.fire();
6072                 docReadyEvent.clearListeners();
6073             }
6074         }
6075     };
6076     
6077     var initDocReady = function(){
6078         docReadyEvent = new Roo.util.Event();
6079         if(Roo.isGecko || Roo.isOpera) {
6080             document.addEventListener("DOMContentLoaded", fireDocReady, false);
6081         }else if(Roo.isIE){
6082             document.write("<s"+'cript id="ie-deferred-loader" defer="defer" src="/'+'/:"></s'+"cript>");
6083             var defer = document.getElementById("ie-deferred-loader");
6084             defer.onreadystatechange = function(){
6085                 if(this.readyState == "complete"){
6086                     fireDocReady();
6087                 }
6088             };
6089         }else if(Roo.isSafari){ 
6090             docReadyProcId = setInterval(function(){
6091                 var rs = document.readyState;
6092                 if(rs == "complete") {
6093                     fireDocReady();     
6094                  }
6095             }, 10);
6096         }
6097         // no matter what, make sure it fires on load
6098         E.on(window, "load", fireDocReady);
6099     };
6100
6101     var createBuffered = function(h, o){
6102         var task = new Roo.util.DelayedTask(h);
6103         return function(e){
6104             // create new event object impl so new events don't wipe out properties
6105             e = new Roo.EventObjectImpl(e);
6106             task.delay(o.buffer, h, null, [e]);
6107         };
6108     };
6109
6110     var createSingle = function(h, el, ename, fn){
6111         return function(e){
6112             Roo.EventManager.removeListener(el, ename, fn);
6113             h(e);
6114         };
6115     };
6116
6117     var createDelayed = function(h, o){
6118         return function(e){
6119             // create new event object impl so new events don't wipe out properties
6120             e = new Roo.EventObjectImpl(e);
6121             setTimeout(function(){
6122                 h(e);
6123             }, o.delay || 10);
6124         };
6125     };
6126     var transitionEndVal = false;
6127     
6128     var transitionEnd = function()
6129     {
6130         if (transitionEndVal) {
6131             return transitionEndVal;
6132         }
6133         var el = document.createElement('div');
6134
6135         var transEndEventNames = {
6136             WebkitTransition : 'webkitTransitionEnd',
6137             MozTransition    : 'transitionend',
6138             OTransition      : 'oTransitionEnd otransitionend',
6139             transition       : 'transitionend'
6140         };
6141     
6142         for (var name in transEndEventNames) {
6143             if (el.style[name] !== undefined) {
6144                 transitionEndVal = transEndEventNames[name];
6145                 return  transitionEndVal ;
6146             }
6147         }
6148     }
6149     
6150
6151     var listen = function(element, ename, opt, fn, scope){
6152         var o = (!opt || typeof opt == "boolean") ? {} : opt;
6153         fn = fn || o.fn; scope = scope || o.scope;
6154         var el = Roo.getDom(element);
6155         
6156         
6157         if(!el){
6158             throw "Error listening for \"" + ename + '\". Element "' + element + '" doesn\'t exist.';
6159         }
6160         
6161         if (ename == 'transitionend') {
6162             ename = transitionEnd();
6163         }
6164         var h = function(e){
6165             e = Roo.EventObject.setEvent(e);
6166             var t;
6167             if(o.delegate){
6168                 t = e.getTarget(o.delegate, el);
6169                 if(!t){
6170                     return;
6171                 }
6172             }else{
6173                 t = e.target;
6174             }
6175             if(o.stopEvent === true){
6176                 e.stopEvent();
6177             }
6178             if(o.preventDefault === true){
6179                e.preventDefault();
6180             }
6181             if(o.stopPropagation === true){
6182                 e.stopPropagation();
6183             }
6184
6185             if(o.normalized === false){
6186                 e = e.browserEvent;
6187             }
6188
6189             fn.call(scope || el, e, t, o);
6190         };
6191         if(o.delay){
6192             h = createDelayed(h, o);
6193         }
6194         if(o.single){
6195             h = createSingle(h, el, ename, fn);
6196         }
6197         if(o.buffer){
6198             h = createBuffered(h, o);
6199         }
6200         fn._handlers = fn._handlers || [];
6201         
6202         
6203         fn._handlers.push([Roo.id(el), ename, h]);
6204         
6205         
6206          
6207         E.on(el, ename, h);
6208         if(ename == "mousewheel" && el.addEventListener){ // workaround for jQuery
6209             el.addEventListener("DOMMouseScroll", h, false);
6210             E.on(window, 'unload', function(){
6211                 el.removeEventListener("DOMMouseScroll", h, false);
6212             });
6213         }
6214         if(ename == "mousedown" && el == document){ // fix stopped mousedowns on the document
6215             Roo.EventManager.stoppedMouseDownEvent.addListener(h);
6216         }
6217         return h;
6218     };
6219
6220     var stopListening = function(el, ename, fn){
6221         var id = Roo.id(el), hds = fn._handlers, hd = fn;
6222         if(hds){
6223             for(var i = 0, len = hds.length; i < len; i++){
6224                 var h = hds[i];
6225                 if(h[0] == id && h[1] == ename){
6226                     hd = h[2];
6227                     hds.splice(i, 1);
6228                     break;
6229                 }
6230             }
6231         }
6232         E.un(el, ename, hd);
6233         el = Roo.getDom(el);
6234         if(ename == "mousewheel" && el.addEventListener){
6235             el.removeEventListener("DOMMouseScroll", hd, false);
6236         }
6237         if(ename == "mousedown" && el == document){ // fix stopped mousedowns on the document
6238             Roo.EventManager.stoppedMouseDownEvent.removeListener(hd);
6239         }
6240     };
6241
6242     var propRe = /^(?:scope|delay|buffer|single|stopEvent|preventDefault|stopPropagation|normalized|args|delegate)$/;
6243     
6244     var pub = {
6245         
6246         
6247         /** 
6248          * Fix for doc tools
6249          * @scope Roo.EventManager
6250          */
6251         
6252         
6253         /** 
6254          * This is no longer needed and is deprecated. Places a simple wrapper around an event handler to override the browser event
6255          * object with a Roo.EventObject
6256          * @param {Function} fn        The method the event invokes
6257          * @param {Object}   scope    An object that becomes the scope of the handler
6258          * @param {boolean}  override If true, the obj passed in becomes
6259          *                             the execution scope of the listener
6260          * @return {Function} The wrapped function
6261          * @deprecated
6262          */
6263         wrap : function(fn, scope, override){
6264             return function(e){
6265                 Roo.EventObject.setEvent(e);
6266                 fn.call(override ? scope || window : window, Roo.EventObject, scope);
6267             };
6268         },
6269         
6270         /**
6271      * Appends an event handler to an element (shorthand for addListener)
6272      * @param {String/HTMLElement}   element        The html element or id to assign the
6273      * @param {String}   eventName The type of event to listen for
6274      * @param {Function} handler The method the event invokes
6275      * @param {Object}   scope (optional) The scope in which to execute the handler
6276      * function. The handler function's "this" context.
6277      * @param {Object}   options (optional) An object containing handler configuration
6278      * properties. This may contain any of the following properties:<ul>
6279      * <li>scope {Object} The scope in which to execute the handler function. The handler function's "this" context.</li>
6280      * <li>delegate {String} A simple selector to filter the target or look for a descendant of the target</li>
6281      * <li>stopEvent {Boolean} True to stop the event. That is stop propagation, and prevent the default action.</li>
6282      * <li>preventDefault {Boolean} True to prevent the default action</li>
6283      * <li>stopPropagation {Boolean} True to prevent event propagation</li>
6284      * <li>normalized {Boolean} False to pass a browser event to the handler function instead of an Roo.EventObject</li>
6285      * <li>delay {Number} The number of milliseconds to delay the invocation of the handler after te event fires.</li>
6286      * <li>single {Boolean} True to add a handler to handle just the next firing of the event, and then remove itself.</li>
6287      * <li>buffer {Number} Causes the handler to be scheduled to run in an {@link Roo.util.DelayedTask} delayed
6288      * by the specified number of milliseconds. If the event fires again within that time, the original
6289      * handler is <em>not</em> invoked, but the new handler is scheduled in its place.</li>
6290      * </ul><br>
6291      * <p>
6292      * <b>Combining Options</b><br>
6293      * Using the options argument, it is possible to combine different types of listeners:<br>
6294      * <br>
6295      * A normalized, delayed, one-time listener that auto stops the event and passes a custom argument (forumId)<div style="margin: 5px 20px 20px;">
6296      * Code:<pre><code>
6297 el.on('click', this.onClick, this, {
6298     single: true,
6299     delay: 100,
6300     stopEvent : true,
6301     forumId: 4
6302 });</code></pre>
6303      * <p>
6304      * <b>Attaching multiple handlers in 1 call</b><br>
6305       * The method also allows for a single argument to be passed which is a config object containing properties
6306      * which specify multiple handlers.
6307      * <p>
6308      * Code:<pre><code>
6309 el.on({
6310     'click' : {
6311         fn: this.onClick
6312         scope: this,
6313         delay: 100
6314     },
6315     'mouseover' : {
6316         fn: this.onMouseOver
6317         scope: this
6318     },
6319     'mouseout' : {
6320         fn: this.onMouseOut
6321         scope: this
6322     }
6323 });</code></pre>
6324      * <p>
6325      * Or a shorthand syntax:<br>
6326      * Code:<pre><code>
6327 el.on({
6328     'click' : this.onClick,
6329     'mouseover' : this.onMouseOver,
6330     'mouseout' : this.onMouseOut
6331     scope: this
6332 });</code></pre>
6333      */
6334         addListener : function(element, eventName, fn, scope, options){
6335             if(typeof eventName == "object"){
6336                 var o = eventName;
6337                 for(var e in o){
6338                     if(propRe.test(e)){
6339                         continue;
6340                     }
6341                     if(typeof o[e] == "function"){
6342                         // shared options
6343                         listen(element, e, o, o[e], o.scope);
6344                     }else{
6345                         // individual options
6346                         listen(element, e, o[e]);
6347                     }
6348                 }
6349                 return;
6350             }
6351             return listen(element, eventName, options, fn, scope);
6352         },
6353         
6354         /**
6355          * Removes an event handler
6356          *
6357          * @param {String/HTMLElement}   element        The id or html element to remove the 
6358          *                             event from
6359          * @param {String}   eventName     The type of event
6360          * @param {Function} fn
6361          * @return {Boolean} True if a listener was actually removed
6362          */
6363         removeListener : function(element, eventName, fn){
6364             return stopListening(element, eventName, fn);
6365         },
6366         
6367         /**
6368          * Fires when the document is ready (before onload and before images are loaded). Can be 
6369          * accessed shorthanded Roo.onReady().
6370          * @param {Function} fn        The method the event invokes
6371          * @param {Object}   scope    An  object that becomes the scope of the handler
6372          * @param {boolean}  options
6373          */
6374         onDocumentReady : function(fn, scope, options){
6375             if(docReadyState){ // if it already fired
6376                 docReadyEvent.addListener(fn, scope, options);
6377                 docReadyEvent.fire();
6378                 docReadyEvent.clearListeners();
6379                 return;
6380             }
6381             if(!docReadyEvent){
6382                 initDocReady();
6383             }
6384             docReadyEvent.addListener(fn, scope, options);
6385         },
6386         
6387         /**
6388          * Fires when the window is resized and provides resize event buffering (50 milliseconds), passes new viewport width and height to handlers.
6389          * @param {Function} fn        The method the event invokes
6390          * @param {Object}   scope    An object that becomes the scope of the handler
6391          * @param {boolean}  options
6392          */
6393         onWindowResize : function(fn, scope, options){
6394             if(!resizeEvent){
6395                 resizeEvent = new Roo.util.Event();
6396                 resizeTask = new Roo.util.DelayedTask(function(){
6397                     resizeEvent.fire(D.getViewWidth(), D.getViewHeight());
6398                 });
6399                 E.on(window, "resize", function(){
6400                     if(Roo.isIE){
6401                         resizeTask.delay(50);
6402                     }else{
6403                         resizeEvent.fire(D.getViewWidth(), D.getViewHeight());
6404                     }
6405                 });
6406             }
6407             resizeEvent.addListener(fn, scope, options);
6408         },
6409
6410         /**
6411          * Fires when the user changes the active text size. Handler gets called with 2 params, the old size and the new size.
6412          * @param {Function} fn        The method the event invokes
6413          * @param {Object}   scope    An object that becomes the scope of the handler
6414          * @param {boolean}  options
6415          */
6416         onTextResize : function(fn, scope, options){
6417             if(!textEvent){
6418                 textEvent = new Roo.util.Event();
6419                 var textEl = new Roo.Element(document.createElement('div'));
6420                 textEl.dom.className = 'x-text-resize';
6421                 textEl.dom.innerHTML = 'X';
6422                 textEl.appendTo(document.body);
6423                 textSize = textEl.dom.offsetHeight;
6424                 setInterval(function(){
6425                     if(textEl.dom.offsetHeight != textSize){
6426                         textEvent.fire(textSize, textSize = textEl.dom.offsetHeight);
6427                     }
6428                 }, this.textResizeInterval);
6429             }
6430             textEvent.addListener(fn, scope, options);
6431         },
6432
6433         /**
6434          * Removes the passed window resize listener.
6435          * @param {Function} fn        The method the event invokes
6436          * @param {Object}   scope    The scope of handler
6437          */
6438         removeResizeListener : function(fn, scope){
6439             if(resizeEvent){
6440                 resizeEvent.removeListener(fn, scope);
6441             }
6442         },
6443
6444         // private
6445         fireResize : function(){
6446             if(resizeEvent){
6447                 resizeEvent.fire(D.getViewWidth(), D.getViewHeight());
6448             }   
6449         },
6450         /**
6451          * Url used for onDocumentReady with using SSL (defaults to Roo.SSL_SECURE_URL)
6452          */
6453         ieDeferSrc : false,
6454         /**
6455          * The frequency, in milliseconds, to check for text resize events (defaults to 50)
6456          */
6457         textResizeInterval : 50
6458     };
6459     
6460     /**
6461      * Fix for doc tools
6462      * @scopeAlias pub=Roo.EventManager
6463      */
6464     
6465      /**
6466      * Appends an event handler to an element (shorthand for addListener)
6467      * @param {String/HTMLElement}   element        The html element or id to assign the
6468      * @param {String}   eventName The type of event to listen for
6469      * @param {Function} handler The method the event invokes
6470      * @param {Object}   scope (optional) The scope in which to execute the handler
6471      * function. The handler function's "this" context.
6472      * @param {Object}   options (optional) An object containing handler configuration
6473      * properties. This may contain any of the following properties:<ul>
6474      * <li>scope {Object} The scope in which to execute the handler function. The handler function's "this" context.</li>
6475      * <li>delegate {String} A simple selector to filter the target or look for a descendant of the target</li>
6476      * <li>stopEvent {Boolean} True to stop the event. That is stop propagation, and prevent the default action.</li>
6477      * <li>preventDefault {Boolean} True to prevent the default action</li>
6478      * <li>stopPropagation {Boolean} True to prevent event propagation</li>
6479      * <li>normalized {Boolean} False to pass a browser event to the handler function instead of an Roo.EventObject</li>
6480      * <li>delay {Number} The number of milliseconds to delay the invocation of the handler after te event fires.</li>
6481      * <li>single {Boolean} True to add a handler to handle just the next firing of the event, and then remove itself.</li>
6482      * <li>buffer {Number} Causes the handler to be scheduled to run in an {@link Roo.util.DelayedTask} delayed
6483      * by the specified number of milliseconds. If the event fires again within that time, the original
6484      * handler is <em>not</em> invoked, but the new handler is scheduled in its place.</li>
6485      * </ul><br>
6486      * <p>
6487      * <b>Combining Options</b><br>
6488      * Using the options argument, it is possible to combine different types of listeners:<br>
6489      * <br>
6490      * A normalized, delayed, one-time listener that auto stops the event and passes a custom argument (forumId)<div style="margin: 5px 20px 20px;">
6491      * Code:<pre><code>
6492 el.on('click', this.onClick, this, {
6493     single: true,
6494     delay: 100,
6495     stopEvent : true,
6496     forumId: 4
6497 });</code></pre>
6498      * <p>
6499      * <b>Attaching multiple handlers in 1 call</b><br>
6500       * The method also allows for a single argument to be passed which is a config object containing properties
6501      * which specify multiple handlers.
6502      * <p>
6503      * Code:<pre><code>
6504 el.on({
6505     'click' : {
6506         fn: this.onClick
6507         scope: this,
6508         delay: 100
6509     },
6510     'mouseover' : {
6511         fn: this.onMouseOver
6512         scope: this
6513     },
6514     'mouseout' : {
6515         fn: this.onMouseOut
6516         scope: this
6517     }
6518 });</code></pre>
6519      * <p>
6520      * Or a shorthand syntax:<br>
6521      * Code:<pre><code>
6522 el.on({
6523     'click' : this.onClick,
6524     'mouseover' : this.onMouseOver,
6525     'mouseout' : this.onMouseOut
6526     scope: this
6527 });</code></pre>
6528      */
6529     pub.on = pub.addListener;
6530     pub.un = pub.removeListener;
6531
6532     pub.stoppedMouseDownEvent = new Roo.util.Event();
6533     return pub;
6534 }();
6535 /**
6536   * Fires when the document is ready (before onload and before images are loaded).  Shorthand of {@link Roo.EventManager#onDocumentReady}.
6537   * @param {Function} fn        The method the event invokes
6538   * @param {Object}   scope    An  object that becomes the scope of the handler
6539   * @param {boolean}  override If true, the obj passed in becomes
6540   *                             the execution scope of the listener
6541   * @member Roo
6542   * @method onReady
6543  */
6544 Roo.onReady = Roo.EventManager.onDocumentReady;
6545
6546 Roo.onReady(function(){
6547     var bd = Roo.get(document.body);
6548     if(!bd){ return; }
6549
6550     var cls = [
6551             Roo.isIE ? "roo-ie"
6552             : Roo.isGecko ? "roo-gecko"
6553             : Roo.isOpera ? "roo-opera"
6554             : Roo.isSafari ? "roo-safari" : ""];
6555
6556     if(Roo.isMac){
6557         cls.push("roo-mac");
6558     }
6559     if(Roo.isLinux){
6560         cls.push("roo-linux");
6561     }
6562     if(Roo.isBorderBox){
6563         cls.push('roo-border-box');
6564     }
6565     if(Roo.isStrict){ // add to the parent to allow for selectors like ".ext-strict .ext-ie"
6566         var p = bd.dom.parentNode;
6567         if(p){
6568             p.className += ' roo-strict';
6569         }
6570     }
6571     bd.addClass(cls.join(' '));
6572 });
6573
6574 /**
6575  * @class Roo.EventObject
6576  * EventObject exposes the Yahoo! UI Event functionality directly on the object
6577  * passed to your event handler. It exists mostly for convenience. It also fixes the annoying null checks automatically to cleanup your code 
6578  * Example:
6579  * <pre><code>
6580  function handleClick(e){ // e is not a standard event object, it is a Roo.EventObject
6581     e.preventDefault();
6582     var target = e.getTarget();
6583     ...
6584  }
6585  var myDiv = Roo.get("myDiv");
6586  myDiv.on("click", handleClick);
6587  //or
6588  Roo.EventManager.on("myDiv", 'click', handleClick);
6589  Roo.EventManager.addListener("myDiv", 'click', handleClick);
6590  </code></pre>
6591  * @singleton
6592  */
6593 Roo.EventObject = function(){
6594     
6595     var E = Roo.lib.Event;
6596     
6597     // safari keypress events for special keys return bad keycodes
6598     var safariKeys = {
6599         63234 : 37, // left
6600         63235 : 39, // right
6601         63232 : 38, // up
6602         63233 : 40, // down
6603         63276 : 33, // page up
6604         63277 : 34, // page down
6605         63272 : 46, // delete
6606         63273 : 36, // home
6607         63275 : 35  // end
6608     };
6609
6610     // normalize button clicks
6611     var btnMap = Roo.isIE ? {1:0,4:1,2:2} :
6612                 (Roo.isSafari ? {1:0,2:1,3:2} : {0:0,1:1,2:2});
6613
6614     Roo.EventObjectImpl = function(e){
6615         if(e){
6616             this.setEvent(e.browserEvent || e);
6617         }
6618     };
6619     Roo.EventObjectImpl.prototype = {
6620         /**
6621          * Used to fix doc tools.
6622          * @scope Roo.EventObject.prototype
6623          */
6624             
6625
6626         
6627         
6628         /** The normal browser event */
6629         browserEvent : null,
6630         /** The button pressed in a mouse event */
6631         button : -1,
6632         /** True if the shift key was down during the event */
6633         shiftKey : false,
6634         /** True if the control key was down during the event */
6635         ctrlKey : false,
6636         /** True if the alt key was down during the event */
6637         altKey : false,
6638
6639         /** Key constant 
6640         * @type Number */
6641         BACKSPACE : 8,
6642         /** Key constant 
6643         * @type Number */
6644         TAB : 9,
6645         /** Key constant 
6646         * @type Number */
6647         RETURN : 13,
6648         /** Key constant 
6649         * @type Number */
6650         ENTER : 13,
6651         /** Key constant 
6652         * @type Number */
6653         SHIFT : 16,
6654         /** Key constant 
6655         * @type Number */
6656         CONTROL : 17,
6657         /** Key constant 
6658         * @type Number */
6659         ESC : 27,
6660         /** Key constant 
6661         * @type Number */
6662         SPACE : 32,
6663         /** Key constant 
6664         * @type Number */
6665         PAGEUP : 33,
6666         /** Key constant 
6667         * @type Number */
6668         PAGEDOWN : 34,
6669         /** Key constant 
6670         * @type Number */
6671         END : 35,
6672         /** Key constant 
6673         * @type Number */
6674         HOME : 36,
6675         /** Key constant 
6676         * @type Number */
6677         LEFT : 37,
6678         /** Key constant 
6679         * @type Number */
6680         UP : 38,
6681         /** Key constant 
6682         * @type Number */
6683         RIGHT : 39,
6684         /** Key constant 
6685         * @type Number */
6686         DOWN : 40,
6687         /** Key constant 
6688         * @type Number */
6689         DELETE : 46,
6690         /** Key constant 
6691         * @type Number */
6692         F5 : 116,
6693
6694            /** @private */
6695         setEvent : function(e){
6696             if(e == this || (e && e.browserEvent)){ // already wrapped
6697                 return e;
6698             }
6699             this.browserEvent = e;
6700             if(e){
6701                 // normalize buttons
6702                 this.button = e.button ? btnMap[e.button] : (e.which ? e.which-1 : -1);
6703                 if(e.type == 'click' && this.button == -1){
6704                     this.button = 0;
6705                 }
6706                 this.type = e.type;
6707                 this.shiftKey = e.shiftKey;
6708                 // mac metaKey behaves like ctrlKey
6709                 this.ctrlKey = e.ctrlKey || e.metaKey;
6710                 this.altKey = e.altKey;
6711                 // in getKey these will be normalized for the mac
6712                 this.keyCode = e.keyCode;
6713                 // keyup warnings on firefox.
6714                 this.charCode = (e.type == 'keyup' || e.type == 'keydown') ? 0 : e.charCode;
6715                 // cache the target for the delayed and or buffered events
6716                 this.target = E.getTarget(e);
6717                 // same for XY
6718                 this.xy = E.getXY(e);
6719             }else{
6720                 this.button = -1;
6721                 this.shiftKey = false;
6722                 this.ctrlKey = false;
6723                 this.altKey = false;
6724                 this.keyCode = 0;
6725                 this.charCode =0;
6726                 this.target = null;
6727                 this.xy = [0, 0];
6728             }
6729             return this;
6730         },
6731
6732         /**
6733          * Stop the event (preventDefault and stopPropagation)
6734          */
6735         stopEvent : function(){
6736             if(this.browserEvent){
6737                 if(this.browserEvent.type == 'mousedown'){
6738                     Roo.EventManager.stoppedMouseDownEvent.fire(this);
6739                 }
6740                 E.stopEvent(this.browserEvent);
6741             }
6742         },
6743
6744         /**
6745          * Prevents the browsers default handling of the event.
6746          */
6747         preventDefault : function(){
6748             if(this.browserEvent){
6749                 E.preventDefault(this.browserEvent);
6750             }
6751         },
6752
6753         /** @private */
6754         isNavKeyPress : function(){
6755             var k = this.keyCode;
6756             k = Roo.isSafari ? (safariKeys[k] || k) : k;
6757             return (k >= 33 && k <= 40) || k == this.RETURN || k == this.TAB || k == this.ESC;
6758         },
6759
6760         isSpecialKey : function(){
6761             var k = this.keyCode;
6762             return (this.type == 'keypress' && this.ctrlKey) || k == 9 || k == 13  || k == 40 || k == 27 ||
6763             (k == 16) || (k == 17) ||
6764             (k >= 18 && k <= 20) ||
6765             (k >= 33 && k <= 35) ||
6766             (k >= 36 && k <= 39) ||
6767             (k >= 44 && k <= 45);
6768         },
6769         /**
6770          * Cancels bubbling of the event.
6771          */
6772         stopPropagation : function(){
6773             if(this.browserEvent){
6774                 if(this.type == 'mousedown'){
6775                     Roo.EventManager.stoppedMouseDownEvent.fire(this);
6776                 }
6777                 E.stopPropagation(this.browserEvent);
6778             }
6779         },
6780
6781         /**
6782          * Gets the key code for the event.
6783          * @return {Number}
6784          */
6785         getCharCode : function(){
6786             return this.charCode || this.keyCode;
6787         },
6788
6789         /**
6790          * Returns a normalized keyCode for the event.
6791          * @return {Number} The key code
6792          */
6793         getKey : function(){
6794             var k = this.keyCode || this.charCode;
6795             return Roo.isSafari ? (safariKeys[k] || k) : k;
6796         },
6797
6798         /**
6799          * Gets the x coordinate of the event.
6800          * @return {Number}
6801          */
6802         getPageX : function(){
6803             return this.xy[0];
6804         },
6805
6806         /**
6807          * Gets the y coordinate of the event.
6808          * @return {Number}
6809          */
6810         getPageY : function(){
6811             return this.xy[1];
6812         },
6813
6814         /**
6815          * Gets the time of the event.
6816          * @return {Number}
6817          */
6818         getTime : function(){
6819             if(this.browserEvent){
6820                 return E.getTime(this.browserEvent);
6821             }
6822             return null;
6823         },
6824
6825         /**
6826          * Gets the page coordinates of the event.
6827          * @return {Array} The xy values like [x, y]
6828          */
6829         getXY : function(){
6830             return this.xy;
6831         },
6832
6833         /**
6834          * Gets the target for the event.
6835          * @param {String} selector (optional) A simple selector to filter the target or look for an ancestor of the target
6836          * @param {Number/String/HTMLElement/Element} maxDepth (optional) The max depth to
6837                 search as a number or element (defaults to 10 || document.body)
6838          * @param {Boolean} returnEl (optional) True to return a Roo.Element object instead of DOM node
6839          * @return {HTMLelement}
6840          */
6841         getTarget : function(selector, maxDepth, returnEl){
6842             return selector ? Roo.fly(this.target).findParent(selector, maxDepth, returnEl) : this.target;
6843         },
6844         /**
6845          * Gets the related target.
6846          * @return {HTMLElement}
6847          */
6848         getRelatedTarget : function(){
6849             if(this.browserEvent){
6850                 return E.getRelatedTarget(this.browserEvent);
6851             }
6852             return null;
6853         },
6854
6855         /**
6856          * Normalizes mouse wheel delta across browsers
6857          * @return {Number} The delta
6858          */
6859         getWheelDelta : function(){
6860             var e = this.browserEvent;
6861             var delta = 0;
6862             if(e.wheelDelta){ /* IE/Opera. */
6863                 delta = e.wheelDelta/120;
6864             }else if(e.detail){ /* Mozilla case. */
6865                 delta = -e.detail/3;
6866             }
6867             return delta;
6868         },
6869
6870         /**
6871          * Returns true if the control, meta, shift or alt key was pressed during this event.
6872          * @return {Boolean}
6873          */
6874         hasModifier : function(){
6875             return !!((this.ctrlKey || this.altKey) || this.shiftKey);
6876         },
6877
6878         /**
6879          * Returns true if the target of this event equals el or is a child of el
6880          * @param {String/HTMLElement/Element} el
6881          * @param {Boolean} related (optional) true to test if the related target is within el instead of the target
6882          * @return {Boolean}
6883          */
6884         within : function(el, related){
6885             var t = this[related ? "getRelatedTarget" : "getTarget"]();
6886             return t && Roo.fly(el).contains(t);
6887         },
6888
6889         getPoint : function(){
6890             return new Roo.lib.Point(this.xy[0], this.xy[1]);
6891         }
6892     };
6893
6894     return new Roo.EventObjectImpl();
6895 }();
6896             
6897     /*
6898  * Based on:
6899  * Ext JS Library 1.1.1
6900  * Copyright(c) 2006-2007, Ext JS, LLC.
6901  *
6902  * Originally Released Under LGPL - original licence link has changed is not relivant.
6903  *
6904  * Fork - LGPL
6905  * <script type="text/javascript">
6906  */
6907
6908  
6909 // was in Composite Element!??!?!
6910  
6911 (function(){
6912     var D = Roo.lib.Dom;
6913     var E = Roo.lib.Event;
6914     var A = Roo.lib.Anim;
6915
6916     // local style camelizing for speed
6917     var propCache = {};
6918     var camelRe = /(-[a-z])/gi;
6919     var camelFn = function(m, a){ return a.charAt(1).toUpperCase(); };
6920     var view = document.defaultView;
6921
6922 /**
6923  * @class Roo.Element
6924  * Represents an Element in the DOM.<br><br>
6925  * Usage:<br>
6926 <pre><code>
6927 var el = Roo.get("my-div");
6928
6929 // or with getEl
6930 var el = getEl("my-div");
6931
6932 // or with a DOM element
6933 var el = Roo.get(myDivElement);
6934 </code></pre>
6935  * Using Roo.get() or getEl() instead of calling the constructor directly ensures you get the same object
6936  * each call instead of constructing a new one.<br><br>
6937  * <b>Animations</b><br />
6938  * Many of the functions for manipulating an element have an optional "animate" parameter. The animate parameter
6939  * should either be a boolean (true) or an object literal with animation options. The animation options are:
6940 <pre>
6941 Option    Default   Description
6942 --------- --------  ---------------------------------------------
6943 duration  .35       The duration of the animation in seconds
6944 easing    easeOut   The YUI easing method
6945 callback  none      A function to execute when the anim completes
6946 scope     this      The scope (this) of the callback function
6947 </pre>
6948 * Also, the Anim object being used for the animation will be set on your options object as "anim", which allows you to stop or
6949 * manipulate the animation. Here's an example:
6950 <pre><code>
6951 var el = Roo.get("my-div");
6952
6953 // no animation
6954 el.setWidth(100);
6955
6956 // default animation
6957 el.setWidth(100, true);
6958
6959 // animation with some options set
6960 el.setWidth(100, {
6961     duration: 1,
6962     callback: this.foo,
6963     scope: this
6964 });
6965
6966 // using the "anim" property to get the Anim object
6967 var opt = {
6968     duration: 1,
6969     callback: this.foo,
6970     scope: this
6971 };
6972 el.setWidth(100, opt);
6973 ...
6974 if(opt.anim.isAnimated()){
6975     opt.anim.stop();
6976 }
6977 </code></pre>
6978 * <b> Composite (Collections of) Elements</b><br />
6979  * For working with collections of Elements, see <a href="Roo.CompositeElement.html">Roo.CompositeElement</a>
6980  * @constructor Create a new Element directly.
6981  * @param {String/HTMLElement} element
6982  * @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).
6983  */
6984     Roo.Element = function(element, forceNew){
6985         var dom = typeof element == "string" ?
6986                 document.getElementById(element) : element;
6987         if(!dom){ // invalid id/element
6988             return null;
6989         }
6990         var id = dom.id;
6991         if(forceNew !== true && id && Roo.Element.cache[id]){ // element object already exists
6992             return Roo.Element.cache[id];
6993         }
6994
6995         /**
6996          * The DOM element
6997          * @type HTMLElement
6998          */
6999         this.dom = dom;
7000
7001         /**
7002          * The DOM element ID
7003          * @type String
7004          */
7005         this.id = id || Roo.id(dom);
7006     };
7007
7008     var El = Roo.Element;
7009
7010     El.prototype = {
7011         /**
7012          * The element's default display mode  (defaults to "")
7013          * @type String
7014          */
7015         originalDisplay : "",
7016
7017         visibilityMode : 1,
7018         /**
7019          * The default unit to append to CSS values where a unit isn't provided (defaults to px).
7020          * @type String
7021          */
7022         defaultUnit : "px",
7023         /**
7024          * Sets the element's visibility mode. When setVisible() is called it
7025          * will use this to determine whether to set the visibility or the display property.
7026          * @param visMode Element.VISIBILITY or Element.DISPLAY
7027          * @return {Roo.Element} this
7028          */
7029         setVisibilityMode : function(visMode){
7030             this.visibilityMode = visMode;
7031             return this;
7032         },
7033         /**
7034          * Convenience method for setVisibilityMode(Element.DISPLAY)
7035          * @param {String} display (optional) What to set display to when visible
7036          * @return {Roo.Element} this
7037          */
7038         enableDisplayMode : function(display){
7039             this.setVisibilityMode(El.DISPLAY);
7040             if(typeof display != "undefined") this.originalDisplay = display;
7041             return this;
7042         },
7043
7044         /**
7045          * 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)
7046          * @param {String} selector The simple selector to test
7047          * @param {Number/String/HTMLElement/Element} maxDepth (optional) The max depth to
7048                 search as a number or element (defaults to 10 || document.body)
7049          * @param {Boolean} returnEl (optional) True to return a Roo.Element object instead of DOM node
7050          * @return {HTMLElement} The matching DOM node (or null if no match was found)
7051          */
7052         findParent : function(simpleSelector, maxDepth, returnEl){
7053             var p = this.dom, b = document.body, depth = 0, dq = Roo.DomQuery, stopEl;
7054             maxDepth = maxDepth || 50;
7055             if(typeof maxDepth != "number"){
7056                 stopEl = Roo.getDom(maxDepth);
7057                 maxDepth = 10;
7058             }
7059             while(p && p.nodeType == 1 && depth < maxDepth && p != b && p != stopEl){
7060                 if(dq.is(p, simpleSelector)){
7061                     return returnEl ? Roo.get(p) : p;
7062                 }
7063                 depth++;
7064                 p = p.parentNode;
7065             }
7066             return null;
7067         },
7068
7069
7070         /**
7071          * Looks at parent nodes for a match of the passed simple selector (e.g. div.some-class or span:first-child)
7072          * @param {String} selector The simple selector to test
7073          * @param {Number/String/HTMLElement/Element} maxDepth (optional) The max depth to
7074                 search as a number or element (defaults to 10 || document.body)
7075          * @param {Boolean} returnEl (optional) True to return a Roo.Element object instead of DOM node
7076          * @return {HTMLElement} The matching DOM node (or null if no match was found)
7077          */
7078         findParentNode : function(simpleSelector, maxDepth, returnEl){
7079             var p = Roo.fly(this.dom.parentNode, '_internal');
7080             return p ? p.findParent(simpleSelector, maxDepth, returnEl) : null;
7081         },
7082
7083         /**
7084          * Walks up the dom looking for a parent node that matches the passed simple selector (e.g. div.some-class or span:first-child).
7085          * This is a shortcut for findParentNode() that always returns an Roo.Element.
7086          * @param {String} selector The simple selector to test
7087          * @param {Number/String/HTMLElement/Element} maxDepth (optional) The max depth to
7088                 search as a number or element (defaults to 10 || document.body)
7089          * @return {Roo.Element} The matching DOM node (or null if no match was found)
7090          */
7091         up : function(simpleSelector, maxDepth){
7092             return this.findParentNode(simpleSelector, maxDepth, true);
7093         },
7094
7095
7096
7097         /**
7098          * Returns true if this element matches the passed simple selector (e.g. div.some-class or span:first-child)
7099          * @param {String} selector The simple selector to test
7100          * @return {Boolean} True if this element matches the selector, else false
7101          */
7102         is : function(simpleSelector){
7103             return Roo.DomQuery.is(this.dom, simpleSelector);
7104         },
7105
7106         /**
7107          * Perform animation on this element.
7108          * @param {Object} args The YUI animation control args
7109          * @param {Float} duration (optional) How long the animation lasts in seconds (defaults to .35)
7110          * @param {Function} onComplete (optional) Function to call when animation completes
7111          * @param {String} easing (optional) Easing method to use (defaults to 'easeOut')
7112          * @param {String} animType (optional) 'run' is the default. Can also be 'color', 'motion', or 'scroll'
7113          * @return {Roo.Element} this
7114          */
7115         animate : function(args, duration, onComplete, easing, animType){
7116             this.anim(args, {duration: duration, callback: onComplete, easing: easing}, animType);
7117             return this;
7118         },
7119
7120         /*
7121          * @private Internal animation call
7122          */
7123         anim : function(args, opt, animType, defaultDur, defaultEase, cb){
7124             animType = animType || 'run';
7125             opt = opt || {};
7126             var anim = Roo.lib.Anim[animType](
7127                 this.dom, args,
7128                 (opt.duration || defaultDur) || .35,
7129                 (opt.easing || defaultEase) || 'easeOut',
7130                 function(){
7131                     Roo.callback(cb, this);
7132                     Roo.callback(opt.callback, opt.scope || this, [this, opt]);
7133                 },
7134                 this
7135             );
7136             opt.anim = anim;
7137             return anim;
7138         },
7139
7140         // private legacy anim prep
7141         preanim : function(a, i){
7142             return !a[i] ? false : (typeof a[i] == "object" ? a[i]: {duration: a[i+1], callback: a[i+2], easing: a[i+3]});
7143         },
7144
7145         /**
7146          * Removes worthless text nodes
7147          * @param {Boolean} forceReclean (optional) By default the element
7148          * keeps track if it has been cleaned already so
7149          * you can call this over and over. However, if you update the element and
7150          * need to force a reclean, you can pass true.
7151          */
7152         clean : function(forceReclean){
7153             if(this.isCleaned && forceReclean !== true){
7154                 return this;
7155             }
7156             var ns = /\S/;
7157             var d = this.dom, n = d.firstChild, ni = -1;
7158             while(n){
7159                 var nx = n.nextSibling;
7160                 if(n.nodeType == 3 && !ns.test(n.nodeValue)){
7161                     d.removeChild(n);
7162                 }else{
7163                     n.nodeIndex = ++ni;
7164                 }
7165                 n = nx;
7166             }
7167             this.isCleaned = true;
7168             return this;
7169         },
7170
7171         // private
7172         calcOffsetsTo : function(el){
7173             el = Roo.get(el);
7174             var d = el.dom;
7175             var restorePos = false;
7176             if(el.getStyle('position') == 'static'){
7177                 el.position('relative');
7178                 restorePos = true;
7179             }
7180             var x = 0, y =0;
7181             var op = this.dom;
7182             while(op && op != d && op.tagName != 'HTML'){
7183                 x+= op.offsetLeft;
7184                 y+= op.offsetTop;
7185                 op = op.offsetParent;
7186             }
7187             if(restorePos){
7188                 el.position('static');
7189             }
7190             return [x, y];
7191         },
7192
7193         /**
7194          * Scrolls this element into view within the passed container.
7195          * @param {String/HTMLElement/Element} container (optional) The container element to scroll (defaults to document.body)
7196          * @param {Boolean} hscroll (optional) False to disable horizontal scroll (defaults to true)
7197          * @return {Roo.Element} this
7198          */
7199         scrollIntoView : function(container, hscroll){
7200             var c = Roo.getDom(container) || document.body;
7201             var el = this.dom;
7202
7203             var o = this.calcOffsetsTo(c),
7204                 l = o[0],
7205                 t = o[1],
7206                 b = t+el.offsetHeight,
7207                 r = l+el.offsetWidth;
7208
7209             var ch = c.clientHeight;
7210             var ct = parseInt(c.scrollTop, 10);
7211             var cl = parseInt(c.scrollLeft, 10);
7212             var cb = ct + ch;
7213             var cr = cl + c.clientWidth;
7214
7215             if(t < ct){
7216                 c.scrollTop = t;
7217             }else if(b > cb){
7218                 c.scrollTop = b-ch;
7219             }
7220
7221             if(hscroll !== false){
7222                 if(l < cl){
7223                     c.scrollLeft = l;
7224                 }else if(r > cr){
7225                     c.scrollLeft = r-c.clientWidth;
7226                 }
7227             }
7228             return this;
7229         },
7230
7231         // private
7232         scrollChildIntoView : function(child, hscroll){
7233             Roo.fly(child, '_scrollChildIntoView').scrollIntoView(this, hscroll);
7234         },
7235
7236         /**
7237          * Measures the element's content height and updates height to match. Note: this function uses setTimeout so
7238          * the new height may not be available immediately.
7239          * @param {Boolean} animate (optional) Animate the transition (defaults to false)
7240          * @param {Float} duration (optional) Length of the animation in seconds (defaults to .35)
7241          * @param {Function} onComplete (optional) Function to call when animation completes
7242          * @param {String} easing (optional) Easing method to use (defaults to easeOut)
7243          * @return {Roo.Element} this
7244          */
7245         autoHeight : function(animate, duration, onComplete, easing){
7246             var oldHeight = this.getHeight();
7247             this.clip();
7248             this.setHeight(1); // force clipping
7249             setTimeout(function(){
7250                 var height = parseInt(this.dom.scrollHeight, 10); // parseInt for Safari
7251                 if(!animate){
7252                     this.setHeight(height);
7253                     this.unclip();
7254                     if(typeof onComplete == "function"){
7255                         onComplete();
7256                     }
7257                 }else{
7258                     this.setHeight(oldHeight); // restore original height
7259                     this.setHeight(height, animate, duration, function(){
7260                         this.unclip();
7261                         if(typeof onComplete == "function") onComplete();
7262                     }.createDelegate(this), easing);
7263                 }
7264             }.createDelegate(this), 0);
7265             return this;
7266         },
7267
7268         /**
7269          * Returns true if this element is an ancestor of the passed element
7270          * @param {HTMLElement/String} el The element to check
7271          * @return {Boolean} True if this element is an ancestor of el, else false
7272          */
7273         contains : function(el){
7274             if(!el){return false;}
7275             return D.isAncestor(this.dom, el.dom ? el.dom : el);
7276         },
7277
7278         /**
7279          * Checks whether the element is currently visible using both visibility and display properties.
7280          * @param {Boolean} deep (optional) True to walk the dom and see if parent elements are hidden (defaults to false)
7281          * @return {Boolean} True if the element is currently visible, else false
7282          */
7283         isVisible : function(deep) {
7284             var vis = !(this.getStyle("visibility") == "hidden" || this.getStyle("display") == "none");
7285             if(deep !== true || !vis){
7286                 return vis;
7287             }
7288             var p = this.dom.parentNode;
7289             while(p && p.tagName.toLowerCase() != "body"){
7290                 if(!Roo.fly(p, '_isVisible').isVisible()){
7291                     return false;
7292                 }
7293                 p = p.parentNode;
7294             }
7295             return true;
7296         },
7297
7298         /**
7299          * Creates a {@link Roo.CompositeElement} for child nodes based on the passed CSS selector (the selector should not contain an id).
7300          * @param {String} selector The CSS selector
7301          * @param {Boolean} unique (optional) True to create a unique Roo.Element for each child (defaults to false, which creates a single shared flyweight object)
7302          * @return {CompositeElement/CompositeElementLite} The composite element
7303          */
7304         select : function(selector, unique){
7305             return El.select(selector, unique, this.dom);
7306         },
7307
7308         /**
7309          * Selects child nodes based on the passed CSS selector (the selector should not contain an id).
7310          * @param {String} selector The CSS selector
7311          * @return {Array} An array of the matched nodes
7312          */
7313         query : function(selector, unique){
7314             return Roo.DomQuery.select(selector, this.dom);
7315         },
7316
7317         /**
7318          * Selects a single child at any depth below this element based on the passed CSS selector (the selector should not contain an id).
7319          * @param {String} selector The CSS selector
7320          * @param {Boolean} returnDom (optional) True to return the DOM node instead of Roo.Element (defaults to false)
7321          * @return {HTMLElement/Roo.Element} The child Roo.Element (or DOM node if returnDom = true)
7322          */
7323         child : function(selector, returnDom){
7324             var n = Roo.DomQuery.selectNode(selector, this.dom);
7325             return returnDom ? n : Roo.get(n);
7326         },
7327
7328         /**
7329          * Selects a single *direct* child based on the passed CSS selector (the selector should not contain an id).
7330          * @param {String} selector The CSS selector
7331          * @param {Boolean} returnDom (optional) True to return the DOM node instead of Roo.Element (defaults to false)
7332          * @return {HTMLElement/Roo.Element} The child Roo.Element (or DOM node if returnDom = true)
7333          */
7334         down : function(selector, returnDom){
7335             var n = Roo.DomQuery.selectNode(" > " + selector, this.dom);
7336             return returnDom ? n : Roo.get(n);
7337         },
7338
7339         /**
7340          * Initializes a {@link Roo.dd.DD} drag drop object for this element.
7341          * @param {String} group The group the DD object is member of
7342          * @param {Object} config The DD config object
7343          * @param {Object} overrides An object containing methods to override/implement on the DD object
7344          * @return {Roo.dd.DD} The DD object
7345          */
7346         initDD : function(group, config, overrides){
7347             var dd = new Roo.dd.DD(Roo.id(this.dom), group, config);
7348             return Roo.apply(dd, overrides);
7349         },
7350
7351         /**
7352          * Initializes a {@link Roo.dd.DDProxy} object for this element.
7353          * @param {String} group The group the DDProxy object is member of
7354          * @param {Object} config The DDProxy config object
7355          * @param {Object} overrides An object containing methods to override/implement on the DDProxy object
7356          * @return {Roo.dd.DDProxy} The DDProxy object
7357          */
7358         initDDProxy : function(group, config, overrides){
7359             var dd = new Roo.dd.DDProxy(Roo.id(this.dom), group, config);
7360             return Roo.apply(dd, overrides);
7361         },
7362
7363         /**
7364          * Initializes a {@link Roo.dd.DDTarget} object for this element.
7365          * @param {String} group The group the DDTarget object is member of
7366          * @param {Object} config The DDTarget config object
7367          * @param {Object} overrides An object containing methods to override/implement on the DDTarget object
7368          * @return {Roo.dd.DDTarget} The DDTarget object
7369          */
7370         initDDTarget : function(group, config, overrides){
7371             var dd = new Roo.dd.DDTarget(Roo.id(this.dom), group, config);
7372             return Roo.apply(dd, overrides);
7373         },
7374
7375         /**
7376          * Sets the visibility of the element (see details). If the visibilityMode is set to Element.DISPLAY, it will use
7377          * the display property to hide the element, otherwise it uses visibility. The default is to hide and show using the visibility property.
7378          * @param {Boolean} visible Whether the element is visible
7379          * @param {Boolean/Object} animate (optional) True for the default animation, or a standard Element animation config object
7380          * @return {Roo.Element} this
7381          */
7382          setVisible : function(visible, animate){
7383             if(!animate || !A){
7384                 if(this.visibilityMode == El.DISPLAY){
7385                     this.setDisplayed(visible);
7386                 }else{
7387                     this.fixDisplay();
7388                     this.dom.style.visibility = visible ? "visible" : "hidden";
7389                 }
7390             }else{
7391                 // closure for composites
7392                 var dom = this.dom;
7393                 var visMode = this.visibilityMode;
7394                 if(visible){
7395                     this.setOpacity(.01);
7396                     this.setVisible(true);
7397                 }
7398                 this.anim({opacity: { to: (visible?1:0) }},
7399                       this.preanim(arguments, 1),
7400                       null, .35, 'easeIn', function(){
7401                          if(!visible){
7402                              if(visMode == El.DISPLAY){
7403                                  dom.style.display = "none";
7404                              }else{
7405                                  dom.style.visibility = "hidden";
7406                              }
7407                              Roo.get(dom).setOpacity(1);
7408                          }
7409                      });
7410             }
7411             return this;
7412         },
7413
7414         /**
7415          * Returns true if display is not "none"
7416          * @return {Boolean}
7417          */
7418         isDisplayed : function() {
7419             return this.getStyle("display") != "none";
7420         },
7421
7422         /**
7423          * Toggles the element's visibility or display, depending on visibility mode.
7424          * @param {Boolean/Object} animate (optional) True for the default animation, or a standard Element animation config object
7425          * @return {Roo.Element} this
7426          */
7427         toggle : function(animate){
7428             this.setVisible(!this.isVisible(), this.preanim(arguments, 0));
7429             return this;
7430         },
7431
7432         /**
7433          * Sets the CSS display property. Uses originalDisplay if the specified value is a boolean true.
7434          * @param {Boolean} value Boolean value to display the element using its default display, or a string to set the display directly
7435          * @return {Roo.Element} this
7436          */
7437         setDisplayed : function(value) {
7438             if(typeof value == "boolean"){
7439                value = value ? this.originalDisplay : "none";
7440             }
7441             this.setStyle("display", value);
7442             return this;
7443         },
7444
7445         /**
7446          * Tries to focus the element. Any exceptions are caught and ignored.
7447          * @return {Roo.Element} this
7448          */
7449         focus : function() {
7450             try{
7451                 this.dom.focus();
7452             }catch(e){}
7453             return this;
7454         },
7455
7456         /**
7457          * Tries to blur the element. Any exceptions are caught and ignored.
7458          * @return {Roo.Element} this
7459          */
7460         blur : function() {
7461             try{
7462                 this.dom.blur();
7463             }catch(e){}
7464             return this;
7465         },
7466
7467         /**
7468          * Adds one or more CSS classes to the element. Duplicate classes are automatically filtered out.
7469          * @param {String/Array} className The CSS class to add, or an array of classes
7470          * @return {Roo.Element} this
7471          */
7472         addClass : function(className){
7473             if(className instanceof Array){
7474                 for(var i = 0, len = className.length; i < len; i++) {
7475                     this.addClass(className[i]);
7476                 }
7477             }else{
7478                 if(className && !this.hasClass(className)){
7479                     this.dom.className = this.dom.className + " " + className;
7480                 }
7481             }
7482             return this;
7483         },
7484
7485         /**
7486          * Adds one or more CSS classes to this element and removes the same class(es) from all siblings.
7487          * @param {String/Array} className The CSS class to add, or an array of classes
7488          * @return {Roo.Element} this
7489          */
7490         radioClass : function(className){
7491             var siblings = this.dom.parentNode.childNodes;
7492             for(var i = 0; i < siblings.length; i++) {
7493                 var s = siblings[i];
7494                 if(s.nodeType == 1){
7495                     Roo.get(s).removeClass(className);
7496                 }
7497             }
7498             this.addClass(className);
7499             return this;
7500         },
7501
7502         /**
7503          * Removes one or more CSS classes from the element.
7504          * @param {String/Array} className The CSS class to remove, or an array of classes
7505          * @return {Roo.Element} this
7506          */
7507         removeClass : function(className){
7508             if(!className || !this.dom.className){
7509                 return this;
7510             }
7511             if(className instanceof Array){
7512                 for(var i = 0, len = className.length; i < len; i++) {
7513                     this.removeClass(className[i]);
7514                 }
7515             }else{
7516                 if(this.hasClass(className)){
7517                     var re = this.classReCache[className];
7518                     if (!re) {
7519                        re = new RegExp('(?:^|\\s+)' + className + '(?:\\s+|$)', "g");
7520                        this.classReCache[className] = re;
7521                     }
7522                     this.dom.className =
7523                         this.dom.className.replace(re, " ");
7524                 }
7525             }
7526             return this;
7527         },
7528
7529         // private
7530         classReCache: {},
7531
7532         /**
7533          * Toggles the specified CSS class on this element (removes it if it already exists, otherwise adds it).
7534          * @param {String} className The CSS class to toggle
7535          * @return {Roo.Element} this
7536          */
7537         toggleClass : function(className){
7538             if(this.hasClass(className)){
7539                 this.removeClass(className);
7540             }else{
7541                 this.addClass(className);
7542             }
7543             return this;
7544         },
7545
7546         /**
7547          * Checks if the specified CSS class exists on this element's DOM node.
7548          * @param {String} className The CSS class to check for
7549          * @return {Boolean} True if the class exists, else false
7550          */
7551         hasClass : function(className){
7552             return className && (' '+this.dom.className+' ').indexOf(' '+className+' ') != -1;
7553         },
7554
7555         /**
7556          * Replaces a CSS class on the element with another.  If the old name does not exist, the new name will simply be added.
7557          * @param {String} oldClassName The CSS class to replace
7558          * @param {String} newClassName The replacement CSS class
7559          * @return {Roo.Element} this
7560          */
7561         replaceClass : function(oldClassName, newClassName){
7562             this.removeClass(oldClassName);
7563             this.addClass(newClassName);
7564             return this;
7565         },
7566
7567         /**
7568          * Returns an object with properties matching the styles requested.
7569          * For example, el.getStyles('color', 'font-size', 'width') might return
7570          * {'color': '#FFFFFF', 'font-size': '13px', 'width': '100px'}.
7571          * @param {String} style1 A style name
7572          * @param {String} style2 A style name
7573          * @param {String} etc.
7574          * @return {Object} The style object
7575          */
7576         getStyles : function(){
7577             var a = arguments, len = a.length, r = {};
7578             for(var i = 0; i < len; i++){
7579                 r[a[i]] = this.getStyle(a[i]);
7580             }
7581             return r;
7582         },
7583
7584         /**
7585          * Normalizes currentStyle and computedStyle. This is not YUI getStyle, it is an optimised version.
7586          * @param {String} property The style property whose value is returned.
7587          * @return {String} The current value of the style property for this element.
7588          */
7589         getStyle : function(){
7590             return view && view.getComputedStyle ?
7591                 function(prop){
7592                     var el = this.dom, v, cs, camel;
7593                     if(prop == 'float'){
7594                         prop = "cssFloat";
7595                     }
7596                     if(el.style && (v = el.style[prop])){
7597                         return v;
7598                     }
7599                     if(cs = view.getComputedStyle(el, "")){
7600                         if(!(camel = propCache[prop])){
7601                             camel = propCache[prop] = prop.replace(camelRe, camelFn);
7602                         }
7603                         return cs[camel];
7604                     }
7605                     return null;
7606                 } :
7607                 function(prop){
7608                     var el = this.dom, v, cs, camel;
7609                     if(prop == 'opacity'){
7610                         if(typeof el.style.filter == 'string'){
7611                             var m = el.style.filter.match(/alpha\(opacity=(.*)\)/i);
7612                             if(m){
7613                                 var fv = parseFloat(m[1]);
7614                                 if(!isNaN(fv)){
7615                                     return fv ? fv / 100 : 0;
7616                                 }
7617                             }
7618                         }
7619                         return 1;
7620                     }else if(prop == 'float'){
7621                         prop = "styleFloat";
7622                     }
7623                     if(!(camel = propCache[prop])){
7624                         camel = propCache[prop] = prop.replace(camelRe, camelFn);
7625                     }
7626                     if(v = el.style[camel]){
7627                         return v;
7628                     }
7629                     if(cs = el.currentStyle){
7630                         return cs[camel];
7631                     }
7632                     return null;
7633                 };
7634         }(),
7635
7636         /**
7637          * Wrapper for setting style properties, also takes single object parameter of multiple styles.
7638          * @param {String/Object} property The style property to be set, or an object of multiple styles.
7639          * @param {String} value (optional) The value to apply to the given property, or null if an object was passed.
7640          * @return {Roo.Element} this
7641          */
7642         setStyle : function(prop, value){
7643             if(typeof prop == "string"){
7644                 
7645                 if (prop == 'float') {
7646                     this.setStyle(Roo.isIE ? 'styleFloat'  : 'cssFloat', value);
7647                     return this;
7648                 }
7649                 
7650                 var camel;
7651                 if(!(camel = propCache[prop])){
7652                     camel = propCache[prop] = prop.replace(camelRe, camelFn);
7653                 }
7654                 
7655                 if(camel == 'opacity') {
7656                     this.setOpacity(value);
7657                 }else{
7658                     this.dom.style[camel] = value;
7659                 }
7660             }else{
7661                 for(var style in prop){
7662                     if(typeof prop[style] != "function"){
7663                        this.setStyle(style, prop[style]);
7664                     }
7665                 }
7666             }
7667             return this;
7668         },
7669
7670         /**
7671          * More flexible version of {@link #setStyle} for setting style properties.
7672          * @param {String/Object/Function} styles A style specification string, e.g. "width:100px", or object in the form {width:"100px"}, or
7673          * a function which returns such a specification.
7674          * @return {Roo.Element} this
7675          */
7676         applyStyles : function(style){
7677             Roo.DomHelper.applyStyles(this.dom, style);
7678             return this;
7679         },
7680
7681         /**
7682           * 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).
7683           * @return {Number} The X position of the element
7684           */
7685         getX : function(){
7686             return D.getX(this.dom);
7687         },
7688
7689         /**
7690           * 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).
7691           * @return {Number} The Y position of the element
7692           */
7693         getY : function(){
7694             return D.getY(this.dom);
7695         },
7696
7697         /**
7698           * 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).
7699           * @return {Array} The XY position of the element
7700           */
7701         getXY : function(){
7702             return D.getXY(this.dom);
7703         },
7704
7705         /**
7706          * 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).
7707          * @param {Number} The X position of the element
7708          * @param {Boolean/Object} animate (optional) True for the default animation, or a standard Element animation config object
7709          * @return {Roo.Element} this
7710          */
7711         setX : function(x, animate){
7712             if(!animate || !A){
7713                 D.setX(this.dom, x);
7714             }else{
7715                 this.setXY([x, this.getY()], this.preanim(arguments, 1));
7716             }
7717             return this;
7718         },
7719
7720         /**
7721          * 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).
7722          * @param {Number} The Y position of the element
7723          * @param {Boolean/Object} animate (optional) True for the default animation, or a standard Element animation config object
7724          * @return {Roo.Element} this
7725          */
7726         setY : function(y, animate){
7727             if(!animate || !A){
7728                 D.setY(this.dom, y);
7729             }else{
7730                 this.setXY([this.getX(), y], this.preanim(arguments, 1));
7731             }
7732             return this;
7733         },
7734
7735         /**
7736          * Sets the element's left position directly using CSS style (instead of {@link #setX}).
7737          * @param {String} left The left CSS property value
7738          * @return {Roo.Element} this
7739          */
7740         setLeft : function(left){
7741             this.setStyle("left", this.addUnits(left));
7742             return this;
7743         },
7744
7745         /**
7746          * Sets the element's top position directly using CSS style (instead of {@link #setY}).
7747          * @param {String} top The top CSS property value
7748          * @return {Roo.Element} this
7749          */
7750         setTop : function(top){
7751             this.setStyle("top", this.addUnits(top));
7752             return this;
7753         },
7754
7755         /**
7756          * Sets the element's CSS right style.
7757          * @param {String} right The right CSS property value
7758          * @return {Roo.Element} this
7759          */
7760         setRight : function(right){
7761             this.setStyle("right", this.addUnits(right));
7762             return this;
7763         },
7764
7765         /**
7766          * Sets the element's CSS bottom style.
7767          * @param {String} bottom The bottom CSS property value
7768          * @return {Roo.Element} this
7769          */
7770         setBottom : function(bottom){
7771             this.setStyle("bottom", this.addUnits(bottom));
7772             return this;
7773         },
7774
7775         /**
7776          * Sets the position of the element in page coordinates, regardless of how the element is positioned.
7777          * The element must be part of the DOM tree to have page coordinates (display:none or elements not appended return false).
7778          * @param {Array} pos Contains X & Y [x, y] values for new position (coordinates are page-based)
7779          * @param {Boolean/Object} animate (optional) True for the default animation, or a standard Element animation config object
7780          * @return {Roo.Element} this
7781          */
7782         setXY : function(pos, animate){
7783             if(!animate || !A){
7784                 D.setXY(this.dom, pos);
7785             }else{
7786                 this.anim({points: {to: pos}}, this.preanim(arguments, 1), 'motion');
7787             }
7788             return this;
7789         },
7790
7791         /**
7792          * Sets the position of the element in page coordinates, regardless of how the element is positioned.
7793          * The element must be part of the DOM tree to have page coordinates (display:none or elements not appended return false).
7794          * @param {Number} x X value for new position (coordinates are page-based)
7795          * @param {Number} y Y value for new position (coordinates are page-based)
7796          * @param {Boolean/Object} animate (optional) True for the default animation, or a standard Element animation config object
7797          * @return {Roo.Element} this
7798          */
7799         setLocation : function(x, y, animate){
7800             this.setXY([x, y], this.preanim(arguments, 2));
7801             return this;
7802         },
7803
7804         /**
7805          * Sets the position of the element in page coordinates, regardless of how the element is positioned.
7806          * The element must be part of the DOM tree to have page coordinates (display:none or elements not appended return false).
7807          * @param {Number} x X value for new position (coordinates are page-based)
7808          * @param {Number} y Y value for new position (coordinates are page-based)
7809          * @param {Boolean/Object} animate (optional) True for the default animation, or a standard Element animation config object
7810          * @return {Roo.Element} this
7811          */
7812         moveTo : function(x, y, animate){
7813             this.setXY([x, y], this.preanim(arguments, 2));
7814             return this;
7815         },
7816
7817         /**
7818          * Returns the region of the given element.
7819          * The element must be part of the DOM tree to have a region (display:none or elements not appended return false).
7820          * @return {Region} A Roo.lib.Region containing "top, left, bottom, right" member data.
7821          */
7822         getRegion : function(){
7823             return D.getRegion(this.dom);
7824         },
7825
7826         /**
7827          * Returns the offset height of the element
7828          * @param {Boolean} contentHeight (optional) true to get the height minus borders and padding
7829          * @return {Number} The element's height
7830          */
7831         getHeight : function(contentHeight){
7832             var h = this.dom.offsetHeight || 0;
7833             return contentHeight !== true ? h : h-this.getBorderWidth("tb")-this.getPadding("tb");
7834         },
7835
7836         /**
7837          * Returns the offset width of the element
7838          * @param {Boolean} contentWidth (optional) true to get the width minus borders and padding
7839          * @return {Number} The element's width
7840          */
7841         getWidth : function(contentWidth){
7842             var w = this.dom.offsetWidth || 0;
7843             return contentWidth !== true ? w : w-this.getBorderWidth("lr")-this.getPadding("lr");
7844         },
7845
7846         /**
7847          * Returns either the offsetHeight or the height of this element based on CSS height adjusted by padding or borders
7848          * when needed to simulate offsetHeight when offsets aren't available. This may not work on display:none elements
7849          * if a height has not been set using CSS.
7850          * @return {Number}
7851          */
7852         getComputedHeight : function(){
7853             var h = Math.max(this.dom.offsetHeight, this.dom.clientHeight);
7854             if(!h){
7855                 h = parseInt(this.getStyle('height'), 10) || 0;
7856                 if(!this.isBorderBox()){
7857                     h += this.getFrameWidth('tb');
7858                 }
7859             }
7860             return h;
7861         },
7862
7863         /**
7864          * Returns either the offsetWidth or the width of this element based on CSS width adjusted by padding or borders
7865          * when needed to simulate offsetWidth when offsets aren't available. This may not work on display:none elements
7866          * if a width has not been set using CSS.
7867          * @return {Number}
7868          */
7869         getComputedWidth : function(){
7870             var w = Math.max(this.dom.offsetWidth, this.dom.clientWidth);
7871             if(!w){
7872                 w = parseInt(this.getStyle('width'), 10) || 0;
7873                 if(!this.isBorderBox()){
7874                     w += this.getFrameWidth('lr');
7875                 }
7876             }
7877             return w;
7878         },
7879
7880         /**
7881          * Returns the size of the element.
7882          * @param {Boolean} contentSize (optional) true to get the width/size minus borders and padding
7883          * @return {Object} An object containing the element's size {width: (element width), height: (element height)}
7884          */
7885         getSize : function(contentSize){
7886             return {width: this.getWidth(contentSize), height: this.getHeight(contentSize)};
7887         },
7888
7889         /**
7890          * Returns the width and height of the viewport.
7891          * @return {Object} An object containing the viewport's size {width: (viewport width), height: (viewport height)}
7892          */
7893         getViewSize : function(){
7894             var d = this.dom, doc = document, aw = 0, ah = 0;
7895             if(d == doc || d == doc.body){
7896                 return {width : D.getViewWidth(), height: D.getViewHeight()};
7897             }else{
7898                 return {
7899                     width : d.clientWidth,
7900                     height: d.clientHeight
7901                 };
7902             }
7903         },
7904
7905         /**
7906          * Returns the value of the "value" attribute
7907          * @param {Boolean} asNumber true to parse the value as a number
7908          * @return {String/Number}
7909          */
7910         getValue : function(asNumber){
7911             return asNumber ? parseInt(this.dom.value, 10) : this.dom.value;
7912         },
7913
7914         // private
7915         adjustWidth : function(width){
7916             if(typeof width == "number"){
7917                 if(this.autoBoxAdjust && !this.isBorderBox()){
7918                    width -= (this.getBorderWidth("lr") + this.getPadding("lr"));
7919                 }
7920                 if(width < 0){
7921                     width = 0;
7922                 }
7923             }
7924             return width;
7925         },
7926
7927         // private
7928         adjustHeight : function(height){
7929             if(typeof height == "number"){
7930                if(this.autoBoxAdjust && !this.isBorderBox()){
7931                    height -= (this.getBorderWidth("tb") + this.getPadding("tb"));
7932                }
7933                if(height < 0){
7934                    height = 0;
7935                }
7936             }
7937             return height;
7938         },
7939
7940         /**
7941          * Set the width of the element
7942          * @param {Number} width The new width
7943          * @param {Boolean/Object} animate (optional) true for the default animation or a standard Element animation config object
7944          * @return {Roo.Element} this
7945          */
7946         setWidth : function(width, animate){
7947             width = this.adjustWidth(width);
7948             if(!animate || !A){
7949                 this.dom.style.width = this.addUnits(width);
7950             }else{
7951                 this.anim({width: {to: width}}, this.preanim(arguments, 1));
7952             }
7953             return this;
7954         },
7955
7956         /**
7957          * Set the height of the element
7958          * @param {Number} height The new height
7959          * @param {Boolean/Object} animate (optional) true for the default animation or a standard Element animation config object
7960          * @return {Roo.Element} this
7961          */
7962          setHeight : function(height, animate){
7963             height = this.adjustHeight(height);
7964             if(!animate || !A){
7965                 this.dom.style.height = this.addUnits(height);
7966             }else{
7967                 this.anim({height: {to: height}}, this.preanim(arguments, 1));
7968             }
7969             return this;
7970         },
7971
7972         /**
7973          * Set the size of the element. If animation is true, both width an height will be animated concurrently.
7974          * @param {Number} width The new width
7975          * @param {Number} height The new height
7976          * @param {Boolean/Object} animate (optional) true for the default animation or a standard Element animation config object
7977          * @return {Roo.Element} this
7978          */
7979          setSize : function(width, height, animate){
7980             if(typeof width == "object"){ // in case of object from getSize()
7981                 height = width.height; width = width.width;
7982             }
7983             width = this.adjustWidth(width); height = this.adjustHeight(height);
7984             if(!animate || !A){
7985                 this.dom.style.width = this.addUnits(width);
7986                 this.dom.style.height = this.addUnits(height);
7987             }else{
7988                 this.anim({width: {to: width}, height: {to: height}}, this.preanim(arguments, 2));
7989             }
7990             return this;
7991         },
7992
7993         /**
7994          * Sets the element's position and size in one shot. If animation is true then width, height, x and y will be animated concurrently.
7995          * @param {Number} x X value for new position (coordinates are page-based)
7996          * @param {Number} y Y value for new position (coordinates are page-based)
7997          * @param {Number} width The new width
7998          * @param {Number} height The new height
7999          * @param {Boolean/Object} animate (optional) true for the default animation or a standard Element animation config object
8000          * @return {Roo.Element} this
8001          */
8002         setBounds : function(x, y, width, height, animate){
8003             if(!animate || !A){
8004                 this.setSize(width, height);
8005                 this.setLocation(x, y);
8006             }else{
8007                 width = this.adjustWidth(width); height = this.adjustHeight(height);
8008                 this.anim({points: {to: [x, y]}, width: {to: width}, height: {to: height}},
8009                               this.preanim(arguments, 4), 'motion');
8010             }
8011             return this;
8012         },
8013
8014         /**
8015          * 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.
8016          * @param {Roo.lib.Region} region The region to fill
8017          * @param {Boolean/Object} animate (optional) true for the default animation or a standard Element animation config object
8018          * @return {Roo.Element} this
8019          */
8020         setRegion : function(region, animate){
8021             this.setBounds(region.left, region.top, region.right-region.left, region.bottom-region.top, this.preanim(arguments, 1));
8022             return this;
8023         },
8024
8025         /**
8026          * Appends an event handler
8027          *
8028          * @param {String}   eventName     The type of event to append
8029          * @param {Function} fn        The method the event invokes
8030          * @param {Object} scope       (optional) The scope (this object) of the fn
8031          * @param {Object}   options   (optional)An object with standard {@link Roo.EventManager#addListener} options
8032          */
8033         addListener : function(eventName, fn, scope, options){
8034             if (this.dom) {
8035                 Roo.EventManager.on(this.dom,  eventName, fn, scope || this, options);
8036             }
8037         },
8038
8039         /**
8040          * Removes an event handler from this element
8041          * @param {String} eventName the type of event to remove
8042          * @param {Function} fn the method the event invokes
8043          * @return {Roo.Element} this
8044          */
8045         removeListener : function(eventName, fn){
8046             Roo.EventManager.removeListener(this.dom,  eventName, fn);
8047             return this;
8048         },
8049
8050         /**
8051          * Removes all previous added listeners from this element
8052          * @return {Roo.Element} this
8053          */
8054         removeAllListeners : function(){
8055             E.purgeElement(this.dom);
8056             return this;
8057         },
8058
8059         relayEvent : function(eventName, observable){
8060             this.on(eventName, function(e){
8061                 observable.fireEvent(eventName, e);
8062             });
8063         },
8064
8065         /**
8066          * Set the opacity of the element
8067          * @param {Float} opacity The new opacity. 0 = transparent, .5 = 50% visibile, 1 = fully visible, etc
8068          * @param {Boolean/Object} animate (optional) true for the default animation or a standard Element animation config object
8069          * @return {Roo.Element} this
8070          */
8071          setOpacity : function(opacity, animate){
8072             if(!animate || !A){
8073                 var s = this.dom.style;
8074                 if(Roo.isIE){
8075                     s.zoom = 1;
8076                     s.filter = (s.filter || '').replace(/alpha\([^\)]*\)/gi,"") +
8077                                (opacity == 1 ? "" : "alpha(opacity=" + opacity * 100 + ")");
8078                 }else{
8079                     s.opacity = opacity;
8080                 }
8081             }else{
8082                 this.anim({opacity: {to: opacity}}, this.preanim(arguments, 1), null, .35, 'easeIn');
8083             }
8084             return this;
8085         },
8086
8087         /**
8088          * Gets the left X coordinate
8089          * @param {Boolean} local True to get the local css position instead of page coordinate
8090          * @return {Number}
8091          */
8092         getLeft : function(local){
8093             if(!local){
8094                 return this.getX();
8095             }else{
8096                 return parseInt(this.getStyle("left"), 10) || 0;
8097             }
8098         },
8099
8100         /**
8101          * Gets the right X coordinate of the element (element X position + element width)
8102          * @param {Boolean} local True to get the local css position instead of page coordinate
8103          * @return {Number}
8104          */
8105         getRight : function(local){
8106             if(!local){
8107                 return this.getX() + this.getWidth();
8108             }else{
8109                 return (this.getLeft(true) + this.getWidth()) || 0;
8110             }
8111         },
8112
8113         /**
8114          * Gets the top Y coordinate
8115          * @param {Boolean} local True to get the local css position instead of page coordinate
8116          * @return {Number}
8117          */
8118         getTop : function(local) {
8119             if(!local){
8120                 return this.getY();
8121             }else{
8122                 return parseInt(this.getStyle("top"), 10) || 0;
8123             }
8124         },
8125
8126         /**
8127          * Gets the bottom Y coordinate of the element (element Y position + element height)
8128          * @param {Boolean} local True to get the local css position instead of page coordinate
8129          * @return {Number}
8130          */
8131         getBottom : function(local){
8132             if(!local){
8133                 return this.getY() + this.getHeight();
8134             }else{
8135                 return (this.getTop(true) + this.getHeight()) || 0;
8136             }
8137         },
8138
8139         /**
8140         * Initializes positioning on this element. If a desired position is not passed, it will make the
8141         * the element positioned relative IF it is not already positioned.
8142         * @param {String} pos (optional) Positioning to use "relative", "absolute" or "fixed"
8143         * @param {Number} zIndex (optional) The zIndex to apply
8144         * @param {Number} x (optional) Set the page X position
8145         * @param {Number} y (optional) Set the page Y position
8146         */
8147         position : function(pos, zIndex, x, y){
8148             if(!pos){
8149                if(this.getStyle('position') == 'static'){
8150                    this.setStyle('position', 'relative');
8151                }
8152             }else{
8153                 this.setStyle("position", pos);
8154             }
8155             if(zIndex){
8156                 this.setStyle("z-index", zIndex);
8157             }
8158             if(x !== undefined && y !== undefined){
8159                 this.setXY([x, y]);
8160             }else if(x !== undefined){
8161                 this.setX(x);
8162             }else if(y !== undefined){
8163                 this.setY(y);
8164             }
8165         },
8166
8167         /**
8168         * Clear positioning back to the default when the document was loaded
8169         * @param {String} value (optional) The value to use for the left,right,top,bottom, defaults to '' (empty string). You could use 'auto'.
8170         * @return {Roo.Element} this
8171          */
8172         clearPositioning : function(value){
8173             value = value ||'';
8174             this.setStyle({
8175                 "left": value,
8176                 "right": value,
8177                 "top": value,
8178                 "bottom": value,
8179                 "z-index": "",
8180                 "position" : "static"
8181             });
8182             return this;
8183         },
8184
8185         /**
8186         * Gets an object with all CSS positioning properties. Useful along with setPostioning to get
8187         * snapshot before performing an update and then restoring the element.
8188         * @return {Object}
8189         */
8190         getPositioning : function(){
8191             var l = this.getStyle("left");
8192             var t = this.getStyle("top");
8193             return {
8194                 "position" : this.getStyle("position"),
8195                 "left" : l,
8196                 "right" : l ? "" : this.getStyle("right"),
8197                 "top" : t,
8198                 "bottom" : t ? "" : this.getStyle("bottom"),
8199                 "z-index" : this.getStyle("z-index")
8200             };
8201         },
8202
8203         /**
8204          * Gets the width of the border(s) for the specified side(s)
8205          * @param {String} side Can be t, l, r, b or any combination of those to add multiple values. For example,
8206          * passing lr would get the border (l)eft width + the border (r)ight width.
8207          * @return {Number} The width of the sides passed added together
8208          */
8209         getBorderWidth : function(side){
8210             return this.addStyles(side, El.borders);
8211         },
8212
8213         /**
8214          * Gets the width of the padding(s) for the specified side(s)
8215          * @param {String} side Can be t, l, r, b or any combination of those to add multiple values. For example,
8216          * passing lr would get the padding (l)eft + the padding (r)ight.
8217          * @return {Number} The padding of the sides passed added together
8218          */
8219         getPadding : function(side){
8220             return this.addStyles(side, El.paddings);
8221         },
8222
8223         /**
8224         * Set positioning with an object returned by getPositioning().
8225         * @param {Object} posCfg
8226         * @return {Roo.Element} this
8227          */
8228         setPositioning : function(pc){
8229             this.applyStyles(pc);
8230             if(pc.right == "auto"){
8231                 this.dom.style.right = "";
8232             }
8233             if(pc.bottom == "auto"){
8234                 this.dom.style.bottom = "";
8235             }
8236             return this;
8237         },
8238
8239         // private
8240         fixDisplay : function(){
8241             if(this.getStyle("display") == "none"){
8242                 this.setStyle("visibility", "hidden");
8243                 this.setStyle("display", this.originalDisplay); // first try reverting to default
8244                 if(this.getStyle("display") == "none"){ // if that fails, default to block
8245                     this.setStyle("display", "block");
8246                 }
8247             }
8248         },
8249
8250         /**
8251          * Quick set left and top adding default units
8252          * @param {String} left The left CSS property value
8253          * @param {String} top The top CSS property value
8254          * @return {Roo.Element} this
8255          */
8256          setLeftTop : function(left, top){
8257             this.dom.style.left = this.addUnits(left);
8258             this.dom.style.top = this.addUnits(top);
8259             return this;
8260         },
8261
8262         /**
8263          * Move this element relative to its current position.
8264          * @param {String} direction Possible values are: "l","left" - "r","right" - "t","top","up" - "b","bottom","down".
8265          * @param {Number} distance How far to move the element in pixels
8266          * @param {Boolean/Object} animate (optional) true for the default animation or a standard Element animation config object
8267          * @return {Roo.Element} this
8268          */
8269          move : function(direction, distance, animate){
8270             var xy = this.getXY();
8271             direction = direction.toLowerCase();
8272             switch(direction){
8273                 case "l":
8274                 case "left":
8275                     this.moveTo(xy[0]-distance, xy[1], this.preanim(arguments, 2));
8276                     break;
8277                case "r":
8278                case "right":
8279                     this.moveTo(xy[0]+distance, xy[1], this.preanim(arguments, 2));
8280                     break;
8281                case "t":
8282                case "top":
8283                case "up":
8284                     this.moveTo(xy[0], xy[1]-distance, this.preanim(arguments, 2));
8285                     break;
8286                case "b":
8287                case "bottom":
8288                case "down":
8289                     this.moveTo(xy[0], xy[1]+distance, this.preanim(arguments, 2));
8290                     break;
8291             }
8292             return this;
8293         },
8294
8295         /**
8296          *  Store the current overflow setting and clip overflow on the element - use {@link #unclip} to remove
8297          * @return {Roo.Element} this
8298          */
8299         clip : function(){
8300             if(!this.isClipped){
8301                this.isClipped = true;
8302                this.originalClip = {
8303                    "o": this.getStyle("overflow"),
8304                    "x": this.getStyle("overflow-x"),
8305                    "y": this.getStyle("overflow-y")
8306                };
8307                this.setStyle("overflow", "hidden");
8308                this.setStyle("overflow-x", "hidden");
8309                this.setStyle("overflow-y", "hidden");
8310             }
8311             return this;
8312         },
8313
8314         /**
8315          *  Return clipping (overflow) to original clipping before clip() was called
8316          * @return {Roo.Element} this
8317          */
8318         unclip : function(){
8319             if(this.isClipped){
8320                 this.isClipped = false;
8321                 var o = this.originalClip;
8322                 if(o.o){this.setStyle("overflow", o.o);}
8323                 if(o.x){this.setStyle("overflow-x", o.x);}
8324                 if(o.y){this.setStyle("overflow-y", o.y);}
8325             }
8326             return this;
8327         },
8328
8329
8330         /**
8331          * Gets the x,y coordinates specified by the anchor position on the element.
8332          * @param {String} anchor (optional) The specified anchor position (defaults to "c").  See {@link #alignTo} for details on supported anchor positions.
8333          * @param {Object} size (optional) An object containing the size to use for calculating anchor position
8334          *                       {width: (target width), height: (target height)} (defaults to the element's current size)
8335          * @param {Boolean} local (optional) True to get the local (element top/left-relative) anchor position instead of page coordinates
8336          * @return {Array} [x, y] An array containing the element's x and y coordinates
8337          */
8338         getAnchorXY : function(anchor, local, s){
8339             //Passing a different size is useful for pre-calculating anchors,
8340             //especially for anchored animations that change the el size.
8341
8342             var w, h, vp = false;
8343             if(!s){
8344                 var d = this.dom;
8345                 if(d == document.body || d == document){
8346                     vp = true;
8347                     w = D.getViewWidth(); h = D.getViewHeight();
8348                 }else{
8349                     w = this.getWidth(); h = this.getHeight();
8350                 }
8351             }else{
8352                 w = s.width;  h = s.height;
8353             }
8354             var x = 0, y = 0, r = Math.round;
8355             switch((anchor || "tl").toLowerCase()){
8356                 case "c":
8357                     x = r(w*.5);
8358                     y = r(h*.5);
8359                 break;
8360                 case "t":
8361                     x = r(w*.5);
8362                     y = 0;
8363                 break;
8364                 case "l":
8365                     x = 0;
8366                     y = r(h*.5);
8367                 break;
8368                 case "r":
8369                     x = w;
8370                     y = r(h*.5);
8371                 break;
8372                 case "b":
8373                     x = r(w*.5);
8374                     y = h;
8375                 break;
8376                 case "tl":
8377                     x = 0;
8378                     y = 0;
8379                 break;
8380                 case "bl":
8381                     x = 0;
8382                     y = h;
8383                 break;
8384                 case "br":
8385                     x = w;
8386                     y = h;
8387                 break;
8388                 case "tr":
8389                     x = w;
8390                     y = 0;
8391                 break;
8392             }
8393             if(local === true){
8394                 return [x, y];
8395             }
8396             if(vp){
8397                 var sc = this.getScroll();
8398                 return [x + sc.left, y + sc.top];
8399             }
8400             //Add the element's offset xy
8401             var o = this.getXY();
8402             return [x+o[0], y+o[1]];
8403         },
8404
8405         /**
8406          * Gets the x,y coordinates to align this element with another element. See {@link #alignTo} for more info on the
8407          * supported position values.
8408          * @param {String/HTMLElement/Roo.Element} element The element to align to.
8409          * @param {String} position The position to align to.
8410          * @param {Array} offsets (optional) Offset the positioning by [x, y]
8411          * @return {Array} [x, y]
8412          */
8413         getAlignToXY : function(el, p, o){
8414             el = Roo.get(el);
8415             var d = this.dom;
8416             if(!el.dom){
8417                 throw "Element.alignTo with an element that doesn't exist";
8418             }
8419             var c = false; //constrain to viewport
8420             var p1 = "", p2 = "";
8421             o = o || [0,0];
8422
8423             if(!p){
8424                 p = "tl-bl";
8425             }else if(p == "?"){
8426                 p = "tl-bl?";
8427             }else if(p.indexOf("-") == -1){
8428                 p = "tl-" + p;
8429             }
8430             p = p.toLowerCase();
8431             var m = p.match(/^([a-z]+)-([a-z]+)(\?)?$/);
8432             if(!m){
8433                throw "Element.alignTo with an invalid alignment " + p;
8434             }
8435             p1 = m[1]; p2 = m[2]; c = !!m[3];
8436
8437             //Subtract the aligned el's internal xy from the target's offset xy
8438             //plus custom offset to get the aligned el's new offset xy
8439             var a1 = this.getAnchorXY(p1, true);
8440             var a2 = el.getAnchorXY(p2, false);
8441             var x = a2[0] - a1[0] + o[0];
8442             var y = a2[1] - a1[1] + o[1];
8443             if(c){
8444                 //constrain the aligned el to viewport if necessary
8445                 var w = this.getWidth(), h = this.getHeight(), r = el.getRegion();
8446                 // 5px of margin for ie
8447                 var dw = D.getViewWidth()-5, dh = D.getViewHeight()-5;
8448
8449                 //If we are at a viewport boundary and the aligned el is anchored on a target border that is
8450                 //perpendicular to the vp border, allow the aligned el to slide on that border,
8451                 //otherwise swap the aligned el to the opposite border of the target.
8452                 var p1y = p1.charAt(0), p1x = p1.charAt(p1.length-1);
8453                var p2y = p2.charAt(0), p2x = p2.charAt(p2.length-1);
8454                var swapY = ((p1y=="t" && p2y=="b") || (p1y=="b" && p2y=="t"));
8455                var swapX = ((p1x=="r" && p2x=="l") || (p1x=="l" && p2x=="r"));
8456
8457                var doc = document;
8458                var scrollX = (doc.documentElement.scrollLeft || doc.body.scrollLeft || 0)+5;
8459                var scrollY = (doc.documentElement.scrollTop || doc.body.scrollTop || 0)+5;
8460
8461                if((x+w) > dw + scrollX){
8462                     x = swapX ? r.left-w : dw+scrollX-w;
8463                 }
8464                if(x < scrollX){
8465                    x = swapX ? r.right : scrollX;
8466                }
8467                if((y+h) > dh + scrollY){
8468                     y = swapY ? r.top-h : dh+scrollY-h;
8469                 }
8470                if (y < scrollY){
8471                    y = swapY ? r.bottom : scrollY;
8472                }
8473             }
8474             return [x,y];
8475         },
8476
8477         // private
8478         getConstrainToXY : function(){
8479             var os = {top:0, left:0, bottom:0, right: 0};
8480
8481             return function(el, local, offsets, proposedXY){
8482                 el = Roo.get(el);
8483                 offsets = offsets ? Roo.applyIf(offsets, os) : os;
8484
8485                 var vw, vh, vx = 0, vy = 0;
8486                 if(el.dom == document.body || el.dom == document){
8487                     vw = Roo.lib.Dom.getViewWidth();
8488                     vh = Roo.lib.Dom.getViewHeight();
8489                 }else{
8490                     vw = el.dom.clientWidth;
8491                     vh = el.dom.clientHeight;
8492                     if(!local){
8493                         var vxy = el.getXY();
8494                         vx = vxy[0];
8495                         vy = vxy[1];
8496                     }
8497                 }
8498
8499                 var s = el.getScroll();
8500
8501                 vx += offsets.left + s.left;
8502                 vy += offsets.top + s.top;
8503
8504                 vw -= offsets.right;
8505                 vh -= offsets.bottom;
8506
8507                 var vr = vx+vw;
8508                 var vb = vy+vh;
8509
8510                 var xy = proposedXY || (!local ? this.getXY() : [this.getLeft(true), this.getTop(true)]);
8511                 var x = xy[0], y = xy[1];
8512                 var w = this.dom.offsetWidth, h = this.dom.offsetHeight;
8513
8514                 // only move it if it needs it
8515                 var moved = false;
8516
8517                 // first validate right/bottom
8518                 if((x + w) > vr){
8519                     x = vr - w;
8520                     moved = true;
8521                 }
8522                 if((y + h) > vb){
8523                     y = vb - h;
8524                     moved = true;
8525                 }
8526                 // then make sure top/left isn't negative
8527                 if(x < vx){
8528                     x = vx;
8529                     moved = true;
8530                 }
8531                 if(y < vy){
8532                     y = vy;
8533                     moved = true;
8534                 }
8535                 return moved ? [x, y] : false;
8536             };
8537         }(),
8538
8539         // private
8540         adjustForConstraints : function(xy, parent, offsets){
8541             return this.getConstrainToXY(parent || document, false, offsets, xy) ||  xy;
8542         },
8543
8544         /**
8545          * Aligns this element with another element relative to the specified anchor points. If the other element is the
8546          * document it aligns it to the viewport.
8547          * The position parameter is optional, and can be specified in any one of the following formats:
8548          * <ul>
8549          *   <li><b>Blank</b>: Defaults to aligning the element's top-left corner to the target's bottom-left corner ("tl-bl").</li>
8550          *   <li><b>One anchor (deprecated)</b>: The passed anchor position is used as the target element's anchor point.
8551          *       The element being aligned will position its top-left corner (tl) to that point.  <i>This method has been
8552          *       deprecated in favor of the newer two anchor syntax below</i>.</li>
8553          *   <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
8554          *       element's anchor point, and the second value is used as the target's anchor point.</li>
8555          * </ul>
8556          * In addition to the anchor points, the position parameter also supports the "?" character.  If "?" is passed at the end of
8557          * the position string, the element will attempt to align as specified, but the position will be adjusted to constrain to
8558          * the viewport if necessary.  Note that the element being aligned might be swapped to align to a different position than
8559          * that specified in order to enforce the viewport constraints.
8560          * Following are all of the supported anchor positions:
8561     <pre>
8562     Value  Description
8563     -----  -----------------------------
8564     tl     The top left corner (default)
8565     t      The center of the top edge
8566     tr     The top right corner
8567     l      The center of the left edge
8568     c      In the center of the element
8569     r      The center of the right edge
8570     bl     The bottom left corner
8571     b      The center of the bottom edge
8572     br     The bottom right corner
8573     </pre>
8574     Example Usage:
8575     <pre><code>
8576     // align el to other-el using the default positioning ("tl-bl", non-constrained)
8577     el.alignTo("other-el");
8578
8579     // align the top left corner of el with the top right corner of other-el (constrained to viewport)
8580     el.alignTo("other-el", "tr?");
8581
8582     // align the bottom right corner of el with the center left edge of other-el
8583     el.alignTo("other-el", "br-l?");
8584
8585     // align the center of el with the bottom left corner of other-el and
8586     // adjust the x position by -6 pixels (and the y position by 0)
8587     el.alignTo("other-el", "c-bl", [-6, 0]);
8588     </code></pre>
8589          * @param {String/HTMLElement/Roo.Element} element The element to align to.
8590          * @param {String} position The position to align to.
8591          * @param {Array} offsets (optional) Offset the positioning by [x, y]
8592          * @param {Boolean/Object} animate (optional) true for the default animation or a standard Element animation config object
8593          * @return {Roo.Element} this
8594          */
8595         alignTo : function(element, position, offsets, animate){
8596             var xy = this.getAlignToXY(element, position, offsets);
8597             this.setXY(xy, this.preanim(arguments, 3));
8598             return this;
8599         },
8600
8601         /**
8602          * Anchors an element to another element and realigns it when the window is resized.
8603          * @param {String/HTMLElement/Roo.Element} element The element to align to.
8604          * @param {String} position The position to align to.
8605          * @param {Array} offsets (optional) Offset the positioning by [x, y]
8606          * @param {Boolean/Object} animate (optional) True for the default animation or a standard Element animation config object
8607          * @param {Boolean/Number} monitorScroll (optional) True to monitor body scroll and reposition. If this parameter
8608          * is a number, it is used as the buffer delay (defaults to 50ms).
8609          * @param {Function} callback The function to call after the animation finishes
8610          * @return {Roo.Element} this
8611          */
8612         anchorTo : function(el, alignment, offsets, animate, monitorScroll, callback){
8613             var action = function(){
8614                 this.alignTo(el, alignment, offsets, animate);
8615                 Roo.callback(callback, this);
8616             };
8617             Roo.EventManager.onWindowResize(action, this);
8618             var tm = typeof monitorScroll;
8619             if(tm != 'undefined'){
8620                 Roo.EventManager.on(window, 'scroll', action, this,
8621                     {buffer: tm == 'number' ? monitorScroll : 50});
8622             }
8623             action.call(this); // align immediately
8624             return this;
8625         },
8626         /**
8627          * Clears any opacity settings from this element. Required in some cases for IE.
8628          * @return {Roo.Element} this
8629          */
8630         clearOpacity : function(){
8631             if (window.ActiveXObject) {
8632                 if(typeof this.dom.style.filter == 'string' && (/alpha/i).test(this.dom.style.filter)){
8633                     this.dom.style.filter = "";
8634                 }
8635             } else {
8636                 this.dom.style.opacity = "";
8637                 this.dom.style["-moz-opacity"] = "";
8638                 this.dom.style["-khtml-opacity"] = "";
8639             }
8640             return this;
8641         },
8642
8643         /**
8644          * Hide this element - Uses display mode to determine whether to use "display" or "visibility". See {@link #setVisible}.
8645          * @param {Boolean/Object} animate (optional) true for the default animation or a standard Element animation config object
8646          * @return {Roo.Element} this
8647          */
8648         hide : function(animate){
8649             this.setVisible(false, this.preanim(arguments, 0));
8650             return this;
8651         },
8652
8653         /**
8654         * Show this element - Uses display mode to determine whether to use "display" or "visibility". See {@link #setVisible}.
8655         * @param {Boolean/Object} animate (optional) true for the default animation or a standard Element animation config object
8656          * @return {Roo.Element} this
8657          */
8658         show : function(animate){
8659             this.setVisible(true, this.preanim(arguments, 0));
8660             return this;
8661         },
8662
8663         /**
8664          * @private Test if size has a unit, otherwise appends the default
8665          */
8666         addUnits : function(size){
8667             return Roo.Element.addUnits(size, this.defaultUnit);
8668         },
8669
8670         /**
8671          * Temporarily enables offsets (width,height,x,y) for an element with display:none, use endMeasure() when done.
8672          * @return {Roo.Element} this
8673          */
8674         beginMeasure : function(){
8675             var el = this.dom;
8676             if(el.offsetWidth || el.offsetHeight){
8677                 return this; // offsets work already
8678             }
8679             var changed = [];
8680             var p = this.dom, b = document.body; // start with this element
8681             while((!el.offsetWidth && !el.offsetHeight) && p && p.tagName && p != b){
8682                 var pe = Roo.get(p);
8683                 if(pe.getStyle('display') == 'none'){
8684                     changed.push({el: p, visibility: pe.getStyle("visibility")});
8685                     p.style.visibility = "hidden";
8686                     p.style.display = "block";
8687                 }
8688                 p = p.parentNode;
8689             }
8690             this._measureChanged = changed;
8691             return this;
8692
8693         },
8694
8695         /**
8696          * Restores displays to before beginMeasure was called
8697          * @return {Roo.Element} this
8698          */
8699         endMeasure : function(){
8700             var changed = this._measureChanged;
8701             if(changed){
8702                 for(var i = 0, len = changed.length; i < len; i++) {
8703                     var r = changed[i];
8704                     r.el.style.visibility = r.visibility;
8705                     r.el.style.display = "none";
8706                 }
8707                 this._measureChanged = null;
8708             }
8709             return this;
8710         },
8711
8712         /**
8713         * Update the innerHTML of this element, optionally searching for and processing scripts
8714         * @param {String} html The new HTML
8715         * @param {Boolean} loadScripts (optional) true to look for and process scripts
8716         * @param {Function} callback For async script loading you can be noticed when the update completes
8717         * @return {Roo.Element} this
8718          */
8719         update : function(html, loadScripts, callback){
8720             if(typeof html == "undefined"){
8721                 html = "";
8722             }
8723             if(loadScripts !== true){
8724                 this.dom.innerHTML = html;
8725                 if(typeof callback == "function"){
8726                     callback();
8727                 }
8728                 return this;
8729             }
8730             var id = Roo.id();
8731             var dom = this.dom;
8732
8733             html += '<span id="' + id + '"></span>';
8734
8735             E.onAvailable(id, function(){
8736                 var hd = document.getElementsByTagName("head")[0];
8737                 var re = /(?:<script([^>]*)?>)((\n|\r|.)*?)(?:<\/script>)/ig;
8738                 var srcRe = /\ssrc=([\'\"])(.*?)\1/i;
8739                 var typeRe = /\stype=([\'\"])(.*?)\1/i;
8740
8741                 var match;
8742                 while(match = re.exec(html)){
8743                     var attrs = match[1];
8744                     var srcMatch = attrs ? attrs.match(srcRe) : false;
8745                     if(srcMatch && srcMatch[2]){
8746                        var s = document.createElement("script");
8747                        s.src = srcMatch[2];
8748                        var typeMatch = attrs.match(typeRe);
8749                        if(typeMatch && typeMatch[2]){
8750                            s.type = typeMatch[2];
8751                        }
8752                        hd.appendChild(s);
8753                     }else if(match[2] && match[2].length > 0){
8754                         if(window.execScript) {
8755                            window.execScript(match[2]);
8756                         } else {
8757                             /**
8758                              * eval:var:id
8759                              * eval:var:dom
8760                              * eval:var:html
8761                              * 
8762                              */
8763                            window.eval(match[2]);
8764                         }
8765                     }
8766                 }
8767                 var el = document.getElementById(id);
8768                 if(el){el.parentNode.removeChild(el);}
8769                 if(typeof callback == "function"){
8770                     callback();
8771                 }
8772             });
8773             dom.innerHTML = html.replace(/(?:<script.*?>)((\n|\r|.)*?)(?:<\/script>)/ig, "");
8774             return this;
8775         },
8776
8777         /**
8778          * Direct access to the UpdateManager update() method (takes the same parameters).
8779          * @param {String/Function} url The url for this request or a function to call to get the url
8780          * @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}
8781          * @param {Function} callback (optional) Callback when transaction is complete - called with signature (oElement, bSuccess)
8782          * @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.
8783          * @return {Roo.Element} this
8784          */
8785         load : function(){
8786             var um = this.getUpdateManager();
8787             um.update.apply(um, arguments);
8788             return this;
8789         },
8790
8791         /**
8792         * Gets this element's UpdateManager
8793         * @return {Roo.UpdateManager} The UpdateManager
8794         */
8795         getUpdateManager : function(){
8796             if(!this.updateManager){
8797                 this.updateManager = new Roo.UpdateManager(this);
8798             }
8799             return this.updateManager;
8800         },
8801
8802         /**
8803          * Disables text selection for this element (normalized across browsers)
8804          * @return {Roo.Element} this
8805          */
8806         unselectable : function(){
8807             this.dom.unselectable = "on";
8808             this.swallowEvent("selectstart", true);
8809             this.applyStyles("-moz-user-select:none;-khtml-user-select:none;");
8810             this.addClass("x-unselectable");
8811             return this;
8812         },
8813
8814         /**
8815         * Calculates the x, y to center this element on the screen
8816         * @return {Array} The x, y values [x, y]
8817         */
8818         getCenterXY : function(){
8819             return this.getAlignToXY(document, 'c-c');
8820         },
8821
8822         /**
8823         * Centers the Element in either the viewport, or another Element.
8824         * @param {String/HTMLElement/Roo.Element} centerIn (optional) The element in which to center the element.
8825         */
8826         center : function(centerIn){
8827             this.alignTo(centerIn || document, 'c-c');
8828             return this;
8829         },
8830
8831         /**
8832          * Tests various css rules/browsers to determine if this element uses a border box
8833          * @return {Boolean}
8834          */
8835         isBorderBox : function(){
8836             return noBoxAdjust[this.dom.tagName.toLowerCase()] || Roo.isBorderBox;
8837         },
8838
8839         /**
8840          * Return a box {x, y, width, height} that can be used to set another elements
8841          * size/location to match this element.
8842          * @param {Boolean} contentBox (optional) If true a box for the content of the element is returned.
8843          * @param {Boolean} local (optional) If true the element's left and top are returned instead of page x/y.
8844          * @return {Object} box An object in the format {x, y, width, height}
8845          */
8846         getBox : function(contentBox, local){
8847             var xy;
8848             if(!local){
8849                 xy = this.getXY();
8850             }else{
8851                 var left = parseInt(this.getStyle("left"), 10) || 0;
8852                 var top = parseInt(this.getStyle("top"), 10) || 0;
8853                 xy = [left, top];
8854             }
8855             var el = this.dom, w = el.offsetWidth, h = el.offsetHeight, bx;
8856             if(!contentBox){
8857                 bx = {x: xy[0], y: xy[1], 0: xy[0], 1: xy[1], width: w, height: h};
8858             }else{
8859                 var l = this.getBorderWidth("l")+this.getPadding("l");
8860                 var r = this.getBorderWidth("r")+this.getPadding("r");
8861                 var t = this.getBorderWidth("t")+this.getPadding("t");
8862                 var b = this.getBorderWidth("b")+this.getPadding("b");
8863                 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)};
8864             }
8865             bx.right = bx.x + bx.width;
8866             bx.bottom = bx.y + bx.height;
8867             return bx;
8868         },
8869
8870         /**
8871          * Returns the sum width of the padding and borders for the passed "sides". See getBorderWidth()
8872          for more information about the sides.
8873          * @param {String} sides
8874          * @return {Number}
8875          */
8876         getFrameWidth : function(sides, onlyContentBox){
8877             return onlyContentBox && Roo.isBorderBox ? 0 : (this.getPadding(sides) + this.getBorderWidth(sides));
8878         },
8879
8880         /**
8881          * 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.
8882          * @param {Object} box The box to fill {x, y, width, height}
8883          * @param {Boolean} adjust (optional) Whether to adjust for box-model issues automatically
8884          * @param {Boolean/Object} animate (optional) true for the default animation or a standard Element animation config object
8885          * @return {Roo.Element} this
8886          */
8887         setBox : function(box, adjust, animate){
8888             var w = box.width, h = box.height;
8889             if((adjust && !this.autoBoxAdjust) && !this.isBorderBox()){
8890                w -= (this.getBorderWidth("lr") + this.getPadding("lr"));
8891                h -= (this.getBorderWidth("tb") + this.getPadding("tb"));
8892             }
8893             this.setBounds(box.x, box.y, w, h, this.preanim(arguments, 2));
8894             return this;
8895         },
8896
8897         /**
8898          * Forces the browser to repaint this element
8899          * @return {Roo.Element} this
8900          */
8901          repaint : function(){
8902             var dom = this.dom;
8903             this.addClass("x-repaint");
8904             setTimeout(function(){
8905                 Roo.get(dom).removeClass("x-repaint");
8906             }, 1);
8907             return this;
8908         },
8909
8910         /**
8911          * Returns an object with properties top, left, right and bottom representing the margins of this element unless sides is passed,
8912          * then it returns the calculated width of the sides (see getPadding)
8913          * @param {String} sides (optional) Any combination of l, r, t, b to get the sum of those sides
8914          * @return {Object/Number}
8915          */
8916         getMargins : function(side){
8917             if(!side){
8918                 return {
8919                     top: parseInt(this.getStyle("margin-top"), 10) || 0,
8920                     left: parseInt(this.getStyle("margin-left"), 10) || 0,
8921                     bottom: parseInt(this.getStyle("margin-bottom"), 10) || 0,
8922                     right: parseInt(this.getStyle("margin-right"), 10) || 0
8923                 };
8924             }else{
8925                 return this.addStyles(side, El.margins);
8926              }
8927         },
8928
8929         // private
8930         addStyles : function(sides, styles){
8931             var val = 0, v, w;
8932             for(var i = 0, len = sides.length; i < len; i++){
8933                 v = this.getStyle(styles[sides.charAt(i)]);
8934                 if(v){
8935                      w = parseInt(v, 10);
8936                      if(w){ val += w; }
8937                 }
8938             }
8939             return val;
8940         },
8941
8942         /**
8943          * Creates a proxy element of this element
8944          * @param {String/Object} config The class name of the proxy element or a DomHelper config object
8945          * @param {String/HTMLElement} renderTo (optional) The element or element id to render the proxy to (defaults to document.body)
8946          * @param {Boolean} matchBox (optional) True to align and size the proxy to this element now (defaults to false)
8947          * @return {Roo.Element} The new proxy element
8948          */
8949         createProxy : function(config, renderTo, matchBox){
8950             if(renderTo){
8951                 renderTo = Roo.getDom(renderTo);
8952             }else{
8953                 renderTo = document.body;
8954             }
8955             config = typeof config == "object" ?
8956                 config : {tag : "div", cls: config};
8957             var proxy = Roo.DomHelper.append(renderTo, config, true);
8958             if(matchBox){
8959                proxy.setBox(this.getBox());
8960             }
8961             return proxy;
8962         },
8963
8964         /**
8965          * Puts a mask over this element to disable user interaction. Requires core.css.
8966          * This method can only be applied to elements which accept child nodes.
8967          * @param {String} msg (optional) A message to display in the mask
8968          * @param {String} msgCls (optional) A css class to apply to the msg element
8969          * @return {Element} The mask  element
8970          */
8971         mask : function(msg, msgCls)
8972         {
8973             if(this.getStyle("position") == "static" && this.dom.tagName !== 'BODY'){
8974                 this.setStyle("position", "relative");
8975             }
8976             if(!this._mask){
8977                 this._mask = Roo.DomHelper.append(this.dom, {cls:"roo-el-mask"}, true);
8978             }
8979             this.addClass("x-masked");
8980             this._mask.setDisplayed(true);
8981             
8982             // we wander
8983             var z = 0;
8984             var dom = this.dom
8985             while (dom && dom.style) {
8986                 if (!isNaN(parseInt(dom.style.zIndex))) {
8987                     z = Math.max(z, parseInt(dom.style.zIndex));
8988                 }
8989                 dom = dom.parentNode;
8990             }
8991             // if we are masking the body - then it hides everything..
8992             if (this.dom == document.body) {
8993                 z = 1000000;
8994                 this._mask.setWidth(Roo.lib.Dom.getDocumentWidth());
8995                 this._mask.setHeight(Roo.lib.Dom.getDocumentHeight());
8996             }
8997            
8998             if(typeof msg == 'string'){
8999                 if(!this._maskMsg){
9000                     this._maskMsg = Roo.DomHelper.append(this.dom, {cls:"roo-el-mask-msg", cn:{tag:'div'}}, true);
9001                 }
9002                 var mm = this._maskMsg;
9003                 mm.dom.className = msgCls ? "roo-el-mask-msg " + msgCls : "roo-el-mask-msg";
9004                 mm.dom.firstChild.innerHTML = msg;
9005                 mm.setDisplayed(true);
9006                 mm.center(this);
9007                 mm.setStyle('z-index', z + 102);
9008             }
9009             if(Roo.isIE && !(Roo.isIE7 && Roo.isStrict) && this.getStyle('height') == 'auto'){ // ie will not expand full height automatically
9010                 this._mask.setHeight(this.getHeight());
9011             }
9012             this._mask.setStyle('z-index', z + 100);
9013             
9014             return this._mask;
9015         },
9016
9017         /**
9018          * Removes a previously applied mask. If removeEl is true the mask overlay is destroyed, otherwise
9019          * it is cached for reuse.
9020          */
9021         unmask : function(removeEl){
9022             if(this._mask){
9023                 if(removeEl === true){
9024                     this._mask.remove();
9025                     delete this._mask;
9026                     if(this._maskMsg){
9027                         this._maskMsg.remove();
9028                         delete this._maskMsg;
9029                     }
9030                 }else{
9031                     this._mask.setDisplayed(false);
9032                     if(this._maskMsg){
9033                         this._maskMsg.setDisplayed(false);
9034                     }
9035                 }
9036             }
9037             this.removeClass("x-masked");
9038         },
9039
9040         /**
9041          * Returns true if this element is masked
9042          * @return {Boolean}
9043          */
9044         isMasked : function(){
9045             return this._mask && this._mask.isVisible();
9046         },
9047
9048         /**
9049          * Creates an iframe shim for this element to keep selects and other windowed objects from
9050          * showing through.
9051          * @return {Roo.Element} The new shim element
9052          */
9053         createShim : function(){
9054             var el = document.createElement('iframe');
9055             el.frameBorder = 'no';
9056             el.className = 'roo-shim';
9057             if(Roo.isIE && Roo.isSecure){
9058                 el.src = Roo.SSL_SECURE_URL;
9059             }
9060             var shim = Roo.get(this.dom.parentNode.insertBefore(el, this.dom));
9061             shim.autoBoxAdjust = false;
9062             return shim;
9063         },
9064
9065         /**
9066          * Removes this element from the DOM and deletes it from the cache
9067          */
9068         remove : function(){
9069             if(this.dom.parentNode){
9070                 this.dom.parentNode.removeChild(this.dom);
9071             }
9072             delete El.cache[this.dom.id];
9073         },
9074
9075         /**
9076          * Sets up event handlers to add and remove a css class when the mouse is over this element
9077          * @param {String} className
9078          * @param {Boolean} preventFlicker (optional) If set to true, it prevents flickering by filtering
9079          * mouseout events for children elements
9080          * @return {Roo.Element} this
9081          */
9082         addClassOnOver : function(className, preventFlicker){
9083             this.on("mouseover", function(){
9084                 Roo.fly(this, '_internal').addClass(className);
9085             }, this.dom);
9086             var removeFn = function(e){
9087                 if(preventFlicker !== true || !e.within(this, true)){
9088                     Roo.fly(this, '_internal').removeClass(className);
9089                 }
9090             };
9091             this.on("mouseout", removeFn, this.dom);
9092             return this;
9093         },
9094
9095         /**
9096          * Sets up event handlers to add and remove a css class when this element has the focus
9097          * @param {String} className
9098          * @return {Roo.Element} this
9099          */
9100         addClassOnFocus : function(className){
9101             this.on("focus", function(){
9102                 Roo.fly(this, '_internal').addClass(className);
9103             }, this.dom);
9104             this.on("blur", function(){
9105                 Roo.fly(this, '_internal').removeClass(className);
9106             }, this.dom);
9107             return this;
9108         },
9109         /**
9110          * 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)
9111          * @param {String} className
9112          * @return {Roo.Element} this
9113          */
9114         addClassOnClick : function(className){
9115             var dom = this.dom;
9116             this.on("mousedown", function(){
9117                 Roo.fly(dom, '_internal').addClass(className);
9118                 var d = Roo.get(document);
9119                 var fn = function(){
9120                     Roo.fly(dom, '_internal').removeClass(className);
9121                     d.removeListener("mouseup", fn);
9122                 };
9123                 d.on("mouseup", fn);
9124             });
9125             return this;
9126         },
9127
9128         /**
9129          * Stops the specified event from bubbling and optionally prevents the default action
9130          * @param {String} eventName
9131          * @param {Boolean} preventDefault (optional) true to prevent the default action too
9132          * @return {Roo.Element} this
9133          */
9134         swallowEvent : function(eventName, preventDefault){
9135             var fn = function(e){
9136                 e.stopPropagation();
9137                 if(preventDefault){
9138                     e.preventDefault();
9139                 }
9140             };
9141             if(eventName instanceof Array){
9142                 for(var i = 0, len = eventName.length; i < len; i++){
9143                      this.on(eventName[i], fn);
9144                 }
9145                 return this;
9146             }
9147             this.on(eventName, fn);
9148             return this;
9149         },
9150
9151         /**
9152          * @private
9153          */
9154       fitToParentDelegate : Roo.emptyFn, // keep a reference to the fitToParent delegate
9155
9156         /**
9157          * Sizes this element to its parent element's dimensions performing
9158          * neccessary box adjustments.
9159          * @param {Boolean} monitorResize (optional) If true maintains the fit when the browser window is resized.
9160          * @param {String/HTMLElment/Element} targetParent (optional) The target parent, default to the parentNode.
9161          * @return {Roo.Element} this
9162          */
9163         fitToParent : function(monitorResize, targetParent) {
9164           Roo.EventManager.removeResizeListener(this.fitToParentDelegate); // always remove previous fitToParent delegate from onWindowResize
9165           this.fitToParentDelegate = Roo.emptyFn; // remove reference to previous delegate
9166           if (monitorResize === true && !this.dom.parentNode) { // check if this Element still exists
9167             return;
9168           }
9169           var p = Roo.get(targetParent || this.dom.parentNode);
9170           this.setSize(p.getComputedWidth() - p.getFrameWidth('lr'), p.getComputedHeight() - p.getFrameWidth('tb'));
9171           if (monitorResize === true) {
9172             this.fitToParentDelegate = this.fitToParent.createDelegate(this, [true, targetParent]);
9173             Roo.EventManager.onWindowResize(this.fitToParentDelegate);
9174           }
9175           return this;
9176         },
9177
9178         /**
9179          * Gets the next sibling, skipping text nodes
9180          * @return {HTMLElement} The next sibling or null
9181          */
9182         getNextSibling : function(){
9183             var n = this.dom.nextSibling;
9184             while(n && n.nodeType != 1){
9185                 n = n.nextSibling;
9186             }
9187             return n;
9188         },
9189
9190         /**
9191          * Gets the previous sibling, skipping text nodes
9192          * @return {HTMLElement} The previous sibling or null
9193          */
9194         getPrevSibling : function(){
9195             var n = this.dom.previousSibling;
9196             while(n && n.nodeType != 1){
9197                 n = n.previousSibling;
9198             }
9199             return n;
9200         },
9201
9202
9203         /**
9204          * Appends the passed element(s) to this element
9205          * @param {String/HTMLElement/Array/Element/CompositeElement} el
9206          * @return {Roo.Element} this
9207          */
9208         appendChild: function(el){
9209             el = Roo.get(el);
9210             el.appendTo(this);
9211             return this;
9212         },
9213
9214         /**
9215          * Creates the passed DomHelper config and appends it to this element or optionally inserts it before the passed child element.
9216          * @param {Object} config DomHelper element config object.  If no tag is specified (e.g., {tag:'input'}) then a div will be
9217          * automatically generated with the specified attributes.
9218          * @param {HTMLElement} insertBefore (optional) a child element of this element
9219          * @param {Boolean} returnDom (optional) true to return the dom node instead of creating an Element
9220          * @return {Roo.Element} The new child element
9221          */
9222         createChild: function(config, insertBefore, returnDom){
9223             config = config || {tag:'div'};
9224             if(insertBefore){
9225                 return Roo.DomHelper.insertBefore(insertBefore, config, returnDom !== true);
9226             }
9227             return Roo.DomHelper[!this.dom.firstChild ? 'overwrite' : 'append'](this.dom, config,  returnDom !== true);
9228         },
9229
9230         /**
9231          * Appends this element to the passed element
9232          * @param {String/HTMLElement/Element} el The new parent element
9233          * @return {Roo.Element} this
9234          */
9235         appendTo: function(el){
9236             el = Roo.getDom(el);
9237             el.appendChild(this.dom);
9238             return this;
9239         },
9240
9241         /**
9242          * Inserts this element before the passed element in the DOM
9243          * @param {String/HTMLElement/Element} el The element to insert before
9244          * @return {Roo.Element} this
9245          */
9246         insertBefore: function(el){
9247             el = Roo.getDom(el);
9248             el.parentNode.insertBefore(this.dom, el);
9249             return this;
9250         },
9251
9252         /**
9253          * Inserts this element after the passed element in the DOM
9254          * @param {String/HTMLElement/Element} el The element to insert after
9255          * @return {Roo.Element} this
9256          */
9257         insertAfter: function(el){
9258             el = Roo.getDom(el);
9259             el.parentNode.insertBefore(this.dom, el.nextSibling);
9260             return this;
9261         },
9262
9263         /**
9264          * Inserts (or creates) an element (or DomHelper config) as the first child of the this element
9265          * @param {String/HTMLElement/Element/Object} el The id or element to insert or a DomHelper config to create and insert
9266          * @return {Roo.Element} The new child
9267          */
9268         insertFirst: function(el, returnDom){
9269             el = el || {};
9270             if(typeof el == 'object' && !el.nodeType){ // dh config
9271                 return this.createChild(el, this.dom.firstChild, returnDom);
9272             }else{
9273                 el = Roo.getDom(el);
9274                 this.dom.insertBefore(el, this.dom.firstChild);
9275                 return !returnDom ? Roo.get(el) : el;
9276             }
9277         },
9278
9279         /**
9280          * Inserts (or creates) the passed element (or DomHelper config) as a sibling of this element
9281          * @param {String/HTMLElement/Element/Object} el The id or element to insert or a DomHelper config to create and insert
9282          * @param {String} where (optional) 'before' or 'after' defaults to before
9283          * @param {Boolean} returnDom (optional) True to return the raw DOM element instead of Roo.Element
9284          * @return {Roo.Element} the inserted Element
9285          */
9286         insertSibling: function(el, where, returnDom){
9287             where = where ? where.toLowerCase() : 'before';
9288             el = el || {};
9289             var rt, refNode = where == 'before' ? this.dom : this.dom.nextSibling;
9290
9291             if(typeof el == 'object' && !el.nodeType){ // dh config
9292                 if(where == 'after' && !this.dom.nextSibling){
9293                     rt = Roo.DomHelper.append(this.dom.parentNode, el, !returnDom);
9294                 }else{
9295                     rt = Roo.DomHelper[where == 'after' ? 'insertAfter' : 'insertBefore'](this.dom, el, !returnDom);
9296                 }
9297
9298             }else{
9299                 rt = this.dom.parentNode.insertBefore(Roo.getDom(el),
9300                             where == 'before' ? this.dom : this.dom.nextSibling);
9301                 if(!returnDom){
9302                     rt = Roo.get(rt);
9303                 }
9304             }
9305             return rt;
9306         },
9307
9308         /**
9309          * Creates and wraps this element with another element
9310          * @param {Object} config (optional) DomHelper element config object for the wrapper element or null for an empty div
9311          * @param {Boolean} returnDom (optional) True to return the raw DOM element instead of Roo.Element
9312          * @return {HTMLElement/Element} The newly created wrapper element
9313          */
9314         wrap: function(config, returnDom){
9315             if(!config){
9316                 config = {tag: "div"};
9317             }
9318             var newEl = Roo.DomHelper.insertBefore(this.dom, config, !returnDom);
9319             newEl.dom ? newEl.dom.appendChild(this.dom) : newEl.appendChild(this.dom);
9320             return newEl;
9321         },
9322
9323         /**
9324          * Replaces the passed element with this element
9325          * @param {String/HTMLElement/Element} el The element to replace
9326          * @return {Roo.Element} this
9327          */
9328         replace: function(el){
9329             el = Roo.get(el);
9330             this.insertBefore(el);
9331             el.remove();
9332             return this;
9333         },
9334
9335         /**
9336          * Inserts an html fragment into this element
9337          * @param {String} where Where to insert the html in relation to the this element - beforeBegin, afterBegin, beforeEnd, afterEnd.
9338          * @param {String} html The HTML fragment
9339          * @param {Boolean} returnEl True to return an Roo.Element
9340          * @return {HTMLElement/Roo.Element} The inserted node (or nearest related if more than 1 inserted)
9341          */
9342         insertHtml : function(where, html, returnEl){
9343             var el = Roo.DomHelper.insertHtml(where, this.dom, html);
9344             return returnEl ? Roo.get(el) : el;
9345         },
9346
9347         /**
9348          * Sets the passed attributes as attributes of this element (a style attribute can be a string, object or function)
9349          * @param {Object} o The object with the attributes
9350          * @param {Boolean} useSet (optional) false to override the default setAttribute to use expandos.
9351          * @return {Roo.Element} this
9352          */
9353         set : function(o, useSet){
9354             var el = this.dom;
9355             useSet = typeof useSet == 'undefined' ? (el.setAttribute ? true : false) : useSet;
9356             for(var attr in o){
9357                 if(attr == "style" || typeof o[attr] == "function") continue;
9358                 if(attr=="cls"){
9359                     el.className = o["cls"];
9360                 }else{
9361                     if(useSet) el.setAttribute(attr, o[attr]);
9362                     else el[attr] = o[attr];
9363                 }
9364             }
9365             if(o.style){
9366                 Roo.DomHelper.applyStyles(el, o.style);
9367             }
9368             return this;
9369         },
9370
9371         /**
9372          * Convenience method for constructing a KeyMap
9373          * @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:
9374          *                                  {key: (number or array), shift: (true/false), ctrl: (true/false), alt: (true/false)}
9375          * @param {Function} fn The function to call
9376          * @param {Object} scope (optional) The scope of the function
9377          * @return {Roo.KeyMap} The KeyMap created
9378          */
9379         addKeyListener : function(key, fn, scope){
9380             var config;
9381             if(typeof key != "object" || key instanceof Array){
9382                 config = {
9383                     key: key,
9384                     fn: fn,
9385                     scope: scope
9386                 };
9387             }else{
9388                 config = {
9389                     key : key.key,
9390                     shift : key.shift,
9391                     ctrl : key.ctrl,
9392                     alt : key.alt,
9393                     fn: fn,
9394                     scope: scope
9395                 };
9396             }
9397             return new Roo.KeyMap(this, config);
9398         },
9399
9400         /**
9401          * Creates a KeyMap for this element
9402          * @param {Object} config The KeyMap config. See {@link Roo.KeyMap} for more details
9403          * @return {Roo.KeyMap} The KeyMap created
9404          */
9405         addKeyMap : function(config){
9406             return new Roo.KeyMap(this, config);
9407         },
9408
9409         /**
9410          * Returns true if this element is scrollable.
9411          * @return {Boolean}
9412          */
9413          isScrollable : function(){
9414             var dom = this.dom;
9415             return dom.scrollHeight > dom.clientHeight || dom.scrollWidth > dom.clientWidth;
9416         },
9417
9418         /**
9419          * 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().
9420          * @param {String} side Either "left" for scrollLeft values or "top" for scrollTop values.
9421          * @param {Number} value The new scroll value
9422          * @param {Boolean/Object} animate (optional) true for the default animation or a standard Element animation config object
9423          * @return {Element} this
9424          */
9425
9426         scrollTo : function(side, value, animate){
9427             var prop = side.toLowerCase() == "left" ? "scrollLeft" : "scrollTop";
9428             if(!animate || !A){
9429                 this.dom[prop] = value;
9430             }else{
9431                 var to = prop == "scrollLeft" ? [value, this.dom.scrollTop] : [this.dom.scrollLeft, value];
9432                 this.anim({scroll: {"to": to}}, this.preanim(arguments, 2), 'scroll');
9433             }
9434             return this;
9435         },
9436
9437         /**
9438          * Scrolls this element the specified direction. Does bounds checking to make sure the scroll is
9439          * within this element's scrollable range.
9440          * @param {String} direction Possible values are: "l","left" - "r","right" - "t","top","up" - "b","bottom","down".
9441          * @param {Number} distance How far to scroll the element in pixels
9442          * @param {Boolean/Object} animate (optional) true for the default animation or a standard Element animation config object
9443          * @return {Boolean} Returns true if a scroll was triggered or false if the element
9444          * was scrolled as far as it could go.
9445          */
9446          scroll : function(direction, distance, animate){
9447              if(!this.isScrollable()){
9448                  return;
9449              }
9450              var el = this.dom;
9451              var l = el.scrollLeft, t = el.scrollTop;
9452              var w = el.scrollWidth, h = el.scrollHeight;
9453              var cw = el.clientWidth, ch = el.clientHeight;
9454              direction = direction.toLowerCase();
9455              var scrolled = false;
9456              var a = this.preanim(arguments, 2);
9457              switch(direction){
9458                  case "l":
9459                  case "left":
9460                      if(w - l > cw){
9461                          var v = Math.min(l + distance, w-cw);
9462                          this.scrollTo("left", v, a);
9463                          scrolled = true;
9464                      }
9465                      break;
9466                 case "r":
9467                 case "right":
9468                      if(l > 0){
9469                          var v = Math.max(l - distance, 0);
9470                          this.scrollTo("left", v, a);
9471                          scrolled = true;
9472                      }
9473                      break;
9474                 case "t":
9475                 case "top":
9476                 case "up":
9477                      if(t > 0){
9478                          var v = Math.max(t - distance, 0);
9479                          this.scrollTo("top", v, a);
9480                          scrolled = true;
9481                      }
9482                      break;
9483                 case "b":
9484                 case "bottom":
9485                 case "down":
9486                      if(h - t > ch){
9487                          var v = Math.min(t + distance, h-ch);
9488                          this.scrollTo("top", v, a);
9489                          scrolled = true;
9490                      }
9491                      break;
9492              }
9493              return scrolled;
9494         },
9495
9496         /**
9497          * Translates the passed page coordinates into left/top css values for this element
9498          * @param {Number/Array} x The page x or an array containing [x, y]
9499          * @param {Number} y The page y
9500          * @return {Object} An object with left and top properties. e.g. {left: (value), top: (value)}
9501          */
9502         translatePoints : function(x, y){
9503             if(typeof x == 'object' || x instanceof Array){
9504                 y = x[1]; x = x[0];
9505             }
9506             var p = this.getStyle('position');
9507             var o = this.getXY();
9508
9509             var l = parseInt(this.getStyle('left'), 10);
9510             var t = parseInt(this.getStyle('top'), 10);
9511
9512             if(isNaN(l)){
9513                 l = (p == "relative") ? 0 : this.dom.offsetLeft;
9514             }
9515             if(isNaN(t)){
9516                 t = (p == "relative") ? 0 : this.dom.offsetTop;
9517             }
9518
9519             return {left: (x - o[0] + l), top: (y - o[1] + t)};
9520         },
9521
9522         /**
9523          * Returns the current scroll position of the element.
9524          * @return {Object} An object containing the scroll position in the format {left: (scrollLeft), top: (scrollTop)}
9525          */
9526         getScroll : function(){
9527             var d = this.dom, doc = document;
9528             if(d == doc || d == doc.body){
9529                 var l = window.pageXOffset || doc.documentElement.scrollLeft || doc.body.scrollLeft || 0;
9530                 var t = window.pageYOffset || doc.documentElement.scrollTop || doc.body.scrollTop || 0;
9531                 return {left: l, top: t};
9532             }else{
9533                 return {left: d.scrollLeft, top: d.scrollTop};
9534             }
9535         },
9536
9537         /**
9538          * Return the CSS color for the specified CSS attribute. rgb, 3 digit (like #fff) and valid values
9539          * are convert to standard 6 digit hex color.
9540          * @param {String} attr The css attribute
9541          * @param {String} defaultValue The default value to use when a valid color isn't found
9542          * @param {String} prefix (optional) defaults to #. Use an empty string when working with
9543          * YUI color anims.
9544          */
9545         getColor : function(attr, defaultValue, prefix){
9546             var v = this.getStyle(attr);
9547             if(!v || v == "transparent" || v == "inherit") {
9548                 return defaultValue;
9549             }
9550             var color = typeof prefix == "undefined" ? "#" : prefix;
9551             if(v.substr(0, 4) == "rgb("){
9552                 var rvs = v.slice(4, v.length -1).split(",");
9553                 for(var i = 0; i < 3; i++){
9554                     var h = parseInt(rvs[i]).toString(16);
9555                     if(h < 16){
9556                         h = "0" + h;
9557                     }
9558                     color += h;
9559                 }
9560             } else {
9561                 if(v.substr(0, 1) == "#"){
9562                     if(v.length == 4) {
9563                         for(var i = 1; i < 4; i++){
9564                             var c = v.charAt(i);
9565                             color +=  c + c;
9566                         }
9567                     }else if(v.length == 7){
9568                         color += v.substr(1);
9569                     }
9570                 }
9571             }
9572             return(color.length > 5 ? color.toLowerCase() : defaultValue);
9573         },
9574
9575         /**
9576          * Wraps the specified element with a special markup/CSS block that renders by default as a gray container with a
9577          * gradient background, rounded corners and a 4-way shadow.
9578          * @param {String} class (optional) A base CSS class to apply to the containing wrapper element (defaults to 'x-box').
9579          * Note that there are a number of CSS rules that are dependent on this name to make the overall effect work,
9580          * so if you supply an alternate base class, make sure you also supply all of the necessary rules.
9581          * @return {Roo.Element} this
9582          */
9583         boxWrap : function(cls){
9584             cls = cls || 'x-box';
9585             var el = Roo.get(this.insertHtml('beforeBegin', String.format('<div class="{0}">'+El.boxMarkup+'</div>', cls)));
9586             el.child('.'+cls+'-mc').dom.appendChild(this.dom);
9587             return el;
9588         },
9589
9590         /**
9591          * Returns the value of a namespaced attribute from the element's underlying DOM node.
9592          * @param {String} namespace The namespace in which to look for the attribute
9593          * @param {String} name The attribute name
9594          * @return {String} The attribute value
9595          */
9596         getAttributeNS : Roo.isIE ? function(ns, name){
9597             var d = this.dom;
9598             var type = typeof d[ns+":"+name];
9599             if(type != 'undefined' && type != 'unknown'){
9600                 return d[ns+":"+name];
9601             }
9602             return d[name];
9603         } : function(ns, name){
9604             var d = this.dom;
9605             return d.getAttributeNS(ns, name) || d.getAttribute(ns+":"+name) || d.getAttribute(name) || d[name];
9606         },
9607         
9608         
9609         /**
9610          * Sets or Returns the value the dom attribute value
9611          * @param {String|Object} name The attribute name (or object to set multiple attributes)
9612          * @param {String} value (optional) The value to set the attribute to
9613          * @return {String} The attribute value
9614          */
9615         attr : function(name){
9616             if (arguments.length > 1) {
9617                 this.dom.setAttribute(name, arguments[1]);
9618                 return arguments[1];
9619             }
9620             if (typeof(name) == 'object') {
9621                 for(var i in name) {
9622                     this.attr(i, name[i]);
9623                 }
9624                 return name;
9625             }
9626             
9627             
9628             if (!this.dom.hasAttribute(name)) {
9629                 return undefined;
9630             }
9631             return this.dom.getAttribute(name);
9632         }
9633         
9634         
9635         
9636     };
9637
9638     var ep = El.prototype;
9639
9640     /**
9641      * Appends an event handler (Shorthand for addListener)
9642      * @param {String}   eventName     The type of event to append
9643      * @param {Function} fn        The method the event invokes
9644      * @param {Object} scope       (optional) The scope (this object) of the fn
9645      * @param {Object}   options   (optional)An object with standard {@link Roo.EventManager#addListener} options
9646      * @method
9647      */
9648     ep.on = ep.addListener;
9649         // backwards compat
9650     ep.mon = ep.addListener;
9651
9652     /**
9653      * Removes an event handler from this element (shorthand for removeListener)
9654      * @param {String} eventName the type of event to remove
9655      * @param {Function} fn the method the event invokes
9656      * @return {Roo.Element} this
9657      * @method
9658      */
9659     ep.un = ep.removeListener;
9660
9661     /**
9662      * true to automatically adjust width and height settings for box-model issues (default to true)
9663      */
9664     ep.autoBoxAdjust = true;
9665
9666     // private
9667     El.unitPattern = /\d+(px|em|%|en|ex|pt|in|cm|mm|pc)$/i;
9668
9669     // private
9670     El.addUnits = function(v, defaultUnit){
9671         if(v === "" || v == "auto"){
9672             return v;
9673         }
9674         if(v === undefined){
9675             return '';
9676         }
9677         if(typeof v == "number" || !El.unitPattern.test(v)){
9678             return v + (defaultUnit || 'px');
9679         }
9680         return v;
9681     };
9682
9683     // special markup used throughout Roo when box wrapping elements
9684     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>';
9685     /**
9686      * Visibility mode constant - Use visibility to hide element
9687      * @static
9688      * @type Number
9689      */
9690     El.VISIBILITY = 1;
9691     /**
9692      * Visibility mode constant - Use display to hide element
9693      * @static
9694      * @type Number
9695      */
9696     El.DISPLAY = 2;
9697
9698     El.borders = {l: "border-left-width", r: "border-right-width", t: "border-top-width", b: "border-bottom-width"};
9699     El.paddings = {l: "padding-left", r: "padding-right", t: "padding-top", b: "padding-bottom"};
9700     El.margins = {l: "margin-left", r: "margin-right", t: "margin-top", b: "margin-bottom"};
9701
9702
9703
9704     /**
9705      * @private
9706      */
9707     El.cache = {};
9708
9709     var docEl;
9710
9711     /**
9712      * Static method to retrieve Element objects. Uses simple caching to consistently return the same object.
9713      * Automatically fixes if an object was recreated with the same id via AJAX or DOM.
9714      * @param {String/HTMLElement/Element} el The id of the node, a DOM Node or an existing Element.
9715      * @return {Element} The Element object
9716      * @static
9717      */
9718     El.get = function(el){
9719         var ex, elm, id;
9720         if(!el){ return null; }
9721         if(typeof el == "string"){ // element id
9722             if(!(elm = document.getElementById(el))){
9723                 return null;
9724             }
9725             if(ex = El.cache[el]){
9726                 ex.dom = elm;
9727             }else{
9728                 ex = El.cache[el] = new El(elm);
9729             }
9730             return ex;
9731         }else if(el.tagName){ // dom element
9732             if(!(id = el.id)){
9733                 id = Roo.id(el);
9734             }
9735             if(ex = El.cache[id]){
9736                 ex.dom = el;
9737             }else{
9738                 ex = El.cache[id] = new El(el);
9739             }
9740             return ex;
9741         }else if(el instanceof El){
9742             if(el != docEl){
9743                 el.dom = document.getElementById(el.id) || el.dom; // refresh dom element in case no longer valid,
9744                                                               // catch case where it hasn't been appended
9745                 El.cache[el.id] = el; // in case it was created directly with Element(), let's cache it
9746             }
9747             return el;
9748         }else if(el.isComposite){
9749             return el;
9750         }else if(el instanceof Array){
9751             return El.select(el);
9752         }else if(el == document){
9753             // create a bogus element object representing the document object
9754             if(!docEl){
9755                 var f = function(){};
9756                 f.prototype = El.prototype;
9757                 docEl = new f();
9758                 docEl.dom = document;
9759             }
9760             return docEl;
9761         }
9762         return null;
9763     };
9764
9765     // private
9766     El.uncache = function(el){
9767         for(var i = 0, a = arguments, len = a.length; i < len; i++) {
9768             if(a[i]){
9769                 delete El.cache[a[i].id || a[i]];
9770             }
9771         }
9772     };
9773
9774     // private
9775     // Garbage collection - uncache elements/purge listeners on orphaned elements
9776     // so we don't hold a reference and cause the browser to retain them
9777     El.garbageCollect = function(){
9778         if(!Roo.enableGarbageCollector){
9779             clearInterval(El.collectorThread);
9780             return;
9781         }
9782         for(var eid in El.cache){
9783             var el = El.cache[eid], d = el.dom;
9784             // -------------------------------------------------------
9785             // Determining what is garbage:
9786             // -------------------------------------------------------
9787             // !d
9788             // dom node is null, definitely garbage
9789             // -------------------------------------------------------
9790             // !d.parentNode
9791             // no parentNode == direct orphan, definitely garbage
9792             // -------------------------------------------------------
9793             // !d.offsetParent && !document.getElementById(eid)
9794             // display none elements have no offsetParent so we will
9795             // also try to look it up by it's id. However, check
9796             // offsetParent first so we don't do unneeded lookups.
9797             // This enables collection of elements that are not orphans
9798             // directly, but somewhere up the line they have an orphan
9799             // parent.
9800             // -------------------------------------------------------
9801             if(!d || !d.parentNode || (!d.offsetParent && !document.getElementById(eid))){
9802                 delete El.cache[eid];
9803                 if(d && Roo.enableListenerCollection){
9804                     E.purgeElement(d);
9805                 }
9806             }
9807         }
9808     }
9809     El.collectorThreadId = setInterval(El.garbageCollect, 30000);
9810
9811
9812     // dom is optional
9813     El.Flyweight = function(dom){
9814         this.dom = dom;
9815     };
9816     El.Flyweight.prototype = El.prototype;
9817
9818     El._flyweights = {};
9819     /**
9820      * Gets the globally shared flyweight Element, with the passed node as the active element. Do not store a reference to this element -
9821      * the dom node can be overwritten by other code.
9822      * @param {String/HTMLElement} el The dom node or id
9823      * @param {String} named (optional) Allows for creation of named reusable flyweights to
9824      *                                  prevent conflicts (e.g. internally Roo uses "_internal")
9825      * @static
9826      * @return {Element} The shared Element object
9827      */
9828     El.fly = function(el, named){
9829         named = named || '_global';
9830         el = Roo.getDom(el);
9831         if(!el){
9832             return null;
9833         }
9834         if(!El._flyweights[named]){
9835             El._flyweights[named] = new El.Flyweight();
9836         }
9837         El._flyweights[named].dom = el;
9838         return El._flyweights[named];
9839     };
9840
9841     /**
9842      * Static method to retrieve Element objects. Uses simple caching to consistently return the same object.
9843      * Automatically fixes if an object was recreated with the same id via AJAX or DOM.
9844      * Shorthand of {@link Roo.Element#get}
9845      * @param {String/HTMLElement/Element} el The id of the node, a DOM Node or an existing Element.
9846      * @return {Element} The Element object
9847      * @member Roo
9848      * @method get
9849      */
9850     Roo.get = El.get;
9851     /**
9852      * Gets the globally shared flyweight Element, with the passed node as the active element. Do not store a reference to this element -
9853      * the dom node can be overwritten by other code.
9854      * Shorthand of {@link Roo.Element#fly}
9855      * @param {String/HTMLElement} el The dom node or id
9856      * @param {String} named (optional) Allows for creation of named reusable flyweights to
9857      *                                  prevent conflicts (e.g. internally Roo uses "_internal")
9858      * @static
9859      * @return {Element} The shared Element object
9860      * @member Roo
9861      * @method fly
9862      */
9863     Roo.fly = El.fly;
9864
9865     // speedy lookup for elements never to box adjust
9866     var noBoxAdjust = Roo.isStrict ? {
9867         select:1
9868     } : {
9869         input:1, select:1, textarea:1
9870     };
9871     if(Roo.isIE || Roo.isGecko){
9872         noBoxAdjust['button'] = 1;
9873     }
9874
9875
9876     Roo.EventManager.on(window, 'unload', function(){
9877         delete El.cache;
9878         delete El._flyweights;
9879     });
9880 })();
9881
9882
9883
9884
9885 if(Roo.DomQuery){
9886     Roo.Element.selectorFunction = Roo.DomQuery.select;
9887 }
9888
9889 Roo.Element.select = function(selector, unique, root){
9890     var els;
9891     if(typeof selector == "string"){
9892         els = Roo.Element.selectorFunction(selector, root);
9893     }else if(selector.length !== undefined){
9894         els = selector;
9895     }else{
9896         throw "Invalid selector";
9897     }
9898     if(unique === true){
9899         return new Roo.CompositeElement(els);
9900     }else{
9901         return new Roo.CompositeElementLite(els);
9902     }
9903 };
9904 /**
9905  * Selects elements based on the passed CSS selector to enable working on them as 1.
9906  * @param {String/Array} selector The CSS selector or an array of elements
9907  * @param {Boolean} unique (optional) true to create a unique Roo.Element for each element (defaults to a shared flyweight object)
9908  * @param {HTMLElement/String} root (optional) The root element of the query or id of the root
9909  * @return {CompositeElementLite/CompositeElement}
9910  * @member Roo
9911  * @method select
9912  */
9913 Roo.select = Roo.Element.select;
9914
9915
9916
9917
9918
9919
9920
9921
9922
9923
9924
9925
9926
9927
9928 /*
9929  * Based on:
9930  * Ext JS Library 1.1.1
9931  * Copyright(c) 2006-2007, Ext JS, LLC.
9932  *
9933  * Originally Released Under LGPL - original licence link has changed is not relivant.
9934  *
9935  * Fork - LGPL
9936  * <script type="text/javascript">
9937  */
9938
9939
9940
9941 //Notifies Element that fx methods are available
9942 Roo.enableFx = true;
9943
9944 /**
9945  * @class Roo.Fx
9946  * <p>A class to provide basic animation and visual effects support.  <b>Note:</b> This class is automatically applied
9947  * to the {@link Roo.Element} interface when included, so all effects calls should be performed via Element.
9948  * Conversely, since the effects are not actually defined in Element, Roo.Fx <b>must</b> be included in order for the 
9949  * Element effects to work.</p><br/>
9950  *
9951  * <p>It is important to note that although the Fx methods and many non-Fx Element methods support "method chaining" in that
9952  * they return the Element object itself as the method return value, it is not always possible to mix the two in a single
9953  * method chain.  The Fx methods use an internal effects queue so that each effect can be properly timed and sequenced.
9954  * Non-Fx methods, on the other hand, have no such internal queueing and will always execute immediately.  For this reason,
9955  * while it may be possible to mix certain Fx and non-Fx method calls in a single chain, it may not always provide the
9956  * expected results and should be done with care.</p><br/>
9957  *
9958  * <p>Motion effects support 8-way anchoring, meaning that you can choose one of 8 different anchor points on the Element
9959  * that will serve as either the start or end point of the animation.  Following are all of the supported anchor positions:</p>
9960 <pre>
9961 Value  Description
9962 -----  -----------------------------
9963 tl     The top left corner
9964 t      The center of the top edge
9965 tr     The top right corner
9966 l      The center of the left edge
9967 r      The center of the right edge
9968 bl     The bottom left corner
9969 b      The center of the bottom edge
9970 br     The bottom right corner
9971 </pre>
9972  * <b>Although some Fx methods accept specific custom config parameters, the ones shown in the Config Options section
9973  * below are common options that can be passed to any Fx method.</b>
9974  * @cfg {Function} callback A function called when the effect is finished
9975  * @cfg {Object} scope The scope of the effect function
9976  * @cfg {String} easing A valid Easing value for the effect
9977  * @cfg {String} afterCls A css class to apply after the effect
9978  * @cfg {Number} duration The length of time (in seconds) that the effect should last
9979  * @cfg {Boolean} remove Whether the Element should be removed from the DOM and destroyed after the effect finishes
9980  * @cfg {Boolean} useDisplay Whether to use the <i>display</i> CSS property instead of <i>visibility</i> when hiding Elements (only applies to 
9981  * effects that end with the element being visually hidden, ignored otherwise)
9982  * @cfg {String/Object/Function} afterStyle A style specification string, e.g. "width:100px", or an object in the form {width:"100px"}, or
9983  * a function which returns such a specification that will be applied to the Element after the effect finishes
9984  * @cfg {Boolean} block Whether the effect should block other effects from queueing while it runs
9985  * @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
9986  * @cfg {Boolean} stopFx Whether subsequent effects should be stopped and removed after the current effect finishes
9987  */
9988 Roo.Fx = {
9989         /**
9990          * Slides the element into view.  An anchor point can be optionally passed to set the point of
9991          * origin for the slide effect.  This function automatically handles wrapping the element with
9992          * a fixed-size container if needed.  See the Fx class overview for valid anchor point options.
9993          * Usage:
9994          *<pre><code>
9995 // default: slide the element in from the top
9996 el.slideIn();
9997
9998 // custom: slide the element in from the right with a 2-second duration
9999 el.slideIn('r', { duration: 2 });
10000
10001 // common config options shown with default values
10002 el.slideIn('t', {
10003     easing: 'easeOut',
10004     duration: .5
10005 });
10006 </code></pre>
10007          * @param {String} anchor (optional) One of the valid Fx anchor positions (defaults to top: 't')
10008          * @param {Object} options (optional) Object literal with any of the Fx config options
10009          * @return {Roo.Element} The Element
10010          */
10011     slideIn : function(anchor, o){
10012         var el = this.getFxEl();
10013         o = o || {};
10014
10015         el.queueFx(o, function(){
10016
10017             anchor = anchor || "t";
10018
10019             // fix display to visibility
10020             this.fixDisplay();
10021
10022             // restore values after effect
10023             var r = this.getFxRestore();
10024             var b = this.getBox();
10025             // fixed size for slide
10026             this.setSize(b);
10027
10028             // wrap if needed
10029             var wrap = this.fxWrap(r.pos, o, "hidden");
10030
10031             var st = this.dom.style;
10032             st.visibility = "visible";
10033             st.position = "absolute";
10034
10035             // clear out temp styles after slide and unwrap
10036             var after = function(){
10037                 el.fxUnwrap(wrap, r.pos, o);
10038                 st.width = r.width;
10039                 st.height = r.height;
10040                 el.afterFx(o);
10041             };
10042             // time to calc the positions
10043             var a, pt = {to: [b.x, b.y]}, bw = {to: b.width}, bh = {to: b.height};
10044
10045             switch(anchor.toLowerCase()){
10046                 case "t":
10047                     wrap.setSize(b.width, 0);
10048                     st.left = st.bottom = "0";
10049                     a = {height: bh};
10050                 break;
10051                 case "l":
10052                     wrap.setSize(0, b.height);
10053                     st.right = st.top = "0";
10054                     a = {width: bw};
10055                 break;
10056                 case "r":
10057                     wrap.setSize(0, b.height);
10058                     wrap.setX(b.right);
10059                     st.left = st.top = "0";
10060                     a = {width: bw, points: pt};
10061                 break;
10062                 case "b":
10063                     wrap.setSize(b.width, 0);
10064                     wrap.setY(b.bottom);
10065                     st.left = st.top = "0";
10066                     a = {height: bh, points: pt};
10067                 break;
10068                 case "tl":
10069                     wrap.setSize(0, 0);
10070                     st.right = st.bottom = "0";
10071                     a = {width: bw, height: bh};
10072                 break;
10073                 case "bl":
10074                     wrap.setSize(0, 0);
10075                     wrap.setY(b.y+b.height);
10076                     st.right = st.top = "0";
10077                     a = {width: bw, height: bh, points: pt};
10078                 break;
10079                 case "br":
10080                     wrap.setSize(0, 0);
10081                     wrap.setXY([b.right, b.bottom]);
10082                     st.left = st.top = "0";
10083                     a = {width: bw, height: bh, points: pt};
10084                 break;
10085                 case "tr":
10086                     wrap.setSize(0, 0);
10087                     wrap.setX(b.x+b.width);
10088                     st.left = st.bottom = "0";
10089                     a = {width: bw, height: bh, points: pt};
10090                 break;
10091             }
10092             this.dom.style.visibility = "visible";
10093             wrap.show();
10094
10095             arguments.callee.anim = wrap.fxanim(a,
10096                 o,
10097                 'motion',
10098                 .5,
10099                 'easeOut', after);
10100         });
10101         return this;
10102     },
10103     
10104         /**
10105          * Slides the element out of view.  An anchor point can be optionally passed to set the end point
10106          * for the slide effect.  When the effect is completed, the element will be hidden (visibility = 
10107          * 'hidden') but block elements will still take up space in the document.  The element must be removed
10108          * from the DOM using the 'remove' config option if desired.  This function automatically handles 
10109          * wrapping the element with a fixed-size container if needed.  See the Fx class overview for valid anchor point options.
10110          * Usage:
10111          *<pre><code>
10112 // default: slide the element out to the top
10113 el.slideOut();
10114
10115 // custom: slide the element out to the right with a 2-second duration
10116 el.slideOut('r', { duration: 2 });
10117
10118 // common config options shown with default values
10119 el.slideOut('t', {
10120     easing: 'easeOut',
10121     duration: .5,
10122     remove: false,
10123     useDisplay: false
10124 });
10125 </code></pre>
10126          * @param {String} anchor (optional) One of the valid Fx anchor positions (defaults to top: 't')
10127          * @param {Object} options (optional) Object literal with any of the Fx config options
10128          * @return {Roo.Element} The Element
10129          */
10130     slideOut : function(anchor, o){
10131         var el = this.getFxEl();
10132         o = o || {};
10133
10134         el.queueFx(o, function(){
10135
10136             anchor = anchor || "t";
10137
10138             // restore values after effect
10139             var r = this.getFxRestore();
10140             
10141             var b = this.getBox();
10142             // fixed size for slide
10143             this.setSize(b);
10144
10145             // wrap if needed
10146             var wrap = this.fxWrap(r.pos, o, "visible");
10147
10148             var st = this.dom.style;
10149             st.visibility = "visible";
10150             st.position = "absolute";
10151
10152             wrap.setSize(b);
10153
10154             var after = function(){
10155                 if(o.useDisplay){
10156                     el.setDisplayed(false);
10157                 }else{
10158                     el.hide();
10159                 }
10160
10161                 el.fxUnwrap(wrap, r.pos, o);
10162
10163                 st.width = r.width;
10164                 st.height = r.height;
10165
10166                 el.afterFx(o);
10167             };
10168
10169             var a, zero = {to: 0};
10170             switch(anchor.toLowerCase()){
10171                 case "t":
10172                     st.left = st.bottom = "0";
10173                     a = {height: zero};
10174                 break;
10175                 case "l":
10176                     st.right = st.top = "0";
10177                     a = {width: zero};
10178                 break;
10179                 case "r":
10180                     st.left = st.top = "0";
10181                     a = {width: zero, points: {to:[b.right, b.y]}};
10182                 break;
10183                 case "b":
10184                     st.left = st.top = "0";
10185                     a = {height: zero, points: {to:[b.x, b.bottom]}};
10186                 break;
10187                 case "tl":
10188                     st.right = st.bottom = "0";
10189                     a = {width: zero, height: zero};
10190                 break;
10191                 case "bl":
10192                     st.right = st.top = "0";
10193                     a = {width: zero, height: zero, points: {to:[b.x, b.bottom]}};
10194                 break;
10195                 case "br":
10196                     st.left = st.top = "0";
10197                     a = {width: zero, height: zero, points: {to:[b.x+b.width, b.bottom]}};
10198                 break;
10199                 case "tr":
10200                     st.left = st.bottom = "0";
10201                     a = {width: zero, height: zero, points: {to:[b.right, b.y]}};
10202                 break;
10203             }
10204
10205             arguments.callee.anim = wrap.fxanim(a,
10206                 o,
10207                 'motion',
10208                 .5,
10209                 "easeOut", after);
10210         });
10211         return this;
10212     },
10213
10214         /**
10215          * Fades the element out while slowly expanding it in all directions.  When the effect is completed, the 
10216          * element will be hidden (visibility = 'hidden') but block elements will still take up space in the document. 
10217          * The element must be removed from the DOM using the 'remove' config option if desired.
10218          * Usage:
10219          *<pre><code>
10220 // default
10221 el.puff();
10222
10223 // common config options shown with default values
10224 el.puff({
10225     easing: 'easeOut',
10226     duration: .5,
10227     remove: false,
10228     useDisplay: false
10229 });
10230 </code></pre>
10231          * @param {Object} options (optional) Object literal with any of the Fx config options
10232          * @return {Roo.Element} The Element
10233          */
10234     puff : function(o){
10235         var el = this.getFxEl();
10236         o = o || {};
10237
10238         el.queueFx(o, function(){
10239             this.clearOpacity();
10240             this.show();
10241
10242             // restore values after effect
10243             var r = this.getFxRestore();
10244             var st = this.dom.style;
10245
10246             var after = function(){
10247                 if(o.useDisplay){
10248                     el.setDisplayed(false);
10249                 }else{
10250                     el.hide();
10251                 }
10252
10253                 el.clearOpacity();
10254
10255                 el.setPositioning(r.pos);
10256                 st.width = r.width;
10257                 st.height = r.height;
10258                 st.fontSize = '';
10259                 el.afterFx(o);
10260             };
10261
10262             var width = this.getWidth();
10263             var height = this.getHeight();
10264
10265             arguments.callee.anim = this.fxanim({
10266                     width : {to: this.adjustWidth(width * 2)},
10267                     height : {to: this.adjustHeight(height * 2)},
10268                     points : {by: [-(width * .5), -(height * .5)]},
10269                     opacity : {to: 0},
10270                     fontSize: {to:200, unit: "%"}
10271                 },
10272                 o,
10273                 'motion',
10274                 .5,
10275                 "easeOut", after);
10276         });
10277         return this;
10278     },
10279
10280         /**
10281          * Blinks the element as if it was clicked and then collapses on its center (similar to switching off a television).
10282          * When the effect is completed, the element will be hidden (visibility = 'hidden') but block elements will still 
10283          * take up space in the document. The element must be removed from the DOM using the 'remove' config option if desired.
10284          * Usage:
10285          *<pre><code>
10286 // default
10287 el.switchOff();
10288
10289 // all config options shown with default values
10290 el.switchOff({
10291     easing: 'easeIn',
10292     duration: .3,
10293     remove: false,
10294     useDisplay: false
10295 });
10296 </code></pre>
10297          * @param {Object} options (optional) Object literal with any of the Fx config options
10298          * @return {Roo.Element} The Element
10299          */
10300     switchOff : function(o){
10301         var el = this.getFxEl();
10302         o = o || {};
10303
10304         el.queueFx(o, function(){
10305             this.clearOpacity();
10306             this.clip();
10307
10308             // restore values after effect
10309             var r = this.getFxRestore();
10310             var st = this.dom.style;
10311
10312             var after = function(){
10313                 if(o.useDisplay){
10314                     el.setDisplayed(false);
10315                 }else{
10316                     el.hide();
10317                 }
10318
10319                 el.clearOpacity();
10320                 el.setPositioning(r.pos);
10321                 st.width = r.width;
10322                 st.height = r.height;
10323
10324                 el.afterFx(o);
10325             };
10326
10327             this.fxanim({opacity:{to:0.3}}, null, null, .1, null, function(){
10328                 this.clearOpacity();
10329                 (function(){
10330                     this.fxanim({
10331                         height:{to:1},
10332                         points:{by:[0, this.getHeight() * .5]}
10333                     }, o, 'motion', 0.3, 'easeIn', after);
10334                 }).defer(100, this);
10335             });
10336         });
10337         return this;
10338     },
10339
10340     /**
10341      * Highlights the Element by setting a color (applies to the background-color by default, but can be
10342      * changed using the "attr" config option) and then fading back to the original color. If no original
10343      * color is available, you should provide the "endColor" config option which will be cleared after the animation.
10344      * Usage:
10345 <pre><code>
10346 // default: highlight background to yellow
10347 el.highlight();
10348
10349 // custom: highlight foreground text to blue for 2 seconds
10350 el.highlight("0000ff", { attr: 'color', duration: 2 });
10351
10352 // common config options shown with default values
10353 el.highlight("ffff9c", {
10354     attr: "background-color", //can be any valid CSS property (attribute) that supports a color value
10355     endColor: (current color) or "ffffff",
10356     easing: 'easeIn',
10357     duration: 1
10358 });
10359 </code></pre>
10360      * @param {String} color (optional) The highlight color. Should be a 6 char hex color without the leading # (defaults to yellow: 'ffff9c')
10361      * @param {Object} options (optional) Object literal with any of the Fx config options
10362      * @return {Roo.Element} The Element
10363      */ 
10364     highlight : function(color, o){
10365         var el = this.getFxEl();
10366         o = o || {};
10367
10368         el.queueFx(o, function(){
10369             color = color || "ffff9c";
10370             attr = o.attr || "backgroundColor";
10371
10372             this.clearOpacity();
10373             this.show();
10374
10375             var origColor = this.getColor(attr);
10376             var restoreColor = this.dom.style[attr];
10377             endColor = (o.endColor || origColor) || "ffffff";
10378
10379             var after = function(){
10380                 el.dom.style[attr] = restoreColor;
10381                 el.afterFx(o);
10382             };
10383
10384             var a = {};
10385             a[attr] = {from: color, to: endColor};
10386             arguments.callee.anim = this.fxanim(a,
10387                 o,
10388                 'color',
10389                 1,
10390                 'easeIn', after);
10391         });
10392         return this;
10393     },
10394
10395    /**
10396     * Shows a ripple of exploding, attenuating borders to draw attention to an Element.
10397     * Usage:
10398 <pre><code>
10399 // default: a single light blue ripple
10400 el.frame();
10401
10402 // custom: 3 red ripples lasting 3 seconds total
10403 el.frame("ff0000", 3, { duration: 3 });
10404
10405 // common config options shown with default values
10406 el.frame("C3DAF9", 1, {
10407     duration: 1 //duration of entire animation (not each individual ripple)
10408     // Note: Easing is not configurable and will be ignored if included
10409 });
10410 </code></pre>
10411     * @param {String} color (optional) The color of the border.  Should be a 6 char hex color without the leading # (defaults to light blue: 'C3DAF9').
10412     * @param {Number} count (optional) The number of ripples to display (defaults to 1)
10413     * @param {Object} options (optional) Object literal with any of the Fx config options
10414     * @return {Roo.Element} The Element
10415     */
10416     frame : function(color, count, o){
10417         var el = this.getFxEl();
10418         o = o || {};
10419
10420         el.queueFx(o, function(){
10421             color = color || "#C3DAF9";
10422             if(color.length == 6){
10423                 color = "#" + color;
10424             }
10425             count = count || 1;
10426             duration = o.duration || 1;
10427             this.show();
10428
10429             var b = this.getBox();
10430             var animFn = function(){
10431                 var proxy = this.createProxy({
10432
10433                      style:{
10434                         visbility:"hidden",
10435                         position:"absolute",
10436                         "z-index":"35000", // yee haw
10437                         border:"0px solid " + color
10438                      }
10439                   });
10440                 var scale = Roo.isBorderBox ? 2 : 1;
10441                 proxy.animate({
10442                     top:{from:b.y, to:b.y - 20},
10443                     left:{from:b.x, to:b.x - 20},
10444                     borderWidth:{from:0, to:10},
10445                     opacity:{from:1, to:0},
10446                     height:{from:b.height, to:(b.height + (20*scale))},
10447                     width:{from:b.width, to:(b.width + (20*scale))}
10448                 }, duration, function(){
10449                     proxy.remove();
10450                 });
10451                 if(--count > 0){
10452                      animFn.defer((duration/2)*1000, this);
10453                 }else{
10454                     el.afterFx(o);
10455                 }
10456             };
10457             animFn.call(this);
10458         });
10459         return this;
10460     },
10461
10462    /**
10463     * Creates a pause before any subsequent queued effects begin.  If there are
10464     * no effects queued after the pause it will have no effect.
10465     * Usage:
10466 <pre><code>
10467 el.pause(1);
10468 </code></pre>
10469     * @param {Number} seconds The length of time to pause (in seconds)
10470     * @return {Roo.Element} The Element
10471     */
10472     pause : function(seconds){
10473         var el = this.getFxEl();
10474         var o = {};
10475
10476         el.queueFx(o, function(){
10477             setTimeout(function(){
10478                 el.afterFx(o);
10479             }, seconds * 1000);
10480         });
10481         return this;
10482     },
10483
10484    /**
10485     * Fade an element in (from transparent to opaque).  The ending opacity can be specified
10486     * using the "endOpacity" config option.
10487     * Usage:
10488 <pre><code>
10489 // default: fade in from opacity 0 to 100%
10490 el.fadeIn();
10491
10492 // custom: fade in from opacity 0 to 75% over 2 seconds
10493 el.fadeIn({ endOpacity: .75, duration: 2});
10494
10495 // common config options shown with default values
10496 el.fadeIn({
10497     endOpacity: 1, //can be any value between 0 and 1 (e.g. .5)
10498     easing: 'easeOut',
10499     duration: .5
10500 });
10501 </code></pre>
10502     * @param {Object} options (optional) Object literal with any of the Fx config options
10503     * @return {Roo.Element} The Element
10504     */
10505     fadeIn : function(o){
10506         var el = this.getFxEl();
10507         o = o || {};
10508         el.queueFx(o, function(){
10509             this.setOpacity(0);
10510             this.fixDisplay();
10511             this.dom.style.visibility = 'visible';
10512             var to = o.endOpacity || 1;
10513             arguments.callee.anim = this.fxanim({opacity:{to:to}},
10514                 o, null, .5, "easeOut", function(){
10515                 if(to == 1){
10516                     this.clearOpacity();
10517                 }
10518                 el.afterFx(o);
10519             });
10520         });
10521         return this;
10522     },
10523
10524    /**
10525     * Fade an element out (from opaque to transparent).  The ending opacity can be specified
10526     * using the "endOpacity" config option.
10527     * Usage:
10528 <pre><code>
10529 // default: fade out from the element's current opacity to 0
10530 el.fadeOut();
10531
10532 // custom: fade out from the element's current opacity to 25% over 2 seconds
10533 el.fadeOut({ endOpacity: .25, duration: 2});
10534
10535 // common config options shown with default values
10536 el.fadeOut({
10537     endOpacity: 0, //can be any value between 0 and 1 (e.g. .5)
10538     easing: 'easeOut',
10539     duration: .5
10540     remove: false,
10541     useDisplay: false
10542 });
10543 </code></pre>
10544     * @param {Object} options (optional) Object literal with any of the Fx config options
10545     * @return {Roo.Element} The Element
10546     */
10547     fadeOut : function(o){
10548         var el = this.getFxEl();
10549         o = o || {};
10550         el.queueFx(o, function(){
10551             arguments.callee.anim = this.fxanim({opacity:{to:o.endOpacity || 0}},
10552                 o, null, .5, "easeOut", function(){
10553                 if(this.visibilityMode == Roo.Element.DISPLAY || o.useDisplay){
10554                      this.dom.style.display = "none";
10555                 }else{
10556                      this.dom.style.visibility = "hidden";
10557                 }
10558                 this.clearOpacity();
10559                 el.afterFx(o);
10560             });
10561         });
10562         return this;
10563     },
10564
10565    /**
10566     * Animates the transition of an element's dimensions from a starting height/width
10567     * to an ending height/width.
10568     * Usage:
10569 <pre><code>
10570 // change height and width to 100x100 pixels
10571 el.scale(100, 100);
10572
10573 // common config options shown with default values.  The height and width will default to
10574 // the element's existing values if passed as null.
10575 el.scale(
10576     [element's width],
10577     [element's height], {
10578     easing: 'easeOut',
10579     duration: .35
10580 });
10581 </code></pre>
10582     * @param {Number} width  The new width (pass undefined to keep the original width)
10583     * @param {Number} height  The new height (pass undefined to keep the original height)
10584     * @param {Object} options (optional) Object literal with any of the Fx config options
10585     * @return {Roo.Element} The Element
10586     */
10587     scale : function(w, h, o){
10588         this.shift(Roo.apply({}, o, {
10589             width: w,
10590             height: h
10591         }));
10592         return this;
10593     },
10594
10595    /**
10596     * Animates the transition of any combination of an element's dimensions, xy position and/or opacity.
10597     * Any of these properties not specified in the config object will not be changed.  This effect 
10598     * requires that at least one new dimension, position or opacity setting must be passed in on
10599     * the config object in order for the function to have any effect.
10600     * Usage:
10601 <pre><code>
10602 // slide the element horizontally to x position 200 while changing the height and opacity
10603 el.shift({ x: 200, height: 50, opacity: .8 });
10604
10605 // common config options shown with default values.
10606 el.shift({
10607     width: [element's width],
10608     height: [element's height],
10609     x: [element's x position],
10610     y: [element's y position],
10611     opacity: [element's opacity],
10612     easing: 'easeOut',
10613     duration: .35
10614 });
10615 </code></pre>
10616     * @param {Object} options  Object literal with any of the Fx config options
10617     * @return {Roo.Element} The Element
10618     */
10619     shift : function(o){
10620         var el = this.getFxEl();
10621         o = o || {};
10622         el.queueFx(o, function(){
10623             var a = {}, w = o.width, h = o.height, x = o.x, y = o.y,  op = o.opacity;
10624             if(w !== undefined){
10625                 a.width = {to: this.adjustWidth(w)};
10626             }
10627             if(h !== undefined){
10628                 a.height = {to: this.adjustHeight(h)};
10629             }
10630             if(x !== undefined || y !== undefined){
10631                 a.points = {to: [
10632                     x !== undefined ? x : this.getX(),
10633                     y !== undefined ? y : this.getY()
10634                 ]};
10635             }
10636             if(op !== undefined){
10637                 a.opacity = {to: op};
10638             }
10639             if(o.xy !== undefined){
10640                 a.points = {to: o.xy};
10641             }
10642             arguments.callee.anim = this.fxanim(a,
10643                 o, 'motion', .35, "easeOut", function(){
10644                 el.afterFx(o);
10645             });
10646         });
10647         return this;
10648     },
10649
10650         /**
10651          * Slides the element while fading it out of view.  An anchor point can be optionally passed to set the 
10652          * ending point of the effect.
10653          * Usage:
10654          *<pre><code>
10655 // default: slide the element downward while fading out
10656 el.ghost();
10657
10658 // custom: slide the element out to the right with a 2-second duration
10659 el.ghost('r', { duration: 2 });
10660
10661 // common config options shown with default values
10662 el.ghost('b', {
10663     easing: 'easeOut',
10664     duration: .5
10665     remove: false,
10666     useDisplay: false
10667 });
10668 </code></pre>
10669          * @param {String} anchor (optional) One of the valid Fx anchor positions (defaults to bottom: 'b')
10670          * @param {Object} options (optional) Object literal with any of the Fx config options
10671          * @return {Roo.Element} The Element
10672          */
10673     ghost : function(anchor, o){
10674         var el = this.getFxEl();
10675         o = o || {};
10676
10677         el.queueFx(o, function(){
10678             anchor = anchor || "b";
10679
10680             // restore values after effect
10681             var r = this.getFxRestore();
10682             var w = this.getWidth(),
10683                 h = this.getHeight();
10684
10685             var st = this.dom.style;
10686
10687             var after = function(){
10688                 if(o.useDisplay){
10689                     el.setDisplayed(false);
10690                 }else{
10691                     el.hide();
10692                 }
10693
10694                 el.clearOpacity();
10695                 el.setPositioning(r.pos);
10696                 st.width = r.width;
10697                 st.height = r.height;
10698
10699                 el.afterFx(o);
10700             };
10701
10702             var a = {opacity: {to: 0}, points: {}}, pt = a.points;
10703             switch(anchor.toLowerCase()){
10704                 case "t":
10705                     pt.by = [0, -h];
10706                 break;
10707                 case "l":
10708                     pt.by = [-w, 0];
10709                 break;
10710                 case "r":
10711                     pt.by = [w, 0];
10712                 break;
10713                 case "b":
10714                     pt.by = [0, h];
10715                 break;
10716                 case "tl":
10717                     pt.by = [-w, -h];
10718                 break;
10719                 case "bl":
10720                     pt.by = [-w, h];
10721                 break;
10722                 case "br":
10723                     pt.by = [w, h];
10724                 break;
10725                 case "tr":
10726                     pt.by = [w, -h];
10727                 break;
10728             }
10729
10730             arguments.callee.anim = this.fxanim(a,
10731                 o,
10732                 'motion',
10733                 .5,
10734                 "easeOut", after);
10735         });
10736         return this;
10737     },
10738
10739         /**
10740          * Ensures that all effects queued after syncFx is called on the element are
10741          * run concurrently.  This is the opposite of {@link #sequenceFx}.
10742          * @return {Roo.Element} The Element
10743          */
10744     syncFx : function(){
10745         this.fxDefaults = Roo.apply(this.fxDefaults || {}, {
10746             block : false,
10747             concurrent : true,
10748             stopFx : false
10749         });
10750         return this;
10751     },
10752
10753         /**
10754          * Ensures that all effects queued after sequenceFx is called on the element are
10755          * run in sequence.  This is the opposite of {@link #syncFx}.
10756          * @return {Roo.Element} The Element
10757          */
10758     sequenceFx : function(){
10759         this.fxDefaults = Roo.apply(this.fxDefaults || {}, {
10760             block : false,
10761             concurrent : false,
10762             stopFx : false
10763         });
10764         return this;
10765     },
10766
10767         /* @private */
10768     nextFx : function(){
10769         var ef = this.fxQueue[0];
10770         if(ef){
10771             ef.call(this);
10772         }
10773     },
10774
10775         /**
10776          * Returns true if the element has any effects actively running or queued, else returns false.
10777          * @return {Boolean} True if element has active effects, else false
10778          */
10779     hasActiveFx : function(){
10780         return this.fxQueue && this.fxQueue[0];
10781     },
10782
10783         /**
10784          * Stops any running effects and clears the element's internal effects queue if it contains
10785          * any additional effects that haven't started yet.
10786          * @return {Roo.Element} The Element
10787          */
10788     stopFx : function(){
10789         if(this.hasActiveFx()){
10790             var cur = this.fxQueue[0];
10791             if(cur && cur.anim && cur.anim.isAnimated()){
10792                 this.fxQueue = [cur]; // clear out others
10793                 cur.anim.stop(true);
10794             }
10795         }
10796         return this;
10797     },
10798
10799         /* @private */
10800     beforeFx : function(o){
10801         if(this.hasActiveFx() && !o.concurrent){
10802            if(o.stopFx){
10803                this.stopFx();
10804                return true;
10805            }
10806            return false;
10807         }
10808         return true;
10809     },
10810
10811         /**
10812          * Returns true if the element is currently blocking so that no other effect can be queued
10813          * until this effect is finished, else returns false if blocking is not set.  This is commonly
10814          * used to ensure that an effect initiated by a user action runs to completion prior to the
10815          * same effect being restarted (e.g., firing only one effect even if the user clicks several times).
10816          * @return {Boolean} True if blocking, else false
10817          */
10818     hasFxBlock : function(){
10819         var q = this.fxQueue;
10820         return q && q[0] && q[0].block;
10821     },
10822
10823         /* @private */
10824     queueFx : function(o, fn){
10825         if(!this.fxQueue){
10826             this.fxQueue = [];
10827         }
10828         if(!this.hasFxBlock()){
10829             Roo.applyIf(o, this.fxDefaults);
10830             if(!o.concurrent){
10831                 var run = this.beforeFx(o);
10832                 fn.block = o.block;
10833                 this.fxQueue.push(fn);
10834                 if(run){
10835                     this.nextFx();
10836                 }
10837             }else{
10838                 fn.call(this);
10839             }
10840         }
10841         return this;
10842     },
10843
10844         /* @private */
10845     fxWrap : function(pos, o, vis){
10846         var wrap;
10847         if(!o.wrap || !(wrap = Roo.get(o.wrap))){
10848             var wrapXY;
10849             if(o.fixPosition){
10850                 wrapXY = this.getXY();
10851             }
10852             var div = document.createElement("div");
10853             div.style.visibility = vis;
10854             wrap = Roo.get(this.dom.parentNode.insertBefore(div, this.dom));
10855             wrap.setPositioning(pos);
10856             if(wrap.getStyle("position") == "static"){
10857                 wrap.position("relative");
10858             }
10859             this.clearPositioning('auto');
10860             wrap.clip();
10861             wrap.dom.appendChild(this.dom);
10862             if(wrapXY){
10863                 wrap.setXY(wrapXY);
10864             }
10865         }
10866         return wrap;
10867     },
10868
10869         /* @private */
10870     fxUnwrap : function(wrap, pos, o){
10871         this.clearPositioning();
10872         this.setPositioning(pos);
10873         if(!o.wrap){
10874             wrap.dom.parentNode.insertBefore(this.dom, wrap.dom);
10875             wrap.remove();
10876         }
10877     },
10878
10879         /* @private */
10880     getFxRestore : function(){
10881         var st = this.dom.style;
10882         return {pos: this.getPositioning(), width: st.width, height : st.height};
10883     },
10884
10885         /* @private */
10886     afterFx : function(o){
10887         if(o.afterStyle){
10888             this.applyStyles(o.afterStyle);
10889         }
10890         if(o.afterCls){
10891             this.addClass(o.afterCls);
10892         }
10893         if(o.remove === true){
10894             this.remove();
10895         }
10896         Roo.callback(o.callback, o.scope, [this]);
10897         if(!o.concurrent){
10898             this.fxQueue.shift();
10899             this.nextFx();
10900         }
10901     },
10902
10903         /* @private */
10904     getFxEl : function(){ // support for composite element fx
10905         return Roo.get(this.dom);
10906     },
10907
10908         /* @private */
10909     fxanim : function(args, opt, animType, defaultDur, defaultEase, cb){
10910         animType = animType || 'run';
10911         opt = opt || {};
10912         var anim = Roo.lib.Anim[animType](
10913             this.dom, args,
10914             (opt.duration || defaultDur) || .35,
10915             (opt.easing || defaultEase) || 'easeOut',
10916             function(){
10917                 Roo.callback(cb, this);
10918             },
10919             this
10920         );
10921         opt.anim = anim;
10922         return anim;
10923     }
10924 };
10925
10926 // backwords compat
10927 Roo.Fx.resize = Roo.Fx.scale;
10928
10929 //When included, Roo.Fx is automatically applied to Element so that all basic
10930 //effects are available directly via the Element API
10931 Roo.apply(Roo.Element.prototype, Roo.Fx);/*
10932  * Based on:
10933  * Ext JS Library 1.1.1
10934  * Copyright(c) 2006-2007, Ext JS, LLC.
10935  *
10936  * Originally Released Under LGPL - original licence link has changed is not relivant.
10937  *
10938  * Fork - LGPL
10939  * <script type="text/javascript">
10940  */
10941
10942
10943 /**
10944  * @class Roo.CompositeElement
10945  * Standard composite class. Creates a Roo.Element for every element in the collection.
10946  * <br><br>
10947  * <b>NOTE: Although they are not listed, this class supports all of the set/update methods of Roo.Element. All Roo.Element
10948  * actions will be performed on all the elements in this collection.</b>
10949  * <br><br>
10950  * All methods return <i>this</i> and can be chained.
10951  <pre><code>
10952  var els = Roo.select("#some-el div.some-class", true);
10953  // or select directly from an existing element
10954  var el = Roo.get('some-el');
10955  el.select('div.some-class', true);
10956
10957  els.setWidth(100); // all elements become 100 width
10958  els.hide(true); // all elements fade out and hide
10959  // or
10960  els.setWidth(100).hide(true);
10961  </code></pre>
10962  */
10963 Roo.CompositeElement = function(els){
10964     this.elements = [];
10965     this.addElements(els);
10966 };
10967 Roo.CompositeElement.prototype = {
10968     isComposite: true,
10969     addElements : function(els){
10970         if(!els) return this;
10971         if(typeof els == "string"){
10972             els = Roo.Element.selectorFunction(els);
10973         }
10974         var yels = this.elements;
10975         var index = yels.length-1;
10976         for(var i = 0, len = els.length; i < len; i++) {
10977                 yels[++index] = Roo.get(els[i]);
10978         }
10979         return this;
10980     },
10981
10982     /**
10983     * Clears this composite and adds the elements returned by the passed selector.
10984     * @param {String/Array} els A string CSS selector, an array of elements or an element
10985     * @return {CompositeElement} this
10986     */
10987     fill : function(els){
10988         this.elements = [];
10989         this.add(els);
10990         return this;
10991     },
10992
10993     /**
10994     * Filters this composite to only elements that match the passed selector.
10995     * @param {String} selector A string CSS selector
10996     * @param {Boolean} inverse return inverse filter (not matches)
10997     * @return {CompositeElement} this
10998     */
10999     filter : function(selector, inverse){
11000         var els = [];
11001         inverse = inverse || false;
11002         this.each(function(el){
11003             var match = inverse ? !el.is(selector) : el.is(selector);
11004             if(match){
11005                 els[els.length] = el.dom;
11006             }
11007         });
11008         this.fill(els);
11009         return this;
11010     },
11011
11012     invoke : function(fn, args){
11013         var els = this.elements;
11014         for(var i = 0, len = els.length; i < len; i++) {
11015                 Roo.Element.prototype[fn].apply(els[i], args);
11016         }
11017         return this;
11018     },
11019     /**
11020     * Adds elements to this composite.
11021     * @param {String/Array} els A string CSS selector, an array of elements or an element
11022     * @return {CompositeElement} this
11023     */
11024     add : function(els){
11025         if(typeof els == "string"){
11026             this.addElements(Roo.Element.selectorFunction(els));
11027         }else if(els.length !== undefined){
11028             this.addElements(els);
11029         }else{
11030             this.addElements([els]);
11031         }
11032         return this;
11033     },
11034     /**
11035     * Calls the passed function passing (el, this, index) for each element in this composite.
11036     * @param {Function} fn The function to call
11037     * @param {Object} scope (optional) The <i>this</i> object (defaults to the element)
11038     * @return {CompositeElement} this
11039     */
11040     each : function(fn, scope){
11041         var els = this.elements;
11042         for(var i = 0, len = els.length; i < len; i++){
11043             if(fn.call(scope || els[i], els[i], this, i) === false) {
11044                 break;
11045             }
11046         }
11047         return this;
11048     },
11049
11050     /**
11051      * Returns the Element object at the specified index
11052      * @param {Number} index
11053      * @return {Roo.Element}
11054      */
11055     item : function(index){
11056         return this.elements[index] || null;
11057     },
11058
11059     /**
11060      * Returns the first Element
11061      * @return {Roo.Element}
11062      */
11063     first : function(){
11064         return this.item(0);
11065     },
11066
11067     /**
11068      * Returns the last Element
11069      * @return {Roo.Element}
11070      */
11071     last : function(){
11072         return this.item(this.elements.length-1);
11073     },
11074
11075     /**
11076      * Returns the number of elements in this composite
11077      * @return Number
11078      */
11079     getCount : function(){
11080         return this.elements.length;
11081     },
11082
11083     /**
11084      * Returns true if this composite contains the passed element
11085      * @return Boolean
11086      */
11087     contains : function(el){
11088         return this.indexOf(el) !== -1;
11089     },
11090
11091     /**
11092      * Returns true if this composite contains the passed element
11093      * @return Boolean
11094      */
11095     indexOf : function(el){
11096         return this.elements.indexOf(Roo.get(el));
11097     },
11098
11099
11100     /**
11101     * Removes the specified element(s).
11102     * @param {Mixed} el The id of an element, the Element itself, the index of the element in this composite
11103     * or an array of any of those.
11104     * @param {Boolean} removeDom (optional) True to also remove the element from the document
11105     * @return {CompositeElement} this
11106     */
11107     removeElement : function(el, removeDom){
11108         if(el instanceof Array){
11109             for(var i = 0, len = el.length; i < len; i++){
11110                 this.removeElement(el[i]);
11111             }
11112             return this;
11113         }
11114         var index = typeof el == 'number' ? el : this.indexOf(el);
11115         if(index !== -1){
11116             if(removeDom){
11117                 var d = this.elements[index];
11118                 if(d.dom){
11119                     d.remove();
11120                 }else{
11121                     d.parentNode.removeChild(d);
11122                 }
11123             }
11124             this.elements.splice(index, 1);
11125         }
11126         return this;
11127     },
11128
11129     /**
11130     * Replaces the specified element with the passed element.
11131     * @param {String/HTMLElement/Element/Number} el The id of an element, the Element itself, the index of the element in this composite
11132     * to replace.
11133     * @param {String/HTMLElement/Element} replacement The id of an element or the Element itself.
11134     * @param {Boolean} domReplace (Optional) True to remove and replace the element in the document too.
11135     * @return {CompositeElement} this
11136     */
11137     replaceElement : function(el, replacement, domReplace){
11138         var index = typeof el == 'number' ? el : this.indexOf(el);
11139         if(index !== -1){
11140             if(domReplace){
11141                 this.elements[index].replaceWith(replacement);
11142             }else{
11143                 this.elements.splice(index, 1, Roo.get(replacement))
11144             }
11145         }
11146         return this;
11147     },
11148
11149     /**
11150      * Removes all elements.
11151      */
11152     clear : function(){
11153         this.elements = [];
11154     }
11155 };
11156 (function(){
11157     Roo.CompositeElement.createCall = function(proto, fnName){
11158         if(!proto[fnName]){
11159             proto[fnName] = function(){
11160                 return this.invoke(fnName, arguments);
11161             };
11162         }
11163     };
11164     for(var fnName in Roo.Element.prototype){
11165         if(typeof Roo.Element.prototype[fnName] == "function"){
11166             Roo.CompositeElement.createCall(Roo.CompositeElement.prototype, fnName);
11167         }
11168     };
11169 })();
11170 /*
11171  * Based on:
11172  * Ext JS Library 1.1.1
11173  * Copyright(c) 2006-2007, Ext JS, LLC.
11174  *
11175  * Originally Released Under LGPL - original licence link has changed is not relivant.
11176  *
11177  * Fork - LGPL
11178  * <script type="text/javascript">
11179  */
11180
11181 /**
11182  * @class Roo.CompositeElementLite
11183  * @extends Roo.CompositeElement
11184  * Flyweight composite class. Reuses the same Roo.Element for element operations.
11185  <pre><code>
11186  var els = Roo.select("#some-el div.some-class");
11187  // or select directly from an existing element
11188  var el = Roo.get('some-el');
11189  el.select('div.some-class');
11190
11191  els.setWidth(100); // all elements become 100 width
11192  els.hide(true); // all elements fade out and hide
11193  // or
11194  els.setWidth(100).hide(true);
11195  </code></pre><br><br>
11196  * <b>NOTE: Although they are not listed, this class supports all of the set/update methods of Roo.Element. All Roo.Element
11197  * actions will be performed on all the elements in this collection.</b>
11198  */
11199 Roo.CompositeElementLite = function(els){
11200     Roo.CompositeElementLite.superclass.constructor.call(this, els);
11201     this.el = new Roo.Element.Flyweight();
11202 };
11203 Roo.extend(Roo.CompositeElementLite, Roo.CompositeElement, {
11204     addElements : function(els){
11205         if(els){
11206             if(els instanceof Array){
11207                 this.elements = this.elements.concat(els);
11208             }else{
11209                 var yels = this.elements;
11210                 var index = yels.length-1;
11211                 for(var i = 0, len = els.length; i < len; i++) {
11212                     yels[++index] = els[i];
11213                 }
11214             }
11215         }
11216         return this;
11217     },
11218     invoke : function(fn, args){
11219         var els = this.elements;
11220         var el = this.el;
11221         for(var i = 0, len = els.length; i < len; i++) {
11222             el.dom = els[i];
11223                 Roo.Element.prototype[fn].apply(el, args);
11224         }
11225         return this;
11226     },
11227     /**
11228      * Returns a flyweight Element of the dom element object at the specified index
11229      * @param {Number} index
11230      * @return {Roo.Element}
11231      */
11232     item : function(index){
11233         if(!this.elements[index]){
11234             return null;
11235         }
11236         this.el.dom = this.elements[index];
11237         return this.el;
11238     },
11239
11240     // fixes scope with flyweight
11241     addListener : function(eventName, handler, scope, opt){
11242         var els = this.elements;
11243         for(var i = 0, len = els.length; i < len; i++) {
11244             Roo.EventManager.on(els[i], eventName, handler, scope || els[i], opt);
11245         }
11246         return this;
11247     },
11248
11249     /**
11250     * Calls the passed function passing (el, this, index) for each element in this composite. <b>The element
11251     * passed is the flyweight (shared) Roo.Element instance, so if you require a
11252     * a reference to the dom node, use el.dom.</b>
11253     * @param {Function} fn The function to call
11254     * @param {Object} scope (optional) The <i>this</i> object (defaults to the element)
11255     * @return {CompositeElement} this
11256     */
11257     each : function(fn, scope){
11258         var els = this.elements;
11259         var el = this.el;
11260         for(var i = 0, len = els.length; i < len; i++){
11261             el.dom = els[i];
11262                 if(fn.call(scope || el, el, this, i) === false){
11263                 break;
11264             }
11265         }
11266         return this;
11267     },
11268
11269     indexOf : function(el){
11270         return this.elements.indexOf(Roo.getDom(el));
11271     },
11272
11273     replaceElement : function(el, replacement, domReplace){
11274         var index = typeof el == 'number' ? el : this.indexOf(el);
11275         if(index !== -1){
11276             replacement = Roo.getDom(replacement);
11277             if(domReplace){
11278                 var d = this.elements[index];
11279                 d.parentNode.insertBefore(replacement, d);
11280                 d.parentNode.removeChild(d);
11281             }
11282             this.elements.splice(index, 1, replacement);
11283         }
11284         return this;
11285     }
11286 });
11287 Roo.CompositeElementLite.prototype.on = Roo.CompositeElementLite.prototype.addListener;
11288
11289 /*
11290  * Based on:
11291  * Ext JS Library 1.1.1
11292  * Copyright(c) 2006-2007, Ext JS, LLC.
11293  *
11294  * Originally Released Under LGPL - original licence link has changed is not relivant.
11295  *
11296  * Fork - LGPL
11297  * <script type="text/javascript">
11298  */
11299
11300  
11301
11302 /**
11303  * @class Roo.data.Connection
11304  * @extends Roo.util.Observable
11305  * The class encapsulates a connection to the page's originating domain, allowing requests to be made
11306  * either to a configured URL, or to a URL specified at request time.<br><br>
11307  * <p>
11308  * Requests made by this class are asynchronous, and will return immediately. No data from
11309  * the server will be available to the statement immediately following the {@link #request} call.
11310  * To process returned data, use a callback in the request options object, or an event listener.</p><br>
11311  * <p>
11312  * Note: If you are doing a file upload, you will not get a normal response object sent back to
11313  * your callback or event handler.  Since the upload is handled via in IFRAME, there is no XMLHttpRequest.
11314  * The response object is created using the innerHTML of the IFRAME's document as the responseText
11315  * property and, if present, the IFRAME's XML document as the responseXML property.</p><br>
11316  * This means that a valid XML or HTML document must be returned. If JSON data is required, it is suggested
11317  * that it be placed either inside a &lt;textarea> in an HTML document and retrieved from the responseText
11318  * using a regex, or inside a CDATA section in an XML document and retrieved from the responseXML using
11319  * standard DOM methods.
11320  * @constructor
11321  * @param {Object} config a configuration object.
11322  */
11323 Roo.data.Connection = function(config){
11324     Roo.apply(this, config);
11325     this.addEvents({
11326         /**
11327          * @event beforerequest
11328          * Fires before a network request is made to retrieve a data object.
11329          * @param {Connection} conn This Connection object.
11330          * @param {Object} options The options config object passed to the {@link #request} method.
11331          */
11332         "beforerequest" : true,
11333         /**
11334          * @event requestcomplete
11335          * Fires if the request was successfully completed.
11336          * @param {Connection} conn This Connection object.
11337          * @param {Object} response The XHR object containing the response data.
11338          * See {@link http://www.w3.org/TR/XMLHttpRequest/} for details.
11339          * @param {Object} options The options config object passed to the {@link #request} method.
11340          */
11341         "requestcomplete" : true,
11342         /**
11343          * @event requestexception
11344          * Fires if an error HTTP status was returned from the server.
11345          * See {@link http://www.w3.org/Protocols/rfc2616/rfc2616-sec10.html} for details of HTTP status codes.
11346          * @param {Connection} conn This Connection object.
11347          * @param {Object} response The XHR object containing the response data.
11348          * See {@link http://www.w3.org/TR/XMLHttpRequest/} for details.
11349          * @param {Object} options The options config object passed to the {@link #request} method.
11350          */
11351         "requestexception" : true
11352     });
11353     Roo.data.Connection.superclass.constructor.call(this);
11354 };
11355
11356 Roo.extend(Roo.data.Connection, Roo.util.Observable, {
11357     /**
11358      * @cfg {String} url (Optional) The default URL to be used for requests to the server. (defaults to undefined)
11359      */
11360     /**
11361      * @cfg {Object} extraParams (Optional) An object containing properties which are used as
11362      * extra parameters to each request made by this object. (defaults to undefined)
11363      */
11364     /**
11365      * @cfg {Object} defaultHeaders (Optional) An object containing request headers which are added
11366      *  to each request made by this object. (defaults to undefined)
11367      */
11368     /**
11369      * @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)
11370      */
11371     /**
11372      * @cfg {Number} timeout (Optional) The timeout in milliseconds to be used for requests. (defaults to 30000)
11373      */
11374     timeout : 30000,
11375     /**
11376      * @cfg {Boolean} autoAbort (Optional) Whether this request should abort any pending requests. (defaults to false)
11377      * @type Boolean
11378      */
11379     autoAbort:false,
11380
11381     /**
11382      * @cfg {Boolean} disableCaching (Optional) True to add a unique cache-buster param to GET requests. (defaults to true)
11383      * @type Boolean
11384      */
11385     disableCaching: true,
11386
11387     /**
11388      * Sends an HTTP request to a remote server.
11389      * @param {Object} options An object which may contain the following properties:<ul>
11390      * <li><b>url</b> {String} (Optional) The URL to which to send the request. Defaults to configured URL</li>
11391      * <li><b>params</b> {Object/String/Function} (Optional) An object containing properties which are used as parameters to the
11392      * request, a url encoded string or a function to call to get either.</li>
11393      * <li><b>method</b> {String} (Optional) The HTTP method to use for the request. Defaults to the configured method, or
11394      * if no method was configured, "GET" if no parameters are being sent, and "POST" if parameters are being sent.</li>
11395      * <li><b>callback</b> {Function} (Optional) The function to be called upon receipt of the HTTP response.
11396      * The callback is called regardless of success or failure and is passed the following parameters:<ul>
11397      * <li>options {Object} The parameter to the request call.</li>
11398      * <li>success {Boolean} True if the request succeeded.</li>
11399      * <li>response {Object} The XMLHttpRequest object containing the response data.</li>
11400      * </ul></li>
11401      * <li><b>success</b> {Function} (Optional) The function to be called upon success of the request.
11402      * The callback is passed the following parameters:<ul>
11403      * <li>response {Object} The XMLHttpRequest object containing the response data.</li>
11404      * <li>options {Object} The parameter to the request call.</li>
11405      * </ul></li>
11406      * <li><b>failure</b> {Function} (Optional) The function to be called upon failure of the request.
11407      * The callback is passed the following parameters:<ul>
11408      * <li>response {Object} The XMLHttpRequest object containing the response data.</li>
11409      * <li>options {Object} The parameter to the request call.</li>
11410      * </ul></li>
11411      * <li><b>scope</b> {Object} (Optional) The scope in which to execute the callbacks: The "this" object
11412      * for the callback function. Defaults to the browser window.</li>
11413      * <li><b>form</b> {Object/String} (Optional) A form object or id to pull parameters from.</li>
11414      * <li><b>isUpload</b> {Boolean} (Optional) True if the form object is a file upload (will usually be automatically detected).</li>
11415      * <li><b>headers</b> {Object} (Optional) Request headers to set for the request.</li>
11416      * <li><b>xmlData</b> {Object} (Optional) XML document to use for the post. Note: This will be used instead of
11417      * params for the post data. Any params will be appended to the URL.</li>
11418      * <li><b>disableCaching</b> {Boolean} (Optional) True to add a unique cache-buster param to GET requests.</li>
11419      * </ul>
11420      * @return {Number} transactionId
11421      */
11422     request : function(o){
11423         if(this.fireEvent("beforerequest", this, o) !== false){
11424             var p = o.params;
11425
11426             if(typeof p == "function"){
11427                 p = p.call(o.scope||window, o);
11428             }
11429             if(typeof p == "object"){
11430                 p = Roo.urlEncode(o.params);
11431             }
11432             if(this.extraParams){
11433                 var extras = Roo.urlEncode(this.extraParams);
11434                 p = p ? (p + '&' + extras) : extras;
11435             }
11436
11437             var url = o.url || this.url;
11438             if(typeof url == 'function'){
11439                 url = url.call(o.scope||window, o);
11440             }
11441
11442             if(o.form){
11443                 var form = Roo.getDom(o.form);
11444                 url = url || form.action;
11445
11446                 var enctype = form.getAttribute("enctype");
11447                 if(o.isUpload || (enctype && enctype.toLowerCase() == 'multipart/form-data')){
11448                     return this.doFormUpload(o, p, url);
11449                 }
11450                 var f = Roo.lib.Ajax.serializeForm(form);
11451                 p = p ? (p + '&' + f) : f;
11452             }
11453
11454             var hs = o.headers;
11455             if(this.defaultHeaders){
11456                 hs = Roo.apply(hs || {}, this.defaultHeaders);
11457                 if(!o.headers){
11458                     o.headers = hs;
11459                 }
11460             }
11461
11462             var cb = {
11463                 success: this.handleResponse,
11464                 failure: this.handleFailure,
11465                 scope: this,
11466                 argument: {options: o},
11467                 timeout : o.timeout || this.timeout
11468             };
11469
11470             var method = o.method||this.method||(p ? "POST" : "GET");
11471
11472             if(method == 'GET' && (this.disableCaching && o.disableCaching !== false) || o.disableCaching === true){
11473                 url += (url.indexOf('?') != -1 ? '&' : '?') + '_dc=' + (new Date().getTime());
11474             }
11475
11476             if(typeof o.autoAbort == 'boolean'){ // options gets top priority
11477                 if(o.autoAbort){
11478                     this.abort();
11479                 }
11480             }else if(this.autoAbort !== false){
11481                 this.abort();
11482             }
11483
11484             if((method == 'GET' && p) || o.xmlData){
11485                 url += (url.indexOf('?') != -1 ? '&' : '?') + p;
11486                 p = '';
11487             }
11488             this.transId = Roo.lib.Ajax.request(method, url, cb, p, o);
11489             return this.transId;
11490         }else{
11491             Roo.callback(o.callback, o.scope, [o, null, null]);
11492             return null;
11493         }
11494     },
11495
11496     /**
11497      * Determine whether this object has a request outstanding.
11498      * @param {Number} transactionId (Optional) defaults to the last transaction
11499      * @return {Boolean} True if there is an outstanding request.
11500      */
11501     isLoading : function(transId){
11502         if(transId){
11503             return Roo.lib.Ajax.isCallInProgress(transId);
11504         }else{
11505             return this.transId ? true : false;
11506         }
11507     },
11508
11509     /**
11510      * Aborts any outstanding request.
11511      * @param {Number} transactionId (Optional) defaults to the last transaction
11512      */
11513     abort : function(transId){
11514         if(transId || this.isLoading()){
11515             Roo.lib.Ajax.abort(transId || this.transId);
11516         }
11517     },
11518
11519     // private
11520     handleResponse : function(response){
11521         this.transId = false;
11522         var options = response.argument.options;
11523         response.argument = options ? options.argument : null;
11524         this.fireEvent("requestcomplete", this, response, options);
11525         Roo.callback(options.success, options.scope, [response, options]);
11526         Roo.callback(options.callback, options.scope, [options, true, response]);
11527     },
11528
11529     // private
11530     handleFailure : function(response, e){
11531         this.transId = false;
11532         var options = response.argument.options;
11533         response.argument = options ? options.argument : null;
11534         this.fireEvent("requestexception", this, response, options, e);
11535         Roo.callback(options.failure, options.scope, [response, options]);
11536         Roo.callback(options.callback, options.scope, [options, false, response]);
11537     },
11538
11539     // private
11540     doFormUpload : function(o, ps, url){
11541         var id = Roo.id();
11542         var frame = document.createElement('iframe');
11543         frame.id = id;
11544         frame.name = id;
11545         frame.className = 'x-hidden';
11546         if(Roo.isIE){
11547             frame.src = Roo.SSL_SECURE_URL;
11548         }
11549         document.body.appendChild(frame);
11550
11551         if(Roo.isIE){
11552            document.frames[id].name = id;
11553         }
11554
11555         var form = Roo.getDom(o.form);
11556         form.target = id;
11557         form.method = 'POST';
11558         form.enctype = form.encoding = 'multipart/form-data';
11559         if(url){
11560             form.action = url;
11561         }
11562
11563         var hiddens, hd;
11564         if(ps){ // add dynamic params
11565             hiddens = [];
11566             ps = Roo.urlDecode(ps, false);
11567             for(var k in ps){
11568                 if(ps.hasOwnProperty(k)){
11569                     hd = document.createElement('input');
11570                     hd.type = 'hidden';
11571                     hd.name = k;
11572                     hd.value = ps[k];
11573                     form.appendChild(hd);
11574                     hiddens.push(hd);
11575                 }
11576             }
11577         }
11578
11579         function cb(){
11580             var r = {  // bogus response object
11581                 responseText : '',
11582                 responseXML : null
11583             };
11584
11585             r.argument = o ? o.argument : null;
11586
11587             try { //
11588                 var doc;
11589                 if(Roo.isIE){
11590                     doc = frame.contentWindow.document;
11591                 }else {
11592                     doc = (frame.contentDocument || window.frames[id].document);
11593                 }
11594                 if(doc && doc.body){
11595                     r.responseText = doc.body.innerHTML;
11596                 }
11597                 if(doc && doc.XMLDocument){
11598                     r.responseXML = doc.XMLDocument;
11599                 }else {
11600                     r.responseXML = doc;
11601                 }
11602             }
11603             catch(e) {
11604                 // ignore
11605             }
11606
11607             Roo.EventManager.removeListener(frame, 'load', cb, this);
11608
11609             this.fireEvent("requestcomplete", this, r, o);
11610             Roo.callback(o.success, o.scope, [r, o]);
11611             Roo.callback(o.callback, o.scope, [o, true, r]);
11612
11613             setTimeout(function(){document.body.removeChild(frame);}, 100);
11614         }
11615
11616         Roo.EventManager.on(frame, 'load', cb, this);
11617         form.submit();
11618
11619         if(hiddens){ // remove dynamic params
11620             for(var i = 0, len = hiddens.length; i < len; i++){
11621                 form.removeChild(hiddens[i]);
11622             }
11623         }
11624     }
11625 });
11626 /*
11627  * Based on:
11628  * Ext JS Library 1.1.1
11629  * Copyright(c) 2006-2007, Ext JS, LLC.
11630  *
11631  * Originally Released Under LGPL - original licence link has changed is not relivant.
11632  *
11633  * Fork - LGPL
11634  * <script type="text/javascript">
11635  */
11636  
11637 /**
11638  * Global Ajax request class.
11639  * 
11640  * @class Roo.Ajax
11641  * @extends Roo.data.Connection
11642  * @static
11643  * 
11644  * @cfg {String} url  The default URL to be used for requests to the server. (defaults to undefined)
11645  * @cfg {Object} extraParams  An object containing properties which are used as extra parameters to each request made by this object. (defaults to undefined)
11646  * @cfg {Object} defaultHeaders  An object containing request headers which are added to each request made by this object. (defaults to undefined)
11647  * @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)
11648  * @cfg {Number} timeout (Optional) The timeout in milliseconds to be used for requests. (defaults to 30000)
11649  * @cfg {Boolean} autoAbort (Optional) Whether a new request should abort any pending requests. (defaults to false)
11650  * @cfg {Boolean} disableCaching (Optional)   True to add a unique cache-buster param to GET requests. (defaults to true)
11651  */
11652 Roo.Ajax = new Roo.data.Connection({
11653     // fix up the docs
11654     /**
11655      * @scope Roo.Ajax
11656      * @type {Boolear} 
11657      */
11658     autoAbort : false,
11659
11660     /**
11661      * Serialize the passed form into a url encoded string
11662      * @scope Roo.Ajax
11663      * @param {String/HTMLElement} form
11664      * @return {String}
11665      */
11666     serializeForm : function(form){
11667         return Roo.lib.Ajax.serializeForm(form);
11668     }
11669 });/*
11670  * Based on:
11671  * Ext JS Library 1.1.1
11672  * Copyright(c) 2006-2007, Ext JS, LLC.
11673  *
11674  * Originally Released Under LGPL - original licence link has changed is not relivant.
11675  *
11676  * Fork - LGPL
11677  * <script type="text/javascript">
11678  */
11679
11680  
11681 /**
11682  * @class Roo.UpdateManager
11683  * @extends Roo.util.Observable
11684  * Provides AJAX-style update for Element object.<br><br>
11685  * Usage:<br>
11686  * <pre><code>
11687  * // Get it from a Roo.Element object
11688  * var el = Roo.get("foo");
11689  * var mgr = el.getUpdateManager();
11690  * mgr.update("http://myserver.com/index.php", "param1=1&amp;param2=2");
11691  * ...
11692  * mgr.formUpdate("myFormId", "http://myserver.com/index.php");
11693  * <br>
11694  * // or directly (returns the same UpdateManager instance)
11695  * var mgr = new Roo.UpdateManager("myElementId");
11696  * mgr.startAutoRefresh(60, "http://myserver.com/index.php");
11697  * mgr.on("update", myFcnNeedsToKnow);
11698  * <br>
11699    // short handed call directly from the element object
11700    Roo.get("foo").load({
11701         url: "bar.php",
11702         scripts:true,
11703         params: "for=bar",
11704         text: "Loading Foo..."
11705    });
11706  * </code></pre>
11707  * @constructor
11708  * Create new UpdateManager directly.
11709  * @param {String/HTMLElement/Roo.Element} el The element to update
11710  * @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).
11711  */
11712 Roo.UpdateManager = function(el, forceNew){
11713     el = Roo.get(el);
11714     if(!forceNew && el.updateManager){
11715         return el.updateManager;
11716     }
11717     /**
11718      * The Element object
11719      * @type Roo.Element
11720      */
11721     this.el = el;
11722     /**
11723      * Cached url to use for refreshes. Overwritten every time update() is called unless "discardUrl" param is set to true.
11724      * @type String
11725      */
11726     this.defaultUrl = null;
11727
11728     this.addEvents({
11729         /**
11730          * @event beforeupdate
11731          * Fired before an update is made, return false from your handler and the update is cancelled.
11732          * @param {Roo.Element} el
11733          * @param {String/Object/Function} url
11734          * @param {String/Object} params
11735          */
11736         "beforeupdate": true,
11737         /**
11738          * @event update
11739          * Fired after successful update is made.
11740          * @param {Roo.Element} el
11741          * @param {Object} oResponseObject The response Object
11742          */
11743         "update": true,
11744         /**
11745          * @event failure
11746          * Fired on update failure.
11747          * @param {Roo.Element} el
11748          * @param {Object} oResponseObject The response Object
11749          */
11750         "failure": true
11751     });
11752     var d = Roo.UpdateManager.defaults;
11753     /**
11754      * Blank page URL to use with SSL file uploads (Defaults to Roo.UpdateManager.defaults.sslBlankUrl or "about:blank").
11755      * @type String
11756      */
11757     this.sslBlankUrl = d.sslBlankUrl;
11758     /**
11759      * Whether to append unique parameter on get request to disable caching (Defaults to Roo.UpdateManager.defaults.disableCaching or false).
11760      * @type Boolean
11761      */
11762     this.disableCaching = d.disableCaching;
11763     /**
11764      * Text for loading indicator (Defaults to Roo.UpdateManager.defaults.indicatorText or '&lt;div class="loading-indicator"&gt;Loading...&lt;/div&gt;').
11765      * @type String
11766      */
11767     this.indicatorText = d.indicatorText;
11768     /**
11769      * Whether to show indicatorText when loading (Defaults to Roo.UpdateManager.defaults.showLoadIndicator or true).
11770      * @type String
11771      */
11772     this.showLoadIndicator = d.showLoadIndicator;
11773     /**
11774      * Timeout for requests or form posts in seconds (Defaults to Roo.UpdateManager.defaults.timeout or 30 seconds).
11775      * @type Number
11776      */
11777     this.timeout = d.timeout;
11778
11779     /**
11780      * True to process scripts in the output (Defaults to Roo.UpdateManager.defaults.loadScripts (false)).
11781      * @type Boolean
11782      */
11783     this.loadScripts = d.loadScripts;
11784
11785     /**
11786      * Transaction object of current executing transaction
11787      */
11788     this.transaction = null;
11789
11790     /**
11791      * @private
11792      */
11793     this.autoRefreshProcId = null;
11794     /**
11795      * Delegate for refresh() prebound to "this", use myUpdater.refreshDelegate.createCallback(arg1, arg2) to bind arguments
11796      * @type Function
11797      */
11798     this.refreshDelegate = this.refresh.createDelegate(this);
11799     /**
11800      * Delegate for update() prebound to "this", use myUpdater.updateDelegate.createCallback(arg1, arg2) to bind arguments
11801      * @type Function
11802      */
11803     this.updateDelegate = this.update.createDelegate(this);
11804     /**
11805      * Delegate for formUpdate() prebound to "this", use myUpdater.formUpdateDelegate.createCallback(arg1, arg2) to bind arguments
11806      * @type Function
11807      */
11808     this.formUpdateDelegate = this.formUpdate.createDelegate(this);
11809     /**
11810      * @private
11811      */
11812     this.successDelegate = this.processSuccess.createDelegate(this);
11813     /**
11814      * @private
11815      */
11816     this.failureDelegate = this.processFailure.createDelegate(this);
11817
11818     if(!this.renderer){
11819      /**
11820       * The renderer for this UpdateManager. Defaults to {@link Roo.UpdateManager.BasicRenderer}.
11821       */
11822     this.renderer = new Roo.UpdateManager.BasicRenderer();
11823     }
11824     
11825     Roo.UpdateManager.superclass.constructor.call(this);
11826 };
11827
11828 Roo.extend(Roo.UpdateManager, Roo.util.Observable, {
11829     /**
11830      * Get the Element this UpdateManager is bound to
11831      * @return {Roo.Element} The element
11832      */
11833     getEl : function(){
11834         return this.el;
11835     },
11836     /**
11837      * Performs an async request, updating this element with the response. If params are specified it uses POST, otherwise it uses GET.
11838      * @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:
11839 <pre><code>
11840 um.update({<br/>
11841     url: "your-url.php",<br/>
11842     params: {param1: "foo", param2: "bar"}, // or a URL encoded string<br/>
11843     callback: yourFunction,<br/>
11844     scope: yourObject, //(optional scope)  <br/>
11845     discardUrl: false, <br/>
11846     nocache: false,<br/>
11847     text: "Loading...",<br/>
11848     timeout: 30,<br/>
11849     scripts: false<br/>
11850 });
11851 </code></pre>
11852      * The only required property is url. The optional properties nocache, text and scripts
11853      * are shorthand for disableCaching, indicatorText and loadScripts and are used to set their associated property on this UpdateManager instance.
11854      * @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}
11855      * @param {Function} callback (optional) Callback when transaction is complete - called with signature (oElement, bSuccess, oResponse)
11856      * @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.
11857      */
11858     update : function(url, params, callback, discardUrl){
11859         if(this.fireEvent("beforeupdate", this.el, url, params) !== false){
11860             var method = this.method,
11861                 cfg;
11862             if(typeof url == "object"){ // must be config object
11863                 cfg = url;
11864                 url = cfg.url;
11865                 params = params || cfg.params;
11866                 callback = callback || cfg.callback;
11867                 discardUrl = discardUrl || cfg.discardUrl;
11868                 if(callback && cfg.scope){
11869                     callback = callback.createDelegate(cfg.scope);
11870                 }
11871                 if(typeof cfg.method != "undefined"){method = cfg.method;};
11872                 if(typeof cfg.nocache != "undefined"){this.disableCaching = cfg.nocache;};
11873                 if(typeof cfg.text != "undefined"){this.indicatorText = '<div class="loading-indicator">'+cfg.text+"</div>";};
11874                 if(typeof cfg.scripts != "undefined"){this.loadScripts = cfg.scripts;};
11875                 if(typeof cfg.timeout != "undefined"){this.timeout = cfg.timeout;};
11876             }
11877             this.showLoading();
11878             if(!discardUrl){
11879                 this.defaultUrl = url;
11880             }
11881             if(typeof url == "function"){
11882                 url = url.call(this);
11883             }
11884
11885             method = method || (params ? "POST" : "GET");
11886             if(method == "GET"){
11887                 url = this.prepareUrl(url);
11888             }
11889
11890             var o = Roo.apply(cfg ||{}, {
11891                 url : url,
11892                 params: params,
11893                 success: this.successDelegate,
11894                 failure: this.failureDelegate,
11895                 callback: undefined,
11896                 timeout: (this.timeout*1000),
11897                 argument: {"url": url, "form": null, "callback": callback, "params": params}
11898             });
11899             Roo.log("updated manager called with timeout of " + o.timeout);
11900             this.transaction = Roo.Ajax.request(o);
11901         }
11902     },
11903
11904     /**
11905      * 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.
11906      * Uses this.sslBlankUrl for SSL file uploads to prevent IE security warning.
11907      * @param {String/HTMLElement} form The form Id or form element
11908      * @param {String} url (optional) The url to pass the form to. If omitted the action attribute on the form will be used.
11909      * @param {Boolean} reset (optional) Whether to try to reset the form after the update
11910      * @param {Function} callback (optional) Callback when transaction is complete - called with signature (oElement, bSuccess, oResponse)
11911      */
11912     formUpdate : function(form, url, reset, callback){
11913         if(this.fireEvent("beforeupdate", this.el, form, url) !== false){
11914             if(typeof url == "function"){
11915                 url = url.call(this);
11916             }
11917             form = Roo.getDom(form);
11918             this.transaction = Roo.Ajax.request({
11919                 form: form,
11920                 url:url,
11921                 success: this.successDelegate,
11922                 failure: this.failureDelegate,
11923                 timeout: (this.timeout*1000),
11924                 argument: {"url": url, "form": form, "callback": callback, "reset": reset}
11925             });
11926             this.showLoading.defer(1, this);
11927         }
11928     },
11929
11930     /**
11931      * Refresh the element with the last used url or defaultUrl. If there is no url, it returns immediately
11932      * @param {Function} callback (optional) Callback when transaction is complete - called with signature (oElement, bSuccess)
11933      */
11934     refresh : function(callback){
11935         if(this.defaultUrl == null){
11936             return;
11937         }
11938         this.update(this.defaultUrl, null, callback, true);
11939     },
11940
11941     /**
11942      * Set this element to auto refresh.
11943      * @param {Number} interval How often to update (in seconds).
11944      * @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)
11945      * @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}
11946      * @param {Function} callback (optional) Callback when transaction is complete - called with signature (oElement, bSuccess)
11947      * @param {Boolean} refreshNow (optional) Whether to execute the refresh now, or wait the interval
11948      */
11949     startAutoRefresh : function(interval, url, params, callback, refreshNow){
11950         if(refreshNow){
11951             this.update(url || this.defaultUrl, params, callback, true);
11952         }
11953         if(this.autoRefreshProcId){
11954             clearInterval(this.autoRefreshProcId);
11955         }
11956         this.autoRefreshProcId = setInterval(this.update.createDelegate(this, [url || this.defaultUrl, params, callback, true]), interval*1000);
11957     },
11958
11959     /**
11960      * Stop auto refresh on this element.
11961      */
11962      stopAutoRefresh : function(){
11963         if(this.autoRefreshProcId){
11964             clearInterval(this.autoRefreshProcId);
11965             delete this.autoRefreshProcId;
11966         }
11967     },
11968
11969     isAutoRefreshing : function(){
11970        return this.autoRefreshProcId ? true : false;
11971     },
11972     /**
11973      * Called to update the element to "Loading" state. Override to perform custom action.
11974      */
11975     showLoading : function(){
11976         if(this.showLoadIndicator){
11977             this.el.update(this.indicatorText);
11978         }
11979     },
11980
11981     /**
11982      * Adds unique parameter to query string if disableCaching = true
11983      * @private
11984      */
11985     prepareUrl : function(url){
11986         if(this.disableCaching){
11987             var append = "_dc=" + (new Date().getTime());
11988             if(url.indexOf("?") !== -1){
11989                 url += "&" + append;
11990             }else{
11991                 url += "?" + append;
11992             }
11993         }
11994         return url;
11995     },
11996
11997     /**
11998      * @private
11999      */
12000     processSuccess : function(response){
12001         this.transaction = null;
12002         if(response.argument.form && response.argument.reset){
12003             try{ // put in try/catch since some older FF releases had problems with this
12004                 response.argument.form.reset();
12005             }catch(e){}
12006         }
12007         if(this.loadScripts){
12008             this.renderer.render(this.el, response, this,
12009                 this.updateComplete.createDelegate(this, [response]));
12010         }else{
12011             this.renderer.render(this.el, response, this);
12012             this.updateComplete(response);
12013         }
12014     },
12015
12016     updateComplete : function(response){
12017         this.fireEvent("update", this.el, response);
12018         if(typeof response.argument.callback == "function"){
12019             response.argument.callback(this.el, true, response);
12020         }
12021     },
12022
12023     /**
12024      * @private
12025      */
12026     processFailure : function(response){
12027         this.transaction = null;
12028         this.fireEvent("failure", this.el, response);
12029         if(typeof response.argument.callback == "function"){
12030             response.argument.callback(this.el, false, response);
12031         }
12032     },
12033
12034     /**
12035      * Set the content renderer for this UpdateManager. See {@link Roo.UpdateManager.BasicRenderer#render} for more details.
12036      * @param {Object} renderer The object implementing the render() method
12037      */
12038     setRenderer : function(renderer){
12039         this.renderer = renderer;
12040     },
12041
12042     getRenderer : function(){
12043        return this.renderer;
12044     },
12045
12046     /**
12047      * Set the defaultUrl used for updates
12048      * @param {String/Function} defaultUrl The url or a function to call to get the url
12049      */
12050     setDefaultUrl : function(defaultUrl){
12051         this.defaultUrl = defaultUrl;
12052     },
12053
12054     /**
12055      * Aborts the executing transaction
12056      */
12057     abort : function(){
12058         if(this.transaction){
12059             Roo.Ajax.abort(this.transaction);
12060         }
12061     },
12062
12063     /**
12064      * Returns true if an update is in progress
12065      * @return {Boolean}
12066      */
12067     isUpdating : function(){
12068         if(this.transaction){
12069             return Roo.Ajax.isLoading(this.transaction);
12070         }
12071         return false;
12072     }
12073 });
12074
12075 /**
12076  * @class Roo.UpdateManager.defaults
12077  * @static (not really - but it helps the doc tool)
12078  * The defaults collection enables customizing the default properties of UpdateManager
12079  */
12080    Roo.UpdateManager.defaults = {
12081        /**
12082          * Timeout for requests or form posts in seconds (Defaults 30 seconds).
12083          * @type Number
12084          */
12085          timeout : 30,
12086
12087          /**
12088          * True to process scripts by default (Defaults to false).
12089          * @type Boolean
12090          */
12091         loadScripts : false,
12092
12093         /**
12094         * Blank page URL to use with SSL file uploads (Defaults to "javascript:false").
12095         * @type String
12096         */
12097         sslBlankUrl : (Roo.SSL_SECURE_URL || "javascript:false"),
12098         /**
12099          * Whether to append unique parameter on get request to disable caching (Defaults to false).
12100          * @type Boolean
12101          */
12102         disableCaching : false,
12103         /**
12104          * Whether to show indicatorText when loading (Defaults to true).
12105          * @type Boolean
12106          */
12107         showLoadIndicator : true,
12108         /**
12109          * Text for loading indicator (Defaults to '&lt;div class="loading-indicator"&gt;Loading...&lt;/div&gt;').
12110          * @type String
12111          */
12112         indicatorText : '<div class="loading-indicator">Loading...</div>'
12113    };
12114
12115 /**
12116  * Static convenience method. This method is deprecated in favor of el.load({url:'foo.php', ...}).
12117  *Usage:
12118  * <pre><code>Roo.UpdateManager.updateElement("my-div", "stuff.php");</code></pre>
12119  * @param {String/HTMLElement/Roo.Element} el The element to update
12120  * @param {String} url The url
12121  * @param {String/Object} params (optional) Url encoded param string or an object of name/value pairs
12122  * @param {Object} options (optional) A config object with any of the UpdateManager properties you want to set - for example: {disableCaching:true, indicatorText: "Loading data..."}
12123  * @static
12124  * @deprecated
12125  * @member Roo.UpdateManager
12126  */
12127 Roo.UpdateManager.updateElement = function(el, url, params, options){
12128     var um = Roo.get(el, true).getUpdateManager();
12129     Roo.apply(um, options);
12130     um.update(url, params, options ? options.callback : null);
12131 };
12132 // alias for backwards compat
12133 Roo.UpdateManager.update = Roo.UpdateManager.updateElement;
12134 /**
12135  * @class Roo.UpdateManager.BasicRenderer
12136  * Default Content renderer. Updates the elements innerHTML with the responseText.
12137  */
12138 Roo.UpdateManager.BasicRenderer = function(){};
12139
12140 Roo.UpdateManager.BasicRenderer.prototype = {
12141     /**
12142      * This is called when the transaction is completed and it's time to update the element - The BasicRenderer
12143      * updates the elements innerHTML with the responseText - To perform a custom render (i.e. XML or JSON processing),
12144      * create an object with a "render(el, response)" method and pass it to setRenderer on the UpdateManager.
12145      * @param {Roo.Element} el The element being rendered
12146      * @param {Object} response The YUI Connect response object
12147      * @param {UpdateManager} updateManager The calling update manager
12148      * @param {Function} callback A callback that will need to be called if loadScripts is true on the UpdateManager
12149      */
12150      render : function(el, response, updateManager, callback){
12151         el.update(response.responseText, updateManager.loadScripts, callback);
12152     }
12153 };
12154 /*
12155  * Based on:
12156  * Roo JS
12157  * (c)) Alan Knowles
12158  * Licence : LGPL
12159  */
12160
12161
12162 /**
12163  * @class Roo.DomTemplate
12164  * @extends Roo.Template
12165  * An effort at a dom based template engine..
12166  *
12167  * Similar to XTemplate, except it uses dom parsing to create the template..
12168  *
12169  * Supported features:
12170  *
12171  *  Tags:
12172
12173 <pre><code>
12174       {a_variable} - output encoded.
12175       {a_variable.format:("Y-m-d")} - call a method on the variable
12176       {a_variable:raw} - unencoded output
12177       {a_variable:toFixed(1,2)} - Roo.util.Format."toFixed"
12178       {a_variable:this.method_on_template(...)} - call a method on the template object.
12179  
12180 </code></pre>
12181  *  The tpl tag:
12182 <pre><code>
12183         &lt;div roo-for="a_variable or condition.."&gt;&lt;/div&gt;
12184         &lt;div roo-if="a_variable or condition"&gt;&lt;/div&gt;
12185         &lt;div roo-exec="some javascript"&gt;&lt;/div&gt;
12186         &lt;div roo-name="named_template"&gt;&lt;/div&gt; 
12187   
12188 </code></pre>
12189  *      
12190  */
12191 Roo.DomTemplate = function()
12192 {
12193      Roo.DomTemplate.superclass.constructor.apply(this, arguments);
12194      if (this.html) {
12195         this.compile();
12196      }
12197 };
12198
12199
12200 Roo.extend(Roo.DomTemplate, Roo.Template, {
12201     /**
12202      * id counter for sub templates.
12203      */
12204     id : 0,
12205     /**
12206      * flag to indicate if dom parser is inside a pre,
12207      * it will strip whitespace if not.
12208      */
12209     inPre : false,
12210     
12211     /**
12212      * The various sub templates
12213      */
12214     tpls : false,
12215     
12216     
12217     
12218     /**
12219      *
12220      * basic tag replacing syntax
12221      * WORD:WORD()
12222      *
12223      * // you can fake an object call by doing this
12224      *  x.t:(test,tesT) 
12225      * 
12226      */
12227     re : /(\{|\%7B)([\w-\.]+)(?:\:([\w\.]*)(?:\(([^)]*?)?\))?)?(\}|\%7D)/g,
12228     //re : /\{([\w-\.]+)(?:\:([\w\.]*)(?:\((.*?)?\))?)?\}/g,
12229     
12230     iterChild : function (node, method) {
12231         
12232         var oldPre = this.inPre;
12233         if (node.tagName == 'PRE') {
12234             this.inPre = true;
12235         }
12236         for( var i = 0; i < node.childNodes.length; i++) {
12237             method.call(this, node.childNodes[i]);
12238         }
12239         this.inPre = oldPre;
12240     },
12241     
12242     
12243     
12244     /**
12245      * compile the template
12246      *
12247      * This is not recursive, so I'm not sure how nested templates are really going to be handled..
12248      *
12249      */
12250     compile: function()
12251     {
12252         var s = this.html;
12253         
12254         // covert the html into DOM...
12255         var doc = false;
12256         var div =false;
12257         try {
12258             doc = document.implementation.createHTMLDocument("");
12259             doc.documentElement.innerHTML =   this.html  ;
12260             div = doc.documentElement;
12261         } catch (e) {
12262             // old IE... - nasty -- it causes all sorts of issues.. with
12263             // images getting pulled from server..
12264             div = document.createElement('div');
12265             div.innerHTML = this.html;
12266         }
12267         //doc.documentElement.innerHTML = htmlBody
12268          
12269         
12270         
12271         this.tpls = [];
12272         var _t = this;
12273         this.iterChild(div, function(n) {_t.compileNode(n, true); });
12274         
12275         var tpls = this.tpls;
12276         
12277         // create a top level template from the snippet..
12278         
12279         //Roo.log(div.innerHTML);
12280         
12281         var tpl = {
12282             uid : 'master',
12283             id : this.id++,
12284             attr : false,
12285             value : false,
12286             body : div.innerHTML,
12287             
12288             forCall : false,
12289             execCall : false,
12290             dom : div,
12291             isTop : true
12292             
12293         };
12294         tpls.unshift(tpl);
12295         
12296         
12297         // compile them...
12298         this.tpls = [];
12299         Roo.each(tpls, function(tp){
12300             this.compileTpl(tp);
12301             this.tpls[tp.id] = tp;
12302         }, this);
12303         
12304         this.master = tpls[0];
12305         return this;
12306         
12307         
12308     },
12309     
12310     compileNode : function(node, istop) {
12311         // test for
12312         //Roo.log(node);
12313         
12314         
12315         // skip anything not a tag..
12316         if (node.nodeType != 1) {
12317             if (node.nodeType == 3 && !this.inPre) {
12318                 // reduce white space..
12319                 node.nodeValue = node.nodeValue.replace(/\s+/g, ' '); 
12320                 
12321             }
12322             return;
12323         }
12324         
12325         var tpl = {
12326             uid : false,
12327             id : false,
12328             attr : false,
12329             value : false,
12330             body : '',
12331             
12332             forCall : false,
12333             execCall : false,
12334             dom : false,
12335             isTop : istop
12336             
12337             
12338         };
12339         
12340         
12341         switch(true) {
12342             case (node.hasAttribute('roo-for')): tpl.attr = 'for'; break;
12343             case (node.hasAttribute('roo-if')): tpl.attr = 'if'; break;
12344             case (node.hasAttribute('roo-name')): tpl.attr = 'name'; break;
12345             case (node.hasAttribute('roo-exec')): tpl.attr = 'exec'; break;
12346             // no default..
12347         }
12348         
12349         
12350         if (!tpl.attr) {
12351             // just itterate children..
12352             this.iterChild(node,this.compileNode);
12353             return;
12354         }
12355         tpl.uid = this.id++;
12356         tpl.value = node.getAttribute('roo-' +  tpl.attr);
12357         node.removeAttribute('roo-'+ tpl.attr);
12358         if (tpl.attr != 'name') {
12359             var placeholder = document.createTextNode('{domtpl' + tpl.uid + '}');
12360             node.parentNode.replaceChild(placeholder,  node);
12361         } else {
12362             
12363             var placeholder =  document.createElement('span');
12364             placeholder.className = 'roo-tpl-' + tpl.value;
12365             node.parentNode.replaceChild(placeholder,  node);
12366         }
12367         
12368         // parent now sees '{domtplXXXX}
12369         this.iterChild(node,this.compileNode);
12370         
12371         // we should now have node body...
12372         var div = document.createElement('div');
12373         div.appendChild(node);
12374         tpl.dom = node;
12375         // this has the unfortunate side effect of converting tagged attributes
12376         // eg. href="{...}" into %7C...%7D
12377         // this has been fixed by searching for those combo's although it's a bit hacky..
12378         
12379         
12380         tpl.body = div.innerHTML;
12381         
12382         
12383          
12384         tpl.id = tpl.uid;
12385         switch(tpl.attr) {
12386             case 'for' :
12387                 switch (tpl.value) {
12388                     case '.':  tpl.forCall = new Function('values', 'parent', 'with(values){ return values; }'); break;
12389                     case '..': tpl.forCall= new Function('values', 'parent', 'with(values){ return parent; }'); break;
12390                     default:   tpl.forCall= new Function('values', 'parent', 'with(values){ return '+tpl.value+'; }');
12391                 }
12392                 break;
12393             
12394             case 'exec':
12395                 tpl.execCall = new Function('values', 'parent', 'with(values){ '+(Roo.util.Format.htmlDecode(tpl.value))+'; }');
12396                 break;
12397             
12398             case 'if':     
12399                 tpl.ifCall = new Function('values', 'parent', 'with(values){ return '+(Roo.util.Format.htmlDecode(tpl.value))+'; }');
12400                 break;
12401             
12402             case 'name':
12403                 tpl.id  = tpl.value; // replace non characters???
12404                 break;
12405             
12406         }
12407         
12408         
12409         this.tpls.push(tpl);
12410         
12411         
12412         
12413     },
12414     
12415     
12416     
12417     
12418     /**
12419      * Compile a segment of the template into a 'sub-template'
12420      *
12421      * 
12422      * 
12423      *
12424      */
12425     compileTpl : function(tpl)
12426     {
12427         var fm = Roo.util.Format;
12428         var useF = this.disableFormats !== true;
12429         
12430         var sep = Roo.isGecko ? "+\n" : ",\n";
12431         
12432         var undef = function(str) {
12433             Roo.debug && Roo.log("Property not found :"  + str);
12434             return '';
12435         };
12436           
12437         //Roo.log(tpl.body);
12438         
12439         
12440         
12441         var fn = function(m, lbrace, name, format, args)
12442         {
12443             //Roo.log("ARGS");
12444             //Roo.log(arguments);
12445             args = args ? args.replace(/\\'/g,"'") : args;
12446             //["{TEST:(a,b,c)}", "TEST", "", "a,b,c", 0, "{TEST:(a,b,c)}"]
12447             if (typeof(format) == 'undefined') {
12448                 format =  'htmlEncode'; 
12449             }
12450             if (format == 'raw' ) {
12451                 format = false;
12452             }
12453             
12454             if(name.substr(0, 6) == 'domtpl'){
12455                 return "'"+ sep +'this.applySubTemplate('+name.substr(6)+', values, parent)'+sep+"'";
12456             }
12457             
12458             // build an array of options to determine if value is undefined..
12459             
12460             // basically get 'xxxx.yyyy' then do
12461             // (typeof(xxxx) == 'undefined' || typeof(xxx.yyyy) == 'undefined') ?
12462             //    (function () { Roo.log("Property not found"); return ''; })() :
12463             //    ......
12464             
12465             var udef_ar = [];
12466             var lookfor = '';
12467             Roo.each(name.split('.'), function(st) {
12468                 lookfor += (lookfor.length ? '.': '') + st;
12469                 udef_ar.push(  "(typeof(" + lookfor + ") == 'undefined')"  );
12470             });
12471             
12472             var udef_st = '((' + udef_ar.join(" || ") +") ? undef('" + name + "') : "; // .. needs )
12473             
12474             
12475             if(format && useF){
12476                 
12477                 args = args ? ',' + args : "";
12478                  
12479                 if(format.substr(0, 5) != "this."){
12480                     format = "fm." + format + '(';
12481                 }else{
12482                     format = 'this.call("'+ format.substr(5) + '", ';
12483                     args = ", values";
12484                 }
12485                 
12486                 return "'"+ sep +   udef_st   +    format + name + args + "))"+sep+"'";
12487             }
12488              
12489             if (args && args.length) {
12490                 // called with xxyx.yuu:(test,test)
12491                 // change to ()
12492                 return "'"+ sep + udef_st  + name + '(' +  args + "))"+sep+"'";
12493             }
12494             // raw.. - :raw modifier..
12495             return "'"+ sep + udef_st  + name + ")"+sep+"'";
12496             
12497         };
12498         var body;
12499         // branched to use + in gecko and [].join() in others
12500         if(Roo.isGecko){
12501             body = "tpl.compiled = function(values, parent){  with(values) { return '" +
12502                    tpl.body.replace(/(\r\n|\n)/g, '\\n').replace(/'/g, "\\'").replace(this.re, fn) +
12503                     "';};};";
12504         }else{
12505             body = ["tpl.compiled = function(values, parent){  with (values) { return ['"];
12506             body.push(tpl.body.replace(/(\r\n|\n)/g,
12507                             '\\n').replace(/'/g, "\\'").replace(this.re, fn));
12508             body.push("'].join('');};};");
12509             body = body.join('');
12510         }
12511         
12512         Roo.debug && Roo.log(body.replace(/\\n/,'\n'));
12513        
12514         /** eval:var:tpl eval:var:fm eval:var:useF eval:var:undef  */
12515         eval(body);
12516         
12517         return this;
12518     },
12519      
12520     /**
12521      * same as applyTemplate, except it's done to one of the subTemplates
12522      * when using named templates, you can do:
12523      *
12524      * var str = pl.applySubTemplate('your-name', values);
12525      *
12526      * 
12527      * @param {Number} id of the template
12528      * @param {Object} values to apply to template
12529      * @param {Object} parent (normaly the instance of this object)
12530      */
12531     applySubTemplate : function(id, values, parent)
12532     {
12533         
12534         
12535         var t = this.tpls[id];
12536         
12537         
12538         try { 
12539             if(t.ifCall && !t.ifCall.call(this, values, parent)){
12540                 Roo.debug && Roo.log('if call on ' + t.value + ' return false');
12541                 return '';
12542             }
12543         } catch(e) {
12544             Roo.log('Xtemplate.applySubTemplate('+ id+ '): Exception thrown on roo-if="' + t.value + '" - ' + e.toString());
12545             Roo.log(values);
12546           
12547             return '';
12548         }
12549         try { 
12550             
12551             if(t.execCall && t.execCall.call(this, values, parent)){
12552                 return '';
12553             }
12554         } catch(e) {
12555             Roo.log('Xtemplate.applySubTemplate('+ id+ '): Exception thrown on roo-for="' + t.value + '" - ' + e.toString());
12556             Roo.log(values);
12557             return '';
12558         }
12559         
12560         try {
12561             var vs = t.forCall ? t.forCall.call(this, values, parent) : values;
12562             parent = t.target ? values : parent;
12563             if(t.forCall && vs instanceof Array){
12564                 var buf = [];
12565                 for(var i = 0, len = vs.length; i < len; i++){
12566                     try {
12567                         buf[buf.length] = t.compiled.call(this, vs[i], parent);
12568                     } catch (e) {
12569                         Roo.log('Xtemplate.applySubTemplate('+ id+ '): Exception thrown on body="' + t.value + '" - ' + e.toString());
12570                         Roo.log(e.body);
12571                         //Roo.log(t.compiled);
12572                         Roo.log(vs[i]);
12573                     }   
12574                 }
12575                 return buf.join('');
12576             }
12577         } catch (e) {
12578             Roo.log('Xtemplate.applySubTemplate('+ id+ '): Exception thrown on roo-for="' + t.value + '" - ' + e.toString());
12579             Roo.log(values);
12580             return '';
12581         }
12582         try {
12583             return t.compiled.call(this, vs, parent);
12584         } catch (e) {
12585             Roo.log('Xtemplate.applySubTemplate('+ id+ '): Exception thrown on body="' + t.value + '" - ' + e.toString());
12586             Roo.log(e.body);
12587             //Roo.log(t.compiled);
12588             Roo.log(values);
12589             return '';
12590         }
12591     },
12592
12593    
12594
12595     applyTemplate : function(values){
12596         return this.master.compiled.call(this, values, {});
12597         //var s = this.subs;
12598     },
12599
12600     apply : function(){
12601         return this.applyTemplate.apply(this, arguments);
12602     }
12603
12604  });
12605
12606 Roo.DomTemplate.from = function(el){
12607     el = Roo.getDom(el);
12608     return new Roo.Domtemplate(el.value || el.innerHTML);
12609 };/*
12610  * Based on:
12611  * Ext JS Library 1.1.1
12612  * Copyright(c) 2006-2007, Ext JS, LLC.
12613  *
12614  * Originally Released Under LGPL - original licence link has changed is not relivant.
12615  *
12616  * Fork - LGPL
12617  * <script type="text/javascript">
12618  */
12619
12620 /**
12621  * @class Roo.util.DelayedTask
12622  * Provides a convenient method of performing setTimeout where a new
12623  * timeout cancels the old timeout. An example would be performing validation on a keypress.
12624  * You can use this class to buffer
12625  * the keypress events for a certain number of milliseconds, and perform only if they stop
12626  * for that amount of time.
12627  * @constructor The parameters to this constructor serve as defaults and are not required.
12628  * @param {Function} fn (optional) The default function to timeout
12629  * @param {Object} scope (optional) The default scope of that timeout
12630  * @param {Array} args (optional) The default Array of arguments
12631  */
12632 Roo.util.DelayedTask = function(fn, scope, args){
12633     var id = null, d, t;
12634
12635     var call = function(){
12636         var now = new Date().getTime();
12637         if(now - t >= d){
12638             clearInterval(id);
12639             id = null;
12640             fn.apply(scope, args || []);
12641         }
12642     };
12643     /**
12644      * Cancels any pending timeout and queues a new one
12645      * @param {Number} delay The milliseconds to delay
12646      * @param {Function} newFn (optional) Overrides function passed to constructor
12647      * @param {Object} newScope (optional) Overrides scope passed to constructor
12648      * @param {Array} newArgs (optional) Overrides args passed to constructor
12649      */
12650     this.delay = function(delay, newFn, newScope, newArgs){
12651         if(id && delay != d){
12652             this.cancel();
12653         }
12654         d = delay;
12655         t = new Date().getTime();
12656         fn = newFn || fn;
12657         scope = newScope || scope;
12658         args = newArgs || args;
12659         if(!id){
12660             id = setInterval(call, d);
12661         }
12662     };
12663
12664     /**
12665      * Cancel the last queued timeout
12666      */
12667     this.cancel = function(){
12668         if(id){
12669             clearInterval(id);
12670             id = null;
12671         }
12672     };
12673 };/*
12674  * Based on:
12675  * Ext JS Library 1.1.1
12676  * Copyright(c) 2006-2007, Ext JS, LLC.
12677  *
12678  * Originally Released Under LGPL - original licence link has changed is not relivant.
12679  *
12680  * Fork - LGPL
12681  * <script type="text/javascript">
12682  */
12683  
12684  
12685 Roo.util.TaskRunner = function(interval){
12686     interval = interval || 10;
12687     var tasks = [], removeQueue = [];
12688     var id = 0;
12689     var running = false;
12690
12691     var stopThread = function(){
12692         running = false;
12693         clearInterval(id);
12694         id = 0;
12695     };
12696
12697     var startThread = function(){
12698         if(!running){
12699             running = true;
12700             id = setInterval(runTasks, interval);
12701         }
12702     };
12703
12704     var removeTask = function(task){
12705         removeQueue.push(task);
12706         if(task.onStop){
12707             task.onStop();
12708         }
12709     };
12710
12711     var runTasks = function(){
12712         if(removeQueue.length > 0){
12713             for(var i = 0, len = removeQueue.length; i < len; i++){
12714                 tasks.remove(removeQueue[i]);
12715             }
12716             removeQueue = [];
12717             if(tasks.length < 1){
12718                 stopThread();
12719                 return;
12720             }
12721         }
12722         var now = new Date().getTime();
12723         for(var i = 0, len = tasks.length; i < len; ++i){
12724             var t = tasks[i];
12725             var itime = now - t.taskRunTime;
12726             if(t.interval <= itime){
12727                 var rt = t.run.apply(t.scope || t, t.args || [++t.taskRunCount]);
12728                 t.taskRunTime = now;
12729                 if(rt === false || t.taskRunCount === t.repeat){
12730                     removeTask(t);
12731                     return;
12732                 }
12733             }
12734             if(t.duration && t.duration <= (now - t.taskStartTime)){
12735                 removeTask(t);
12736             }
12737         }
12738     };
12739
12740     /**
12741      * Queues a new task.
12742      * @param {Object} task
12743      */
12744     this.start = function(task){
12745         tasks.push(task);
12746         task.taskStartTime = new Date().getTime();
12747         task.taskRunTime = 0;
12748         task.taskRunCount = 0;
12749         startThread();
12750         return task;
12751     };
12752
12753     this.stop = function(task){
12754         removeTask(task);
12755         return task;
12756     };
12757
12758     this.stopAll = function(){
12759         stopThread();
12760         for(var i = 0, len = tasks.length; i < len; i++){
12761             if(tasks[i].onStop){
12762                 tasks[i].onStop();
12763             }
12764         }
12765         tasks = [];
12766         removeQueue = [];
12767     };
12768 };
12769
12770 Roo.TaskMgr = new Roo.util.TaskRunner();/*
12771  * Based on:
12772  * Ext JS Library 1.1.1
12773  * Copyright(c) 2006-2007, Ext JS, LLC.
12774  *
12775  * Originally Released Under LGPL - original licence link has changed is not relivant.
12776  *
12777  * Fork - LGPL
12778  * <script type="text/javascript">
12779  */
12780
12781  
12782 /**
12783  * @class Roo.util.MixedCollection
12784  * @extends Roo.util.Observable
12785  * A Collection class that maintains both numeric indexes and keys and exposes events.
12786  * @constructor
12787  * @param {Boolean} allowFunctions True if the addAll function should add function references to the
12788  * collection (defaults to false)
12789  * @param {Function} keyFn A function that can accept an item of the type(s) stored in this MixedCollection
12790  * and return the key value for that item.  This is used when available to look up the key on items that
12791  * were passed without an explicit key parameter to a MixedCollection method.  Passing this parameter is
12792  * equivalent to providing an implementation for the {@link #getKey} method.
12793  */
12794 Roo.util.MixedCollection = function(allowFunctions, keyFn){
12795     this.items = [];
12796     this.map = {};
12797     this.keys = [];
12798     this.length = 0;
12799     this.addEvents({
12800         /**
12801          * @event clear
12802          * Fires when the collection is cleared.
12803          */
12804         "clear" : true,
12805         /**
12806          * @event add
12807          * Fires when an item is added to the collection.
12808          * @param {Number} index The index at which the item was added.
12809          * @param {Object} o The item added.
12810          * @param {String} key The key associated with the added item.
12811          */
12812         "add" : true,
12813         /**
12814          * @event replace
12815          * Fires when an item is replaced in the collection.
12816          * @param {String} key he key associated with the new added.
12817          * @param {Object} old The item being replaced.
12818          * @param {Object} new The new item.
12819          */
12820         "replace" : true,
12821         /**
12822          * @event remove
12823          * Fires when an item is removed from the collection.
12824          * @param {Object} o The item being removed.
12825          * @param {String} key (optional) The key associated with the removed item.
12826          */
12827         "remove" : true,
12828         "sort" : true
12829     });
12830     this.allowFunctions = allowFunctions === true;
12831     if(keyFn){
12832         this.getKey = keyFn;
12833     }
12834     Roo.util.MixedCollection.superclass.constructor.call(this);
12835 };
12836
12837 Roo.extend(Roo.util.MixedCollection, Roo.util.Observable, {
12838     allowFunctions : false,
12839     
12840 /**
12841  * Adds an item to the collection.
12842  * @param {String} key The key to associate with the item
12843  * @param {Object} o The item to add.
12844  * @return {Object} The item added.
12845  */
12846     add : function(key, o){
12847         if(arguments.length == 1){
12848             o = arguments[0];
12849             key = this.getKey(o);
12850         }
12851         if(typeof key == "undefined" || key === null){
12852             this.length++;
12853             this.items.push(o);
12854             this.keys.push(null);
12855         }else{
12856             var old = this.map[key];
12857             if(old){
12858                 return this.replace(key, o);
12859             }
12860             this.length++;
12861             this.items.push(o);
12862             this.map[key] = o;
12863             this.keys.push(key);
12864         }
12865         this.fireEvent("add", this.length-1, o, key);
12866         return o;
12867     },
12868        
12869 /**
12870   * MixedCollection has a generic way to fetch keys if you implement getKey.
12871 <pre><code>
12872 // normal way
12873 var mc = new Roo.util.MixedCollection();
12874 mc.add(someEl.dom.id, someEl);
12875 mc.add(otherEl.dom.id, otherEl);
12876 //and so on
12877
12878 // using getKey
12879 var mc = new Roo.util.MixedCollection();
12880 mc.getKey = function(el){
12881    return el.dom.id;
12882 };
12883 mc.add(someEl);
12884 mc.add(otherEl);
12885
12886 // or via the constructor
12887 var mc = new Roo.util.MixedCollection(false, function(el){
12888    return el.dom.id;
12889 });
12890 mc.add(someEl);
12891 mc.add(otherEl);
12892 </code></pre>
12893  * @param o {Object} The item for which to find the key.
12894  * @return {Object} The key for the passed item.
12895  */
12896     getKey : function(o){
12897          return o.id; 
12898     },
12899    
12900 /**
12901  * Replaces an item in the collection.
12902  * @param {String} key The key associated with the item to replace, or the item to replace.
12903  * @param o {Object} o (optional) If the first parameter passed was a key, the item to associate with that key.
12904  * @return {Object}  The new item.
12905  */
12906     replace : function(key, o){
12907         if(arguments.length == 1){
12908             o = arguments[0];
12909             key = this.getKey(o);
12910         }
12911         var old = this.item(key);
12912         if(typeof key == "undefined" || key === null || typeof old == "undefined"){
12913              return this.add(key, o);
12914         }
12915         var index = this.indexOfKey(key);
12916         this.items[index] = o;
12917         this.map[key] = o;
12918         this.fireEvent("replace", key, old, o);
12919         return o;
12920     },
12921    
12922 /**
12923  * Adds all elements of an Array or an Object to the collection.
12924  * @param {Object/Array} objs An Object containing properties which will be added to the collection, or
12925  * an Array of values, each of which are added to the collection.
12926  */
12927     addAll : function(objs){
12928         if(arguments.length > 1 || objs instanceof Array){
12929             var args = arguments.length > 1 ? arguments : objs;
12930             for(var i = 0, len = args.length; i < len; i++){
12931                 this.add(args[i]);
12932             }
12933         }else{
12934             for(var key in objs){
12935                 if(this.allowFunctions || typeof objs[key] != "function"){
12936                     this.add(key, objs[key]);
12937                 }
12938             }
12939         }
12940     },
12941    
12942 /**
12943  * Executes the specified function once for every item in the collection, passing each
12944  * item as the first and only parameter. returning false from the function will stop the iteration.
12945  * @param {Function} fn The function to execute for each item.
12946  * @param {Object} scope (optional) The scope in which to execute the function.
12947  */
12948     each : function(fn, scope){
12949         var items = [].concat(this.items); // each safe for removal
12950         for(var i = 0, len = items.length; i < len; i++){
12951             if(fn.call(scope || items[i], items[i], i, len) === false){
12952                 break;
12953             }
12954         }
12955     },
12956    
12957 /**
12958  * Executes the specified function once for every key in the collection, passing each
12959  * key, and its associated item as the first two parameters.
12960  * @param {Function} fn The function to execute for each item.
12961  * @param {Object} scope (optional) The scope in which to execute the function.
12962  */
12963     eachKey : function(fn, scope){
12964         for(var i = 0, len = this.keys.length; i < len; i++){
12965             fn.call(scope || window, this.keys[i], this.items[i], i, len);
12966         }
12967     },
12968    
12969 /**
12970  * Returns the first item in the collection which elicits a true return value from the
12971  * passed selection function.
12972  * @param {Function} fn The selection function to execute for each item.
12973  * @param {Object} scope (optional) The scope in which to execute the function.
12974  * @return {Object} The first item in the collection which returned true from the selection function.
12975  */
12976     find : function(fn, scope){
12977         for(var i = 0, len = this.items.length; i < len; i++){
12978             if(fn.call(scope || window, this.items[i], this.keys[i])){
12979                 return this.items[i];
12980             }
12981         }
12982         return null;
12983     },
12984    
12985 /**
12986  * Inserts an item at the specified index in the collection.
12987  * @param {Number} index The index to insert the item at.
12988  * @param {String} key The key to associate with the new item, or the item itself.
12989  * @param {Object} o  (optional) If the second parameter was a key, the new item.
12990  * @return {Object} The item inserted.
12991  */
12992     insert : function(index, key, o){
12993         if(arguments.length == 2){
12994             o = arguments[1];
12995             key = this.getKey(o);
12996         }
12997         if(index >= this.length){
12998             return this.add(key, o);
12999         }
13000         this.length++;
13001         this.items.splice(index, 0, o);
13002         if(typeof key != "undefined" && key != null){
13003             this.map[key] = o;
13004         }
13005         this.keys.splice(index, 0, key);
13006         this.fireEvent("add", index, o, key);
13007         return o;
13008     },
13009    
13010 /**
13011  * Removed an item from the collection.
13012  * @param {Object} o The item to remove.
13013  * @return {Object} The item removed.
13014  */
13015     remove : function(o){
13016         return this.removeAt(this.indexOf(o));
13017     },
13018    
13019 /**
13020  * Remove an item from a specified index in the collection.
13021  * @param {Number} index The index within the collection of the item to remove.
13022  */
13023     removeAt : function(index){
13024         if(index < this.length && index >= 0){
13025             this.length--;
13026             var o = this.items[index];
13027             this.items.splice(index, 1);
13028             var key = this.keys[index];
13029             if(typeof key != "undefined"){
13030                 delete this.map[key];
13031             }
13032             this.keys.splice(index, 1);
13033             this.fireEvent("remove", o, key);
13034         }
13035     },
13036    
13037 /**
13038  * Removed an item associated with the passed key fom the collection.
13039  * @param {String} key The key of the item to remove.
13040  */
13041     removeKey : function(key){
13042         return this.removeAt(this.indexOfKey(key));
13043     },
13044    
13045 /**
13046  * Returns the number of items in the collection.
13047  * @return {Number} the number of items in the collection.
13048  */
13049     getCount : function(){
13050         return this.length; 
13051     },
13052    
13053 /**
13054  * Returns index within the collection of the passed Object.
13055  * @param {Object} o The item to find the index of.
13056  * @return {Number} index of the item.
13057  */
13058     indexOf : function(o){
13059         if(!this.items.indexOf){
13060             for(var i = 0, len = this.items.length; i < len; i++){
13061                 if(this.items[i] == o) return i;
13062             }
13063             return -1;
13064         }else{
13065             return this.items.indexOf(o);
13066         }
13067     },
13068    
13069 /**
13070  * Returns index within the collection of the passed key.
13071  * @param {String} key The key to find the index of.
13072  * @return {Number} index of the key.
13073  */
13074     indexOfKey : function(key){
13075         if(!this.keys.indexOf){
13076             for(var i = 0, len = this.keys.length; i < len; i++){
13077                 if(this.keys[i] == key) return i;
13078             }
13079             return -1;
13080         }else{
13081             return this.keys.indexOf(key);
13082         }
13083     },
13084    
13085 /**
13086  * Returns the item associated with the passed key OR index. Key has priority over index.
13087  * @param {String/Number} key The key or index of the item.
13088  * @return {Object} The item associated with the passed key.
13089  */
13090     item : function(key){
13091         var item = typeof this.map[key] != "undefined" ? this.map[key] : this.items[key];
13092         return typeof item != 'function' || this.allowFunctions ? item : null; // for prototype!
13093     },
13094     
13095 /**
13096  * Returns the item at the specified index.
13097  * @param {Number} index The index of the item.
13098  * @return {Object}
13099  */
13100     itemAt : function(index){
13101         return this.items[index];
13102     },
13103     
13104 /**
13105  * Returns the item associated with the passed key.
13106  * @param {String/Number} key The key of the item.
13107  * @return {Object} The item associated with the passed key.
13108  */
13109     key : function(key){
13110         return this.map[key];
13111     },
13112    
13113 /**
13114  * Returns true if the collection contains the passed Object as an item.
13115  * @param {Object} o  The Object to look for in the collection.
13116  * @return {Boolean} True if the collection contains the Object as an item.
13117  */
13118     contains : function(o){
13119         return this.indexOf(o) != -1;
13120     },
13121    
13122 /**
13123  * Returns true if the collection contains the passed Object as a key.
13124  * @param {String} key The key to look for in the collection.
13125  * @return {Boolean} True if the collection contains the Object as a key.
13126  */
13127     containsKey : function(key){
13128         return typeof this.map[key] != "undefined";
13129     },
13130    
13131 /**
13132  * Removes all items from the collection.
13133  */
13134     clear : function(){
13135         this.length = 0;
13136         this.items = [];
13137         this.keys = [];
13138         this.map = {};
13139         this.fireEvent("clear");
13140     },
13141    
13142 /**
13143  * Returns the first item in the collection.
13144  * @return {Object} the first item in the collection..
13145  */
13146     first : function(){
13147         return this.items[0]; 
13148     },
13149    
13150 /**
13151  * Returns the last item in the collection.
13152  * @return {Object} the last item in the collection..
13153  */
13154     last : function(){
13155         return this.items[this.length-1];   
13156     },
13157     
13158     _sort : function(property, dir, fn){
13159         var dsc = String(dir).toUpperCase() == "DESC" ? -1 : 1;
13160         fn = fn || function(a, b){
13161             return a-b;
13162         };
13163         var c = [], k = this.keys, items = this.items;
13164         for(var i = 0, len = items.length; i < len; i++){
13165             c[c.length] = {key: k[i], value: items[i], index: i};
13166         }
13167         c.sort(function(a, b){
13168             var v = fn(a[property], b[property]) * dsc;
13169             if(v == 0){
13170                 v = (a.index < b.index ? -1 : 1);
13171             }
13172             return v;
13173         });
13174         for(var i = 0, len = c.length; i < len; i++){
13175             items[i] = c[i].value;
13176             k[i] = c[i].key;
13177         }
13178         this.fireEvent("sort", this);
13179     },
13180     
13181     /**
13182      * Sorts this collection with the passed comparison function
13183      * @param {String} direction (optional) "ASC" or "DESC"
13184      * @param {Function} fn (optional) comparison function
13185      */
13186     sort : function(dir, fn){
13187         this._sort("value", dir, fn);
13188     },
13189     
13190     /**
13191      * Sorts this collection by keys
13192      * @param {String} direction (optional) "ASC" or "DESC"
13193      * @param {Function} fn (optional) a comparison function (defaults to case insensitive string)
13194      */
13195     keySort : function(dir, fn){
13196         this._sort("key", dir, fn || function(a, b){
13197             return String(a).toUpperCase()-String(b).toUpperCase();
13198         });
13199     },
13200     
13201     /**
13202      * Returns a range of items in this collection
13203      * @param {Number} startIndex (optional) defaults to 0
13204      * @param {Number} endIndex (optional) default to the last item
13205      * @return {Array} An array of items
13206      */
13207     getRange : function(start, end){
13208         var items = this.items;
13209         if(items.length < 1){
13210             return [];
13211         }
13212         start = start || 0;
13213         end = Math.min(typeof end == "undefined" ? this.length-1 : end, this.length-1);
13214         var r = [];
13215         if(start <= end){
13216             for(var i = start; i <= end; i++) {
13217                     r[r.length] = items[i];
13218             }
13219         }else{
13220             for(var i = start; i >= end; i--) {
13221                     r[r.length] = items[i];
13222             }
13223         }
13224         return r;
13225     },
13226         
13227     /**
13228      * Filter the <i>objects</i> in this collection by a specific property. 
13229      * Returns a new collection that has been filtered.
13230      * @param {String} property A property on your objects
13231      * @param {String/RegExp} value Either string that the property values 
13232      * should start with or a RegExp to test against the property
13233      * @return {MixedCollection} The new filtered collection
13234      */
13235     filter : function(property, value){
13236         if(!value.exec){ // not a regex
13237             value = String(value);
13238             if(value.length == 0){
13239                 return this.clone();
13240             }
13241             value = new RegExp("^" + Roo.escapeRe(value), "i");
13242         }
13243         return this.filterBy(function(o){
13244             return o && value.test(o[property]);
13245         });
13246         },
13247     
13248     /**
13249      * Filter by a function. * Returns a new collection that has been filtered.
13250      * The passed function will be called with each 
13251      * object in the collection. If the function returns true, the value is included 
13252      * otherwise it is filtered.
13253      * @param {Function} fn The function to be called, it will receive the args o (the object), k (the key)
13254      * @param {Object} scope (optional) The scope of the function (defaults to this) 
13255      * @return {MixedCollection} The new filtered collection
13256      */
13257     filterBy : function(fn, scope){
13258         var r = new Roo.util.MixedCollection();
13259         r.getKey = this.getKey;
13260         var k = this.keys, it = this.items;
13261         for(var i = 0, len = it.length; i < len; i++){
13262             if(fn.call(scope||this, it[i], k[i])){
13263                                 r.add(k[i], it[i]);
13264                         }
13265         }
13266         return r;
13267     },
13268     
13269     /**
13270      * Creates a duplicate of this collection
13271      * @return {MixedCollection}
13272      */
13273     clone : function(){
13274         var r = new Roo.util.MixedCollection();
13275         var k = this.keys, it = this.items;
13276         for(var i = 0, len = it.length; i < len; i++){
13277             r.add(k[i], it[i]);
13278         }
13279         r.getKey = this.getKey;
13280         return r;
13281     }
13282 });
13283 /**
13284  * Returns the item associated with the passed key or index.
13285  * @method
13286  * @param {String/Number} key The key or index of the item.
13287  * @return {Object} The item associated with the passed key.
13288  */
13289 Roo.util.MixedCollection.prototype.get = Roo.util.MixedCollection.prototype.item;/*
13290  * Based on:
13291  * Ext JS Library 1.1.1
13292  * Copyright(c) 2006-2007, Ext JS, LLC.
13293  *
13294  * Originally Released Under LGPL - original licence link has changed is not relivant.
13295  *
13296  * Fork - LGPL
13297  * <script type="text/javascript">
13298  */
13299 /**
13300  * @class Roo.util.JSON
13301  * Modified version of Douglas Crockford"s json.js that doesn"t
13302  * mess with the Object prototype 
13303  * http://www.json.org/js.html
13304  * @singleton
13305  */
13306 Roo.util.JSON = new (function(){
13307     var useHasOwn = {}.hasOwnProperty ? true : false;
13308     
13309     // crashes Safari in some instances
13310     //var validRE = /^("(\\.|[^"\\\n\r])*?"|[,:{}\[\]0-9.\-+Eaeflnr-u \n\r\t])+?$/;
13311     
13312     var pad = function(n) {
13313         return n < 10 ? "0" + n : n;
13314     };
13315     
13316     var m = {
13317         "\b": '\\b',
13318         "\t": '\\t',
13319         "\n": '\\n',
13320         "\f": '\\f',
13321         "\r": '\\r',
13322         '"' : '\\"',
13323         "\\": '\\\\'
13324     };
13325
13326     var encodeString = function(s){
13327         if (/["\\\x00-\x1f]/.test(s)) {
13328             return '"' + s.replace(/([\x00-\x1f\\"])/g, function(a, b) {
13329                 var c = m[b];
13330                 if(c){
13331                     return c;
13332                 }
13333                 c = b.charCodeAt();
13334                 return "\\u00" +
13335                     Math.floor(c / 16).toString(16) +
13336                     (c % 16).toString(16);
13337             }) + '"';
13338         }
13339         return '"' + s + '"';
13340     };
13341     
13342     var encodeArray = function(o){
13343         var a = ["["], b, i, l = o.length, v;
13344             for (i = 0; i < l; i += 1) {
13345                 v = o[i];
13346                 switch (typeof v) {
13347                     case "undefined":
13348                     case "function":
13349                     case "unknown":
13350                         break;
13351                     default:
13352                         if (b) {
13353                             a.push(',');
13354                         }
13355                         a.push(v === null ? "null" : Roo.util.JSON.encode(v));
13356                         b = true;
13357                 }
13358             }
13359             a.push("]");
13360             return a.join("");
13361     };
13362     
13363     var encodeDate = function(o){
13364         return '"' + o.getFullYear() + "-" +
13365                 pad(o.getMonth() + 1) + "-" +
13366                 pad(o.getDate()) + "T" +
13367                 pad(o.getHours()) + ":" +
13368                 pad(o.getMinutes()) + ":" +
13369                 pad(o.getSeconds()) + '"';
13370     };
13371     
13372     /**
13373      * Encodes an Object, Array or other value
13374      * @param {Mixed} o The variable to encode
13375      * @return {String} The JSON string
13376      */
13377     this.encode = function(o)
13378     {
13379         // should this be extended to fully wrap stringify..
13380         
13381         if(typeof o == "undefined" || o === null){
13382             return "null";
13383         }else if(o instanceof Array){
13384             return encodeArray(o);
13385         }else if(o instanceof Date){
13386             return encodeDate(o);
13387         }else if(typeof o == "string"){
13388             return encodeString(o);
13389         }else if(typeof o == "number"){
13390             return isFinite(o) ? String(o) : "null";
13391         }else if(typeof o == "boolean"){
13392             return String(o);
13393         }else {
13394             var a = ["{"], b, i, v;
13395             for (i in o) {
13396                 if(!useHasOwn || o.hasOwnProperty(i)) {
13397                     v = o[i];
13398                     switch (typeof v) {
13399                     case "undefined":
13400                     case "function":
13401                     case "unknown":
13402                         break;
13403                     default:
13404                         if(b){
13405                             a.push(',');
13406                         }
13407                         a.push(this.encode(i), ":",
13408                                 v === null ? "null" : this.encode(v));
13409                         b = true;
13410                     }
13411                 }
13412             }
13413             a.push("}");
13414             return a.join("");
13415         }
13416     };
13417     
13418     /**
13419      * Decodes (parses) a JSON string to an object. If the JSON is invalid, this function throws a SyntaxError.
13420      * @param {String} json The JSON string
13421      * @return {Object} The resulting object
13422      */
13423     this.decode = function(json){
13424         
13425         return  /** eval:var:json */ eval("(" + json + ')');
13426     };
13427 })();
13428 /** 
13429  * Shorthand for {@link Roo.util.JSON#encode}
13430  * @member Roo encode 
13431  * @method */
13432 Roo.encode = typeof(JSON) != 'undefined' && JSON.stringify ? JSON.stringify : Roo.util.JSON.encode;
13433 /** 
13434  * Shorthand for {@link Roo.util.JSON#decode}
13435  * @member Roo decode 
13436  * @method */
13437 Roo.decode = typeof(JSON) != 'undefined' && JSON.parse ? JSON.parse : Roo.util.JSON.decode;
13438 /*
13439  * Based on:
13440  * Ext JS Library 1.1.1
13441  * Copyright(c) 2006-2007, Ext JS, LLC.
13442  *
13443  * Originally Released Under LGPL - original licence link has changed is not relivant.
13444  *
13445  * Fork - LGPL
13446  * <script type="text/javascript">
13447  */
13448  
13449 /**
13450  * @class Roo.util.Format
13451  * Reusable data formatting functions
13452  * @singleton
13453  */
13454 Roo.util.Format = function(){
13455     var trimRe = /^\s+|\s+$/g;
13456     return {
13457         /**
13458          * Truncate a string and add an ellipsis ('...') to the end if it exceeds the specified length
13459          * @param {String} value The string to truncate
13460          * @param {Number} length The maximum length to allow before truncating
13461          * @return {String} The converted text
13462          */
13463         ellipsis : function(value, len){
13464             if(value && value.length > len){
13465                 return value.substr(0, len-3)+"...";
13466             }
13467             return value;
13468         },
13469
13470         /**
13471          * Checks a reference and converts it to empty string if it is undefined
13472          * @param {Mixed} value Reference to check
13473          * @return {Mixed} Empty string if converted, otherwise the original value
13474          */
13475         undef : function(value){
13476             return typeof value != "undefined" ? value : "";
13477         },
13478
13479         /**
13480          * Convert certain characters (&, <, >, and ') to their HTML character equivalents for literal display in web pages.
13481          * @param {String} value The string to encode
13482          * @return {String} The encoded text
13483          */
13484         htmlEncode : function(value){
13485             return !value ? value : String(value).replace(/&/g, "&amp;").replace(/>/g, "&gt;").replace(/</g, "&lt;").replace(/"/g, "&quot;");
13486         },
13487
13488         /**
13489          * Convert certain characters (&, <, >, and ') from their HTML character equivalents.
13490          * @param {String} value The string to decode
13491          * @return {String} The decoded text
13492          */
13493         htmlDecode : function(value){
13494             return !value ? value : String(value).replace(/&amp;/g, "&").replace(/&gt;/g, ">").replace(/&lt;/g, "<").replace(/&quot;/g, '"');
13495         },
13496
13497         /**
13498          * Trims any whitespace from either side of a string
13499          * @param {String} value The text to trim
13500          * @return {String} The trimmed text
13501          */
13502         trim : function(value){
13503             return String(value).replace(trimRe, "");
13504         },
13505
13506         /**
13507          * Returns a substring from within an original string
13508          * @param {String} value The original text
13509          * @param {Number} start The start index of the substring
13510          * @param {Number} length The length of the substring
13511          * @return {String} The substring
13512          */
13513         substr : function(value, start, length){
13514             return String(value).substr(start, length);
13515         },
13516
13517         /**
13518          * Converts a string to all lower case letters
13519          * @param {String} value The text to convert
13520          * @return {String} The converted text
13521          */
13522         lowercase : function(value){
13523             return String(value).toLowerCase();
13524         },
13525
13526         /**
13527          * Converts a string to all upper case letters
13528          * @param {String} value The text to convert
13529          * @return {String} The converted text
13530          */
13531         uppercase : function(value){
13532             return String(value).toUpperCase();
13533         },
13534
13535         /**
13536          * Converts the first character only of a string to upper case
13537          * @param {String} value The text to convert
13538          * @return {String} The converted text
13539          */
13540         capitalize : function(value){
13541             return !value ? value : value.charAt(0).toUpperCase() + value.substr(1).toLowerCase();
13542         },
13543
13544         // private
13545         call : function(value, fn){
13546             if(arguments.length > 2){
13547                 var args = Array.prototype.slice.call(arguments, 2);
13548                 args.unshift(value);
13549                  
13550                 return /** eval:var:value */  eval(fn).apply(window, args);
13551             }else{
13552                 /** eval:var:value */
13553                 return /** eval:var:value */ eval(fn).call(window, value);
13554             }
13555         },
13556
13557        
13558         /**
13559          * safer version of Math.toFixed..??/
13560          * @param {Number/String} value The numeric value to format
13561          * @param {Number/String} value Decimal places 
13562          * @return {String} The formatted currency string
13563          */
13564         toFixed : function(v, n)
13565         {
13566             // why not use to fixed - precision is buggered???
13567             if (!n) {
13568                 return Math.round(v-0);
13569             }
13570             var fact = Math.pow(10,n+1);
13571             v = (Math.round((v-0)*fact))/fact;
13572             var z = (''+fact).substring(2);
13573             if (v == Math.floor(v)) {
13574                 return Math.floor(v) + '.' + z;
13575             }
13576             
13577             // now just padd decimals..
13578             var ps = String(v).split('.');
13579             var fd = (ps[1] + z);
13580             var r = fd.substring(0,n); 
13581             var rm = fd.substring(n); 
13582             if (rm < 5) {
13583                 return ps[0] + '.' + r;
13584             }
13585             r*=1; // turn it into a number;
13586             r++;
13587             if (String(r).length != n) {
13588                 ps[0]*=1;
13589                 ps[0]++;
13590                 r = String(r).substring(1); // chop the end off.
13591             }
13592             
13593             return ps[0] + '.' + r;
13594              
13595         },
13596         
13597         /**
13598          * Format a number as US currency
13599          * @param {Number/String} value The numeric value to format
13600          * @return {String} The formatted currency string
13601          */
13602         usMoney : function(v){
13603             return '$' + Roo.util.Format.number(v);
13604         },
13605         
13606         /**
13607          * Format a number
13608          * eventually this should probably emulate php's number_format
13609          * @param {Number/String} value The numeric value to format
13610          * @param {Number} decimals number of decimal places
13611          * @return {String} The formatted currency string
13612          */
13613         number : function(v,decimals)
13614         {
13615             // multiply and round.
13616             decimals = typeof(decimals) == 'undefined' ? 2 : decimals;
13617             var mul = Math.pow(10, decimals);
13618             var zero = String(mul).substring(1);
13619             v = (Math.round((v-0)*mul))/mul;
13620             
13621             // if it's '0' number.. then
13622             
13623             //v = (v == Math.floor(v)) ? v + "." + zero : ((v*10 == Math.floor(v*10)) ? v + "0" : v);
13624             v = String(v);
13625             var ps = v.split('.');
13626             var whole = ps[0];
13627             
13628             
13629             var r = /(\d+)(\d{3})/;
13630             // add comma's
13631             while (r.test(whole)) {
13632                 whole = whole.replace(r, '$1' + ',' + '$2');
13633             }
13634             
13635             
13636             var sub = ps[1] ?
13637                     // has decimals..
13638                     (decimals ?  ('.'+ ps[1] + zero.substring(ps[1].length)) : '') :
13639                     // does not have decimals
13640                     (decimals ? ('.' + zero) : '');
13641             
13642             
13643             return whole + sub ;
13644         },
13645         
13646         /**
13647          * Parse a value into a formatted date using the specified format pattern.
13648          * @param {Mixed} value The value to format
13649          * @param {String} format (optional) Any valid date format string (defaults to 'm/d/Y')
13650          * @return {String} The formatted date string
13651          */
13652         date : function(v, format){
13653             if(!v){
13654                 return "";
13655             }
13656             if(!(v instanceof Date)){
13657                 v = new Date(Date.parse(v));
13658             }
13659             return v.dateFormat(format || Roo.util.Format.defaults.date);
13660         },
13661
13662         /**
13663          * Returns a date rendering function that can be reused to apply a date format multiple times efficiently
13664          * @param {String} format Any valid date format string
13665          * @return {Function} The date formatting function
13666          */
13667         dateRenderer : function(format){
13668             return function(v){
13669                 return Roo.util.Format.date(v, format);  
13670             };
13671         },
13672
13673         // private
13674         stripTagsRE : /<\/?[^>]+>/gi,
13675         
13676         /**
13677          * Strips all HTML tags
13678          * @param {Mixed} value The text from which to strip tags
13679          * @return {String} The stripped text
13680          */
13681         stripTags : function(v){
13682             return !v ? v : String(v).replace(this.stripTagsRE, "");
13683         }
13684     };
13685 }();
13686 Roo.util.Format.defaults = {
13687     date : 'd/M/Y'
13688 };/*
13689  * Based on:
13690  * Ext JS Library 1.1.1
13691  * Copyright(c) 2006-2007, Ext JS, LLC.
13692  *
13693  * Originally Released Under LGPL - original licence link has changed is not relivant.
13694  *
13695  * Fork - LGPL
13696  * <script type="text/javascript">
13697  */
13698
13699
13700  
13701
13702 /**
13703  * @class Roo.MasterTemplate
13704  * @extends Roo.Template
13705  * Provides a template that can have child templates. The syntax is:
13706 <pre><code>
13707 var t = new Roo.MasterTemplate(
13708         '&lt;select name="{name}"&gt;',
13709                 '&lt;tpl name="options"&gt;&lt;option value="{value:trim}"&gt;{text:ellipsis(10)}&lt;/option&gt;&lt;/tpl&gt;',
13710         '&lt;/select&gt;'
13711 );
13712 t.add('options', {value: 'foo', text: 'bar'});
13713 // or you can add multiple child elements in one shot
13714 t.addAll('options', [
13715     {value: 'foo', text: 'bar'},
13716     {value: 'foo2', text: 'bar2'},
13717     {value: 'foo3', text: 'bar3'}
13718 ]);
13719 // then append, applying the master template values
13720 t.append('my-form', {name: 'my-select'});
13721 </code></pre>
13722 * A name attribute for the child template is not required if you have only one child
13723 * template or you want to refer to them by index.
13724  */
13725 Roo.MasterTemplate = function(){
13726     Roo.MasterTemplate.superclass.constructor.apply(this, arguments);
13727     this.originalHtml = this.html;
13728     var st = {};
13729     var m, re = this.subTemplateRe;
13730     re.lastIndex = 0;
13731     var subIndex = 0;
13732     while(m = re.exec(this.html)){
13733         var name = m[1], content = m[2];
13734         st[subIndex] = {
13735             name: name,
13736             index: subIndex,
13737             buffer: [],
13738             tpl : new Roo.Template(content)
13739         };
13740         if(name){
13741             st[name] = st[subIndex];
13742         }
13743         st[subIndex].tpl.compile();
13744         st[subIndex].tpl.call = this.call.createDelegate(this);
13745         subIndex++;
13746     }
13747     this.subCount = subIndex;
13748     this.subs = st;
13749 };
13750 Roo.extend(Roo.MasterTemplate, Roo.Template, {
13751     /**
13752     * The regular expression used to match sub templates
13753     * @type RegExp
13754     * @property
13755     */
13756     subTemplateRe : /<tpl(?:\sname="([\w-]+)")?>((?:.|\n)*?)<\/tpl>/gi,
13757
13758     /**
13759      * Applies the passed values to a child template.
13760      * @param {String/Number} name (optional) The name or index of the child template
13761      * @param {Array/Object} values The values to be applied to the template
13762      * @return {MasterTemplate} this
13763      */
13764      add : function(name, values){
13765         if(arguments.length == 1){
13766             values = arguments[0];
13767             name = 0;
13768         }
13769         var s = this.subs[name];
13770         s.buffer[s.buffer.length] = s.tpl.apply(values);
13771         return this;
13772     },
13773
13774     /**
13775      * Applies all the passed values to a child template.
13776      * @param {String/Number} name (optional) The name or index of the child template
13777      * @param {Array} values The values to be applied to the template, this should be an array of objects.
13778      * @param {Boolean} reset (optional) True to reset the template first
13779      * @return {MasterTemplate} this
13780      */
13781     fill : function(name, values, reset){
13782         var a = arguments;
13783         if(a.length == 1 || (a.length == 2 && typeof a[1] == "boolean")){
13784             values = a[0];
13785             name = 0;
13786             reset = a[1];
13787         }
13788         if(reset){
13789             this.reset();
13790         }
13791         for(var i = 0, len = values.length; i < len; i++){
13792             this.add(name, values[i]);
13793         }
13794         return this;
13795     },
13796
13797     /**
13798      * Resets the template for reuse
13799      * @return {MasterTemplate} this
13800      */
13801      reset : function(){
13802         var s = this.subs;
13803         for(var i = 0; i < this.subCount; i++){
13804             s[i].buffer = [];
13805         }
13806         return this;
13807     },
13808
13809     applyTemplate : function(values){
13810         var s = this.subs;
13811         var replaceIndex = -1;
13812         this.html = this.originalHtml.replace(this.subTemplateRe, function(m, name){
13813             return s[++replaceIndex].buffer.join("");
13814         });
13815         return Roo.MasterTemplate.superclass.applyTemplate.call(this, values);
13816     },
13817
13818     apply : function(){
13819         return this.applyTemplate.apply(this, arguments);
13820     },
13821
13822     compile : function(){return this;}
13823 });
13824
13825 /**
13826  * Alias for fill().
13827  * @method
13828  */
13829 Roo.MasterTemplate.prototype.addAll = Roo.MasterTemplate.prototype.fill;
13830  /**
13831  * Creates a template from the passed element's value (display:none textarea, preferred) or innerHTML. e.g.
13832  * var tpl = Roo.MasterTemplate.from('element-id');
13833  * @param {String/HTMLElement} el
13834  * @param {Object} config
13835  * @static
13836  */
13837 Roo.MasterTemplate.from = function(el, config){
13838     el = Roo.getDom(el);
13839     return new Roo.MasterTemplate(el.value || el.innerHTML, config || '');
13840 };/*
13841  * Based on:
13842  * Ext JS Library 1.1.1
13843  * Copyright(c) 2006-2007, Ext JS, LLC.
13844  *
13845  * Originally Released Under LGPL - original licence link has changed is not relivant.
13846  *
13847  * Fork - LGPL
13848  * <script type="text/javascript">
13849  */
13850
13851  
13852 /**
13853  * @class Roo.util.CSS
13854  * Utility class for manipulating CSS rules
13855  * @singleton
13856  */
13857 Roo.util.CSS = function(){
13858         var rules = null;
13859         var doc = document;
13860
13861     var camelRe = /(-[a-z])/gi;
13862     var camelFn = function(m, a){ return a.charAt(1).toUpperCase(); };
13863
13864    return {
13865    /**
13866     * Very simple dynamic creation of stylesheets from a text blob of rules.  The text will wrapped in a style
13867     * tag and appended to the HEAD of the document.
13868     * @param {String|Object} cssText The text containing the css rules
13869     * @param {String} id An id to add to the stylesheet for later removal
13870     * @return {StyleSheet}
13871     */
13872     createStyleSheet : function(cssText, id){
13873         var ss;
13874         var head = doc.getElementsByTagName("head")[0];
13875         var nrules = doc.createElement("style");
13876         nrules.setAttribute("type", "text/css");
13877         if(id){
13878             nrules.setAttribute("id", id);
13879         }
13880         if (typeof(cssText) != 'string') {
13881             // support object maps..
13882             // not sure if this a good idea.. 
13883             // perhaps it should be merged with the general css handling
13884             // and handle js style props.
13885             var cssTextNew = [];
13886             for(var n in cssText) {
13887                 var citems = [];
13888                 for(var k in cssText[n]) {
13889                     citems.push( k + ' : ' +cssText[n][k] + ';' );
13890                 }
13891                 cssTextNew.push( n + ' { ' + citems.join(' ') + '} ');
13892                 
13893             }
13894             cssText = cssTextNew.join("\n");
13895             
13896         }
13897        
13898        
13899        if(Roo.isIE){
13900            head.appendChild(nrules);
13901            ss = nrules.styleSheet;
13902            ss.cssText = cssText;
13903        }else{
13904            try{
13905                 nrules.appendChild(doc.createTextNode(cssText));
13906            }catch(e){
13907                nrules.cssText = cssText; 
13908            }
13909            head.appendChild(nrules);
13910            ss = nrules.styleSheet ? nrules.styleSheet : (nrules.sheet || doc.styleSheets[doc.styleSheets.length-1]);
13911        }
13912        this.cacheStyleSheet(ss);
13913        return ss;
13914    },
13915
13916    /**
13917     * Removes a style or link tag by id
13918     * @param {String} id The id of the tag
13919     */
13920    removeStyleSheet : function(id){
13921        var existing = doc.getElementById(id);
13922        if(existing){
13923            existing.parentNode.removeChild(existing);
13924        }
13925    },
13926
13927    /**
13928     * Dynamically swaps an existing stylesheet reference for a new one
13929     * @param {String} id The id of an existing link tag to remove
13930     * @param {String} url The href of the new stylesheet to include
13931     */
13932    swapStyleSheet : function(id, url){
13933        this.removeStyleSheet(id);
13934        var ss = doc.createElement("link");
13935        ss.setAttribute("rel", "stylesheet");
13936        ss.setAttribute("type", "text/css");
13937        ss.setAttribute("id", id);
13938        ss.setAttribute("href", url);
13939        doc.getElementsByTagName("head")[0].appendChild(ss);
13940    },
13941    
13942    /**
13943     * Refresh the rule cache if you have dynamically added stylesheets
13944     * @return {Object} An object (hash) of rules indexed by selector
13945     */
13946    refreshCache : function(){
13947        return this.getRules(true);
13948    },
13949
13950    // private
13951    cacheStyleSheet : function(stylesheet){
13952        if(!rules){
13953            rules = {};
13954        }
13955        try{// try catch for cross domain access issue
13956            var ssRules = stylesheet.cssRules || stylesheet.rules;
13957            for(var j = ssRules.length-1; j >= 0; --j){
13958                rules[ssRules[j].selectorText] = ssRules[j];
13959            }
13960        }catch(e){}
13961    },
13962    
13963    /**
13964     * Gets all css rules for the document
13965     * @param {Boolean} refreshCache true to refresh the internal cache
13966     * @return {Object} An object (hash) of rules indexed by selector
13967     */
13968    getRules : function(refreshCache){
13969                 if(rules == null || refreshCache){
13970                         rules = {};
13971                         var ds = doc.styleSheets;
13972                         for(var i =0, len = ds.length; i < len; i++){
13973                             try{
13974                         this.cacheStyleSheet(ds[i]);
13975                     }catch(e){} 
13976                 }
13977                 }
13978                 return rules;
13979         },
13980         
13981         /**
13982     * Gets an an individual CSS rule by selector(s)
13983     * @param {String/Array} selector The CSS selector or an array of selectors to try. The first selector that is found is returned.
13984     * @param {Boolean} refreshCache true to refresh the internal cache if you have recently updated any rules or added styles dynamically
13985     * @return {CSSRule} The CSS rule or null if one is not found
13986     */
13987    getRule : function(selector, refreshCache){
13988                 var rs = this.getRules(refreshCache);
13989                 if(!(selector instanceof Array)){
13990                     return rs[selector];
13991                 }
13992                 for(var i = 0; i < selector.length; i++){
13993                         if(rs[selector[i]]){
13994                                 return rs[selector[i]];
13995                         }
13996                 }
13997                 return null;
13998         },
13999         
14000         
14001         /**
14002     * Updates a rule property
14003     * @param {String/Array} selector If it's an array it tries each selector until it finds one. Stops immediately once one is found.
14004     * @param {String} property The css property
14005     * @param {String} value The new value for the property
14006     * @return {Boolean} true If a rule was found and updated
14007     */
14008    updateRule : function(selector, property, value){
14009                 if(!(selector instanceof Array)){
14010                         var rule = this.getRule(selector);
14011                         if(rule){
14012                                 rule.style[property.replace(camelRe, camelFn)] = value;
14013                                 return true;
14014                         }
14015                 }else{
14016                         for(var i = 0; i < selector.length; i++){
14017                                 if(this.updateRule(selector[i], property, value)){
14018                                         return true;
14019                                 }
14020                         }
14021                 }
14022                 return false;
14023         }
14024    };   
14025 }();/*
14026  * Based on:
14027  * Ext JS Library 1.1.1
14028  * Copyright(c) 2006-2007, Ext JS, LLC.
14029  *
14030  * Originally Released Under LGPL - original licence link has changed is not relivant.
14031  *
14032  * Fork - LGPL
14033  * <script type="text/javascript">
14034  */
14035
14036  
14037
14038 /**
14039  * @class Roo.util.ClickRepeater
14040  * @extends Roo.util.Observable
14041  * 
14042  * A wrapper class which can be applied to any element. Fires a "click" event while the
14043  * mouse is pressed. The interval between firings may be specified in the config but
14044  * defaults to 10 milliseconds.
14045  * 
14046  * Optionally, a CSS class may be applied to the element during the time it is pressed.
14047  * 
14048  * @cfg {String/HTMLElement/Element} el The element to act as a button.
14049  * @cfg {Number} delay The initial delay before the repeating event begins firing.
14050  * Similar to an autorepeat key delay.
14051  * @cfg {Number} interval The interval between firings of the "click" event. Default 10 ms.
14052  * @cfg {String} pressClass A CSS class name to be applied to the element while pressed.
14053  * @cfg {Boolean} accelerate True if autorepeating should start slowly and accelerate.
14054  *           "interval" and "delay" are ignored. "immediate" is honored.
14055  * @cfg {Boolean} preventDefault True to prevent the default click event
14056  * @cfg {Boolean} stopDefault True to stop the default click event
14057  * 
14058  * @history
14059  *     2007-02-02 jvs Original code contributed by Nige "Animal" White
14060  *     2007-02-02 jvs Renamed to ClickRepeater
14061  *   2007-02-03 jvs Modifications for FF Mac and Safari 
14062  *
14063  *  @constructor
14064  * @param {String/HTMLElement/Element} el The element to listen on
14065  * @param {Object} config
14066  **/
14067 Roo.util.ClickRepeater = function(el, config)
14068 {
14069     this.el = Roo.get(el);
14070     this.el.unselectable();
14071
14072     Roo.apply(this, config);
14073
14074     this.addEvents({
14075     /**
14076      * @event mousedown
14077      * Fires when the mouse button is depressed.
14078      * @param {Roo.util.ClickRepeater} this
14079      */
14080         "mousedown" : true,
14081     /**
14082      * @event click
14083      * Fires on a specified interval during the time the element is pressed.
14084      * @param {Roo.util.ClickRepeater} this
14085      */
14086         "click" : true,
14087     /**
14088      * @event mouseup
14089      * Fires when the mouse key is released.
14090      * @param {Roo.util.ClickRepeater} this
14091      */
14092         "mouseup" : true
14093     });
14094
14095     this.el.on("mousedown", this.handleMouseDown, this);
14096     if(this.preventDefault || this.stopDefault){
14097         this.el.on("click", function(e){
14098             if(this.preventDefault){
14099                 e.preventDefault();
14100             }
14101             if(this.stopDefault){
14102                 e.stopEvent();
14103             }
14104         }, this);
14105     }
14106
14107     // allow inline handler
14108     if(this.handler){
14109         this.on("click", this.handler,  this.scope || this);
14110     }
14111
14112     Roo.util.ClickRepeater.superclass.constructor.call(this);
14113 };
14114
14115 Roo.extend(Roo.util.ClickRepeater, Roo.util.Observable, {
14116     interval : 20,
14117     delay: 250,
14118     preventDefault : true,
14119     stopDefault : false,
14120     timer : 0,
14121
14122     // private
14123     handleMouseDown : function(){
14124         clearTimeout(this.timer);
14125         this.el.blur();
14126         if(this.pressClass){
14127             this.el.addClass(this.pressClass);
14128         }
14129         this.mousedownTime = new Date();
14130
14131         Roo.get(document).on("mouseup", this.handleMouseUp, this);
14132         this.el.on("mouseout", this.handleMouseOut, this);
14133
14134         this.fireEvent("mousedown", this);
14135         this.fireEvent("click", this);
14136         
14137         this.timer = this.click.defer(this.delay || this.interval, this);
14138     },
14139
14140     // private
14141     click : function(){
14142         this.fireEvent("click", this);
14143         this.timer = this.click.defer(this.getInterval(), this);
14144     },
14145
14146     // private
14147     getInterval: function(){
14148         if(!this.accelerate){
14149             return this.interval;
14150         }
14151         var pressTime = this.mousedownTime.getElapsed();
14152         if(pressTime < 500){
14153             return 400;
14154         }else if(pressTime < 1700){
14155             return 320;
14156         }else if(pressTime < 2600){
14157             return 250;
14158         }else if(pressTime < 3500){
14159             return 180;
14160         }else if(pressTime < 4400){
14161             return 140;
14162         }else if(pressTime < 5300){
14163             return 80;
14164         }else if(pressTime < 6200){
14165             return 50;
14166         }else{
14167             return 10;
14168         }
14169     },
14170
14171     // private
14172     handleMouseOut : function(){
14173         clearTimeout(this.timer);
14174         if(this.pressClass){
14175             this.el.removeClass(this.pressClass);
14176         }
14177         this.el.on("mouseover", this.handleMouseReturn, this);
14178     },
14179
14180     // private
14181     handleMouseReturn : function(){
14182         this.el.un("mouseover", this.handleMouseReturn);
14183         if(this.pressClass){
14184             this.el.addClass(this.pressClass);
14185         }
14186         this.click();
14187     },
14188
14189     // private
14190     handleMouseUp : function(){
14191         clearTimeout(this.timer);
14192         this.el.un("mouseover", this.handleMouseReturn);
14193         this.el.un("mouseout", this.handleMouseOut);
14194         Roo.get(document).un("mouseup", this.handleMouseUp);
14195         this.el.removeClass(this.pressClass);
14196         this.fireEvent("mouseup", this);
14197     }
14198 });/*
14199  * Based on:
14200  * Ext JS Library 1.1.1
14201  * Copyright(c) 2006-2007, Ext JS, LLC.
14202  *
14203  * Originally Released Under LGPL - original licence link has changed is not relivant.
14204  *
14205  * Fork - LGPL
14206  * <script type="text/javascript">
14207  */
14208
14209  
14210 /**
14211  * @class Roo.KeyNav
14212  * <p>Provides a convenient wrapper for normalized keyboard navigation.  KeyNav allows you to bind
14213  * navigation keys to function calls that will get called when the keys are pressed, providing an easy
14214  * way to implement custom navigation schemes for any UI component.</p>
14215  * <p>The following are all of the possible keys that can be implemented: enter, left, right, up, down, tab, esc,
14216  * pageUp, pageDown, del, home, end.  Usage:</p>
14217  <pre><code>
14218 var nav = new Roo.KeyNav("my-element", {
14219     "left" : function(e){
14220         this.moveLeft(e.ctrlKey);
14221     },
14222     "right" : function(e){
14223         this.moveRight(e.ctrlKey);
14224     },
14225     "enter" : function(e){
14226         this.save();
14227     },
14228     scope : this
14229 });
14230 </code></pre>
14231  * @constructor
14232  * @param {String/HTMLElement/Roo.Element} el The element to bind to
14233  * @param {Object} config The config
14234  */
14235 Roo.KeyNav = function(el, config){
14236     this.el = Roo.get(el);
14237     Roo.apply(this, config);
14238     if(!this.disabled){
14239         this.disabled = true;
14240         this.enable();
14241     }
14242 };
14243
14244 Roo.KeyNav.prototype = {
14245     /**
14246      * @cfg {Boolean} disabled
14247      * True to disable this KeyNav instance (defaults to false)
14248      */
14249     disabled : false,
14250     /**
14251      * @cfg {String} defaultEventAction
14252      * The method to call on the {@link Roo.EventObject} after this KeyNav intercepts a key.  Valid values are
14253      * {@link Roo.EventObject#stopEvent}, {@link Roo.EventObject#preventDefault} and
14254      * {@link Roo.EventObject#stopPropagation} (defaults to 'stopEvent')
14255      */
14256     defaultEventAction: "stopEvent",
14257     /**
14258      * @cfg {Boolean} forceKeyDown
14259      * Handle the keydown event instead of keypress (defaults to false).  KeyNav automatically does this for IE since
14260      * IE does not propagate special keys on keypress, but setting this to true will force other browsers to also
14261      * handle keydown instead of keypress.
14262      */
14263     forceKeyDown : false,
14264
14265     // private
14266     prepareEvent : function(e){
14267         var k = e.getKey();
14268         var h = this.keyToHandler[k];
14269         //if(h && this[h]){
14270         //    e.stopPropagation();
14271         //}
14272         if(Roo.isSafari && h && k >= 37 && k <= 40){
14273             e.stopEvent();
14274         }
14275     },
14276
14277     // private
14278     relay : function(e){
14279         var k = e.getKey();
14280         var h = this.keyToHandler[k];
14281         if(h && this[h]){
14282             if(this.doRelay(e, this[h], h) !== true){
14283                 e[this.defaultEventAction]();
14284             }
14285         }
14286     },
14287
14288     // private
14289     doRelay : function(e, h, hname){
14290         return h.call(this.scope || this, e);
14291     },
14292
14293     // possible handlers
14294     enter : false,
14295     left : false,
14296     right : false,
14297     up : false,
14298     down : false,
14299     tab : false,
14300     esc : false,
14301     pageUp : false,
14302     pageDown : false,
14303     del : false,
14304     home : false,
14305     end : false,
14306
14307     // quick lookup hash
14308     keyToHandler : {
14309         37 : "left",
14310         39 : "right",
14311         38 : "up",
14312         40 : "down",
14313         33 : "pageUp",
14314         34 : "pageDown",
14315         46 : "del",
14316         36 : "home",
14317         35 : "end",
14318         13 : "enter",
14319         27 : "esc",
14320         9  : "tab"
14321     },
14322
14323         /**
14324          * Enable this KeyNav
14325          */
14326         enable: function(){
14327                 if(this.disabled){
14328             // ie won't do special keys on keypress, no one else will repeat keys with keydown
14329             // the EventObject will normalize Safari automatically
14330             if(this.forceKeyDown || Roo.isIE || Roo.isAir){
14331                 this.el.on("keydown", this.relay,  this);
14332             }else{
14333                 this.el.on("keydown", this.prepareEvent,  this);
14334                 this.el.on("keypress", this.relay,  this);
14335             }
14336                     this.disabled = false;
14337                 }
14338         },
14339
14340         /**
14341          * Disable this KeyNav
14342          */
14343         disable: function(){
14344                 if(!this.disabled){
14345                     if(this.forceKeyDown || Roo.isIE || Roo.isAir){
14346                 this.el.un("keydown", this.relay);
14347             }else{
14348                 this.el.un("keydown", this.prepareEvent);
14349                 this.el.un("keypress", this.relay);
14350             }
14351                     this.disabled = true;
14352                 }
14353         }
14354 };/*
14355  * Based on:
14356  * Ext JS Library 1.1.1
14357  * Copyright(c) 2006-2007, Ext JS, LLC.
14358  *
14359  * Originally Released Under LGPL - original licence link has changed is not relivant.
14360  *
14361  * Fork - LGPL
14362  * <script type="text/javascript">
14363  */
14364
14365  
14366 /**
14367  * @class Roo.KeyMap
14368  * Handles mapping keys to actions for an element. One key map can be used for multiple actions.
14369  * The constructor accepts the same config object as defined by {@link #addBinding}.
14370  * If you bind a callback function to a KeyMap, anytime the KeyMap handles an expected key
14371  * combination it will call the function with this signature (if the match is a multi-key
14372  * combination the callback will still be called only once): (String key, Roo.EventObject e)
14373  * A KeyMap can also handle a string representation of keys.<br />
14374  * Usage:
14375  <pre><code>
14376 // map one key by key code
14377 var map = new Roo.KeyMap("my-element", {
14378     key: 13, // or Roo.EventObject.ENTER
14379     fn: myHandler,
14380     scope: myObject
14381 });
14382
14383 // map multiple keys to one action by string
14384 var map = new Roo.KeyMap("my-element", {
14385     key: "a\r\n\t",
14386     fn: myHandler,
14387     scope: myObject
14388 });
14389
14390 // map multiple keys to multiple actions by strings and array of codes
14391 var map = new Roo.KeyMap("my-element", [
14392     {
14393         key: [10,13],
14394         fn: function(){ alert("Return was pressed"); }
14395     }, {
14396         key: "abc",
14397         fn: function(){ alert('a, b or c was pressed'); }
14398     }, {
14399         key: "\t",
14400         ctrl:true,
14401         shift:true,
14402         fn: function(){ alert('Control + shift + tab was pressed.'); }
14403     }
14404 ]);
14405 </code></pre>
14406  * <b>Note: A KeyMap starts enabled</b>
14407  * @constructor
14408  * @param {String/HTMLElement/Roo.Element} el The element to bind to
14409  * @param {Object} config The config (see {@link #addBinding})
14410  * @param {String} eventName (optional) The event to bind to (defaults to "keydown")
14411  */
14412 Roo.KeyMap = function(el, config, eventName){
14413     this.el  = Roo.get(el);
14414     this.eventName = eventName || "keydown";
14415     this.bindings = [];
14416     if(config){
14417         this.addBinding(config);
14418     }
14419     this.enable();
14420 };
14421
14422 Roo.KeyMap.prototype = {
14423     /**
14424      * True to stop the event from bubbling and prevent the default browser action if the
14425      * key was handled by the KeyMap (defaults to false)
14426      * @type Boolean
14427      */
14428     stopEvent : false,
14429
14430     /**
14431      * Add a new binding to this KeyMap. The following config object properties are supported:
14432      * <pre>
14433 Property    Type             Description
14434 ----------  ---------------  ----------------------------------------------------------------------
14435 key         String/Array     A single keycode or an array of keycodes to handle
14436 shift       Boolean          True to handle key only when shift is pressed (defaults to false)
14437 ctrl        Boolean          True to handle key only when ctrl is pressed (defaults to false)
14438 alt         Boolean          True to handle key only when alt is pressed (defaults to false)
14439 fn          Function         The function to call when KeyMap finds the expected key combination
14440 scope       Object           The scope of the callback function
14441 </pre>
14442      *
14443      * Usage:
14444      * <pre><code>
14445 // Create a KeyMap
14446 var map = new Roo.KeyMap(document, {
14447     key: Roo.EventObject.ENTER,
14448     fn: handleKey,
14449     scope: this
14450 });
14451
14452 //Add a new binding to the existing KeyMap later
14453 map.addBinding({
14454     key: 'abc',
14455     shift: true,
14456     fn: handleKey,
14457     scope: this
14458 });
14459 </code></pre>
14460      * @param {Object/Array} config A single KeyMap config or an array of configs
14461      */
14462         addBinding : function(config){
14463         if(config instanceof Array){
14464             for(var i = 0, len = config.length; i < len; i++){
14465                 this.addBinding(config[i]);
14466             }
14467             return;
14468         }
14469         var keyCode = config.key,
14470             shift = config.shift, 
14471             ctrl = config.ctrl, 
14472             alt = config.alt,
14473             fn = config.fn,
14474             scope = config.scope;
14475         if(typeof keyCode == "string"){
14476             var ks = [];
14477             var keyString = keyCode.toUpperCase();
14478             for(var j = 0, len = keyString.length; j < len; j++){
14479                 ks.push(keyString.charCodeAt(j));
14480             }
14481             keyCode = ks;
14482         }
14483         var keyArray = keyCode instanceof Array;
14484         var handler = function(e){
14485             if((!shift || e.shiftKey) && (!ctrl || e.ctrlKey) &&  (!alt || e.altKey)){
14486                 var k = e.getKey();
14487                 if(keyArray){
14488                     for(var i = 0, len = keyCode.length; i < len; i++){
14489                         if(keyCode[i] == k){
14490                           if(this.stopEvent){
14491                               e.stopEvent();
14492                           }
14493                           fn.call(scope || window, k, e);
14494                           return;
14495                         }
14496                     }
14497                 }else{
14498                     if(k == keyCode){
14499                         if(this.stopEvent){
14500                            e.stopEvent();
14501                         }
14502                         fn.call(scope || window, k, e);
14503                     }
14504                 }
14505             }
14506         };
14507         this.bindings.push(handler);  
14508         },
14509
14510     /**
14511      * Shorthand for adding a single key listener
14512      * @param {Number/Array/Object} key Either the numeric key code, array of key codes or an object with the
14513      * following options:
14514      * {key: (number or array), shift: (true/false), ctrl: (true/false), alt: (true/false)}
14515      * @param {Function} fn The function to call
14516      * @param {Object} scope (optional) The scope of the function
14517      */
14518     on : function(key, fn, scope){
14519         var keyCode, shift, ctrl, alt;
14520         if(typeof key == "object" && !(key instanceof Array)){
14521             keyCode = key.key;
14522             shift = key.shift;
14523             ctrl = key.ctrl;
14524             alt = key.alt;
14525         }else{
14526             keyCode = key;
14527         }
14528         this.addBinding({
14529             key: keyCode,
14530             shift: shift,
14531             ctrl: ctrl,
14532             alt: alt,
14533             fn: fn,
14534             scope: scope
14535         })
14536     },
14537
14538     // private
14539     handleKeyDown : function(e){
14540             if(this.enabled){ //just in case
14541             var b = this.bindings;
14542             for(var i = 0, len = b.length; i < len; i++){
14543                 b[i].call(this, e);
14544             }
14545             }
14546         },
14547         
14548         /**
14549          * Returns true if this KeyMap is enabled
14550          * @return {Boolean} 
14551          */
14552         isEnabled : function(){
14553             return this.enabled;  
14554         },
14555         
14556         /**
14557          * Enables this KeyMap
14558          */
14559         enable: function(){
14560                 if(!this.enabled){
14561                     this.el.on(this.eventName, this.handleKeyDown, this);
14562                     this.enabled = true;
14563                 }
14564         },
14565
14566         /**
14567          * Disable this KeyMap
14568          */
14569         disable: function(){
14570                 if(this.enabled){
14571                     this.el.removeListener(this.eventName, this.handleKeyDown, this);
14572                     this.enabled = false;
14573                 }
14574         }
14575 };/*
14576  * Based on:
14577  * Ext JS Library 1.1.1
14578  * Copyright(c) 2006-2007, Ext JS, LLC.
14579  *
14580  * Originally Released Under LGPL - original licence link has changed is not relivant.
14581  *
14582  * Fork - LGPL
14583  * <script type="text/javascript">
14584  */
14585
14586  
14587 /**
14588  * @class Roo.util.TextMetrics
14589  * Provides precise pixel measurements for blocks of text so that you can determine exactly how high and
14590  * wide, in pixels, a given block of text will be.
14591  * @singleton
14592  */
14593 Roo.util.TextMetrics = function(){
14594     var shared;
14595     return {
14596         /**
14597          * Measures the size of the specified text
14598          * @param {String/HTMLElement} el The element, dom node or id from which to copy existing CSS styles
14599          * that can affect the size of the rendered text
14600          * @param {String} text The text to measure
14601          * @param {Number} fixedWidth (optional) If the text will be multiline, you have to set a fixed width
14602          * in order to accurately measure the text height
14603          * @return {Object} An object containing the text's size {width: (width), height: (height)}
14604          */
14605         measure : function(el, text, fixedWidth){
14606             if(!shared){
14607                 shared = Roo.util.TextMetrics.Instance(el, fixedWidth);
14608             }
14609             shared.bind(el);
14610             shared.setFixedWidth(fixedWidth || 'auto');
14611             return shared.getSize(text);
14612         },
14613
14614         /**
14615          * Return a unique TextMetrics instance that can be bound directly to an element and reused.  This reduces
14616          * the overhead of multiple calls to initialize the style properties on each measurement.
14617          * @param {String/HTMLElement} el The element, dom node or id that the instance will be bound to
14618          * @param {Number} fixedWidth (optional) If the text will be multiline, you have to set a fixed width
14619          * in order to accurately measure the text height
14620          * @return {Roo.util.TextMetrics.Instance} instance The new instance
14621          */
14622         createInstance : function(el, fixedWidth){
14623             return Roo.util.TextMetrics.Instance(el, fixedWidth);
14624         }
14625     };
14626 }();
14627
14628  
14629
14630 Roo.util.TextMetrics.Instance = function(bindTo, fixedWidth){
14631     var ml = new Roo.Element(document.createElement('div'));
14632     document.body.appendChild(ml.dom);
14633     ml.position('absolute');
14634     ml.setLeftTop(-1000, -1000);
14635     ml.hide();
14636
14637     if(fixedWidth){
14638         ml.setWidth(fixedWidth);
14639     }
14640      
14641     var instance = {
14642         /**
14643          * Returns the size of the specified text based on the internal element's style and width properties
14644          * @memberOf Roo.util.TextMetrics.Instance#
14645          * @param {String} text The text to measure
14646          * @return {Object} An object containing the text's size {width: (width), height: (height)}
14647          */
14648         getSize : function(text){
14649             ml.update(text);
14650             var s = ml.getSize();
14651             ml.update('');
14652             return s;
14653         },
14654
14655         /**
14656          * Binds this TextMetrics instance to an element from which to copy existing CSS styles
14657          * that can affect the size of the rendered text
14658          * @memberOf Roo.util.TextMetrics.Instance#
14659          * @param {String/HTMLElement} el The element, dom node or id
14660          */
14661         bind : function(el){
14662             ml.setStyle(
14663                 Roo.fly(el).getStyles('font-size','font-style', 'font-weight', 'font-family','line-height')
14664             );
14665         },
14666
14667         /**
14668          * Sets a fixed width on the internal measurement element.  If the text will be multiline, you have
14669          * to set a fixed width in order to accurately measure the text height.
14670          * @memberOf Roo.util.TextMetrics.Instance#
14671          * @param {Number} width The width to set on the element
14672          */
14673         setFixedWidth : function(width){
14674             ml.setWidth(width);
14675         },
14676
14677         /**
14678          * Returns the measured width of the specified text
14679          * @memberOf Roo.util.TextMetrics.Instance#
14680          * @param {String} text The text to measure
14681          * @return {Number} width The width in pixels
14682          */
14683         getWidth : function(text){
14684             ml.dom.style.width = 'auto';
14685             return this.getSize(text).width;
14686         },
14687
14688         /**
14689          * Returns the measured height of the specified text.  For multiline text, be sure to call
14690          * {@link #setFixedWidth} if necessary.
14691          * @memberOf Roo.util.TextMetrics.Instance#
14692          * @param {String} text The text to measure
14693          * @return {Number} height The height in pixels
14694          */
14695         getHeight : function(text){
14696             return this.getSize(text).height;
14697         }
14698     };
14699
14700     instance.bind(bindTo);
14701
14702     return instance;
14703 };
14704
14705 // backwards compat
14706 Roo.Element.measureText = Roo.util.TextMetrics.measure;/*
14707  * Based on:
14708  * Ext JS Library 1.1.1
14709  * Copyright(c) 2006-2007, Ext JS, LLC.
14710  *
14711  * Originally Released Under LGPL - original licence link has changed is not relivant.
14712  *
14713  * Fork - LGPL
14714  * <script type="text/javascript">
14715  */
14716
14717 /**
14718  * @class Roo.state.Provider
14719  * Abstract base class for state provider implementations. This class provides methods
14720  * for encoding and decoding <b>typed</b> variables including dates and defines the 
14721  * Provider interface.
14722  */
14723 Roo.state.Provider = function(){
14724     /**
14725      * @event statechange
14726      * Fires when a state change occurs.
14727      * @param {Provider} this This state provider
14728      * @param {String} key The state key which was changed
14729      * @param {String} value The encoded value for the state
14730      */
14731     this.addEvents({
14732         "statechange": true
14733     });
14734     this.state = {};
14735     Roo.state.Provider.superclass.constructor.call(this);
14736 };
14737 Roo.extend(Roo.state.Provider, Roo.util.Observable, {
14738     /**
14739      * Returns the current value for a key
14740      * @param {String} name The key name
14741      * @param {Mixed} defaultValue A default value to return if the key's value is not found
14742      * @return {Mixed} The state data
14743      */
14744     get : function(name, defaultValue){
14745         return typeof this.state[name] == "undefined" ?
14746             defaultValue : this.state[name];
14747     },
14748     
14749     /**
14750      * Clears a value from the state
14751      * @param {String} name The key name
14752      */
14753     clear : function(name){
14754         delete this.state[name];
14755         this.fireEvent("statechange", this, name, null);
14756     },
14757     
14758     /**
14759      * Sets the value for a key
14760      * @param {String} name The key name
14761      * @param {Mixed} value The value to set
14762      */
14763     set : function(name, value){
14764         this.state[name] = value;
14765         this.fireEvent("statechange", this, name, value);
14766     },
14767     
14768     /**
14769      * Decodes a string previously encoded with {@link #encodeValue}.
14770      * @param {String} value The value to decode
14771      * @return {Mixed} The decoded value
14772      */
14773     decodeValue : function(cookie){
14774         var re = /^(a|n|d|b|s|o)\:(.*)$/;
14775         var matches = re.exec(unescape(cookie));
14776         if(!matches || !matches[1]) return; // non state cookie
14777         var type = matches[1];
14778         var v = matches[2];
14779         switch(type){
14780             case "n":
14781                 return parseFloat(v);
14782             case "d":
14783                 return new Date(Date.parse(v));
14784             case "b":
14785                 return (v == "1");
14786             case "a":
14787                 var all = [];
14788                 var values = v.split("^");
14789                 for(var i = 0, len = values.length; i < len; i++){
14790                     all.push(this.decodeValue(values[i]));
14791                 }
14792                 return all;
14793            case "o":
14794                 var all = {};
14795                 var values = v.split("^");
14796                 for(var i = 0, len = values.length; i < len; i++){
14797                     var kv = values[i].split("=");
14798                     all[kv[0]] = this.decodeValue(kv[1]);
14799                 }
14800                 return all;
14801            default:
14802                 return v;
14803         }
14804     },
14805     
14806     /**
14807      * Encodes a value including type information.  Decode with {@link #decodeValue}.
14808      * @param {Mixed} value The value to encode
14809      * @return {String} The encoded value
14810      */
14811     encodeValue : function(v){
14812         var enc;
14813         if(typeof v == "number"){
14814             enc = "n:" + v;
14815         }else if(typeof v == "boolean"){
14816             enc = "b:" + (v ? "1" : "0");
14817         }else if(v instanceof Date){
14818             enc = "d:" + v.toGMTString();
14819         }else if(v instanceof Array){
14820             var flat = "";
14821             for(var i = 0, len = v.length; i < len; i++){
14822                 flat += this.encodeValue(v[i]);
14823                 if(i != len-1) flat += "^";
14824             }
14825             enc = "a:" + flat;
14826         }else if(typeof v == "object"){
14827             var flat = "";
14828             for(var key in v){
14829                 if(typeof v[key] != "function"){
14830                     flat += key + "=" + this.encodeValue(v[key]) + "^";
14831                 }
14832             }
14833             enc = "o:" + flat.substring(0, flat.length-1);
14834         }else{
14835             enc = "s:" + v;
14836         }
14837         return escape(enc);        
14838     }
14839 });
14840
14841 /*
14842  * Based on:
14843  * Ext JS Library 1.1.1
14844  * Copyright(c) 2006-2007, Ext JS, LLC.
14845  *
14846  * Originally Released Under LGPL - original licence link has changed is not relivant.
14847  *
14848  * Fork - LGPL
14849  * <script type="text/javascript">
14850  */
14851 /**
14852  * @class Roo.state.Manager
14853  * This is the global state manager. By default all components that are "state aware" check this class
14854  * for state information if you don't pass them a custom state provider. In order for this class
14855  * to be useful, it must be initialized with a provider when your application initializes.
14856  <pre><code>
14857 // in your initialization function
14858 init : function(){
14859    Roo.state.Manager.setProvider(new Roo.state.CookieProvider());
14860    ...
14861    // supposed you have a {@link Roo.BorderLayout}
14862    var layout = new Roo.BorderLayout(...);
14863    layout.restoreState();
14864    // or a {Roo.BasicDialog}
14865    var dialog = new Roo.BasicDialog(...);
14866    dialog.restoreState();
14867  </code></pre>
14868  * @singleton
14869  */
14870 Roo.state.Manager = function(){
14871     var provider = new Roo.state.Provider();
14872     
14873     return {
14874         /**
14875          * Configures the default state provider for your application
14876          * @param {Provider} stateProvider The state provider to set
14877          */
14878         setProvider : function(stateProvider){
14879             provider = stateProvider;
14880         },
14881         
14882         /**
14883          * Returns the current value for a key
14884          * @param {String} name The key name
14885          * @param {Mixed} defaultValue The default value to return if the key lookup does not match
14886          * @return {Mixed} The state data
14887          */
14888         get : function(key, defaultValue){
14889             return provider.get(key, defaultValue);
14890         },
14891         
14892         /**
14893          * Sets the value for a key
14894          * @param {String} name The key name
14895          * @param {Mixed} value The state data
14896          */
14897          set : function(key, value){
14898             provider.set(key, value);
14899         },
14900         
14901         /**
14902          * Clears a value from the state
14903          * @param {String} name The key name
14904          */
14905         clear : function(key){
14906             provider.clear(key);
14907         },
14908         
14909         /**
14910          * Gets the currently configured state provider
14911          * @return {Provider} The state provider
14912          */
14913         getProvider : function(){
14914             return provider;
14915         }
14916     };
14917 }();
14918 /*
14919  * Based on:
14920  * Ext JS Library 1.1.1
14921  * Copyright(c) 2006-2007, Ext JS, LLC.
14922  *
14923  * Originally Released Under LGPL - original licence link has changed is not relivant.
14924  *
14925  * Fork - LGPL
14926  * <script type="text/javascript">
14927  */
14928 /**
14929  * @class Roo.state.CookieProvider
14930  * @extends Roo.state.Provider
14931  * The default Provider implementation which saves state via cookies.
14932  * <br />Usage:
14933  <pre><code>
14934    var cp = new Roo.state.CookieProvider({
14935        path: "/cgi-bin/",
14936        expires: new Date(new Date().getTime()+(1000*60*60*24*30)); //30 days
14937        domain: "roojs.com"
14938    })
14939    Roo.state.Manager.setProvider(cp);
14940  </code></pre>
14941  * @cfg {String} path The path for which the cookie is active (defaults to root '/' which makes it active for all pages in the site)
14942  * @cfg {Date} expires The cookie expiration date (defaults to 7 days from now)
14943  * @cfg {String} domain The domain to save the cookie for.  Note that you cannot specify a different domain than
14944  * your page is on, but you can specify a sub-domain, or simply the domain itself like 'roojs.com' to include
14945  * all sub-domains if you need to access cookies across different sub-domains (defaults to null which uses the same
14946  * domain the page is running on including the 'www' like 'www.roojs.com')
14947  * @cfg {Boolean} secure True if the site is using SSL (defaults to false)
14948  * @constructor
14949  * Create a new CookieProvider
14950  * @param {Object} config The configuration object
14951  */
14952 Roo.state.CookieProvider = function(config){
14953     Roo.state.CookieProvider.superclass.constructor.call(this);
14954     this.path = "/";
14955     this.expires = new Date(new Date().getTime()+(1000*60*60*24*7)); //7 days
14956     this.domain = null;
14957     this.secure = false;
14958     Roo.apply(this, config);
14959     this.state = this.readCookies();
14960 };
14961
14962 Roo.extend(Roo.state.CookieProvider, Roo.state.Provider, {
14963     // private
14964     set : function(name, value){
14965         if(typeof value == "undefined" || value === null){
14966             this.clear(name);
14967             return;
14968         }
14969         this.setCookie(name, value);
14970         Roo.state.CookieProvider.superclass.set.call(this, name, value);
14971     },
14972
14973     // private
14974     clear : function(name){
14975         this.clearCookie(name);
14976         Roo.state.CookieProvider.superclass.clear.call(this, name);
14977     },
14978
14979     // private
14980     readCookies : function(){
14981         var cookies = {};
14982         var c = document.cookie + ";";
14983         var re = /\s?(.*?)=(.*?);/g;
14984         var matches;
14985         while((matches = re.exec(c)) != null){
14986             var name = matches[1];
14987             var value = matches[2];
14988             if(name && name.substring(0,3) == "ys-"){
14989                 cookies[name.substr(3)] = this.decodeValue(value);
14990             }
14991         }
14992         return cookies;
14993     },
14994
14995     // private
14996     setCookie : function(name, value){
14997         document.cookie = "ys-"+ name + "=" + this.encodeValue(value) +
14998            ((this.expires == null) ? "" : ("; expires=" + this.expires.toGMTString())) +
14999            ((this.path == null) ? "" : ("; path=" + this.path)) +
15000            ((this.domain == null) ? "" : ("; domain=" + this.domain)) +
15001            ((this.secure == true) ? "; secure" : "");
15002     },
15003
15004     // private
15005     clearCookie : function(name){
15006         document.cookie = "ys-" + name + "=null; expires=Thu, 01-Jan-70 00:00:01 GMT" +
15007            ((this.path == null) ? "" : ("; path=" + this.path)) +
15008            ((this.domain == null) ? "" : ("; domain=" + this.domain)) +
15009            ((this.secure == true) ? "; secure" : "");
15010     }
15011 });/*
15012  * Based on:
15013  * Ext JS Library 1.1.1
15014  * Copyright(c) 2006-2007, Ext JS, LLC.
15015  *
15016  * Originally Released Under LGPL - original licence link has changed is not relivant.
15017  *
15018  * Fork - LGPL
15019  * <script type="text/javascript">
15020  */
15021  
15022
15023 /**
15024  * @class Roo.ComponentMgr
15025  * Provides a common registry of all components on a page so that they can be easily accessed by component id (see {@link Roo.getCmp}).
15026  * @singleton
15027  */
15028 Roo.ComponentMgr = function(){
15029     var all = new Roo.util.MixedCollection();
15030
15031     return {
15032         /**
15033          * Registers a component.
15034          * @param {Roo.Component} c The component
15035          */
15036         register : function(c){
15037             all.add(c);
15038         },
15039
15040         /**
15041          * Unregisters a component.
15042          * @param {Roo.Component} c The component
15043          */
15044         unregister : function(c){
15045             all.remove(c);
15046         },
15047
15048         /**
15049          * Returns a component by id
15050          * @param {String} id The component id
15051          */
15052         get : function(id){
15053             return all.get(id);
15054         },
15055
15056         /**
15057          * Registers a function that will be called when a specified component is added to ComponentMgr
15058          * @param {String} id The component id
15059          * @param {Funtction} fn The callback function
15060          * @param {Object} scope The scope of the callback
15061          */
15062         onAvailable : function(id, fn, scope){
15063             all.on("add", function(index, o){
15064                 if(o.id == id){
15065                     fn.call(scope || o, o);
15066                     all.un("add", fn, scope);
15067                 }
15068             });
15069         }
15070     };
15071 }();/*
15072  * Based on:
15073  * Ext JS Library 1.1.1
15074  * Copyright(c) 2006-2007, Ext JS, LLC.
15075  *
15076  * Originally Released Under LGPL - original licence link has changed is not relivant.
15077  *
15078  * Fork - LGPL
15079  * <script type="text/javascript">
15080  */
15081  
15082 /**
15083  * @class Roo.Component
15084  * @extends Roo.util.Observable
15085  * Base class for all major Roo components.  All subclasses of Component can automatically participate in the standard
15086  * Roo component lifecycle of creation, rendering and destruction.  They also have automatic support for basic hide/show
15087  * and enable/disable behavior.  Component allows any subclass to be lazy-rendered into any {@link Roo.Container} and
15088  * to be automatically registered with the {@link Roo.ComponentMgr} so that it can be referenced at any time via {@link Roo.getCmp}.
15089  * All visual components (widgets) that require rendering into a layout should subclass Component.
15090  * @constructor
15091  * @param {Roo.Element/String/Object} config The configuration options.  If an element is passed, it is set as the internal
15092  * 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
15093  * and is used as the component id.  Otherwise, it is assumed to be a standard config object and is applied to the component.
15094  */
15095 Roo.Component = function(config){
15096     config = config || {};
15097     if(config.tagName || config.dom || typeof config == "string"){ // element object
15098         config = {el: config, id: config.id || config};
15099     }
15100     this.initialConfig = config;
15101
15102     Roo.apply(this, config);
15103     this.addEvents({
15104         /**
15105          * @event disable
15106          * Fires after the component is disabled.
15107              * @param {Roo.Component} this
15108              */
15109         disable : true,
15110         /**
15111          * @event enable
15112          * Fires after the component is enabled.
15113              * @param {Roo.Component} this
15114              */
15115         enable : true,
15116         /**
15117          * @event beforeshow
15118          * Fires before the component is shown.  Return false to stop the show.
15119              * @param {Roo.Component} this
15120              */
15121         beforeshow : true,
15122         /**
15123          * @event show
15124          * Fires after the component is shown.
15125              * @param {Roo.Component} this
15126              */
15127         show : true,
15128         /**
15129          * @event beforehide
15130          * Fires before the component is hidden. Return false to stop the hide.
15131              * @param {Roo.Component} this
15132              */
15133         beforehide : true,
15134         /**
15135          * @event hide
15136          * Fires after the component is hidden.
15137              * @param {Roo.Component} this
15138              */
15139         hide : true,
15140         /**
15141          * @event beforerender
15142          * Fires before the component is rendered. Return false to stop the render.
15143              * @param {Roo.Component} this
15144              */
15145         beforerender : true,
15146         /**
15147          * @event render
15148          * Fires after the component is rendered.
15149              * @param {Roo.Component} this
15150              */
15151         render : true,
15152         /**
15153          * @event beforedestroy
15154          * Fires before the component is destroyed. Return false to stop the destroy.
15155              * @param {Roo.Component} this
15156              */
15157         beforedestroy : true,
15158         /**
15159          * @event destroy
15160          * Fires after the component is destroyed.
15161              * @param {Roo.Component} this
15162              */
15163         destroy : true
15164     });
15165     if(!this.id){
15166         this.id = "ext-comp-" + (++Roo.Component.AUTO_ID);
15167     }
15168     Roo.ComponentMgr.register(this);
15169     Roo.Component.superclass.constructor.call(this);
15170     this.initComponent();
15171     if(this.renderTo){ // not supported by all components yet. use at your own risk!
15172         this.render(this.renderTo);
15173         delete this.renderTo;
15174     }
15175 };
15176
15177 /** @private */
15178 Roo.Component.AUTO_ID = 1000;
15179
15180 Roo.extend(Roo.Component, Roo.util.Observable, {
15181     /**
15182      * @scope Roo.Component.prototype
15183      * @type {Boolean}
15184      * true if this component is hidden. Read-only.
15185      */
15186     hidden : false,
15187     /**
15188      * @type {Boolean}
15189      * true if this component is disabled. Read-only.
15190      */
15191     disabled : false,
15192     /**
15193      * @type {Boolean}
15194      * true if this component has been rendered. Read-only.
15195      */
15196     rendered : false,
15197     
15198     /** @cfg {String} disableClass
15199      * CSS class added to the component when it is disabled (defaults to "x-item-disabled").
15200      */
15201     disabledClass : "x-item-disabled",
15202         /** @cfg {Boolean} allowDomMove
15203          * Whether the component can move the Dom node when rendering (defaults to true).
15204          */
15205     allowDomMove : true,
15206     /** @cfg {String} hideMode
15207      * How this component should hidden. Supported values are
15208      * "visibility" (css visibility), "offsets" (negative offset position) and
15209      * "display" (css display) - defaults to "display".
15210      */
15211     hideMode: 'display',
15212
15213     /** @private */
15214     ctype : "Roo.Component",
15215
15216     /**
15217      * @cfg {String} actionMode 
15218      * which property holds the element that used for  hide() / show() / disable() / enable()
15219      * default is 'el' 
15220      */
15221     actionMode : "el",
15222
15223     /** @private */
15224     getActionEl : function(){
15225         return this[this.actionMode];
15226     },
15227
15228     initComponent : Roo.emptyFn,
15229     /**
15230      * If this is a lazy rendering component, render it to its container element.
15231      * @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.
15232      */
15233     render : function(container, position){
15234         if(!this.rendered && this.fireEvent("beforerender", this) !== false){
15235             if(!container && this.el){
15236                 this.el = Roo.get(this.el);
15237                 container = this.el.dom.parentNode;
15238                 this.allowDomMove = false;
15239             }
15240             this.container = Roo.get(container);
15241             this.rendered = true;
15242             if(position !== undefined){
15243                 if(typeof position == 'number'){
15244                     position = this.container.dom.childNodes[position];
15245                 }else{
15246                     position = Roo.getDom(position);
15247                 }
15248             }
15249             this.onRender(this.container, position || null);
15250             if(this.cls){
15251                 this.el.addClass(this.cls);
15252                 delete this.cls;
15253             }
15254             if(this.style){
15255                 this.el.applyStyles(this.style);
15256                 delete this.style;
15257             }
15258             this.fireEvent("render", this);
15259             this.afterRender(this.container);
15260             if(this.hidden){
15261                 this.hide();
15262             }
15263             if(this.disabled){
15264                 this.disable();
15265             }
15266         }
15267         return this;
15268     },
15269
15270     /** @private */
15271     // default function is not really useful
15272     onRender : function(ct, position){
15273         if(this.el){
15274             this.el = Roo.get(this.el);
15275             if(this.allowDomMove !== false){
15276                 ct.dom.insertBefore(this.el.dom, position);
15277             }
15278         }
15279     },
15280
15281     /** @private */
15282     getAutoCreate : function(){
15283         var cfg = typeof this.autoCreate == "object" ?
15284                       this.autoCreate : Roo.apply({}, this.defaultAutoCreate);
15285         if(this.id && !cfg.id){
15286             cfg.id = this.id;
15287         }
15288         return cfg;
15289     },
15290
15291     /** @private */
15292     afterRender : Roo.emptyFn,
15293
15294     /**
15295      * Destroys this component by purging any event listeners, removing the component's element from the DOM,
15296      * removing the component from its {@link Roo.Container} (if applicable) and unregistering it from {@link Roo.ComponentMgr}.
15297      */
15298     destroy : function(){
15299         if(this.fireEvent("beforedestroy", this) !== false){
15300             this.purgeListeners();
15301             this.beforeDestroy();
15302             if(this.rendered){
15303                 this.el.removeAllListeners();
15304                 this.el.remove();
15305                 if(this.actionMode == "container"){
15306                     this.container.remove();
15307                 }
15308             }
15309             this.onDestroy();
15310             Roo.ComponentMgr.unregister(this);
15311             this.fireEvent("destroy", this);
15312         }
15313     },
15314
15315         /** @private */
15316     beforeDestroy : function(){
15317
15318     },
15319
15320         /** @private */
15321         onDestroy : function(){
15322
15323     },
15324
15325     /**
15326      * Returns the underlying {@link Roo.Element}.
15327      * @return {Roo.Element} The element
15328      */
15329     getEl : function(){
15330         return this.el;
15331     },
15332
15333     /**
15334      * Returns the id of this component.
15335      * @return {String}
15336      */
15337     getId : function(){
15338         return this.id;
15339     },
15340
15341     /**
15342      * Try to focus this component.
15343      * @param {Boolean} selectText True to also select the text in this component (if applicable)
15344      * @return {Roo.Component} this
15345      */
15346     focus : function(selectText){
15347         if(this.rendered){
15348             this.el.focus();
15349             if(selectText === true){
15350                 this.el.dom.select();
15351             }
15352         }
15353         return this;
15354     },
15355
15356     /** @private */
15357     blur : function(){
15358         if(this.rendered){
15359             this.el.blur();
15360         }
15361         return this;
15362     },
15363
15364     /**
15365      * Disable this component.
15366      * @return {Roo.Component} this
15367      */
15368     disable : function(){
15369         if(this.rendered){
15370             this.onDisable();
15371         }
15372         this.disabled = true;
15373         this.fireEvent("disable", this);
15374         return this;
15375     },
15376
15377         // private
15378     onDisable : function(){
15379         this.getActionEl().addClass(this.disabledClass);
15380         this.el.dom.disabled = true;
15381     },
15382
15383     /**
15384      * Enable this component.
15385      * @return {Roo.Component} this
15386      */
15387     enable : function(){
15388         if(this.rendered){
15389             this.onEnable();
15390         }
15391         this.disabled = false;
15392         this.fireEvent("enable", this);
15393         return this;
15394     },
15395
15396         // private
15397     onEnable : function(){
15398         this.getActionEl().removeClass(this.disabledClass);
15399         this.el.dom.disabled = false;
15400     },
15401
15402     /**
15403      * Convenience function for setting disabled/enabled by boolean.
15404      * @param {Boolean} disabled
15405      */
15406     setDisabled : function(disabled){
15407         this[disabled ? "disable" : "enable"]();
15408     },
15409
15410     /**
15411      * Show this component.
15412      * @return {Roo.Component} this
15413      */
15414     show: function(){
15415         if(this.fireEvent("beforeshow", this) !== false){
15416             this.hidden = false;
15417             if(this.rendered){
15418                 this.onShow();
15419             }
15420             this.fireEvent("show", this);
15421         }
15422         return this;
15423     },
15424
15425     // private
15426     onShow : function(){
15427         var ae = this.getActionEl();
15428         if(this.hideMode == 'visibility'){
15429             ae.dom.style.visibility = "visible";
15430         }else if(this.hideMode == 'offsets'){
15431             ae.removeClass('x-hidden');
15432         }else{
15433             ae.dom.style.display = "";
15434         }
15435     },
15436
15437     /**
15438      * Hide this component.
15439      * @return {Roo.Component} this
15440      */
15441     hide: function(){
15442         if(this.fireEvent("beforehide", this) !== false){
15443             this.hidden = true;
15444             if(this.rendered){
15445                 this.onHide();
15446             }
15447             this.fireEvent("hide", this);
15448         }
15449         return this;
15450     },
15451
15452     // private
15453     onHide : function(){
15454         var ae = this.getActionEl();
15455         if(this.hideMode == 'visibility'){
15456             ae.dom.style.visibility = "hidden";
15457         }else if(this.hideMode == 'offsets'){
15458             ae.addClass('x-hidden');
15459         }else{
15460             ae.dom.style.display = "none";
15461         }
15462     },
15463
15464     /**
15465      * Convenience function to hide or show this component by boolean.
15466      * @param {Boolean} visible True to show, false to hide
15467      * @return {Roo.Component} this
15468      */
15469     setVisible: function(visible){
15470         if(visible) {
15471             this.show();
15472         }else{
15473             this.hide();
15474         }
15475         return this;
15476     },
15477
15478     /**
15479      * Returns true if this component is visible.
15480      */
15481     isVisible : function(){
15482         return this.getActionEl().isVisible();
15483     },
15484
15485     cloneConfig : function(overrides){
15486         overrides = overrides || {};
15487         var id = overrides.id || Roo.id();
15488         var cfg = Roo.applyIf(overrides, this.initialConfig);
15489         cfg.id = id; // prevent dup id
15490         return new this.constructor(cfg);
15491     }
15492 });/*
15493  * Based on:
15494  * Ext JS Library 1.1.1
15495  * Copyright(c) 2006-2007, Ext JS, LLC.
15496  *
15497  * Originally Released Under LGPL - original licence link has changed is not relivant.
15498  *
15499  * Fork - LGPL
15500  * <script type="text/javascript">
15501  */
15502
15503 /**
15504  * @class Roo.BoxComponent
15505  * @extends Roo.Component
15506  * Base class for any visual {@link Roo.Component} that uses a box container.  BoxComponent provides automatic box
15507  * model adjustments for sizing and positioning and will work correctly withnin the Component rendering model.  All
15508  * container classes should subclass BoxComponent so that they will work consistently when nested within other Ext
15509  * layout containers.
15510  * @constructor
15511  * @param {Roo.Element/String/Object} config The configuration options.
15512  */
15513 Roo.BoxComponent = function(config){
15514     Roo.Component.call(this, config);
15515     this.addEvents({
15516         /**
15517          * @event resize
15518          * Fires after the component is resized.
15519              * @param {Roo.Component} this
15520              * @param {Number} adjWidth The box-adjusted width that was set
15521              * @param {Number} adjHeight The box-adjusted height that was set
15522              * @param {Number} rawWidth The width that was originally specified
15523              * @param {Number} rawHeight The height that was originally specified
15524              */
15525         resize : true,
15526         /**
15527          * @event move
15528          * Fires after the component is moved.
15529              * @param {Roo.Component} this
15530              * @param {Number} x The new x position
15531              * @param {Number} y The new y position
15532              */
15533         move : true
15534     });
15535 };
15536
15537 Roo.extend(Roo.BoxComponent, Roo.Component, {
15538     // private, set in afterRender to signify that the component has been rendered
15539     boxReady : false,
15540     // private, used to defer height settings to subclasses
15541     deferHeight: false,
15542     /** @cfg {Number} width
15543      * width (optional) size of component
15544      */
15545      /** @cfg {Number} height
15546      * height (optional) size of component
15547      */
15548      
15549     /**
15550      * Sets the width and height of the component.  This method fires the resize event.  This method can accept
15551      * either width and height as separate numeric arguments, or you can pass a size object like {width:10, height:20}.
15552      * @param {Number/Object} width The new width to set, or a size object in the format {width, height}
15553      * @param {Number} height The new height to set (not required if a size object is passed as the first arg)
15554      * @return {Roo.BoxComponent} this
15555      */
15556     setSize : function(w, h){
15557         // support for standard size objects
15558         if(typeof w == 'object'){
15559             h = w.height;
15560             w = w.width;
15561         }
15562         // not rendered
15563         if(!this.boxReady){
15564             this.width = w;
15565             this.height = h;
15566             return this;
15567         }
15568
15569         // prevent recalcs when not needed
15570         if(this.lastSize && this.lastSize.width == w && this.lastSize.height == h){
15571             return this;
15572         }
15573         this.lastSize = {width: w, height: h};
15574
15575         var adj = this.adjustSize(w, h);
15576         var aw = adj.width, ah = adj.height;
15577         if(aw !== undefined || ah !== undefined){ // this code is nasty but performs better with floaters
15578             var rz = this.getResizeEl();
15579             if(!this.deferHeight && aw !== undefined && ah !== undefined){
15580                 rz.setSize(aw, ah);
15581             }else if(!this.deferHeight && ah !== undefined){
15582                 rz.setHeight(ah);
15583             }else if(aw !== undefined){
15584                 rz.setWidth(aw);
15585             }
15586             this.onResize(aw, ah, w, h);
15587             this.fireEvent('resize', this, aw, ah, w, h);
15588         }
15589         return this;
15590     },
15591
15592     /**
15593      * Gets the current size of the component's underlying element.
15594      * @return {Object} An object containing the element's size {width: (element width), height: (element height)}
15595      */
15596     getSize : function(){
15597         return this.el.getSize();
15598     },
15599
15600     /**
15601      * Gets the current XY position of the component's underlying element.
15602      * @param {Boolean} local (optional) If true the element's left and top are returned instead of page XY (defaults to false)
15603      * @return {Array} The XY position of the element (e.g., [100, 200])
15604      */
15605     getPosition : function(local){
15606         if(local === true){
15607             return [this.el.getLeft(true), this.el.getTop(true)];
15608         }
15609         return this.xy || this.el.getXY();
15610     },
15611
15612     /**
15613      * Gets the current box measurements of the component's underlying element.
15614      * @param {Boolean} local (optional) If true the element's left and top are returned instead of page XY (defaults to false)
15615      * @returns {Object} box An object in the format {x, y, width, height}
15616      */
15617     getBox : function(local){
15618         var s = this.el.getSize();
15619         if(local){
15620             s.x = this.el.getLeft(true);
15621             s.y = this.el.getTop(true);
15622         }else{
15623             var xy = this.xy || this.el.getXY();
15624             s.x = xy[0];
15625             s.y = xy[1];
15626         }
15627         return s;
15628     },
15629
15630     /**
15631      * Sets the current box measurements of the component's underlying element.
15632      * @param {Object} box An object in the format {x, y, width, height}
15633      * @returns {Roo.BoxComponent} this
15634      */
15635     updateBox : function(box){
15636         this.setSize(box.width, box.height);
15637         this.setPagePosition(box.x, box.y);
15638         return this;
15639     },
15640
15641     // protected
15642     getResizeEl : function(){
15643         return this.resizeEl || this.el;
15644     },
15645
15646     // protected
15647     getPositionEl : function(){
15648         return this.positionEl || this.el;
15649     },
15650
15651     /**
15652      * Sets the left and top of the component.  To set the page XY position instead, use {@link #setPagePosition}.
15653      * This method fires the move event.
15654      * @param {Number} left The new left
15655      * @param {Number} top The new top
15656      * @returns {Roo.BoxComponent} this
15657      */
15658     setPosition : function(x, y){
15659         this.x = x;
15660         this.y = y;
15661         if(!this.boxReady){
15662             return this;
15663         }
15664         var adj = this.adjustPosition(x, y);
15665         var ax = adj.x, ay = adj.y;
15666
15667         var el = this.getPositionEl();
15668         if(ax !== undefined || ay !== undefined){
15669             if(ax !== undefined && ay !== undefined){
15670                 el.setLeftTop(ax, ay);
15671             }else if(ax !== undefined){
15672                 el.setLeft(ax);
15673             }else if(ay !== undefined){
15674                 el.setTop(ay);
15675             }
15676             this.onPosition(ax, ay);
15677             this.fireEvent('move', this, ax, ay);
15678         }
15679         return this;
15680     },
15681
15682     /**
15683      * Sets the page XY position of the component.  To set the left and top instead, use {@link #setPosition}.
15684      * This method fires the move event.
15685      * @param {Number} x The new x position
15686      * @param {Number} y The new y position
15687      * @returns {Roo.BoxComponent} this
15688      */
15689     setPagePosition : function(x, y){
15690         this.pageX = x;
15691         this.pageY = y;
15692         if(!this.boxReady){
15693             return;
15694         }
15695         if(x === undefined || y === undefined){ // cannot translate undefined points
15696             return;
15697         }
15698         var p = this.el.translatePoints(x, y);
15699         this.setPosition(p.left, p.top);
15700         return this;
15701     },
15702
15703     // private
15704     onRender : function(ct, position){
15705         Roo.BoxComponent.superclass.onRender.call(this, ct, position);
15706         if(this.resizeEl){
15707             this.resizeEl = Roo.get(this.resizeEl);
15708         }
15709         if(this.positionEl){
15710             this.positionEl = Roo.get(this.positionEl);
15711         }
15712     },
15713
15714     // private
15715     afterRender : function(){
15716         Roo.BoxComponent.superclass.afterRender.call(this);
15717         this.boxReady = true;
15718         this.setSize(this.width, this.height);
15719         if(this.x || this.y){
15720             this.setPosition(this.x, this.y);
15721         }
15722         if(this.pageX || this.pageY){
15723             this.setPagePosition(this.pageX, this.pageY);
15724         }
15725     },
15726
15727     /**
15728      * Force the component's size to recalculate based on the underlying element's current height and width.
15729      * @returns {Roo.BoxComponent} this
15730      */
15731     syncSize : function(){
15732         delete this.lastSize;
15733         this.setSize(this.el.getWidth(), this.el.getHeight());
15734         return this;
15735     },
15736
15737     /**
15738      * Called after the component is resized, this method is empty by default but can be implemented by any
15739      * subclass that needs to perform custom logic after a resize occurs.
15740      * @param {Number} adjWidth The box-adjusted width that was set
15741      * @param {Number} adjHeight The box-adjusted height that was set
15742      * @param {Number} rawWidth The width that was originally specified
15743      * @param {Number} rawHeight The height that was originally specified
15744      */
15745     onResize : function(adjWidth, adjHeight, rawWidth, rawHeight){
15746
15747     },
15748
15749     /**
15750      * Called after the component is moved, this method is empty by default but can be implemented by any
15751      * subclass that needs to perform custom logic after a move occurs.
15752      * @param {Number} x The new x position
15753      * @param {Number} y The new y position
15754      */
15755     onPosition : function(x, y){
15756
15757     },
15758
15759     // private
15760     adjustSize : function(w, h){
15761         if(this.autoWidth){
15762             w = 'auto';
15763         }
15764         if(this.autoHeight){
15765             h = 'auto';
15766         }
15767         return {width : w, height: h};
15768     },
15769
15770     // private
15771     adjustPosition : function(x, y){
15772         return {x : x, y: y};
15773     }
15774 });/*
15775  * Original code for Roojs - LGPL
15776  * <script type="text/javascript">
15777  */
15778  
15779 /**
15780  * @class Roo.XComponent
15781  * A delayed Element creator...
15782  * Or a way to group chunks of interface together.
15783  * technically this is a wrapper around a tree of Roo elements (which defines a 'module'),
15784  *  used in conjunction with XComponent.build() it will create an instance of each element,
15785  *  then call addxtype() to build the User interface.
15786  * 
15787  * Mypart.xyx = new Roo.XComponent({
15788
15789     parent : 'Mypart.xyz', // empty == document.element.!!
15790     order : '001',
15791     name : 'xxxx'
15792     region : 'xxxx'
15793     disabled : function() {} 
15794      
15795     tree : function() { // return an tree of xtype declared components
15796         var MODULE = this;
15797         return 
15798         {
15799             xtype : 'NestedLayoutPanel',
15800             // technicall
15801         }
15802      ]
15803  *})
15804  *
15805  *
15806  * It can be used to build a big heiracy, with parent etc.
15807  * or you can just use this to render a single compoent to a dom element
15808  * MYPART.render(Roo.Element | String(id) | dom_element )
15809  *
15810  *
15811  * Usage patterns.
15812  *
15813  * Classic Roo
15814  *
15815  * Roo is designed primarily as a single page application, so the UI build for a standard interface will
15816  * expect a single 'TOP' level module normally indicated by the 'parent' of the XComponent definition being defined as false.
15817  *
15818  * Each sub module is expected to have a parent pointing to the class name of it's parent module.
15819  *
15820  * When the top level is false, a 'Roo.BorderLayout' is created and the element is flagged as 'topModule'
15821  * - if mulitple topModules exist, the last one is defined as the top module.
15822  *
15823  * Embeded Roo
15824  * 
15825  * When the top level or multiple modules are to embedded into a existing HTML page,
15826  * the parent element can container '#id' of the element where the module will be drawn.
15827  *
15828  * Bootstrap Roo
15829  *
15830  * Unlike classic Roo, the bootstrap tends not to be used as a single page.
15831  * it relies more on a include mechanism, where sub modules are included into an outer page.
15832  * This is normally managed by the builder tools using Roo.apply( options, Included.Sub.Module )
15833  * 
15834  * Bootstrap Roo Included elements
15835  *
15836  * Our builder application needs the ability to preview these sub compoennts. They will normally have parent=false set,
15837  * hence confusing the component builder as it thinks there are multiple top level elements. 
15838  *
15839  * 
15840  * 
15841  * @extends Roo.util.Observable
15842  * @constructor
15843  * @param cfg {Object} configuration of component
15844  * 
15845  */
15846 Roo.XComponent = function(cfg) {
15847     Roo.apply(this, cfg);
15848     this.addEvents({ 
15849         /**
15850              * @event built
15851              * Fires when this the componnt is built
15852              * @param {Roo.XComponent} c the component
15853              */
15854         'built' : true
15855         
15856     });
15857     this.region = this.region || 'center'; // default..
15858     Roo.XComponent.register(this);
15859     this.modules = false;
15860     this.el = false; // where the layout goes..
15861     
15862     
15863 }
15864 Roo.extend(Roo.XComponent, Roo.util.Observable, {
15865     /**
15866      * @property el
15867      * The created element (with Roo.factory())
15868      * @type {Roo.Layout}
15869      */
15870     el  : false,
15871     
15872     /**
15873      * @property el
15874      * for BC  - use el in new code
15875      * @type {Roo.Layout}
15876      */
15877     panel : false,
15878     
15879     /**
15880      * @property layout
15881      * for BC  - use el in new code
15882      * @type {Roo.Layout}
15883      */
15884     layout : false,
15885     
15886      /**
15887      * @cfg {Function|boolean} disabled
15888      * If this module is disabled by some rule, return true from the funtion
15889      */
15890     disabled : false,
15891     
15892     /**
15893      * @cfg {String} parent 
15894      * Name of parent element which it get xtype added to..
15895      */
15896     parent: false,
15897     
15898     /**
15899      * @cfg {String} order
15900      * Used to set the order in which elements are created (usefull for multiple tabs)
15901      */
15902     
15903     order : false,
15904     /**
15905      * @cfg {String} name
15906      * String to display while loading.
15907      */
15908     name : false,
15909     /**
15910      * @cfg {String} region
15911      * Region to render component to (defaults to center)
15912      */
15913     region : 'center',
15914     
15915     /**
15916      * @cfg {Array} items
15917      * A single item array - the first element is the root of the tree..
15918      * It's done this way to stay compatible with the Xtype system...
15919      */
15920     items : false,
15921     
15922     /**
15923      * @property _tree
15924      * The method that retuns the tree of parts that make up this compoennt 
15925      * @type {function}
15926      */
15927     _tree  : false,
15928     
15929      /**
15930      * render
15931      * render element to dom or tree
15932      * @param {Roo.Element|String|DomElement} optional render to if parent is not set.
15933      */
15934     
15935     render : function(el)
15936     {
15937         
15938         el = el || false;
15939         var hp = this.parent ? 1 : 0;
15940         Roo.log(this);
15941         
15942         if (!el && typeof(this.parent) == 'string' && this.parent.substring(0,1) == '#') {
15943             // if parent is a '#.....' string, then let's use that..
15944             var ename = this.parent.substr(1);
15945             this.parent = false;
15946             Roo.log(ename);
15947             switch (ename) {
15948                 case 'bootstrap-body' :
15949                     if (typeof(Roo.bootstrap.Body) != 'undefined') {
15950                         this.parent = { el :  new  Roo.bootstrap.Body() };
15951                         Roo.log("setting el to doc body");
15952                          
15953                     } else {
15954                         throw "Container is bootstrap body, but Roo.bootstrap.Body is not defined";
15955                     }
15956                     break;
15957                 case 'bootstrap':
15958                     this.parent = { el : true};
15959                     // fall through
15960                 default:
15961                     el = Roo.get(ename);
15962                     break;
15963             }
15964                 
15965             
15966             if (!el && !this.parent) {
15967                 Roo.log("Warning - element can not be found :#" + ename );
15968                 return;
15969             }
15970         }
15971         Roo.log("EL:");Roo.log(el);
15972         Roo.log("this.parent.el:");Roo.log(this.parent.el);
15973         
15974         var tree = this._tree ? this._tree() : this.tree();
15975
15976         // altertive root elements ??? - we need a better way to indicate these.
15977         var is_alt = (typeof(Roo.bootstrap) != 'undefined' && tree.xns == Roo.bootstrap) ||
15978                         (typeof(Roo.mailer) != 'undefined' && tree.xns == Roo.mailer) ;
15979         
15980         if (!this.parent && is_alt) {
15981             //el = Roo.get(document.body);
15982             this.parent = { el : true };
15983         }
15984             
15985             
15986         
15987         if (!this.parent) {
15988             
15989             Roo.log("no parent - creating one");
15990             
15991             el = el ? Roo.get(el) : false;      
15992             
15993             // it's a top level one..
15994             this.parent =  {
15995                 el : new Roo.BorderLayout(el || document.body, {
15996                 
15997                      center: {
15998                          titlebar: false,
15999                          autoScroll:false,
16000                          closeOnTab: true,
16001                          tabPosition: 'top',
16002                           //resizeTabs: true,
16003                          alwaysShowTabs: el && hp? false :  true,
16004                          hideTabs: el || !hp ? true :  false,
16005                          minTabWidth: 140
16006                      }
16007                  })
16008             }
16009         }
16010         
16011         if (!this.parent.el) {
16012                 // probably an old style ctor, which has been disabled.
16013                 return;
16014
16015         }
16016                 // The 'tree' method is  '_tree now' 
16017             
16018         tree.region = tree.region || this.region;
16019         
16020         if (this.parent.el === true) {
16021             // bootstrap... - body..
16022             this.parent.el = Roo.factory(tree);
16023         }
16024         
16025         this.el = this.parent.el.addxtype(tree);
16026         this.fireEvent('built', this);
16027         
16028         this.panel = this.el;
16029         this.layout = this.panel.layout;
16030         this.parentLayout = this.parent.layout  || false;  
16031          
16032     }
16033     
16034 });
16035
16036 Roo.apply(Roo.XComponent, {
16037     /**
16038      * @property  hideProgress
16039      * true to disable the building progress bar.. usefull on single page renders.
16040      * @type Boolean
16041      */
16042     hideProgress : false,
16043     /**
16044      * @property  buildCompleted
16045      * True when the builder has completed building the interface.
16046      * @type Boolean
16047      */
16048     buildCompleted : false,
16049      
16050     /**
16051      * @property  topModule
16052      * the upper most module - uses document.element as it's constructor.
16053      * @type Object
16054      */
16055      
16056     topModule  : false,
16057       
16058     /**
16059      * @property  modules
16060      * array of modules to be created by registration system.
16061      * @type {Array} of Roo.XComponent
16062      */
16063     
16064     modules : [],
16065     /**
16066      * @property  elmodules
16067      * array of modules to be created by which use #ID 
16068      * @type {Array} of Roo.XComponent
16069      */
16070      
16071     elmodules : [],
16072
16073      /**
16074      * @property  build_from_html
16075      * Build elements from html - used by bootstrap HTML stuff 
16076      *    - this is cleared after build is completed
16077      * @type {boolean} true  (default false)
16078      */
16079      
16080     build_from_html : false,
16081
16082     /**
16083      * Register components to be built later.
16084      *
16085      * This solves the following issues
16086      * - Building is not done on page load, but after an authentication process has occured.
16087      * - Interface elements are registered on page load
16088      * - Parent Interface elements may not be loaded before child, so this handles that..
16089      * 
16090      *
16091      * example:
16092      * 
16093      * MyApp.register({
16094           order : '000001',
16095           module : 'Pman.Tab.projectMgr',
16096           region : 'center',
16097           parent : 'Pman.layout',
16098           disabled : false,  // or use a function..
16099         })
16100      
16101      * * @param {Object} details about module
16102      */
16103     register : function(obj) {
16104                 
16105         Roo.XComponent.event.fireEvent('register', obj);
16106         switch(typeof(obj.disabled) ) {
16107                 
16108             case 'undefined':
16109                 break;
16110             
16111             case 'function':
16112                 if ( obj.disabled() ) {
16113                         return;
16114                 }
16115                 break;
16116             
16117             default:
16118                 if (obj.disabled) {
16119                         return;
16120                 }
16121                 break;
16122         }
16123                 
16124         this.modules.push(obj);
16125          
16126     },
16127     /**
16128      * convert a string to an object..
16129      * eg. 'AAA.BBB' -> finds AAA.BBB
16130
16131      */
16132     
16133     toObject : function(str)
16134     {
16135         if (!str || typeof(str) == 'object') {
16136             return str;
16137         }
16138         if (str.substring(0,1) == '#') {
16139             return str;
16140         }
16141
16142         var ar = str.split('.');
16143         var rt, o;
16144         rt = ar.shift();
16145             /** eval:var:o */
16146         try {
16147             eval('if (typeof ' + rt + ' == "undefined"){ o = false;} o = ' + rt + ';');
16148         } catch (e) {
16149             throw "Module not found : " + str;
16150         }
16151         
16152         if (o === false) {
16153             throw "Module not found : " + str;
16154         }
16155         Roo.each(ar, function(e) {
16156             if (typeof(o[e]) == 'undefined') {
16157                 throw "Module not found : " + str;
16158             }
16159             o = o[e];
16160         });
16161         
16162         return o;
16163         
16164     },
16165     
16166     
16167     /**
16168      * move modules into their correct place in the tree..
16169      * 
16170      */
16171     preBuild : function ()
16172     {
16173         var _t = this;
16174         Roo.each(this.modules , function (obj)
16175         {
16176             Roo.XComponent.event.fireEvent('beforebuild', obj);
16177             
16178             var opar = obj.parent;
16179             try { 
16180                 obj.parent = this.toObject(opar);
16181             } catch(e) {
16182                 Roo.log("parent:toObject failed: " + e.toString());
16183                 return;
16184             }
16185             
16186             if (!obj.parent) {
16187                 Roo.debug && Roo.log("GOT top level module");
16188                 Roo.debug && Roo.log(obj);
16189                 obj.modules = new Roo.util.MixedCollection(false, 
16190                     function(o) { return o.order + '' }
16191                 );
16192                 this.topModule = obj;
16193                 return;
16194             }
16195                         // parent is a string (usually a dom element name..)
16196             if (typeof(obj.parent) == 'string') {
16197                 this.elmodules.push(obj);
16198                 return;
16199             }
16200             if (obj.parent.constructor != Roo.XComponent) {
16201                 Roo.log("Warning : Object Parent is not instance of XComponent:" + obj.name)
16202             }
16203             if (!obj.parent.modules) {
16204                 obj.parent.modules = new Roo.util.MixedCollection(false, 
16205                     function(o) { return o.order + '' }
16206                 );
16207             }
16208             if (obj.parent.disabled) {
16209                 obj.disabled = true;
16210             }
16211             obj.parent.modules.add(obj);
16212         }, this);
16213     },
16214     
16215      /**
16216      * make a list of modules to build.
16217      * @return {Array} list of modules. 
16218      */ 
16219     
16220     buildOrder : function()
16221     {
16222         var _this = this;
16223         var cmp = function(a,b) {   
16224             return String(a).toUpperCase() > String(b).toUpperCase() ? 1 : -1;
16225         };
16226         if ((!this.topModule || !this.topModule.modules) && !this.elmodules.length) {
16227             throw "No top level modules to build";
16228         }
16229         
16230         // make a flat list in order of modules to build.
16231         var mods = this.topModule ? [ this.topModule ] : [];
16232                 
16233         
16234         // elmodules (is a list of DOM based modules )
16235         Roo.each(this.elmodules, function(e) {
16236             mods.push(e);
16237             if (!this.topModule &&
16238                 typeof(e.parent) == 'string' &&
16239                 e.parent.substring(0,1) == '#' &&
16240                 Roo.get(e.parent.substr(1))
16241                ) {
16242                 
16243                 _this.topModule = e;
16244             }
16245             
16246         });
16247
16248         
16249         // add modules to their parents..
16250         var addMod = function(m) {
16251             Roo.debug && Roo.log("build Order: add: " + m.name);
16252                 
16253             mods.push(m);
16254             if (m.modules && !m.disabled) {
16255                 Roo.debug && Roo.log("build Order: " + m.modules.length + " child modules");
16256                 m.modules.keySort('ASC',  cmp );
16257                 Roo.debug && Roo.log("build Order: " + m.modules.length + " child modules (after sort)");
16258     
16259                 m.modules.each(addMod);
16260             } else {
16261                 Roo.debug && Roo.log("build Order: no child modules");
16262             }
16263             // not sure if this is used any more..
16264             if (m.finalize) {
16265                 m.finalize.name = m.name + " (clean up) ";
16266                 mods.push(m.finalize);
16267             }
16268             
16269         }
16270         if (this.topModule && this.topModule.modules) { 
16271             this.topModule.modules.keySort('ASC',  cmp );
16272             this.topModule.modules.each(addMod);
16273         } 
16274         return mods;
16275     },
16276     
16277      /**
16278      * Build the registered modules.
16279      * @param {Object} parent element.
16280      * @param {Function} optional method to call after module has been added.
16281      * 
16282      */ 
16283    
16284     build : function(opts) 
16285     {
16286         
16287         if (typeof(opts) != 'undefined') {
16288             Roo.apply(this,opts);
16289         }
16290         
16291         this.preBuild();
16292         var mods = this.buildOrder();
16293       
16294         //this.allmods = mods;
16295         //Roo.debug && Roo.log(mods);
16296         //return;
16297         if (!mods.length) { // should not happen
16298             throw "NO modules!!!";
16299         }
16300         
16301         
16302         var msg = "Building Interface...";
16303         // flash it up as modal - so we store the mask!?
16304         if (!this.hideProgress && Roo.MessageBox) {
16305             Roo.MessageBox.show({ title: 'loading' });
16306             Roo.MessageBox.show({
16307                title: "Please wait...",
16308                msg: msg,
16309                width:450,
16310                progress:true,
16311                closable:false,
16312                modal: false
16313               
16314             });
16315         }
16316         var total = mods.length;
16317         
16318         var _this = this;
16319         var progressRun = function() {
16320             if (!mods.length) {
16321                 Roo.debug && Roo.log('hide?');
16322                 if (!this.hideProgress && Roo.MessageBox) {
16323                     Roo.MessageBox.hide();
16324                 }
16325                 Roo.XComponent.build_from_html = false; // reset, so dialogs will be build from javascript
16326                 
16327                 Roo.XComponent.event.fireEvent('buildcomplete', _this.topModule);
16328                 
16329                 // THE END...
16330                 return false;   
16331             }
16332             
16333             var m = mods.shift();
16334             
16335             
16336             Roo.debug && Roo.log(m);
16337             // not sure if this is supported any more.. - modules that are are just function
16338             if (typeof(m) == 'function') { 
16339                 m.call(this);
16340                 return progressRun.defer(10, _this);
16341             } 
16342             
16343             
16344             msg = "Building Interface " + (total  - mods.length) + 
16345                     " of " + total + 
16346                     (m.name ? (' - ' + m.name) : '');
16347                         Roo.debug && Roo.log(msg);
16348             if (!this.hideProgress &&  Roo.MessageBox) { 
16349                 Roo.MessageBox.updateProgress(  (total  - mods.length)/total, msg  );
16350             }
16351             
16352          
16353             // is the module disabled?
16354             var disabled = (typeof(m.disabled) == 'function') ?
16355                 m.disabled.call(m.module.disabled) : m.disabled;    
16356             
16357             
16358             if (disabled) {
16359                 return progressRun(); // we do not update the display!
16360             }
16361             
16362             // now build 
16363             
16364                         
16365                         
16366             m.render();
16367             // it's 10 on top level, and 1 on others??? why...
16368             return progressRun.defer(10, _this);
16369              
16370         }
16371         progressRun.defer(1, _this);
16372      
16373         
16374         
16375     },
16376         
16377         
16378         /**
16379          * Event Object.
16380          *
16381          *
16382          */
16383         event: false, 
16384     /**
16385          * wrapper for event.on - aliased later..  
16386          * Typically use to register a event handler for register:
16387          *
16388          * eg. Roo.XComponent.on('register', function(comp) { comp.disable = true } );
16389          *
16390          */
16391     on : false
16392    
16393     
16394     
16395 });
16396
16397 Roo.XComponent.event = new Roo.util.Observable({
16398                 events : { 
16399                         /**
16400                          * @event register
16401                          * Fires when an Component is registered,
16402                          * set the disable property on the Component to stop registration.
16403                          * @param {Roo.XComponent} c the component being registerd.
16404                          * 
16405                          */
16406                         'register' : true,
16407             /**
16408                          * @event beforebuild
16409                          * Fires before each Component is built
16410                          * can be used to apply permissions.
16411                          * @param {Roo.XComponent} c the component being registerd.
16412                          * 
16413                          */
16414                         'beforebuild' : true,
16415                         /**
16416                          * @event buildcomplete
16417                          * Fires on the top level element when all elements have been built
16418                          * @param {Roo.XComponent} the top level component.
16419                          */
16420                         'buildcomplete' : true
16421                         
16422                 }
16423 });
16424
16425 Roo.XComponent.on = Roo.XComponent.event.on.createDelegate(Roo.XComponent.event); 
16426  /*
16427  * Based on:
16428  * Ext JS Library 1.1.1
16429  * Copyright(c) 2006-2007, Ext JS, LLC.
16430  *
16431  * Originally Released Under LGPL - original licence link has changed is not relivant.
16432  *
16433  * Fork - LGPL
16434  * <script type="text/javascript">
16435  */
16436
16437
16438
16439 /*
16440  * These classes are derivatives of the similarly named classes in the YUI Library.
16441  * The original license:
16442  * Copyright (c) 2006, Yahoo! Inc. All rights reserved.
16443  * Code licensed under the BSD License:
16444  * http://developer.yahoo.net/yui/license.txt
16445  */
16446
16447 (function() {
16448
16449 var Event=Roo.EventManager;
16450 var Dom=Roo.lib.Dom;
16451
16452 /**
16453  * @class Roo.dd.DragDrop
16454  * @extends Roo.util.Observable
16455  * Defines the interface and base operation of items that that can be
16456  * dragged or can be drop targets.  It was designed to be extended, overriding
16457  * the event handlers for startDrag, onDrag, onDragOver and onDragOut.
16458  * Up to three html elements can be associated with a DragDrop instance:
16459  * <ul>
16460  * <li>linked element: the element that is passed into the constructor.
16461  * This is the element which defines the boundaries for interaction with
16462  * other DragDrop objects.</li>
16463  * <li>handle element(s): The drag operation only occurs if the element that
16464  * was clicked matches a handle element.  By default this is the linked
16465  * element, but there are times that you will want only a portion of the
16466  * linked element to initiate the drag operation, and the setHandleElId()
16467  * method provides a way to define this.</li>
16468  * <li>drag element: this represents the element that would be moved along
16469  * with the cursor during a drag operation.  By default, this is the linked
16470  * element itself as in {@link Roo.dd.DD}.  setDragElId() lets you define
16471  * a separate element that would be moved, as in {@link Roo.dd.DDProxy}.
16472  * </li>
16473  * </ul>
16474  * This class should not be instantiated until the onload event to ensure that
16475  * the associated elements are available.
16476  * The following would define a DragDrop obj that would interact with any
16477  * other DragDrop obj in the "group1" group:
16478  * <pre>
16479  *  dd = new Roo.dd.DragDrop("div1", "group1");
16480  * </pre>
16481  * Since none of the event handlers have been implemented, nothing would
16482  * actually happen if you were to run the code above.  Normally you would
16483  * override this class or one of the default implementations, but you can
16484  * also override the methods you want on an instance of the class...
16485  * <pre>
16486  *  dd.onDragDrop = function(e, id) {
16487  *  &nbsp;&nbsp;alert("dd was dropped on " + id);
16488  *  }
16489  * </pre>
16490  * @constructor
16491  * @param {String} id of the element that is linked to this instance
16492  * @param {String} sGroup the group of related DragDrop objects
16493  * @param {object} config an object containing configurable attributes
16494  *                Valid properties for DragDrop:
16495  *                    padding, isTarget, maintainOffset, primaryButtonOnly
16496  */
16497 Roo.dd.DragDrop = function(id, sGroup, config) {
16498     if (id) {
16499         this.init(id, sGroup, config);
16500     }
16501     
16502 };
16503
16504 Roo.extend(Roo.dd.DragDrop, Roo.util.Observable , {
16505
16506     /**
16507      * The id of the element associated with this object.  This is what we
16508      * refer to as the "linked element" because the size and position of
16509      * this element is used to determine when the drag and drop objects have
16510      * interacted.
16511      * @property id
16512      * @type String
16513      */
16514     id: null,
16515
16516     /**
16517      * Configuration attributes passed into the constructor
16518      * @property config
16519      * @type object
16520      */
16521     config: null,
16522
16523     /**
16524      * The id of the element that will be dragged.  By default this is same
16525      * as the linked element , but could be changed to another element. Ex:
16526      * Roo.dd.DDProxy
16527      * @property dragElId
16528      * @type String
16529      * @private
16530      */
16531     dragElId: null,
16532
16533     /**
16534      * the id of the element that initiates the drag operation.  By default
16535      * this is the linked element, but could be changed to be a child of this
16536      * element.  This lets us do things like only starting the drag when the
16537      * header element within the linked html element is clicked.
16538      * @property handleElId
16539      * @type String
16540      * @private
16541      */
16542     handleElId: null,
16543
16544     /**
16545      * An associative array of HTML tags that will be ignored if clicked.
16546      * @property invalidHandleTypes
16547      * @type {string: string}
16548      */
16549     invalidHandleTypes: null,
16550
16551     /**
16552      * An associative array of ids for elements that will be ignored if clicked
16553      * @property invalidHandleIds
16554      * @type {string: string}
16555      */
16556     invalidHandleIds: null,
16557
16558     /**
16559      * An indexted array of css class names for elements that will be ignored
16560      * if clicked.
16561      * @property invalidHandleClasses
16562      * @type string[]
16563      */
16564     invalidHandleClasses: null,
16565
16566     /**
16567      * The linked element's absolute X position at the time the drag was
16568      * started
16569      * @property startPageX
16570      * @type int
16571      * @private
16572      */
16573     startPageX: 0,
16574
16575     /**
16576      * The linked element's absolute X position at the time the drag was
16577      * started
16578      * @property startPageY
16579      * @type int
16580      * @private
16581      */
16582     startPageY: 0,
16583
16584     /**
16585      * The group defines a logical collection of DragDrop objects that are
16586      * related.  Instances only get events when interacting with other
16587      * DragDrop object in the same group.  This lets us define multiple
16588      * groups using a single DragDrop subclass if we want.
16589      * @property groups
16590      * @type {string: string}
16591      */
16592     groups: null,
16593
16594     /**
16595      * Individual drag/drop instances can be locked.  This will prevent
16596      * onmousedown start drag.
16597      * @property locked
16598      * @type boolean
16599      * @private
16600      */
16601     locked: false,
16602
16603     /**
16604      * Lock this instance
16605      * @method lock
16606      */
16607     lock: function() { this.locked = true; },
16608
16609     /**
16610      * Unlock this instace
16611      * @method unlock
16612      */
16613     unlock: function() { this.locked = false; },
16614
16615     /**
16616      * By default, all insances can be a drop target.  This can be disabled by
16617      * setting isTarget to false.
16618      * @method isTarget
16619      * @type boolean
16620      */
16621     isTarget: true,
16622
16623     /**
16624      * The padding configured for this drag and drop object for calculating
16625      * the drop zone intersection with this object.
16626      * @method padding
16627      * @type int[]
16628      */
16629     padding: null,
16630
16631     /**
16632      * Cached reference to the linked element
16633      * @property _domRef
16634      * @private
16635      */
16636     _domRef: null,
16637
16638     /**
16639      * Internal typeof flag
16640      * @property __ygDragDrop
16641      * @private
16642      */
16643     __ygDragDrop: true,
16644
16645     /**
16646      * Set to true when horizontal contraints are applied
16647      * @property constrainX
16648      * @type boolean
16649      * @private
16650      */
16651     constrainX: false,
16652
16653     /**
16654      * Set to true when vertical contraints are applied
16655      * @property constrainY
16656      * @type boolean
16657      * @private
16658      */
16659     constrainY: false,
16660
16661     /**
16662      * The left constraint
16663      * @property minX
16664      * @type int
16665      * @private
16666      */
16667     minX: 0,
16668
16669     /**
16670      * The right constraint
16671      * @property maxX
16672      * @type int
16673      * @private
16674      */
16675     maxX: 0,
16676
16677     /**
16678      * The up constraint
16679      * @property minY
16680      * @type int
16681      * @type int
16682      * @private
16683      */
16684     minY: 0,
16685
16686     /**
16687      * The down constraint
16688      * @property maxY
16689      * @type int
16690      * @private
16691      */
16692     maxY: 0,
16693
16694     /**
16695      * Maintain offsets when we resetconstraints.  Set to true when you want
16696      * the position of the element relative to its parent to stay the same
16697      * when the page changes
16698      *
16699      * @property maintainOffset
16700      * @type boolean
16701      */
16702     maintainOffset: false,
16703
16704     /**
16705      * Array of pixel locations the element will snap to if we specified a
16706      * horizontal graduation/interval.  This array is generated automatically
16707      * when you define a tick interval.
16708      * @property xTicks
16709      * @type int[]
16710      */
16711     xTicks: null,
16712
16713     /**
16714      * Array of pixel locations the element will snap to if we specified a
16715      * vertical graduation/interval.  This array is generated automatically
16716      * when you define a tick interval.
16717      * @property yTicks
16718      * @type int[]
16719      */
16720     yTicks: null,
16721
16722     /**
16723      * By default the drag and drop instance will only respond to the primary
16724      * button click (left button for a right-handed mouse).  Set to true to
16725      * allow drag and drop to start with any mouse click that is propogated
16726      * by the browser
16727      * @property primaryButtonOnly
16728      * @type boolean
16729      */
16730     primaryButtonOnly: true,
16731
16732     /**
16733      * The availabe property is false until the linked dom element is accessible.
16734      * @property available
16735      * @type boolean
16736      */
16737     available: false,
16738
16739     /**
16740      * By default, drags can only be initiated if the mousedown occurs in the
16741      * region the linked element is.  This is done in part to work around a
16742      * bug in some browsers that mis-report the mousedown if the previous
16743      * mouseup happened outside of the window.  This property is set to true
16744      * if outer handles are defined.
16745      *
16746      * @property hasOuterHandles
16747      * @type boolean
16748      * @default false
16749      */
16750     hasOuterHandles: false,
16751
16752     /**
16753      * Code that executes immediately before the startDrag event
16754      * @method b4StartDrag
16755      * @private
16756      */
16757     b4StartDrag: function(x, y) { },
16758
16759     /**
16760      * Abstract method called after a drag/drop object is clicked
16761      * and the drag or mousedown time thresholds have beeen met.
16762      * @method startDrag
16763      * @param {int} X click location
16764      * @param {int} Y click location
16765      */
16766     startDrag: function(x, y) { /* override this */ },
16767
16768     /**
16769      * Code that executes immediately before the onDrag event
16770      * @method b4Drag
16771      * @private
16772      */
16773     b4Drag: function(e) { },
16774
16775     /**
16776      * Abstract method called during the onMouseMove event while dragging an
16777      * object.
16778      * @method onDrag
16779      * @param {Event} e the mousemove event
16780      */
16781     onDrag: function(e) { /* override this */ },
16782
16783     /**
16784      * Abstract method called when this element fist begins hovering over
16785      * another DragDrop obj
16786      * @method onDragEnter
16787      * @param {Event} e the mousemove event
16788      * @param {String|DragDrop[]} id In POINT mode, the element
16789      * id this is hovering over.  In INTERSECT mode, an array of one or more
16790      * dragdrop items being hovered over.
16791      */
16792     onDragEnter: function(e, id) { /* override this */ },
16793
16794     /**
16795      * Code that executes immediately before the onDragOver event
16796      * @method b4DragOver
16797      * @private
16798      */
16799     b4DragOver: function(e) { },
16800
16801     /**
16802      * Abstract method called when this element is hovering over another
16803      * DragDrop obj
16804      * @method onDragOver
16805      * @param {Event} e the mousemove event
16806      * @param {String|DragDrop[]} id In POINT mode, the element
16807      * id this is hovering over.  In INTERSECT mode, an array of dd items
16808      * being hovered over.
16809      */
16810     onDragOver: function(e, id) { /* override this */ },
16811
16812     /**
16813      * Code that executes immediately before the onDragOut event
16814      * @method b4DragOut
16815      * @private
16816      */
16817     b4DragOut: function(e) { },
16818
16819     /**
16820      * Abstract method called when we are no longer hovering over an element
16821      * @method onDragOut
16822      * @param {Event} e the mousemove event
16823      * @param {String|DragDrop[]} id In POINT mode, the element
16824      * id this was hovering over.  In INTERSECT mode, an array of dd items
16825      * that the mouse is no longer over.
16826      */
16827     onDragOut: function(e, id) { /* override this */ },
16828
16829     /**
16830      * Code that executes immediately before the onDragDrop event
16831      * @method b4DragDrop
16832      * @private
16833      */
16834     b4DragDrop: function(e) { },
16835
16836     /**
16837      * Abstract method called when this item is dropped on another DragDrop
16838      * obj
16839      * @method onDragDrop
16840      * @param {Event} e the mouseup event
16841      * @param {String|DragDrop[]} id In POINT mode, the element
16842      * id this was dropped on.  In INTERSECT mode, an array of dd items this
16843      * was dropped on.
16844      */
16845     onDragDrop: function(e, id) { /* override this */ },
16846
16847     /**
16848      * Abstract method called when this item is dropped on an area with no
16849      * drop target
16850      * @method onInvalidDrop
16851      * @param {Event} e the mouseup event
16852      */
16853     onInvalidDrop: function(e) { /* override this */ },
16854
16855     /**
16856      * Code that executes immediately before the endDrag event
16857      * @method b4EndDrag
16858      * @private
16859      */
16860     b4EndDrag: function(e) { },
16861
16862     /**
16863      * Fired when we are done dragging the object
16864      * @method endDrag
16865      * @param {Event} e the mouseup event
16866      */
16867     endDrag: function(e) { /* override this */ },
16868
16869     /**
16870      * Code executed immediately before the onMouseDown event
16871      * @method b4MouseDown
16872      * @param {Event} e the mousedown event
16873      * @private
16874      */
16875     b4MouseDown: function(e) {  },
16876
16877     /**
16878      * Event handler that fires when a drag/drop obj gets a mousedown
16879      * @method onMouseDown
16880      * @param {Event} e the mousedown event
16881      */
16882     onMouseDown: function(e) { /* override this */ },
16883
16884     /**
16885      * Event handler that fires when a drag/drop obj gets a mouseup
16886      * @method onMouseUp
16887      * @param {Event} e the mouseup event
16888      */
16889     onMouseUp: function(e) { /* override this */ },
16890
16891     /**
16892      * Override the onAvailable method to do what is needed after the initial
16893      * position was determined.
16894      * @method onAvailable
16895      */
16896     onAvailable: function () {
16897     },
16898
16899     /*
16900      * Provides default constraint padding to "constrainTo" elements (defaults to {left: 0, right:0, top:0, bottom:0}).
16901      * @type Object
16902      */
16903     defaultPadding : {left:0, right:0, top:0, bottom:0},
16904
16905     /*
16906      * Initializes the drag drop object's constraints to restrict movement to a certain element.
16907  *
16908  * Usage:
16909  <pre><code>
16910  var dd = new Roo.dd.DDProxy("dragDiv1", "proxytest",
16911                 { dragElId: "existingProxyDiv" });
16912  dd.startDrag = function(){
16913      this.constrainTo("parent-id");
16914  };
16915  </code></pre>
16916  * Or you can initalize it using the {@link Roo.Element} object:
16917  <pre><code>
16918  Roo.get("dragDiv1").initDDProxy("proxytest", {dragElId: "existingProxyDiv"}, {
16919      startDrag : function(){
16920          this.constrainTo("parent-id");
16921      }
16922  });
16923  </code></pre>
16924      * @param {String/HTMLElement/Element} constrainTo The element to constrain to.
16925      * @param {Object/Number} pad (optional) Pad provides a way to specify "padding" of the constraints,
16926      * and can be either a number for symmetrical padding (4 would be equal to {left:4, right:4, top:4, bottom:4}) or
16927      * an object containing the sides to pad. For example: {right:10, bottom:10}
16928      * @param {Boolean} inContent (optional) Constrain the draggable in the content box of the element (inside padding and borders)
16929      */
16930     constrainTo : function(constrainTo, pad, inContent){
16931         if(typeof pad == "number"){
16932             pad = {left: pad, right:pad, top:pad, bottom:pad};
16933         }
16934         pad = pad || this.defaultPadding;
16935         var b = Roo.get(this.getEl()).getBox();
16936         var ce = Roo.get(constrainTo);
16937         var s = ce.getScroll();
16938         var c, cd = ce.dom;
16939         if(cd == document.body){
16940             c = { x: s.left, y: s.top, width: Roo.lib.Dom.getViewWidth(), height: Roo.lib.Dom.getViewHeight()};
16941         }else{
16942             xy = ce.getXY();
16943             c = {x : xy[0]+s.left, y: xy[1]+s.top, width: cd.clientWidth, height: cd.clientHeight};
16944         }
16945
16946
16947         var topSpace = b.y - c.y;
16948         var leftSpace = b.x - c.x;
16949
16950         this.resetConstraints();
16951         this.setXConstraint(leftSpace - (pad.left||0), // left
16952                 c.width - leftSpace - b.width - (pad.right||0) //right
16953         );
16954         this.setYConstraint(topSpace - (pad.top||0), //top
16955                 c.height - topSpace - b.height - (pad.bottom||0) //bottom
16956         );
16957     },
16958
16959     /**
16960      * Returns a reference to the linked element
16961      * @method getEl
16962      * @return {HTMLElement} the html element
16963      */
16964     getEl: function() {
16965         if (!this._domRef) {
16966             this._domRef = Roo.getDom(this.id);
16967         }
16968
16969         return this._domRef;
16970     },
16971
16972     /**
16973      * Returns a reference to the actual element to drag.  By default this is
16974      * the same as the html element, but it can be assigned to another
16975      * element. An example of this can be found in Roo.dd.DDProxy
16976      * @method getDragEl
16977      * @return {HTMLElement} the html element
16978      */
16979     getDragEl: function() {
16980         return Roo.getDom(this.dragElId);
16981     },
16982
16983     /**
16984      * Sets up the DragDrop object.  Must be called in the constructor of any
16985      * Roo.dd.DragDrop subclass
16986      * @method init
16987      * @param id the id of the linked element
16988      * @param {String} sGroup the group of related items
16989      * @param {object} config configuration attributes
16990      */
16991     init: function(id, sGroup, config) {
16992         this.initTarget(id, sGroup, config);
16993         if (!Roo.isTouch) {
16994             Event.on(this.id, "mousedown", this.handleMouseDown, this);
16995         }
16996         Event.on(this.id, "touchstart", this.handleMouseDown, this);
16997         // Event.on(this.id, "selectstart", Event.preventDefault);
16998     },
16999
17000     /**
17001      * Initializes Targeting functionality only... the object does not
17002      * get a mousedown handler.
17003      * @method initTarget
17004      * @param id the id of the linked element
17005      * @param {String} sGroup the group of related items
17006      * @param {object} config configuration attributes
17007      */
17008     initTarget: function(id, sGroup, config) {
17009
17010         // configuration attributes
17011         this.config = config || {};
17012
17013         // create a local reference to the drag and drop manager
17014         this.DDM = Roo.dd.DDM;
17015         // initialize the groups array
17016         this.groups = {};
17017
17018         // assume that we have an element reference instead of an id if the
17019         // parameter is not a string
17020         if (typeof id !== "string") {
17021             id = Roo.id(id);
17022         }
17023
17024         // set the id
17025         this.id = id;
17026
17027         // add to an interaction group
17028         this.addToGroup((sGroup) ? sGroup : "default");
17029
17030         // We don't want to register this as the handle with the manager
17031         // so we just set the id rather than calling the setter.
17032         this.handleElId = id;
17033
17034         // the linked element is the element that gets dragged by default
17035         this.setDragElId(id);
17036
17037         // by default, clicked anchors will not start drag operations.
17038         this.invalidHandleTypes = { A: "A" };
17039         this.invalidHandleIds = {};
17040         this.invalidHandleClasses = [];
17041
17042         this.applyConfig();
17043
17044         this.handleOnAvailable();
17045     },
17046
17047     /**
17048      * Applies the configuration parameters that were passed into the constructor.
17049      * This is supposed to happen at each level through the inheritance chain.  So
17050      * a DDProxy implentation will execute apply config on DDProxy, DD, and
17051      * DragDrop in order to get all of the parameters that are available in
17052      * each object.
17053      * @method applyConfig
17054      */
17055     applyConfig: function() {
17056
17057         // configurable properties:
17058         //    padding, isTarget, maintainOffset, primaryButtonOnly
17059         this.padding           = this.config.padding || [0, 0, 0, 0];
17060         this.isTarget          = (this.config.isTarget !== false);
17061         this.maintainOffset    = (this.config.maintainOffset);
17062         this.primaryButtonOnly = (this.config.primaryButtonOnly !== false);
17063
17064     },
17065
17066     /**
17067      * Executed when the linked element is available
17068      * @method handleOnAvailable
17069      * @private
17070      */
17071     handleOnAvailable: function() {
17072         this.available = true;
17073         this.resetConstraints();
17074         this.onAvailable();
17075     },
17076
17077      /**
17078      * Configures the padding for the target zone in px.  Effectively expands
17079      * (or reduces) the virtual object size for targeting calculations.
17080      * Supports css-style shorthand; if only one parameter is passed, all sides
17081      * will have that padding, and if only two are passed, the top and bottom
17082      * will have the first param, the left and right the second.
17083      * @method setPadding
17084      * @param {int} iTop    Top pad
17085      * @param {int} iRight  Right pad
17086      * @param {int} iBot    Bot pad
17087      * @param {int} iLeft   Left pad
17088      */
17089     setPadding: function(iTop, iRight, iBot, iLeft) {
17090         // this.padding = [iLeft, iRight, iTop, iBot];
17091         if (!iRight && 0 !== iRight) {
17092             this.padding = [iTop, iTop, iTop, iTop];
17093         } else if (!iBot && 0 !== iBot) {
17094             this.padding = [iTop, iRight, iTop, iRight];
17095         } else {
17096             this.padding = [iTop, iRight, iBot, iLeft];
17097         }
17098     },
17099
17100     /**
17101      * Stores the initial placement of the linked element.
17102      * @method setInitialPosition
17103      * @param {int} diffX   the X offset, default 0
17104      * @param {int} diffY   the Y offset, default 0
17105      */
17106     setInitPosition: function(diffX, diffY) {
17107         var el = this.getEl();
17108
17109         if (!this.DDM.verifyEl(el)) {
17110             return;
17111         }
17112
17113         var dx = diffX || 0;
17114         var dy = diffY || 0;
17115
17116         var p = Dom.getXY( el );
17117
17118         this.initPageX = p[0] - dx;
17119         this.initPageY = p[1] - dy;
17120
17121         this.lastPageX = p[0];
17122         this.lastPageY = p[1];
17123
17124
17125         this.setStartPosition(p);
17126     },
17127
17128     /**
17129      * Sets the start position of the element.  This is set when the obj
17130      * is initialized, the reset when a drag is started.
17131      * @method setStartPosition
17132      * @param pos current position (from previous lookup)
17133      * @private
17134      */
17135     setStartPosition: function(pos) {
17136         var p = pos || Dom.getXY( this.getEl() );
17137         this.deltaSetXY = null;
17138
17139         this.startPageX = p[0];
17140         this.startPageY = p[1];
17141     },
17142
17143     /**
17144      * Add this instance to a group of related drag/drop objects.  All
17145      * instances belong to at least one group, and can belong to as many
17146      * groups as needed.
17147      * @method addToGroup
17148      * @param sGroup {string} the name of the group
17149      */
17150     addToGroup: function(sGroup) {
17151         this.groups[sGroup] = true;
17152         this.DDM.regDragDrop(this, sGroup);
17153     },
17154
17155     /**
17156      * Remove's this instance from the supplied interaction group
17157      * @method removeFromGroup
17158      * @param {string}  sGroup  The group to drop
17159      */
17160     removeFromGroup: function(sGroup) {
17161         if (this.groups[sGroup]) {
17162             delete this.groups[sGroup];
17163         }
17164
17165         this.DDM.removeDDFromGroup(this, sGroup);
17166     },
17167
17168     /**
17169      * Allows you to specify that an element other than the linked element
17170      * will be moved with the cursor during a drag
17171      * @method setDragElId
17172      * @param id {string} the id of the element that will be used to initiate the drag
17173      */
17174     setDragElId: function(id) {
17175         this.dragElId = id;
17176     },
17177
17178     /**
17179      * Allows you to specify a child of the linked element that should be
17180      * used to initiate the drag operation.  An example of this would be if
17181      * you have a content div with text and links.  Clicking anywhere in the
17182      * content area would normally start the drag operation.  Use this method
17183      * to specify that an element inside of the content div is the element
17184      * that starts the drag operation.
17185      * @method setHandleElId
17186      * @param id {string} the id of the element that will be used to
17187      * initiate the drag.
17188      */
17189     setHandleElId: function(id) {
17190         if (typeof id !== "string") {
17191             id = Roo.id(id);
17192         }
17193         this.handleElId = id;
17194         this.DDM.regHandle(this.id, id);
17195     },
17196
17197     /**
17198      * Allows you to set an element outside of the linked element as a drag
17199      * handle
17200      * @method setOuterHandleElId
17201      * @param id the id of the element that will be used to initiate the drag
17202      */
17203     setOuterHandleElId: function(id) {
17204         if (typeof id !== "string") {
17205             id = Roo.id(id);
17206         }
17207         Event.on(id, "mousedown",
17208                 this.handleMouseDown, this);
17209         this.setHandleElId(id);
17210
17211         this.hasOuterHandles = true;
17212     },
17213
17214     /**
17215      * Remove all drag and drop hooks for this element
17216      * @method unreg
17217      */
17218     unreg: function() {
17219         Event.un(this.id, "mousedown",
17220                 this.handleMouseDown);
17221         Event.un(this.id, "touchstart",
17222                 this.handleMouseDown);
17223         this._domRef = null;
17224         this.DDM._remove(this);
17225     },
17226
17227     destroy : function(){
17228         this.unreg();
17229     },
17230
17231     /**
17232      * Returns true if this instance is locked, or the drag drop mgr is locked
17233      * (meaning that all drag/drop is disabled on the page.)
17234      * @method isLocked
17235      * @return {boolean} true if this obj or all drag/drop is locked, else
17236      * false
17237      */
17238     isLocked: function() {
17239         return (this.DDM.isLocked() || this.locked);
17240     },
17241
17242     /**
17243      * Fired when this object is clicked
17244      * @method handleMouseDown
17245      * @param {Event} e
17246      * @param {Roo.dd.DragDrop} oDD the clicked dd object (this dd obj)
17247      * @private
17248      */
17249     handleMouseDown: function(e, oDD){
17250      
17251         if (!Roo.isTouch && this.primaryButtonOnly && e.button != 0) {
17252             //Roo.log('not touch/ button !=0');
17253             return;
17254         }
17255         if (e.browserEvent.touches && e.browserEvent.touches.length != 1) {
17256             return; // double touch..
17257         }
17258         
17259
17260         if (this.isLocked()) {
17261             //Roo.log('locked');
17262             return;
17263         }
17264
17265         this.DDM.refreshCache(this.groups);
17266 //        Roo.log([Roo.lib.Event.getPageX(e), Roo.lib.Event.getPageY(e)]);
17267         var pt = new Roo.lib.Point(Roo.lib.Event.getPageX(e), Roo.lib.Event.getPageY(e));
17268         if (!this.hasOuterHandles && !this.DDM.isOverTarget(pt, this) )  {
17269             //Roo.log('no outer handes or not over target');
17270                 // do nothing.
17271         } else {
17272 //            Roo.log('check validator');
17273             if (this.clickValidator(e)) {
17274 //                Roo.log('validate success');
17275                 // set the initial element position
17276                 this.setStartPosition();
17277
17278
17279                 this.b4MouseDown(e);
17280                 this.onMouseDown(e);
17281
17282                 this.DDM.handleMouseDown(e, this);
17283
17284                 this.DDM.stopEvent(e);
17285             } else {
17286
17287
17288             }
17289         }
17290     },
17291
17292     clickValidator: function(e) {
17293         var target = e.getTarget();
17294         return ( this.isValidHandleChild(target) &&
17295                     (this.id == this.handleElId ||
17296                         this.DDM.handleWasClicked(target, this.id)) );
17297     },
17298
17299     /**
17300      * Allows you to specify a tag name that should not start a drag operation
17301      * when clicked.  This is designed to facilitate embedding links within a
17302      * drag handle that do something other than start the drag.
17303      * @method addInvalidHandleType
17304      * @param {string} tagName the type of element to exclude
17305      */
17306     addInvalidHandleType: function(tagName) {
17307         var type = tagName.toUpperCase();
17308         this.invalidHandleTypes[type] = type;
17309     },
17310
17311     /**
17312      * Lets you to specify an element id for a child of a drag handle
17313      * that should not initiate a drag
17314      * @method addInvalidHandleId
17315      * @param {string} id the element id of the element you wish to ignore
17316      */
17317     addInvalidHandleId: function(id) {
17318         if (typeof id !== "string") {
17319             id = Roo.id(id);
17320         }
17321         this.invalidHandleIds[id] = id;
17322     },
17323
17324     /**
17325      * Lets you specify a css class of elements that will not initiate a drag
17326      * @method addInvalidHandleClass
17327      * @param {string} cssClass the class of the elements you wish to ignore
17328      */
17329     addInvalidHandleClass: function(cssClass) {
17330         this.invalidHandleClasses.push(cssClass);
17331     },
17332
17333     /**
17334      * Unsets an excluded tag name set by addInvalidHandleType
17335      * @method removeInvalidHandleType
17336      * @param {string} tagName the type of element to unexclude
17337      */
17338     removeInvalidHandleType: function(tagName) {
17339         var type = tagName.toUpperCase();
17340         // this.invalidHandleTypes[type] = null;
17341         delete this.invalidHandleTypes[type];
17342     },
17343
17344     /**
17345      * Unsets an invalid handle id
17346      * @method removeInvalidHandleId
17347      * @param {string} id the id of the element to re-enable
17348      */
17349     removeInvalidHandleId: function(id) {
17350         if (typeof id !== "string") {
17351             id = Roo.id(id);
17352         }
17353         delete this.invalidHandleIds[id];
17354     },
17355
17356     /**
17357      * Unsets an invalid css class
17358      * @method removeInvalidHandleClass
17359      * @param {string} cssClass the class of the element(s) you wish to
17360      * re-enable
17361      */
17362     removeInvalidHandleClass: function(cssClass) {
17363         for (var i=0, len=this.invalidHandleClasses.length; i<len; ++i) {
17364             if (this.invalidHandleClasses[i] == cssClass) {
17365                 delete this.invalidHandleClasses[i];
17366             }
17367         }
17368     },
17369
17370     /**
17371      * Checks the tag exclusion list to see if this click should be ignored
17372      * @method isValidHandleChild
17373      * @param {HTMLElement} node the HTMLElement to evaluate
17374      * @return {boolean} true if this is a valid tag type, false if not
17375      */
17376     isValidHandleChild: function(node) {
17377
17378         var valid = true;
17379         // var n = (node.nodeName == "#text") ? node.parentNode : node;
17380         var nodeName;
17381         try {
17382             nodeName = node.nodeName.toUpperCase();
17383         } catch(e) {
17384             nodeName = node.nodeName;
17385         }
17386         valid = valid && !this.invalidHandleTypes[nodeName];
17387         valid = valid && !this.invalidHandleIds[node.id];
17388
17389         for (var i=0, len=this.invalidHandleClasses.length; valid && i<len; ++i) {
17390             valid = !Dom.hasClass(node, this.invalidHandleClasses[i]);
17391         }
17392
17393
17394         return valid;
17395
17396     },
17397
17398     /**
17399      * Create the array of horizontal tick marks if an interval was specified
17400      * in setXConstraint().
17401      * @method setXTicks
17402      * @private
17403      */
17404     setXTicks: function(iStartX, iTickSize) {
17405         this.xTicks = [];
17406         this.xTickSize = iTickSize;
17407
17408         var tickMap = {};
17409
17410         for (var i = this.initPageX; i >= this.minX; i = i - iTickSize) {
17411             if (!tickMap[i]) {
17412                 this.xTicks[this.xTicks.length] = i;
17413                 tickMap[i] = true;
17414             }
17415         }
17416
17417         for (i = this.initPageX; i <= this.maxX; i = i + iTickSize) {
17418             if (!tickMap[i]) {
17419                 this.xTicks[this.xTicks.length] = i;
17420                 tickMap[i] = true;
17421             }
17422         }
17423
17424         this.xTicks.sort(this.DDM.numericSort) ;
17425     },
17426
17427     /**
17428      * Create the array of vertical tick marks if an interval was specified in
17429      * setYConstraint().
17430      * @method setYTicks
17431      * @private
17432      */
17433     setYTicks: function(iStartY, iTickSize) {
17434         this.yTicks = [];
17435         this.yTickSize = iTickSize;
17436
17437         var tickMap = {};
17438
17439         for (var i = this.initPageY; i >= this.minY; i = i - iTickSize) {
17440             if (!tickMap[i]) {
17441                 this.yTicks[this.yTicks.length] = i;
17442                 tickMap[i] = true;
17443             }
17444         }
17445
17446         for (i = this.initPageY; i <= this.maxY; i = i + iTickSize) {
17447             if (!tickMap[i]) {
17448                 this.yTicks[this.yTicks.length] = i;
17449                 tickMap[i] = true;
17450             }
17451         }
17452
17453         this.yTicks.sort(this.DDM.numericSort) ;
17454     },
17455
17456     /**
17457      * By default, the element can be dragged any place on the screen.  Use
17458      * this method to limit the horizontal travel of the element.  Pass in
17459      * 0,0 for the parameters if you want to lock the drag to the y axis.
17460      * @method setXConstraint
17461      * @param {int} iLeft the number of pixels the element can move to the left
17462      * @param {int} iRight the number of pixels the element can move to the
17463      * right
17464      * @param {int} iTickSize optional parameter for specifying that the
17465      * element
17466      * should move iTickSize pixels at a time.
17467      */
17468     setXConstraint: function(iLeft, iRight, iTickSize) {
17469         this.leftConstraint = iLeft;
17470         this.rightConstraint = iRight;
17471
17472         this.minX = this.initPageX - iLeft;
17473         this.maxX = this.initPageX + iRight;
17474         if (iTickSize) { this.setXTicks(this.initPageX, iTickSize); }
17475
17476         this.constrainX = true;
17477     },
17478
17479     /**
17480      * Clears any constraints applied to this instance.  Also clears ticks
17481      * since they can't exist independent of a constraint at this time.
17482      * @method clearConstraints
17483      */
17484     clearConstraints: function() {
17485         this.constrainX = false;
17486         this.constrainY = false;
17487         this.clearTicks();
17488     },
17489
17490     /**
17491      * Clears any tick interval defined for this instance
17492      * @method clearTicks
17493      */
17494     clearTicks: function() {
17495         this.xTicks = null;
17496         this.yTicks = null;
17497         this.xTickSize = 0;
17498         this.yTickSize = 0;
17499     },
17500
17501     /**
17502      * By default, the element can be dragged any place on the screen.  Set
17503      * this to limit the vertical travel of the element.  Pass in 0,0 for the
17504      * parameters if you want to lock the drag to the x axis.
17505      * @method setYConstraint
17506      * @param {int} iUp the number of pixels the element can move up
17507      * @param {int} iDown the number of pixels the element can move down
17508      * @param {int} iTickSize optional parameter for specifying that the
17509      * element should move iTickSize pixels at a time.
17510      */
17511     setYConstraint: function(iUp, iDown, iTickSize) {
17512         this.topConstraint = iUp;
17513         this.bottomConstraint = iDown;
17514
17515         this.minY = this.initPageY - iUp;
17516         this.maxY = this.initPageY + iDown;
17517         if (iTickSize) { this.setYTicks(this.initPageY, iTickSize); }
17518
17519         this.constrainY = true;
17520
17521     },
17522
17523     /**
17524      * resetConstraints must be called if you manually reposition a dd element.
17525      * @method resetConstraints
17526      * @param {boolean} maintainOffset
17527      */
17528     resetConstraints: function() {
17529
17530
17531         // Maintain offsets if necessary
17532         if (this.initPageX || this.initPageX === 0) {
17533             // figure out how much this thing has moved
17534             var dx = (this.maintainOffset) ? this.lastPageX - this.initPageX : 0;
17535             var dy = (this.maintainOffset) ? this.lastPageY - this.initPageY : 0;
17536
17537             this.setInitPosition(dx, dy);
17538
17539         // This is the first time we have detected the element's position
17540         } else {
17541             this.setInitPosition();
17542         }
17543
17544         if (this.constrainX) {
17545             this.setXConstraint( this.leftConstraint,
17546                                  this.rightConstraint,
17547                                  this.xTickSize        );
17548         }
17549
17550         if (this.constrainY) {
17551             this.setYConstraint( this.topConstraint,
17552                                  this.bottomConstraint,
17553                                  this.yTickSize         );
17554         }
17555     },
17556
17557     /**
17558      * Normally the drag element is moved pixel by pixel, but we can specify
17559      * that it move a number of pixels at a time.  This method resolves the
17560      * location when we have it set up like this.
17561      * @method getTick
17562      * @param {int} val where we want to place the object
17563      * @param {int[]} tickArray sorted array of valid points
17564      * @return {int} the closest tick
17565      * @private
17566      */
17567     getTick: function(val, tickArray) {
17568
17569         if (!tickArray) {
17570             // If tick interval is not defined, it is effectively 1 pixel,
17571             // so we return the value passed to us.
17572             return val;
17573         } else if (tickArray[0] >= val) {
17574             // The value is lower than the first tick, so we return the first
17575             // tick.
17576             return tickArray[0];
17577         } else {
17578             for (var i=0, len=tickArray.length; i<len; ++i) {
17579                 var next = i + 1;
17580                 if (tickArray[next] && tickArray[next] >= val) {
17581                     var diff1 = val - tickArray[i];
17582                     var diff2 = tickArray[next] - val;
17583                     return (diff2 > diff1) ? tickArray[i] : tickArray[next];
17584                 }
17585             }
17586
17587             // The value is larger than the last tick, so we return the last
17588             // tick.
17589             return tickArray[tickArray.length - 1];
17590         }
17591     },
17592
17593     /**
17594      * toString method
17595      * @method toString
17596      * @return {string} string representation of the dd obj
17597      */
17598     toString: function() {
17599         return ("DragDrop " + this.id);
17600     }
17601
17602 });
17603
17604 })();
17605 /*
17606  * Based on:
17607  * Ext JS Library 1.1.1
17608  * Copyright(c) 2006-2007, Ext JS, LLC.
17609  *
17610  * Originally Released Under LGPL - original licence link has changed is not relivant.
17611  *
17612  * Fork - LGPL
17613  * <script type="text/javascript">
17614  */
17615
17616
17617 /**
17618  * The drag and drop utility provides a framework for building drag and drop
17619  * applications.  In addition to enabling drag and drop for specific elements,
17620  * the drag and drop elements are tracked by the manager class, and the
17621  * interactions between the various elements are tracked during the drag and
17622  * the implementing code is notified about these important moments.
17623  */
17624
17625 // Only load the library once.  Rewriting the manager class would orphan
17626 // existing drag and drop instances.
17627 if (!Roo.dd.DragDropMgr) {
17628
17629 /**
17630  * @class Roo.dd.DragDropMgr
17631  * DragDropMgr is a singleton that tracks the element interaction for
17632  * all DragDrop items in the window.  Generally, you will not call
17633  * this class directly, but it does have helper methods that could
17634  * be useful in your DragDrop implementations.
17635  * @singleton
17636  */
17637 Roo.dd.DragDropMgr = function() {
17638
17639     var Event = Roo.EventManager;
17640
17641     return {
17642
17643         /**
17644          * Two dimensional Array of registered DragDrop objects.  The first
17645          * dimension is the DragDrop item group, the second the DragDrop
17646          * object.
17647          * @property ids
17648          * @type {string: string}
17649          * @private
17650          * @static
17651          */
17652         ids: {},
17653
17654         /**
17655          * Array of element ids defined as drag handles.  Used to determine
17656          * if the element that generated the mousedown event is actually the
17657          * handle and not the html element itself.
17658          * @property handleIds
17659          * @type {string: string}
17660          * @private
17661          * @static
17662          */
17663         handleIds: {},
17664
17665         /**
17666          * the DragDrop object that is currently being dragged
17667          * @property dragCurrent
17668          * @type DragDrop
17669          * @private
17670          * @static
17671          **/
17672         dragCurrent: null,
17673
17674         /**
17675          * the DragDrop object(s) that are being hovered over
17676          * @property dragOvers
17677          * @type Array
17678          * @private
17679          * @static
17680          */
17681         dragOvers: {},
17682
17683         /**
17684          * the X distance between the cursor and the object being dragged
17685          * @property deltaX
17686          * @type int
17687          * @private
17688          * @static
17689          */
17690         deltaX: 0,
17691
17692         /**
17693          * the Y distance between the cursor and the object being dragged
17694          * @property deltaY
17695          * @type int
17696          * @private
17697          * @static
17698          */
17699         deltaY: 0,
17700
17701         /**
17702          * Flag to determine if we should prevent the default behavior of the
17703          * events we define. By default this is true, but this can be set to
17704          * false if you need the default behavior (not recommended)
17705          * @property preventDefault
17706          * @type boolean
17707          * @static
17708          */
17709         preventDefault: true,
17710
17711         /**
17712          * Flag to determine if we should stop the propagation of the events
17713          * we generate. This is true by default but you may want to set it to
17714          * false if the html element contains other features that require the
17715          * mouse click.
17716          * @property stopPropagation
17717          * @type boolean
17718          * @static
17719          */
17720         stopPropagation: true,
17721
17722         /**
17723          * Internal flag that is set to true when drag and drop has been
17724          * intialized
17725          * @property initialized
17726          * @private
17727          * @static
17728          */
17729         initalized: false,
17730
17731         /**
17732          * All drag and drop can be disabled.
17733          * @property locked
17734          * @private
17735          * @static
17736          */
17737         locked: false,
17738
17739         /**
17740          * Called the first time an element is registered.
17741          * @method init
17742          * @private
17743          * @static
17744          */
17745         init: function() {
17746             this.initialized = true;
17747         },
17748
17749         /**
17750          * In point mode, drag and drop interaction is defined by the
17751          * location of the cursor during the drag/drop
17752          * @property POINT
17753          * @type int
17754          * @static
17755          */
17756         POINT: 0,
17757
17758         /**
17759          * In intersect mode, drag and drop interactio nis defined by the
17760          * overlap of two or more drag and drop objects.
17761          * @property INTERSECT
17762          * @type int
17763          * @static
17764          */
17765         INTERSECT: 1,
17766
17767         /**
17768          * The current drag and drop mode.  Default: POINT
17769          * @property mode
17770          * @type int
17771          * @static
17772          */
17773         mode: 0,
17774
17775         /**
17776          * Runs method on all drag and drop objects
17777          * @method _execOnAll
17778          * @private
17779          * @static
17780          */
17781         _execOnAll: function(sMethod, args) {
17782             for (var i in this.ids) {
17783                 for (var j in this.ids[i]) {
17784                     var oDD = this.ids[i][j];
17785                     if (! this.isTypeOfDD(oDD)) {
17786                         continue;
17787                     }
17788                     oDD[sMethod].apply(oDD, args);
17789                 }
17790             }
17791         },
17792
17793         /**
17794          * Drag and drop initialization.  Sets up the global event handlers
17795          * @method _onLoad
17796          * @private
17797          * @static
17798          */
17799         _onLoad: function() {
17800
17801             this.init();
17802
17803             if (!Roo.isTouch) {
17804                 Event.on(document, "mouseup",   this.handleMouseUp, this, true);
17805                 Event.on(document, "mousemove", this.handleMouseMove, this, true);
17806             }
17807             Event.on(document, "touchend",   this.handleMouseUp, this, true);
17808             Event.on(document, "touchmove", this.handleMouseMove, this, true);
17809             
17810             Event.on(window,   "unload",    this._onUnload, this, true);
17811             Event.on(window,   "resize",    this._onResize, this, true);
17812             // Event.on(window,   "mouseout",    this._test);
17813
17814         },
17815
17816         /**
17817          * Reset constraints on all drag and drop objs
17818          * @method _onResize
17819          * @private
17820          * @static
17821          */
17822         _onResize: function(e) {
17823             this._execOnAll("resetConstraints", []);
17824         },
17825
17826         /**
17827          * Lock all drag and drop functionality
17828          * @method lock
17829          * @static
17830          */
17831         lock: function() { this.locked = true; },
17832
17833         /**
17834          * Unlock all drag and drop functionality
17835          * @method unlock
17836          * @static
17837          */
17838         unlock: function() { this.locked = false; },
17839
17840         /**
17841          * Is drag and drop locked?
17842          * @method isLocked
17843          * @return {boolean} True if drag and drop is locked, false otherwise.
17844          * @static
17845          */
17846         isLocked: function() { return this.locked; },
17847
17848         /**
17849          * Location cache that is set for all drag drop objects when a drag is
17850          * initiated, cleared when the drag is finished.
17851          * @property locationCache
17852          * @private
17853          * @static
17854          */
17855         locationCache: {},
17856
17857         /**
17858          * Set useCache to false if you want to force object the lookup of each
17859          * drag and drop linked element constantly during a drag.
17860          * @property useCache
17861          * @type boolean
17862          * @static
17863          */
17864         useCache: true,
17865
17866         /**
17867          * The number of pixels that the mouse needs to move after the
17868          * mousedown before the drag is initiated.  Default=3;
17869          * @property clickPixelThresh
17870          * @type int
17871          * @static
17872          */
17873         clickPixelThresh: 3,
17874
17875         /**
17876          * The number of milliseconds after the mousedown event to initiate the
17877          * drag if we don't get a mouseup event. Default=1000
17878          * @property clickTimeThresh
17879          * @type int
17880          * @static
17881          */
17882         clickTimeThresh: 350,
17883
17884         /**
17885          * Flag that indicates that either the drag pixel threshold or the
17886          * mousdown time threshold has been met
17887          * @property dragThreshMet
17888          * @type boolean
17889          * @private
17890          * @static
17891          */
17892         dragThreshMet: false,
17893
17894         /**
17895          * Timeout used for the click time threshold
17896          * @property clickTimeout
17897          * @type Object
17898          * @private
17899          * @static
17900          */
17901         clickTimeout: null,
17902
17903         /**
17904          * The X position of the mousedown event stored for later use when a
17905          * drag threshold is met.
17906          * @property startX
17907          * @type int
17908          * @private
17909          * @static
17910          */
17911         startX: 0,
17912
17913         /**
17914          * The Y position of the mousedown event stored for later use when a
17915          * drag threshold is met.
17916          * @property startY
17917          * @type int
17918          * @private
17919          * @static
17920          */
17921         startY: 0,
17922
17923         /**
17924          * Each DragDrop instance must be registered with the DragDropMgr.
17925          * This is executed in DragDrop.init()
17926          * @method regDragDrop
17927          * @param {DragDrop} oDD the DragDrop object to register
17928          * @param {String} sGroup the name of the group this element belongs to
17929          * @static
17930          */
17931         regDragDrop: function(oDD, sGroup) {
17932             if (!this.initialized) { this.init(); }
17933
17934             if (!this.ids[sGroup]) {
17935                 this.ids[sGroup] = {};
17936             }
17937             this.ids[sGroup][oDD.id] = oDD;
17938         },
17939
17940         /**
17941          * Removes the supplied dd instance from the supplied group. Executed
17942          * by DragDrop.removeFromGroup, so don't call this function directly.
17943          * @method removeDDFromGroup
17944          * @private
17945          * @static
17946          */
17947         removeDDFromGroup: function(oDD, sGroup) {
17948             if (!this.ids[sGroup]) {
17949                 this.ids[sGroup] = {};
17950             }
17951
17952             var obj = this.ids[sGroup];
17953             if (obj && obj[oDD.id]) {
17954                 delete obj[oDD.id];
17955             }
17956         },
17957
17958         /**
17959          * Unregisters a drag and drop item.  This is executed in
17960          * DragDrop.unreg, use that method instead of calling this directly.
17961          * @method _remove
17962          * @private
17963          * @static
17964          */
17965         _remove: function(oDD) {
17966             for (var g in oDD.groups) {
17967                 if (g && this.ids[g][oDD.id]) {
17968                     delete this.ids[g][oDD.id];
17969                 }
17970             }
17971             delete this.handleIds[oDD.id];
17972         },
17973
17974         /**
17975          * Each DragDrop handle element must be registered.  This is done
17976          * automatically when executing DragDrop.setHandleElId()
17977          * @method regHandle
17978          * @param {String} sDDId the DragDrop id this element is a handle for
17979          * @param {String} sHandleId the id of the element that is the drag
17980          * handle
17981          * @static
17982          */
17983         regHandle: function(sDDId, sHandleId) {
17984             if (!this.handleIds[sDDId]) {
17985                 this.handleIds[sDDId] = {};
17986             }
17987             this.handleIds[sDDId][sHandleId] = sHandleId;
17988         },
17989
17990         /**
17991          * Utility function to determine if a given element has been
17992          * registered as a drag drop item.
17993          * @method isDragDrop
17994          * @param {String} id the element id to check
17995          * @return {boolean} true if this element is a DragDrop item,
17996          * false otherwise
17997          * @static
17998          */
17999         isDragDrop: function(id) {
18000             return ( this.getDDById(id) ) ? true : false;
18001         },
18002
18003         /**
18004          * Returns the drag and drop instances that are in all groups the
18005          * passed in instance belongs to.
18006          * @method getRelated
18007          * @param {DragDrop} p_oDD the obj to get related data for
18008          * @param {boolean} bTargetsOnly if true, only return targetable objs
18009          * @return {DragDrop[]} the related instances
18010          * @static
18011          */
18012         getRelated: function(p_oDD, bTargetsOnly) {
18013             var oDDs = [];
18014             for (var i in p_oDD.groups) {
18015                 for (j in this.ids[i]) {
18016                     var dd = this.ids[i][j];
18017                     if (! this.isTypeOfDD(dd)) {
18018                         continue;
18019                     }
18020                     if (!bTargetsOnly || dd.isTarget) {
18021                         oDDs[oDDs.length] = dd;
18022                     }
18023                 }
18024             }
18025
18026             return oDDs;
18027         },
18028
18029         /**
18030          * Returns true if the specified dd target is a legal target for
18031          * the specifice drag obj
18032          * @method isLegalTarget
18033          * @param {DragDrop} the drag obj
18034          * @param {DragDrop} the target
18035          * @return {boolean} true if the target is a legal target for the
18036          * dd obj
18037          * @static
18038          */
18039         isLegalTarget: function (oDD, oTargetDD) {
18040             var targets = this.getRelated(oDD, true);
18041             for (var i=0, len=targets.length;i<len;++i) {
18042                 if (targets[i].id == oTargetDD.id) {
18043                     return true;
18044                 }
18045             }
18046
18047             return false;
18048         },
18049
18050         /**
18051          * My goal is to be able to transparently determine if an object is
18052          * typeof DragDrop, and the exact subclass of DragDrop.  typeof
18053          * returns "object", oDD.constructor.toString() always returns
18054          * "DragDrop" and not the name of the subclass.  So for now it just
18055          * evaluates a well-known variable in DragDrop.
18056          * @method isTypeOfDD
18057          * @param {Object} the object to evaluate
18058          * @return {boolean} true if typeof oDD = DragDrop
18059          * @static
18060          */
18061         isTypeOfDD: function (oDD) {
18062             return (oDD && oDD.__ygDragDrop);
18063         },
18064
18065         /**
18066          * Utility function to determine if a given element has been
18067          * registered as a drag drop handle for the given Drag Drop object.
18068          * @method isHandle
18069          * @param {String} id the element id to check
18070          * @return {boolean} true if this element is a DragDrop handle, false
18071          * otherwise
18072          * @static
18073          */
18074         isHandle: function(sDDId, sHandleId) {
18075             return ( this.handleIds[sDDId] &&
18076                             this.handleIds[sDDId][sHandleId] );
18077         },
18078
18079         /**
18080          * Returns the DragDrop instance for a given id
18081          * @method getDDById
18082          * @param {String} id the id of the DragDrop object
18083          * @return {DragDrop} the drag drop object, null if it is not found
18084          * @static
18085          */
18086         getDDById: function(id) {
18087             for (var i in this.ids) {
18088                 if (this.ids[i][id]) {
18089                     return this.ids[i][id];
18090                 }
18091             }
18092             return null;
18093         },
18094
18095         /**
18096          * Fired after a registered DragDrop object gets the mousedown event.
18097          * Sets up the events required to track the object being dragged
18098          * @method handleMouseDown
18099          * @param {Event} e the event
18100          * @param oDD the DragDrop object being dragged
18101          * @private
18102          * @static
18103          */
18104         handleMouseDown: function(e, oDD) {
18105             if(Roo.QuickTips){
18106                 Roo.QuickTips.disable();
18107             }
18108             this.currentTarget = e.getTarget();
18109
18110             this.dragCurrent = oDD;
18111
18112             var el = oDD.getEl();
18113
18114             // track start position
18115             this.startX = e.getPageX();
18116             this.startY = e.getPageY();
18117
18118             this.deltaX = this.startX - el.offsetLeft;
18119             this.deltaY = this.startY - el.offsetTop;
18120
18121             this.dragThreshMet = false;
18122
18123             this.clickTimeout = setTimeout(
18124                     function() {
18125                         var DDM = Roo.dd.DDM;
18126                         DDM.startDrag(DDM.startX, DDM.startY);
18127                     },
18128                     this.clickTimeThresh );
18129         },
18130
18131         /**
18132          * Fired when either the drag pixel threshol or the mousedown hold
18133          * time threshold has been met.
18134          * @method startDrag
18135          * @param x {int} the X position of the original mousedown
18136          * @param y {int} the Y position of the original mousedown
18137          * @static
18138          */
18139         startDrag: function(x, y) {
18140             clearTimeout(this.clickTimeout);
18141             if (this.dragCurrent) {
18142                 this.dragCurrent.b4StartDrag(x, y);
18143                 this.dragCurrent.startDrag(x, y);
18144             }
18145             this.dragThreshMet = true;
18146         },
18147
18148         /**
18149          * Internal function to handle the mouseup event.  Will be invoked
18150          * from the context of the document.
18151          * @method handleMouseUp
18152          * @param {Event} e the event
18153          * @private
18154          * @static
18155          */
18156         handleMouseUp: function(e) {
18157
18158             if(Roo.QuickTips){
18159                 Roo.QuickTips.enable();
18160             }
18161             if (! this.dragCurrent) {
18162                 return;
18163             }
18164
18165             clearTimeout(this.clickTimeout);
18166
18167             if (this.dragThreshMet) {
18168                 this.fireEvents(e, true);
18169             } else {
18170             }
18171
18172             this.stopDrag(e);
18173
18174             this.stopEvent(e);
18175         },
18176
18177         /**
18178          * Utility to stop event propagation and event default, if these
18179          * features are turned on.
18180          * @method stopEvent
18181          * @param {Event} e the event as returned by this.getEvent()
18182          * @static
18183          */
18184         stopEvent: function(e){
18185             if(this.stopPropagation) {
18186                 e.stopPropagation();
18187             }
18188
18189             if (this.preventDefault) {
18190                 e.preventDefault();
18191             }
18192         },
18193
18194         /**
18195          * Internal function to clean up event handlers after the drag
18196          * operation is complete
18197          * @method stopDrag
18198          * @param {Event} e the event
18199          * @private
18200          * @static
18201          */
18202         stopDrag: function(e) {
18203             // Fire the drag end event for the item that was dragged
18204             if (this.dragCurrent) {
18205                 if (this.dragThreshMet) {
18206                     this.dragCurrent.b4EndDrag(e);
18207                     this.dragCurrent.endDrag(e);
18208                 }
18209
18210                 this.dragCurrent.onMouseUp(e);
18211             }
18212
18213             this.dragCurrent = null;
18214             this.dragOvers = {};
18215         },
18216
18217         /**
18218          * Internal function to handle the mousemove event.  Will be invoked
18219          * from the context of the html element.
18220          *
18221          * @TODO figure out what we can do about mouse events lost when the
18222          * user drags objects beyond the window boundary.  Currently we can
18223          * detect this in internet explorer by verifying that the mouse is
18224          * down during the mousemove event.  Firefox doesn't give us the
18225          * button state on the mousemove event.
18226          * @method handleMouseMove
18227          * @param {Event} e the event
18228          * @private
18229          * @static
18230          */
18231         handleMouseMove: function(e) {
18232             if (! this.dragCurrent) {
18233                 return true;
18234             }
18235
18236             // var button = e.which || e.button;
18237
18238             // check for IE mouseup outside of page boundary
18239             if (Roo.isIE && (e.button !== 0 && e.button !== 1 && e.button !== 2)) {
18240                 this.stopEvent(e);
18241                 return this.handleMouseUp(e);
18242             }
18243
18244             if (!this.dragThreshMet) {
18245                 var diffX = Math.abs(this.startX - e.getPageX());
18246                 var diffY = Math.abs(this.startY - e.getPageY());
18247                 if (diffX > this.clickPixelThresh ||
18248                             diffY > this.clickPixelThresh) {
18249                     this.startDrag(this.startX, this.startY);
18250                 }
18251             }
18252
18253             if (this.dragThreshMet) {
18254                 this.dragCurrent.b4Drag(e);
18255                 this.dragCurrent.onDrag(e);
18256                 if(!this.dragCurrent.moveOnly){
18257                     this.fireEvents(e, false);
18258                 }
18259             }
18260
18261             this.stopEvent(e);
18262
18263             return true;
18264         },
18265
18266         /**
18267          * Iterates over all of the DragDrop elements to find ones we are
18268          * hovering over or dropping on
18269          * @method fireEvents
18270          * @param {Event} e the event
18271          * @param {boolean} isDrop is this a drop op or a mouseover op?
18272          * @private
18273          * @static
18274          */
18275         fireEvents: function(e, isDrop) {
18276             var dc = this.dragCurrent;
18277
18278             // If the user did the mouse up outside of the window, we could
18279             // get here even though we have ended the drag.
18280             if (!dc || dc.isLocked()) {
18281                 return;
18282             }
18283
18284             var pt = e.getPoint();
18285
18286             // cache the previous dragOver array
18287             var oldOvers = [];
18288
18289             var outEvts   = [];
18290             var overEvts  = [];
18291             var dropEvts  = [];
18292             var enterEvts = [];
18293
18294             // Check to see if the object(s) we were hovering over is no longer
18295             // being hovered over so we can fire the onDragOut event
18296             for (var i in this.dragOvers) {
18297
18298                 var ddo = this.dragOvers[i];
18299
18300                 if (! this.isTypeOfDD(ddo)) {
18301                     continue;
18302                 }
18303
18304                 if (! this.isOverTarget(pt, ddo, this.mode)) {
18305                     outEvts.push( ddo );
18306                 }
18307
18308                 oldOvers[i] = true;
18309                 delete this.dragOvers[i];
18310             }
18311
18312             for (var sGroup in dc.groups) {
18313
18314                 if ("string" != typeof sGroup) {
18315                     continue;
18316                 }
18317
18318                 for (i in this.ids[sGroup]) {
18319                     var oDD = this.ids[sGroup][i];
18320                     if (! this.isTypeOfDD(oDD)) {
18321                         continue;
18322                     }
18323
18324                     if (oDD.isTarget && !oDD.isLocked() && oDD != dc) {
18325                         if (this.isOverTarget(pt, oDD, this.mode)) {
18326                             // look for drop interactions
18327                             if (isDrop) {
18328                                 dropEvts.push( oDD );
18329                             // look for drag enter and drag over interactions
18330                             } else {
18331
18332                                 // initial drag over: dragEnter fires
18333                                 if (!oldOvers[oDD.id]) {
18334                                     enterEvts.push( oDD );
18335                                 // subsequent drag overs: dragOver fires
18336                                 } else {
18337                                     overEvts.push( oDD );
18338                                 }
18339
18340                                 this.dragOvers[oDD.id] = oDD;
18341                             }
18342                         }
18343                     }
18344                 }
18345             }
18346
18347             if (this.mode) {
18348                 if (outEvts.length) {
18349                     dc.b4DragOut(e, outEvts);
18350                     dc.onDragOut(e, outEvts);
18351                 }
18352
18353                 if (enterEvts.length) {
18354                     dc.onDragEnter(e, enterEvts);
18355                 }
18356
18357                 if (overEvts.length) {
18358                     dc.b4DragOver(e, overEvts);
18359                     dc.onDragOver(e, overEvts);
18360                 }
18361
18362                 if (dropEvts.length) {
18363                     dc.b4DragDrop(e, dropEvts);
18364                     dc.onDragDrop(e, dropEvts);
18365                 }
18366
18367             } else {
18368                 // fire dragout events
18369                 var len = 0;
18370                 for (i=0, len=outEvts.length; i<len; ++i) {
18371                     dc.b4DragOut(e, outEvts[i].id);
18372                     dc.onDragOut(e, outEvts[i].id);
18373                 }
18374
18375                 // fire enter events
18376                 for (i=0,len=enterEvts.length; i<len; ++i) {
18377                     // dc.b4DragEnter(e, oDD.id);
18378                     dc.onDragEnter(e, enterEvts[i].id);
18379                 }
18380
18381                 // fire over events
18382                 for (i=0,len=overEvts.length; i<len; ++i) {
18383                     dc.b4DragOver(e, overEvts[i].id);
18384                     dc.onDragOver(e, overEvts[i].id);
18385                 }
18386
18387                 // fire drop events
18388                 for (i=0, len=dropEvts.length; i<len; ++i) {
18389                     dc.b4DragDrop(e, dropEvts[i].id);
18390                     dc.onDragDrop(e, dropEvts[i].id);
18391                 }
18392
18393             }
18394
18395             // notify about a drop that did not find a target
18396             if (isDrop && !dropEvts.length) {
18397                 dc.onInvalidDrop(e);
18398             }
18399
18400         },
18401
18402         /**
18403          * Helper function for getting the best match from the list of drag
18404          * and drop objects returned by the drag and drop events when we are
18405          * in INTERSECT mode.  It returns either the first object that the
18406          * cursor is over, or the object that has the greatest overlap with
18407          * the dragged element.
18408          * @method getBestMatch
18409          * @param  {DragDrop[]} dds The array of drag and drop objects
18410          * targeted
18411          * @return {DragDrop}       The best single match
18412          * @static
18413          */
18414         getBestMatch: function(dds) {
18415             var winner = null;
18416             // Return null if the input is not what we expect
18417             //if (!dds || !dds.length || dds.length == 0) {
18418                // winner = null;
18419             // If there is only one item, it wins
18420             //} else if (dds.length == 1) {
18421
18422             var len = dds.length;
18423
18424             if (len == 1) {
18425                 winner = dds[0];
18426             } else {
18427                 // Loop through the targeted items
18428                 for (var i=0; i<len; ++i) {
18429                     var dd = dds[i];
18430                     // If the cursor is over the object, it wins.  If the
18431                     // cursor is over multiple matches, the first one we come
18432                     // to wins.
18433                     if (dd.cursorIsOver) {
18434                         winner = dd;
18435                         break;
18436                     // Otherwise the object with the most overlap wins
18437                     } else {
18438                         if (!winner ||
18439                             winner.overlap.getArea() < dd.overlap.getArea()) {
18440                             winner = dd;
18441                         }
18442                     }
18443                 }
18444             }
18445
18446             return winner;
18447         },
18448
18449         /**
18450          * Refreshes the cache of the top-left and bottom-right points of the
18451          * drag and drop objects in the specified group(s).  This is in the
18452          * format that is stored in the drag and drop instance, so typical
18453          * usage is:
18454          * <code>
18455          * Roo.dd.DragDropMgr.refreshCache(ddinstance.groups);
18456          * </code>
18457          * Alternatively:
18458          * <code>
18459          * Roo.dd.DragDropMgr.refreshCache({group1:true, group2:true});
18460          * </code>
18461          * @TODO this really should be an indexed array.  Alternatively this
18462          * method could accept both.
18463          * @method refreshCache
18464          * @param {Object} groups an associative array of groups to refresh
18465          * @static
18466          */
18467         refreshCache: function(groups) {
18468             for (var sGroup in groups) {
18469                 if ("string" != typeof sGroup) {
18470                     continue;
18471                 }
18472                 for (var i in this.ids[sGroup]) {
18473                     var oDD = this.ids[sGroup][i];
18474
18475                     if (this.isTypeOfDD(oDD)) {
18476                     // if (this.isTypeOfDD(oDD) && oDD.isTarget) {
18477                         var loc = this.getLocation(oDD);
18478                         if (loc) {
18479                             this.locationCache[oDD.id] = loc;
18480                         } else {
18481                             delete this.locationCache[oDD.id];
18482                             // this will unregister the drag and drop object if
18483                             // the element is not in a usable state
18484                             // oDD.unreg();
18485                         }
18486                     }
18487                 }
18488             }
18489         },
18490
18491         /**
18492          * This checks to make sure an element exists and is in the DOM.  The
18493          * main purpose is to handle cases where innerHTML is used to remove
18494          * drag and drop objects from the DOM.  IE provides an 'unspecified
18495          * error' when trying to access the offsetParent of such an element
18496          * @method verifyEl
18497          * @param {HTMLElement} el the element to check
18498          * @return {boolean} true if the element looks usable
18499          * @static
18500          */
18501         verifyEl: function(el) {
18502             if (el) {
18503                 var parent;
18504                 if(Roo.isIE){
18505                     try{
18506                         parent = el.offsetParent;
18507                     }catch(e){}
18508                 }else{
18509                     parent = el.offsetParent;
18510                 }
18511                 if (parent) {
18512                     return true;
18513                 }
18514             }
18515
18516             return false;
18517         },
18518
18519         /**
18520          * Returns a Region object containing the drag and drop element's position
18521          * and size, including the padding configured for it
18522          * @method getLocation
18523          * @param {DragDrop} oDD the drag and drop object to get the
18524          *                       location for
18525          * @return {Roo.lib.Region} a Region object representing the total area
18526          *                             the element occupies, including any padding
18527          *                             the instance is configured for.
18528          * @static
18529          */
18530         getLocation: function(oDD) {
18531             if (! this.isTypeOfDD(oDD)) {
18532                 return null;
18533             }
18534
18535             var el = oDD.getEl(), pos, x1, x2, y1, y2, t, r, b, l;
18536
18537             try {
18538                 pos= Roo.lib.Dom.getXY(el);
18539             } catch (e) { }
18540
18541             if (!pos) {
18542                 return null;
18543             }
18544
18545             x1 = pos[0];
18546             x2 = x1 + el.offsetWidth;
18547             y1 = pos[1];
18548             y2 = y1 + el.offsetHeight;
18549
18550             t = y1 - oDD.padding[0];
18551             r = x2 + oDD.padding[1];
18552             b = y2 + oDD.padding[2];
18553             l = x1 - oDD.padding[3];
18554
18555             return new Roo.lib.Region( t, r, b, l );
18556         },
18557
18558         /**
18559          * Checks the cursor location to see if it over the target
18560          * @method isOverTarget
18561          * @param {Roo.lib.Point} pt The point to evaluate
18562          * @param {DragDrop} oTarget the DragDrop object we are inspecting
18563          * @return {boolean} true if the mouse is over the target
18564          * @private
18565          * @static
18566          */
18567         isOverTarget: function(pt, oTarget, intersect) {
18568             // use cache if available
18569             var loc = this.locationCache[oTarget.id];
18570             if (!loc || !this.useCache) {
18571                 loc = this.getLocation(oTarget);
18572                 this.locationCache[oTarget.id] = loc;
18573
18574             }
18575
18576             if (!loc) {
18577                 return false;
18578             }
18579
18580             oTarget.cursorIsOver = loc.contains( pt );
18581
18582             // DragDrop is using this as a sanity check for the initial mousedown
18583             // in this case we are done.  In POINT mode, if the drag obj has no
18584             // contraints, we are also done. Otherwise we need to evaluate the
18585             // location of the target as related to the actual location of the
18586             // dragged element.
18587             var dc = this.dragCurrent;
18588             if (!dc || !dc.getTargetCoord ||
18589                     (!intersect && !dc.constrainX && !dc.constrainY)) {
18590                 return oTarget.cursorIsOver;
18591             }
18592
18593             oTarget.overlap = null;
18594
18595             // Get the current location of the drag element, this is the
18596             // location of the mouse event less the delta that represents
18597             // where the original mousedown happened on the element.  We
18598             // need to consider constraints and ticks as well.
18599             var pos = dc.getTargetCoord(pt.x, pt.y);
18600
18601             var el = dc.getDragEl();
18602             var curRegion = new Roo.lib.Region( pos.y,
18603                                                    pos.x + el.offsetWidth,
18604                                                    pos.y + el.offsetHeight,
18605                                                    pos.x );
18606
18607             var overlap = curRegion.intersect(loc);
18608
18609             if (overlap) {
18610                 oTarget.overlap = overlap;
18611                 return (intersect) ? true : oTarget.cursorIsOver;
18612             } else {
18613                 return false;
18614             }
18615         },
18616
18617         /**
18618          * unload event handler
18619          * @method _onUnload
18620          * @private
18621          * @static
18622          */
18623         _onUnload: function(e, me) {
18624             Roo.dd.DragDropMgr.unregAll();
18625         },
18626
18627         /**
18628          * Cleans up the drag and drop events and objects.
18629          * @method unregAll
18630          * @private
18631          * @static
18632          */
18633         unregAll: function() {
18634
18635             if (this.dragCurrent) {
18636                 this.stopDrag();
18637                 this.dragCurrent = null;
18638             }
18639
18640             this._execOnAll("unreg", []);
18641
18642             for (i in this.elementCache) {
18643                 delete this.elementCache[i];
18644             }
18645
18646             this.elementCache = {};
18647             this.ids = {};
18648         },
18649
18650         /**
18651          * A cache of DOM elements
18652          * @property elementCache
18653          * @private
18654          * @static
18655          */
18656         elementCache: {},
18657
18658         /**
18659          * Get the wrapper for the DOM element specified
18660          * @method getElWrapper
18661          * @param {String} id the id of the element to get
18662          * @return {Roo.dd.DDM.ElementWrapper} the wrapped element
18663          * @private
18664          * @deprecated This wrapper isn't that useful
18665          * @static
18666          */
18667         getElWrapper: function(id) {
18668             var oWrapper = this.elementCache[id];
18669             if (!oWrapper || !oWrapper.el) {
18670                 oWrapper = this.elementCache[id] =
18671                     new this.ElementWrapper(Roo.getDom(id));
18672             }
18673             return oWrapper;
18674         },
18675
18676         /**
18677          * Returns the actual DOM element
18678          * @method getElement
18679          * @param {String} id the id of the elment to get
18680          * @return {Object} The element
18681          * @deprecated use Roo.getDom instead
18682          * @static
18683          */
18684         getElement: function(id) {
18685             return Roo.getDom(id);
18686         },
18687
18688         /**
18689          * Returns the style property for the DOM element (i.e.,
18690          * document.getElById(id).style)
18691          * @method getCss
18692          * @param {String} id the id of the elment to get
18693          * @return {Object} The style property of the element
18694          * @deprecated use Roo.getDom instead
18695          * @static
18696          */
18697         getCss: function(id) {
18698             var el = Roo.getDom(id);
18699             return (el) ? el.style : null;
18700         },
18701
18702         /**
18703          * Inner class for cached elements
18704          * @class DragDropMgr.ElementWrapper
18705          * @for DragDropMgr
18706          * @private
18707          * @deprecated
18708          */
18709         ElementWrapper: function(el) {
18710                 /**
18711                  * The element
18712                  * @property el
18713                  */
18714                 this.el = el || null;
18715                 /**
18716                  * The element id
18717                  * @property id
18718                  */
18719                 this.id = this.el && el.id;
18720                 /**
18721                  * A reference to the style property
18722                  * @property css
18723                  */
18724                 this.css = this.el && el.style;
18725             },
18726
18727         /**
18728          * Returns the X position of an html element
18729          * @method getPosX
18730          * @param el the element for which to get the position
18731          * @return {int} the X coordinate
18732          * @for DragDropMgr
18733          * @deprecated use Roo.lib.Dom.getX instead
18734          * @static
18735          */
18736         getPosX: function(el) {
18737             return Roo.lib.Dom.getX(el);
18738         },
18739
18740         /**
18741          * Returns the Y position of an html element
18742          * @method getPosY
18743          * @param el the element for which to get the position
18744          * @return {int} the Y coordinate
18745          * @deprecated use Roo.lib.Dom.getY instead
18746          * @static
18747          */
18748         getPosY: function(el) {
18749             return Roo.lib.Dom.getY(el);
18750         },
18751
18752         /**
18753          * Swap two nodes.  In IE, we use the native method, for others we
18754          * emulate the IE behavior
18755          * @method swapNode
18756          * @param n1 the first node to swap
18757          * @param n2 the other node to swap
18758          * @static
18759          */
18760         swapNode: function(n1, n2) {
18761             if (n1.swapNode) {
18762                 n1.swapNode(n2);
18763             } else {
18764                 var p = n2.parentNode;
18765                 var s = n2.nextSibling;
18766
18767                 if (s == n1) {
18768                     p.insertBefore(n1, n2);
18769                 } else if (n2 == n1.nextSibling) {
18770                     p.insertBefore(n2, n1);
18771                 } else {
18772                     n1.parentNode.replaceChild(n2, n1);
18773                     p.insertBefore(n1, s);
18774                 }
18775             }
18776         },
18777
18778         /**
18779          * Returns the current scroll position
18780          * @method getScroll
18781          * @private
18782          * @static
18783          */
18784         getScroll: function () {
18785             var t, l, dde=document.documentElement, db=document.body;
18786             if (dde && (dde.scrollTop || dde.scrollLeft)) {
18787                 t = dde.scrollTop;
18788                 l = dde.scrollLeft;
18789             } else if (db) {
18790                 t = db.scrollTop;
18791                 l = db.scrollLeft;
18792             } else {
18793
18794             }
18795             return { top: t, left: l };
18796         },
18797
18798         /**
18799          * Returns the specified element style property
18800          * @method getStyle
18801          * @param {HTMLElement} el          the element
18802          * @param {string}      styleProp   the style property
18803          * @return {string} The value of the style property
18804          * @deprecated use Roo.lib.Dom.getStyle
18805          * @static
18806          */
18807         getStyle: function(el, styleProp) {
18808             return Roo.fly(el).getStyle(styleProp);
18809         },
18810
18811         /**
18812          * Gets the scrollTop
18813          * @method getScrollTop
18814          * @return {int} the document's scrollTop
18815          * @static
18816          */
18817         getScrollTop: function () { return this.getScroll().top; },
18818
18819         /**
18820          * Gets the scrollLeft
18821          * @method getScrollLeft
18822          * @return {int} the document's scrollTop
18823          * @static
18824          */
18825         getScrollLeft: function () { return this.getScroll().left; },
18826
18827         /**
18828          * Sets the x/y position of an element to the location of the
18829          * target element.
18830          * @method moveToEl
18831          * @param {HTMLElement} moveEl      The element to move
18832          * @param {HTMLElement} targetEl    The position reference element
18833          * @static
18834          */
18835         moveToEl: function (moveEl, targetEl) {
18836             var aCoord = Roo.lib.Dom.getXY(targetEl);
18837             Roo.lib.Dom.setXY(moveEl, aCoord);
18838         },
18839
18840         /**
18841          * Numeric array sort function
18842          * @method numericSort
18843          * @static
18844          */
18845         numericSort: function(a, b) { return (a - b); },
18846
18847         /**
18848          * Internal counter
18849          * @property _timeoutCount
18850          * @private
18851          * @static
18852          */
18853         _timeoutCount: 0,
18854
18855         /**
18856          * Trying to make the load order less important.  Without this we get
18857          * an error if this file is loaded before the Event Utility.
18858          * @method _addListeners
18859          * @private
18860          * @static
18861          */
18862         _addListeners: function() {
18863             var DDM = Roo.dd.DDM;
18864             if ( Roo.lib.Event && document ) {
18865                 DDM._onLoad();
18866             } else {
18867                 if (DDM._timeoutCount > 2000) {
18868                 } else {
18869                     setTimeout(DDM._addListeners, 10);
18870                     if (document && document.body) {
18871                         DDM._timeoutCount += 1;
18872                     }
18873                 }
18874             }
18875         },
18876
18877         /**
18878          * Recursively searches the immediate parent and all child nodes for
18879          * the handle element in order to determine wheter or not it was
18880          * clicked.
18881          * @method handleWasClicked
18882          * @param node the html element to inspect
18883          * @static
18884          */
18885         handleWasClicked: function(node, id) {
18886             if (this.isHandle(id, node.id)) {
18887                 return true;
18888             } else {
18889                 // check to see if this is a text node child of the one we want
18890                 var p = node.parentNode;
18891
18892                 while (p) {
18893                     if (this.isHandle(id, p.id)) {
18894                         return true;
18895                     } else {
18896                         p = p.parentNode;
18897                     }
18898                 }
18899             }
18900
18901             return false;
18902         }
18903
18904     };
18905
18906 }();
18907
18908 // shorter alias, save a few bytes
18909 Roo.dd.DDM = Roo.dd.DragDropMgr;
18910 Roo.dd.DDM._addListeners();
18911
18912 }/*
18913  * Based on:
18914  * Ext JS Library 1.1.1
18915  * Copyright(c) 2006-2007, Ext JS, LLC.
18916  *
18917  * Originally Released Under LGPL - original licence link has changed is not relivant.
18918  *
18919  * Fork - LGPL
18920  * <script type="text/javascript">
18921  */
18922
18923 /**
18924  * @class Roo.dd.DD
18925  * A DragDrop implementation where the linked element follows the
18926  * mouse cursor during a drag.
18927  * @extends Roo.dd.DragDrop
18928  * @constructor
18929  * @param {String} id the id of the linked element
18930  * @param {String} sGroup the group of related DragDrop items
18931  * @param {object} config an object containing configurable attributes
18932  *                Valid properties for DD:
18933  *                    scroll
18934  */
18935 Roo.dd.DD = function(id, sGroup, config) {
18936     if (id) {
18937         this.init(id, sGroup, config);
18938     }
18939 };
18940
18941 Roo.extend(Roo.dd.DD, Roo.dd.DragDrop, {
18942
18943     /**
18944      * When set to true, the utility automatically tries to scroll the browser
18945      * window wehn a drag and drop element is dragged near the viewport boundary.
18946      * Defaults to true.
18947      * @property scroll
18948      * @type boolean
18949      */
18950     scroll: true,
18951
18952     /**
18953      * Sets the pointer offset to the distance between the linked element's top
18954      * left corner and the location the element was clicked
18955      * @method autoOffset
18956      * @param {int} iPageX the X coordinate of the click
18957      * @param {int} iPageY the Y coordinate of the click
18958      */
18959     autoOffset: function(iPageX, iPageY) {
18960         var x = iPageX - this.startPageX;
18961         var y = iPageY - this.startPageY;
18962         this.setDelta(x, y);
18963     },
18964
18965     /**
18966      * Sets the pointer offset.  You can call this directly to force the
18967      * offset to be in a particular location (e.g., pass in 0,0 to set it
18968      * to the center of the object)
18969      * @method setDelta
18970      * @param {int} iDeltaX the distance from the left
18971      * @param {int} iDeltaY the distance from the top
18972      */
18973     setDelta: function(iDeltaX, iDeltaY) {
18974         this.deltaX = iDeltaX;
18975         this.deltaY = iDeltaY;
18976     },
18977
18978     /**
18979      * Sets the drag element to the location of the mousedown or click event,
18980      * maintaining the cursor location relative to the location on the element
18981      * that was clicked.  Override this if you want to place the element in a
18982      * location other than where the cursor is.
18983      * @method setDragElPos
18984      * @param {int} iPageX the X coordinate of the mousedown or drag event
18985      * @param {int} iPageY the Y coordinate of the mousedown or drag event
18986      */
18987     setDragElPos: function(iPageX, iPageY) {
18988         // the first time we do this, we are going to check to make sure
18989         // the element has css positioning
18990
18991         var el = this.getDragEl();
18992         this.alignElWithMouse(el, iPageX, iPageY);
18993     },
18994
18995     /**
18996      * Sets the element to the location of the mousedown or click event,
18997      * maintaining the cursor location relative to the location on the element
18998      * that was clicked.  Override this if you want to place the element in a
18999      * location other than where the cursor is.
19000      * @method alignElWithMouse
19001      * @param {HTMLElement} el the element to move
19002      * @param {int} iPageX the X coordinate of the mousedown or drag event
19003      * @param {int} iPageY the Y coordinate of the mousedown or drag event
19004      */
19005     alignElWithMouse: function(el, iPageX, iPageY) {
19006         var oCoord = this.getTargetCoord(iPageX, iPageY);
19007         var fly = el.dom ? el : Roo.fly(el);
19008         if (!this.deltaSetXY) {
19009             var aCoord = [oCoord.x, oCoord.y];
19010             fly.setXY(aCoord);
19011             var newLeft = fly.getLeft(true);
19012             var newTop  = fly.getTop(true);
19013             this.deltaSetXY = [ newLeft - oCoord.x, newTop - oCoord.y ];
19014         } else {
19015             fly.setLeftTop(oCoord.x + this.deltaSetXY[0], oCoord.y + this.deltaSetXY[1]);
19016         }
19017
19018         this.cachePosition(oCoord.x, oCoord.y);
19019         this.autoScroll(oCoord.x, oCoord.y, el.offsetHeight, el.offsetWidth);
19020         return oCoord;
19021     },
19022
19023     /**
19024      * Saves the most recent position so that we can reset the constraints and
19025      * tick marks on-demand.  We need to know this so that we can calculate the
19026      * number of pixels the element is offset from its original position.
19027      * @method cachePosition
19028      * @param iPageX the current x position (optional, this just makes it so we
19029      * don't have to look it up again)
19030      * @param iPageY the current y position (optional, this just makes it so we
19031      * don't have to look it up again)
19032      */
19033     cachePosition: function(iPageX, iPageY) {
19034         if (iPageX) {
19035             this.lastPageX = iPageX;
19036             this.lastPageY = iPageY;
19037         } else {
19038             var aCoord = Roo.lib.Dom.getXY(this.getEl());
19039             this.lastPageX = aCoord[0];
19040             this.lastPageY = aCoord[1];
19041         }
19042     },
19043
19044     /**
19045      * Auto-scroll the window if the dragged object has been moved beyond the
19046      * visible window boundary.
19047      * @method autoScroll
19048      * @param {int} x the drag element's x position
19049      * @param {int} y the drag element's y position
19050      * @param {int} h the height of the drag element
19051      * @param {int} w the width of the drag element
19052      * @private
19053      */
19054     autoScroll: function(x, y, h, w) {
19055
19056         if (this.scroll) {
19057             // The client height
19058             var clientH = Roo.lib.Dom.getViewWidth();
19059
19060             // The client width
19061             var clientW = Roo.lib.Dom.getViewHeight();
19062
19063             // The amt scrolled down
19064             var st = this.DDM.getScrollTop();
19065
19066             // The amt scrolled right
19067             var sl = this.DDM.getScrollLeft();
19068
19069             // Location of the bottom of the element
19070             var bot = h + y;
19071
19072             // Location of the right of the element
19073             var right = w + x;
19074
19075             // The distance from the cursor to the bottom of the visible area,
19076             // adjusted so that we don't scroll if the cursor is beyond the
19077             // element drag constraints
19078             var toBot = (clientH + st - y - this.deltaY);
19079
19080             // The distance from the cursor to the right of the visible area
19081             var toRight = (clientW + sl - x - this.deltaX);
19082
19083
19084             // How close to the edge the cursor must be before we scroll
19085             // var thresh = (document.all) ? 100 : 40;
19086             var thresh = 40;
19087
19088             // How many pixels to scroll per autoscroll op.  This helps to reduce
19089             // clunky scrolling. IE is more sensitive about this ... it needs this
19090             // value to be higher.
19091             var scrAmt = (document.all) ? 80 : 30;
19092
19093             // Scroll down if we are near the bottom of the visible page and the
19094             // obj extends below the crease
19095             if ( bot > clientH && toBot < thresh ) {
19096                 window.scrollTo(sl, st + scrAmt);
19097             }
19098
19099             // Scroll up if the window is scrolled down and the top of the object
19100             // goes above the top border
19101             if ( y < st && st > 0 && y - st < thresh ) {
19102                 window.scrollTo(sl, st - scrAmt);
19103             }
19104
19105             // Scroll right if the obj is beyond the right border and the cursor is
19106             // near the border.
19107             if ( right > clientW && toRight < thresh ) {
19108                 window.scrollTo(sl + scrAmt, st);
19109             }
19110
19111             // Scroll left if the window has been scrolled to the right and the obj
19112             // extends past the left border
19113             if ( x < sl && sl > 0 && x - sl < thresh ) {
19114                 window.scrollTo(sl - scrAmt, st);
19115             }
19116         }
19117     },
19118
19119     /**
19120      * Finds the location the element should be placed if we want to move
19121      * it to where the mouse location less the click offset would place us.
19122      * @method getTargetCoord
19123      * @param {int} iPageX the X coordinate of the click
19124      * @param {int} iPageY the Y coordinate of the click
19125      * @return an object that contains the coordinates (Object.x and Object.y)
19126      * @private
19127      */
19128     getTargetCoord: function(iPageX, iPageY) {
19129
19130
19131         var x = iPageX - this.deltaX;
19132         var y = iPageY - this.deltaY;
19133
19134         if (this.constrainX) {
19135             if (x < this.minX) { x = this.minX; }
19136             if (x > this.maxX) { x = this.maxX; }
19137         }
19138
19139         if (this.constrainY) {
19140             if (y < this.minY) { y = this.minY; }
19141             if (y > this.maxY) { y = this.maxY; }
19142         }
19143
19144         x = this.getTick(x, this.xTicks);
19145         y = this.getTick(y, this.yTicks);
19146
19147
19148         return {x:x, y:y};
19149     },
19150
19151     /*
19152      * Sets up config options specific to this class. Overrides
19153      * Roo.dd.DragDrop, but all versions of this method through the
19154      * inheritance chain are called
19155      */
19156     applyConfig: function() {
19157         Roo.dd.DD.superclass.applyConfig.call(this);
19158         this.scroll = (this.config.scroll !== false);
19159     },
19160
19161     /*
19162      * Event that fires prior to the onMouseDown event.  Overrides
19163      * Roo.dd.DragDrop.
19164      */
19165     b4MouseDown: function(e) {
19166         // this.resetConstraints();
19167         this.autoOffset(e.getPageX(),
19168                             e.getPageY());
19169     },
19170
19171     /*
19172      * Event that fires prior to the onDrag event.  Overrides
19173      * Roo.dd.DragDrop.
19174      */
19175     b4Drag: function(e) {
19176         this.setDragElPos(e.getPageX(),
19177                             e.getPageY());
19178     },
19179
19180     toString: function() {
19181         return ("DD " + this.id);
19182     }
19183
19184     //////////////////////////////////////////////////////////////////////////
19185     // Debugging ygDragDrop events that can be overridden
19186     //////////////////////////////////////////////////////////////////////////
19187     /*
19188     startDrag: function(x, y) {
19189     },
19190
19191     onDrag: function(e) {
19192     },
19193
19194     onDragEnter: function(e, id) {
19195     },
19196
19197     onDragOver: function(e, id) {
19198     },
19199
19200     onDragOut: function(e, id) {
19201     },
19202
19203     onDragDrop: function(e, id) {
19204     },
19205
19206     endDrag: function(e) {
19207     }
19208
19209     */
19210
19211 });/*
19212  * Based on:
19213  * Ext JS Library 1.1.1
19214  * Copyright(c) 2006-2007, Ext JS, LLC.
19215  *
19216  * Originally Released Under LGPL - original licence link has changed is not relivant.
19217  *
19218  * Fork - LGPL
19219  * <script type="text/javascript">
19220  */
19221
19222 /**
19223  * @class Roo.dd.DDProxy
19224  * A DragDrop implementation that inserts an empty, bordered div into
19225  * the document that follows the cursor during drag operations.  At the time of
19226  * the click, the frame div is resized to the dimensions of the linked html
19227  * element, and moved to the exact location of the linked element.
19228  *
19229  * References to the "frame" element refer to the single proxy element that
19230  * was created to be dragged in place of all DDProxy elements on the
19231  * page.
19232  *
19233  * @extends Roo.dd.DD
19234  * @constructor
19235  * @param {String} id the id of the linked html element
19236  * @param {String} sGroup the group of related DragDrop objects
19237  * @param {object} config an object containing configurable attributes
19238  *                Valid properties for DDProxy in addition to those in DragDrop:
19239  *                   resizeFrame, centerFrame, dragElId
19240  */
19241 Roo.dd.DDProxy = function(id, sGroup, config) {
19242     if (id) {
19243         this.init(id, sGroup, config);
19244         this.initFrame();
19245     }
19246 };
19247
19248 /**
19249  * The default drag frame div id
19250  * @property Roo.dd.DDProxy.dragElId
19251  * @type String
19252  * @static
19253  */
19254 Roo.dd.DDProxy.dragElId = "ygddfdiv";
19255
19256 Roo.extend(Roo.dd.DDProxy, Roo.dd.DD, {
19257
19258     /**
19259      * By default we resize the drag frame to be the same size as the element
19260      * we want to drag (this is to get the frame effect).  We can turn it off
19261      * if we want a different behavior.
19262      * @property resizeFrame
19263      * @type boolean
19264      */
19265     resizeFrame: true,
19266
19267     /**
19268      * By default the frame is positioned exactly where the drag element is, so
19269      * we use the cursor offset provided by Roo.dd.DD.  Another option that works only if
19270      * you do not have constraints on the obj is to have the drag frame centered
19271      * around the cursor.  Set centerFrame to true for this effect.
19272      * @property centerFrame
19273      * @type boolean
19274      */
19275     centerFrame: false,
19276
19277     /**
19278      * Creates the proxy element if it does not yet exist
19279      * @method createFrame
19280      */
19281     createFrame: function() {
19282         var self = this;
19283         var body = document.body;
19284
19285         if (!body || !body.firstChild) {
19286             setTimeout( function() { self.createFrame(); }, 50 );
19287             return;
19288         }
19289
19290         var div = this.getDragEl();
19291
19292         if (!div) {
19293             div    = document.createElement("div");
19294             div.id = this.dragElId;
19295             var s  = div.style;
19296
19297             s.position   = "absolute";
19298             s.visibility = "hidden";
19299             s.cursor     = "move";
19300             s.border     = "2px solid #aaa";
19301             s.zIndex     = 999;
19302
19303             // appendChild can blow up IE if invoked prior to the window load event
19304             // while rendering a table.  It is possible there are other scenarios
19305             // that would cause this to happen as well.
19306             body.insertBefore(div, body.firstChild);
19307         }
19308     },
19309
19310     /**
19311      * Initialization for the drag frame element.  Must be called in the
19312      * constructor of all subclasses
19313      * @method initFrame
19314      */
19315     initFrame: function() {
19316         this.createFrame();
19317     },
19318
19319     applyConfig: function() {
19320         Roo.dd.DDProxy.superclass.applyConfig.call(this);
19321
19322         this.resizeFrame = (this.config.resizeFrame !== false);
19323         this.centerFrame = (this.config.centerFrame);
19324         this.setDragElId(this.config.dragElId || Roo.dd.DDProxy.dragElId);
19325     },
19326
19327     /**
19328      * Resizes the drag frame to the dimensions of the clicked object, positions
19329      * it over the object, and finally displays it
19330      * @method showFrame
19331      * @param {int} iPageX X click position
19332      * @param {int} iPageY Y click position
19333      * @private
19334      */
19335     showFrame: function(iPageX, iPageY) {
19336         var el = this.getEl();
19337         var dragEl = this.getDragEl();
19338         var s = dragEl.style;
19339
19340         this._resizeProxy();
19341
19342         if (this.centerFrame) {
19343             this.setDelta( Math.round(parseInt(s.width,  10)/2),
19344                            Math.round(parseInt(s.height, 10)/2) );
19345         }
19346
19347         this.setDragElPos(iPageX, iPageY);
19348
19349         Roo.fly(dragEl).show();
19350     },
19351
19352     /**
19353      * The proxy is automatically resized to the dimensions of the linked
19354      * element when a drag is initiated, unless resizeFrame is set to false
19355      * @method _resizeProxy
19356      * @private
19357      */
19358     _resizeProxy: function() {
19359         if (this.resizeFrame) {
19360             var el = this.getEl();
19361             Roo.fly(this.getDragEl()).setSize(el.offsetWidth, el.offsetHeight);
19362         }
19363     },
19364
19365     // overrides Roo.dd.DragDrop
19366     b4MouseDown: function(e) {
19367         var x = e.getPageX();
19368         var y = e.getPageY();
19369         this.autoOffset(x, y);
19370         this.setDragElPos(x, y);
19371     },
19372
19373     // overrides Roo.dd.DragDrop
19374     b4StartDrag: function(x, y) {
19375         // show the drag frame
19376         this.showFrame(x, y);
19377     },
19378
19379     // overrides Roo.dd.DragDrop
19380     b4EndDrag: function(e) {
19381         Roo.fly(this.getDragEl()).hide();
19382     },
19383
19384     // overrides Roo.dd.DragDrop
19385     // By default we try to move the element to the last location of the frame.
19386     // This is so that the default behavior mirrors that of Roo.dd.DD.
19387     endDrag: function(e) {
19388
19389         var lel = this.getEl();
19390         var del = this.getDragEl();
19391
19392         // Show the drag frame briefly so we can get its position
19393         del.style.visibility = "";
19394
19395         this.beforeMove();
19396         // Hide the linked element before the move to get around a Safari
19397         // rendering bug.
19398         lel.style.visibility = "hidden";
19399         Roo.dd.DDM.moveToEl(lel, del);
19400         del.style.visibility = "hidden";
19401         lel.style.visibility = "";
19402
19403         this.afterDrag();
19404     },
19405
19406     beforeMove : function(){
19407
19408     },
19409
19410     afterDrag : function(){
19411
19412     },
19413
19414     toString: function() {
19415         return ("DDProxy " + this.id);
19416     }
19417
19418 });
19419 /*
19420  * Based on:
19421  * Ext JS Library 1.1.1
19422  * Copyright(c) 2006-2007, Ext JS, LLC.
19423  *
19424  * Originally Released Under LGPL - original licence link has changed is not relivant.
19425  *
19426  * Fork - LGPL
19427  * <script type="text/javascript">
19428  */
19429
19430  /**
19431  * @class Roo.dd.DDTarget
19432  * A DragDrop implementation that does not move, but can be a drop
19433  * target.  You would get the same result by simply omitting implementation
19434  * for the event callbacks, but this way we reduce the processing cost of the
19435  * event listener and the callbacks.
19436  * @extends Roo.dd.DragDrop
19437  * @constructor
19438  * @param {String} id the id of the element that is a drop target
19439  * @param {String} sGroup the group of related DragDrop objects
19440  * @param {object} config an object containing configurable attributes
19441  *                 Valid properties for DDTarget in addition to those in
19442  *                 DragDrop:
19443  *                    none
19444  */
19445 Roo.dd.DDTarget = function(id, sGroup, config) {
19446     if (id) {
19447         this.initTarget(id, sGroup, config);
19448     }
19449     if (config.listeners || config.events) { 
19450        Roo.dd.DragDrop.superclass.constructor.call(this,  { 
19451             listeners : config.listeners || {}, 
19452             events : config.events || {} 
19453         });    
19454     }
19455 };
19456
19457 // Roo.dd.DDTarget.prototype = new Roo.dd.DragDrop();
19458 Roo.extend(Roo.dd.DDTarget, Roo.dd.DragDrop, {
19459     toString: function() {
19460         return ("DDTarget " + this.id);
19461     }
19462 });
19463 /*
19464  * Based on:
19465  * Ext JS Library 1.1.1
19466  * Copyright(c) 2006-2007, Ext JS, LLC.
19467  *
19468  * Originally Released Under LGPL - original licence link has changed is not relivant.
19469  *
19470  * Fork - LGPL
19471  * <script type="text/javascript">
19472  */
19473  
19474
19475 /**
19476  * @class Roo.dd.ScrollManager
19477  * Provides automatic scrolling of overflow regions in the page during drag operations.<br><br>
19478  * <b>Note: This class uses "Point Mode" and is untested in "Intersect Mode".</b>
19479  * @singleton
19480  */
19481 Roo.dd.ScrollManager = function(){
19482     var ddm = Roo.dd.DragDropMgr;
19483     var els = {};
19484     var dragEl = null;
19485     var proc = {};
19486     
19487     
19488     
19489     var onStop = function(e){
19490         dragEl = null;
19491         clearProc();
19492     };
19493     
19494     var triggerRefresh = function(){
19495         if(ddm.dragCurrent){
19496              ddm.refreshCache(ddm.dragCurrent.groups);
19497         }
19498     };
19499     
19500     var doScroll = function(){
19501         if(ddm.dragCurrent){
19502             var dds = Roo.dd.ScrollManager;
19503             if(!dds.animate){
19504                 if(proc.el.scroll(proc.dir, dds.increment)){
19505                     triggerRefresh();
19506                 }
19507             }else{
19508                 proc.el.scroll(proc.dir, dds.increment, true, dds.animDuration, triggerRefresh);
19509             }
19510         }
19511     };
19512     
19513     var clearProc = function(){
19514         if(proc.id){
19515             clearInterval(proc.id);
19516         }
19517         proc.id = 0;
19518         proc.el = null;
19519         proc.dir = "";
19520     };
19521     
19522     var startProc = function(el, dir){
19523          Roo.log('scroll startproc');
19524         clearProc();
19525         proc.el = el;
19526         proc.dir = dir;
19527         proc.id = setInterval(doScroll, Roo.dd.ScrollManager.frequency);
19528     };
19529     
19530     var onFire = function(e, isDrop){
19531        
19532         if(isDrop || !ddm.dragCurrent){ return; }
19533         var dds = Roo.dd.ScrollManager;
19534         if(!dragEl || dragEl != ddm.dragCurrent){
19535             dragEl = ddm.dragCurrent;
19536             // refresh regions on drag start
19537             dds.refreshCache();
19538         }
19539         
19540         var xy = Roo.lib.Event.getXY(e);
19541         var pt = new Roo.lib.Point(xy[0], xy[1]);
19542         for(var id in els){
19543             var el = els[id], r = el._region;
19544             if(r && r.contains(pt) && el.isScrollable()){
19545                 if(r.bottom - pt.y <= dds.thresh){
19546                     if(proc.el != el){
19547                         startProc(el, "down");
19548                     }
19549                     return;
19550                 }else if(r.right - pt.x <= dds.thresh){
19551                     if(proc.el != el){
19552                         startProc(el, "left");
19553                     }
19554                     return;
19555                 }else if(pt.y - r.top <= dds.thresh){
19556                     if(proc.el != el){
19557                         startProc(el, "up");
19558                     }
19559                     return;
19560                 }else if(pt.x - r.left <= dds.thresh){
19561                     if(proc.el != el){
19562                         startProc(el, "right");
19563                     }
19564                     return;
19565                 }
19566             }
19567         }
19568         clearProc();
19569     };
19570     
19571     ddm.fireEvents = ddm.fireEvents.createSequence(onFire, ddm);
19572     ddm.stopDrag = ddm.stopDrag.createSequence(onStop, ddm);
19573     
19574     return {
19575         /**
19576          * Registers new overflow element(s) to auto scroll
19577          * @param {String/HTMLElement/Element/Array} el The id of or the element to be scrolled or an array of either
19578          */
19579         register : function(el){
19580             if(el instanceof Array){
19581                 for(var i = 0, len = el.length; i < len; i++) {
19582                         this.register(el[i]);
19583                 }
19584             }else{
19585                 el = Roo.get(el);
19586                 els[el.id] = el;
19587             }
19588             Roo.dd.ScrollManager.els = els;
19589         },
19590         
19591         /**
19592          * Unregisters overflow element(s) so they are no longer scrolled
19593          * @param {String/HTMLElement/Element/Array} el The id of or the element to be removed or an array of either
19594          */
19595         unregister : function(el){
19596             if(el instanceof Array){
19597                 for(var i = 0, len = el.length; i < len; i++) {
19598                         this.unregister(el[i]);
19599                 }
19600             }else{
19601                 el = Roo.get(el);
19602                 delete els[el.id];
19603             }
19604         },
19605         
19606         /**
19607          * The number of pixels from the edge of a container the pointer needs to be to 
19608          * trigger scrolling (defaults to 25)
19609          * @type Number
19610          */
19611         thresh : 25,
19612         
19613         /**
19614          * The number of pixels to scroll in each scroll increment (defaults to 50)
19615          * @type Number
19616          */
19617         increment : 100,
19618         
19619         /**
19620          * The frequency of scrolls in milliseconds (defaults to 500)
19621          * @type Number
19622          */
19623         frequency : 500,
19624         
19625         /**
19626          * True to animate the scroll (defaults to true)
19627          * @type Boolean
19628          */
19629         animate: true,
19630         
19631         /**
19632          * The animation duration in seconds - 
19633          * MUST BE less than Roo.dd.ScrollManager.frequency! (defaults to .4)
19634          * @type Number
19635          */
19636         animDuration: .4,
19637         
19638         /**
19639          * Manually trigger a cache refresh.
19640          */
19641         refreshCache : function(){
19642             for(var id in els){
19643                 if(typeof els[id] == 'object'){ // for people extending the object prototype
19644                     els[id]._region = els[id].getRegion();
19645                 }
19646             }
19647         }
19648     };
19649 }();/*
19650  * Based on:
19651  * Ext JS Library 1.1.1
19652  * Copyright(c) 2006-2007, Ext JS, LLC.
19653  *
19654  * Originally Released Under LGPL - original licence link has changed is not relivant.
19655  *
19656  * Fork - LGPL
19657  * <script type="text/javascript">
19658  */
19659  
19660
19661 /**
19662  * @class Roo.dd.Registry
19663  * Provides easy access to all drag drop components that are registered on a page.  Items can be retrieved either
19664  * directly by DOM node id, or by passing in the drag drop event that occurred and looking up the event target.
19665  * @singleton
19666  */
19667 Roo.dd.Registry = function(){
19668     var elements = {}; 
19669     var handles = {}; 
19670     var autoIdSeed = 0;
19671
19672     var getId = function(el, autogen){
19673         if(typeof el == "string"){
19674             return el;
19675         }
19676         var id = el.id;
19677         if(!id && autogen !== false){
19678             id = "roodd-" + (++autoIdSeed);
19679             el.id = id;
19680         }
19681         return id;
19682     };
19683     
19684     return {
19685     /**
19686      * Register a drag drop element
19687      * @param {String|HTMLElement} element The id or DOM node to register
19688      * @param {Object} data (optional) A custom data object that will be passed between the elements that are involved
19689      * in drag drop operations.  You can populate this object with any arbitrary properties that your own code
19690      * knows how to interpret, plus there are some specific properties known to the Registry that should be
19691      * populated in the data object (if applicable):
19692      * <pre>
19693 Value      Description<br />
19694 ---------  ------------------------------------------<br />
19695 handles    Array of DOM nodes that trigger dragging<br />
19696            for the element being registered<br />
19697 isHandle   True if the element passed in triggers<br />
19698            dragging itself, else false
19699 </pre>
19700      */
19701         register : function(el, data){
19702             data = data || {};
19703             if(typeof el == "string"){
19704                 el = document.getElementById(el);
19705             }
19706             data.ddel = el;
19707             elements[getId(el)] = data;
19708             if(data.isHandle !== false){
19709                 handles[data.ddel.id] = data;
19710             }
19711             if(data.handles){
19712                 var hs = data.handles;
19713                 for(var i = 0, len = hs.length; i < len; i++){
19714                         handles[getId(hs[i])] = data;
19715                 }
19716             }
19717         },
19718
19719     /**
19720      * Unregister a drag drop element
19721      * @param {String|HTMLElement}  element The id or DOM node to unregister
19722      */
19723         unregister : function(el){
19724             var id = getId(el, false);
19725             var data = elements[id];
19726             if(data){
19727                 delete elements[id];
19728                 if(data.handles){
19729                     var hs = data.handles;
19730                     for(var i = 0, len = hs.length; i < len; i++){
19731                         delete handles[getId(hs[i], false)];
19732                     }
19733                 }
19734             }
19735         },
19736
19737     /**
19738      * Returns the handle registered for a DOM Node by id
19739      * @param {String|HTMLElement} id The DOM node or id to look up
19740      * @return {Object} handle The custom handle data
19741      */
19742         getHandle : function(id){
19743             if(typeof id != "string"){ // must be element?
19744                 id = id.id;
19745             }
19746             return handles[id];
19747         },
19748
19749     /**
19750      * Returns the handle that is registered for the DOM node that is the target of the event
19751      * @param {Event} e The event
19752      * @return {Object} handle The custom handle data
19753      */
19754         getHandleFromEvent : function(e){
19755             var t = Roo.lib.Event.getTarget(e);
19756             return t ? handles[t.id] : null;
19757         },
19758
19759     /**
19760      * Returns a custom data object that is registered for a DOM node by id
19761      * @param {String|HTMLElement} id The DOM node or id to look up
19762      * @return {Object} data The custom data
19763      */
19764         getTarget : function(id){
19765             if(typeof id != "string"){ // must be element?
19766                 id = id.id;
19767             }
19768             return elements[id];
19769         },
19770
19771     /**
19772      * Returns a custom data object that is registered for the DOM node that is the target of the event
19773      * @param {Event} e The event
19774      * @return {Object} data The custom data
19775      */
19776         getTargetFromEvent : function(e){
19777             var t = Roo.lib.Event.getTarget(e);
19778             return t ? elements[t.id] || handles[t.id] : null;
19779         }
19780     };
19781 }();/*
19782  * Based on:
19783  * Ext JS Library 1.1.1
19784  * Copyright(c) 2006-2007, Ext JS, LLC.
19785  *
19786  * Originally Released Under LGPL - original licence link has changed is not relivant.
19787  *
19788  * Fork - LGPL
19789  * <script type="text/javascript">
19790  */
19791  
19792
19793 /**
19794  * @class Roo.dd.StatusProxy
19795  * A specialized drag proxy that supports a drop status icon, {@link Roo.Layer} styles and auto-repair.  This is the
19796  * default drag proxy used by all Roo.dd components.
19797  * @constructor
19798  * @param {Object} config
19799  */
19800 Roo.dd.StatusProxy = function(config){
19801     Roo.apply(this, config);
19802     this.id = this.id || Roo.id();
19803     this.el = new Roo.Layer({
19804         dh: {
19805             id: this.id, tag: "div", cls: "x-dd-drag-proxy "+this.dropNotAllowed, children: [
19806                 {tag: "div", cls: "x-dd-drop-icon"},
19807                 {tag: "div", cls: "x-dd-drag-ghost"}
19808             ]
19809         }, 
19810         shadow: !config || config.shadow !== false
19811     });
19812     this.ghost = Roo.get(this.el.dom.childNodes[1]);
19813     this.dropStatus = this.dropNotAllowed;
19814 };
19815
19816 Roo.dd.StatusProxy.prototype = {
19817     /**
19818      * @cfg {String} dropAllowed
19819      * The CSS class to apply to the status element when drop is allowed (defaults to "x-dd-drop-ok").
19820      */
19821     dropAllowed : "x-dd-drop-ok",
19822     /**
19823      * @cfg {String} dropNotAllowed
19824      * The CSS class to apply to the status element when drop is not allowed (defaults to "x-dd-drop-nodrop").
19825      */
19826     dropNotAllowed : "x-dd-drop-nodrop",
19827
19828     /**
19829      * Updates the proxy's visual element to indicate the status of whether or not drop is allowed
19830      * over the current target element.
19831      * @param {String} cssClass The css class for the new drop status indicator image
19832      */
19833     setStatus : function(cssClass){
19834         cssClass = cssClass || this.dropNotAllowed;
19835         if(this.dropStatus != cssClass){
19836             this.el.replaceClass(this.dropStatus, cssClass);
19837             this.dropStatus = cssClass;
19838         }
19839     },
19840
19841     /**
19842      * Resets the status indicator to the default dropNotAllowed value
19843      * @param {Boolean} clearGhost True to also remove all content from the ghost, false to preserve it
19844      */
19845     reset : function(clearGhost){
19846         this.el.dom.className = "x-dd-drag-proxy " + this.dropNotAllowed;
19847         this.dropStatus = this.dropNotAllowed;
19848         if(clearGhost){
19849             this.ghost.update("");
19850         }
19851     },
19852
19853     /**
19854      * Updates the contents of the ghost element
19855      * @param {String} html The html that will replace the current innerHTML of the ghost element
19856      */
19857     update : function(html){
19858         if(typeof html == "string"){
19859             this.ghost.update(html);
19860         }else{
19861             this.ghost.update("");
19862             html.style.margin = "0";
19863             this.ghost.dom.appendChild(html);
19864         }
19865         // ensure float = none set?? cant remember why though.
19866         var el = this.ghost.dom.firstChild;
19867                 if(el){
19868                         Roo.fly(el).setStyle('float', 'none');
19869                 }
19870     },
19871     
19872     /**
19873      * Returns the underlying proxy {@link Roo.Layer}
19874      * @return {Roo.Layer} el
19875     */
19876     getEl : function(){
19877         return this.el;
19878     },
19879
19880     /**
19881      * Returns the ghost element
19882      * @return {Roo.Element} el
19883      */
19884     getGhost : function(){
19885         return this.ghost;
19886     },
19887
19888     /**
19889      * Hides the proxy
19890      * @param {Boolean} clear True to reset the status and clear the ghost contents, false to preserve them
19891      */
19892     hide : function(clear){
19893         this.el.hide();
19894         if(clear){
19895             this.reset(true);
19896         }
19897     },
19898
19899     /**
19900      * Stops the repair animation if it's currently running
19901      */
19902     stop : function(){
19903         if(this.anim && this.anim.isAnimated && this.anim.isAnimated()){
19904             this.anim.stop();
19905         }
19906     },
19907
19908     /**
19909      * Displays this proxy
19910      */
19911     show : function(){
19912         this.el.show();
19913     },
19914
19915     /**
19916      * Force the Layer to sync its shadow and shim positions to the element
19917      */
19918     sync : function(){
19919         this.el.sync();
19920     },
19921
19922     /**
19923      * Causes the proxy to return to its position of origin via an animation.  Should be called after an
19924      * invalid drop operation by the item being dragged.
19925      * @param {Array} xy The XY position of the element ([x, y])
19926      * @param {Function} callback The function to call after the repair is complete
19927      * @param {Object} scope The scope in which to execute the callback
19928      */
19929     repair : function(xy, callback, scope){
19930         this.callback = callback;
19931         this.scope = scope;
19932         if(xy && this.animRepair !== false){
19933             this.el.addClass("x-dd-drag-repair");
19934             this.el.hideUnders(true);
19935             this.anim = this.el.shift({
19936                 duration: this.repairDuration || .5,
19937                 easing: 'easeOut',
19938                 xy: xy,
19939                 stopFx: true,
19940                 callback: this.afterRepair,
19941                 scope: this
19942             });
19943         }else{
19944             this.afterRepair();
19945         }
19946     },
19947
19948     // private
19949     afterRepair : function(){
19950         this.hide(true);
19951         if(typeof this.callback == "function"){
19952             this.callback.call(this.scope || this);
19953         }
19954         this.callback = null;
19955         this.scope = null;
19956     }
19957 };/*
19958  * Based on:
19959  * Ext JS Library 1.1.1
19960  * Copyright(c) 2006-2007, Ext JS, LLC.
19961  *
19962  * Originally Released Under LGPL - original licence link has changed is not relivant.
19963  *
19964  * Fork - LGPL
19965  * <script type="text/javascript">
19966  */
19967
19968 /**
19969  * @class Roo.dd.DragSource
19970  * @extends Roo.dd.DDProxy
19971  * A simple class that provides the basic implementation needed to make any element draggable.
19972  * @constructor
19973  * @param {String/HTMLElement/Element} el The container element
19974  * @param {Object} config
19975  */
19976 Roo.dd.DragSource = function(el, config){
19977     this.el = Roo.get(el);
19978     this.dragData = {};
19979     
19980     Roo.apply(this, config);
19981     
19982     if(!this.proxy){
19983         this.proxy = new Roo.dd.StatusProxy();
19984     }
19985
19986     Roo.dd.DragSource.superclass.constructor.call(this, this.el.dom, this.ddGroup || this.group,
19987           {dragElId : this.proxy.id, resizeFrame: false, isTarget: false, scroll: this.scroll === true});
19988     
19989     this.dragging = false;
19990 };
19991
19992 Roo.extend(Roo.dd.DragSource, Roo.dd.DDProxy, {
19993     /**
19994      * @cfg {String} dropAllowed
19995      * The CSS class returned to the drag source when drop is allowed (defaults to "x-dd-drop-ok").
19996      */
19997     dropAllowed : "x-dd-drop-ok",
19998     /**
19999      * @cfg {String} dropNotAllowed
20000      * The CSS class returned to the drag source when drop is not allowed (defaults to "x-dd-drop-nodrop").
20001      */
20002     dropNotAllowed : "x-dd-drop-nodrop",
20003
20004     /**
20005      * Returns the data object associated with this drag source
20006      * @return {Object} data An object containing arbitrary data
20007      */
20008     getDragData : function(e){
20009         return this.dragData;
20010     },
20011
20012     // private
20013     onDragEnter : function(e, id){
20014         var target = Roo.dd.DragDropMgr.getDDById(id);
20015         this.cachedTarget = target;
20016         if(this.beforeDragEnter(target, e, id) !== false){
20017             if(target.isNotifyTarget){
20018                 var status = target.notifyEnter(this, e, this.dragData);
20019                 this.proxy.setStatus(status);
20020             }else{
20021                 this.proxy.setStatus(this.dropAllowed);
20022             }
20023             
20024             if(this.afterDragEnter){
20025                 /**
20026                  * An empty function by default, but provided so that you can perform a custom action
20027                  * when the dragged item enters the drop target by providing an implementation.
20028                  * @param {Roo.dd.DragDrop} target The drop target
20029                  * @param {Event} e The event object
20030                  * @param {String} id The id of the dragged element
20031                  * @method afterDragEnter
20032                  */
20033                 this.afterDragEnter(target, e, id);
20034             }
20035         }
20036     },
20037
20038     /**
20039      * An empty function by default, but provided so that you can perform a custom action
20040      * before the dragged item enters the drop target and optionally cancel the onDragEnter.
20041      * @param {Roo.dd.DragDrop} target The drop target
20042      * @param {Event} e The event object
20043      * @param {String} id The id of the dragged element
20044      * @return {Boolean} isValid True if the drag event is valid, else false to cancel
20045      */
20046     beforeDragEnter : function(target, e, id){
20047         return true;
20048     },
20049
20050     // private
20051     alignElWithMouse: function() {
20052         Roo.dd.DragSource.superclass.alignElWithMouse.apply(this, arguments);
20053         this.proxy.sync();
20054     },
20055
20056     // private
20057     onDragOver : function(e, id){
20058         var target = this.cachedTarget || Roo.dd.DragDropMgr.getDDById(id);
20059         if(this.beforeDragOver(target, e, id) !== false){
20060             if(target.isNotifyTarget){
20061                 var status = target.notifyOver(this, e, this.dragData);
20062                 this.proxy.setStatus(status);
20063             }
20064
20065             if(this.afterDragOver){
20066                 /**
20067                  * An empty function by default, but provided so that you can perform a custom action
20068                  * while the dragged item is over the drop target by providing an implementation.
20069                  * @param {Roo.dd.DragDrop} target The drop target
20070                  * @param {Event} e The event object
20071                  * @param {String} id The id of the dragged element
20072                  * @method afterDragOver
20073                  */
20074                 this.afterDragOver(target, e, id);
20075             }
20076         }
20077     },
20078
20079     /**
20080      * An empty function by default, but provided so that you can perform a custom action
20081      * while the dragged item is over the drop target and optionally cancel the onDragOver.
20082      * @param {Roo.dd.DragDrop} target The drop target
20083      * @param {Event} e The event object
20084      * @param {String} id The id of the dragged element
20085      * @return {Boolean} isValid True if the drag event is valid, else false to cancel
20086      */
20087     beforeDragOver : function(target, e, id){
20088         return true;
20089     },
20090
20091     // private
20092     onDragOut : function(e, id){
20093         var target = this.cachedTarget || Roo.dd.DragDropMgr.getDDById(id);
20094         if(this.beforeDragOut(target, e, id) !== false){
20095             if(target.isNotifyTarget){
20096                 target.notifyOut(this, e, this.dragData);
20097             }
20098             this.proxy.reset();
20099             if(this.afterDragOut){
20100                 /**
20101                  * An empty function by default, but provided so that you can perform a custom action
20102                  * after the dragged item is dragged out of the target without dropping.
20103                  * @param {Roo.dd.DragDrop} target The drop target
20104                  * @param {Event} e The event object
20105                  * @param {String} id The id of the dragged element
20106                  * @method afterDragOut
20107                  */
20108                 this.afterDragOut(target, e, id);
20109             }
20110         }
20111         this.cachedTarget = null;
20112     },
20113
20114     /**
20115      * An empty function by default, but provided so that you can perform a custom action before the dragged
20116      * item is dragged out of the target without dropping, and optionally cancel the onDragOut.
20117      * @param {Roo.dd.DragDrop} target The drop target
20118      * @param {Event} e The event object
20119      * @param {String} id The id of the dragged element
20120      * @return {Boolean} isValid True if the drag event is valid, else false to cancel
20121      */
20122     beforeDragOut : function(target, e, id){
20123         return true;
20124     },
20125     
20126     // private
20127     onDragDrop : function(e, id){
20128         var target = this.cachedTarget || Roo.dd.DragDropMgr.getDDById(id);
20129         if(this.beforeDragDrop(target, e, id) !== false){
20130             if(target.isNotifyTarget){
20131                 if(target.notifyDrop(this, e, this.dragData)){ // valid drop?
20132                     this.onValidDrop(target, e, id);
20133                 }else{
20134                     this.onInvalidDrop(target, e, id);
20135                 }
20136             }else{
20137                 this.onValidDrop(target, e, id);
20138             }
20139             
20140             if(this.afterDragDrop){
20141                 /**
20142                  * An empty function by default, but provided so that you can perform a custom action
20143                  * after a valid drag drop has occurred by providing an implementation.
20144                  * @param {Roo.dd.DragDrop} target The drop target
20145                  * @param {Event} e The event object
20146                  * @param {String} id The id of the dropped element
20147                  * @method afterDragDrop
20148                  */
20149                 this.afterDragDrop(target, e, id);
20150             }
20151         }
20152         delete this.cachedTarget;
20153     },
20154
20155     /**
20156      * An empty function by default, but provided so that you can perform a custom action before the dragged
20157      * item is dropped onto the target and optionally cancel the onDragDrop.
20158      * @param {Roo.dd.DragDrop} target The drop target
20159      * @param {Event} e The event object
20160      * @param {String} id The id of the dragged element
20161      * @return {Boolean} isValid True if the drag drop event is valid, else false to cancel
20162      */
20163     beforeDragDrop : function(target, e, id){
20164         return true;
20165     },
20166
20167     // private
20168     onValidDrop : function(target, e, id){
20169         this.hideProxy();
20170         if(this.afterValidDrop){
20171             /**
20172              * An empty function by default, but provided so that you can perform a custom action
20173              * after a valid drop has occurred by providing an implementation.
20174              * @param {Object} target The target DD 
20175              * @param {Event} e The event object
20176              * @param {String} id The id of the dropped element
20177              * @method afterInvalidDrop
20178              */
20179             this.afterValidDrop(target, e, id);
20180         }
20181     },
20182
20183     // private
20184     getRepairXY : function(e, data){
20185         return this.el.getXY();  
20186     },
20187
20188     // private
20189     onInvalidDrop : function(target, e, id){
20190         this.beforeInvalidDrop(target, e, id);
20191         if(this.cachedTarget){
20192             if(this.cachedTarget.isNotifyTarget){
20193                 this.cachedTarget.notifyOut(this, e, this.dragData);
20194             }
20195             this.cacheTarget = null;
20196         }
20197         this.proxy.repair(this.getRepairXY(e, this.dragData), this.afterRepair, this);
20198
20199         if(this.afterInvalidDrop){
20200             /**
20201              * An empty function by default, but provided so that you can perform a custom action
20202              * after an invalid drop has occurred by providing an implementation.
20203              * @param {Event} e The event object
20204              * @param {String} id The id of the dropped element
20205              * @method afterInvalidDrop
20206              */
20207             this.afterInvalidDrop(e, id);
20208         }
20209     },
20210
20211     // private
20212     afterRepair : function(){
20213         if(Roo.enableFx){
20214             this.el.highlight(this.hlColor || "c3daf9");
20215         }
20216         this.dragging = false;
20217     },
20218
20219     /**
20220      * An empty function by default, but provided so that you can perform a custom action after an invalid
20221      * drop has occurred.
20222      * @param {Roo.dd.DragDrop} target The drop target
20223      * @param {Event} e The event object
20224      * @param {String} id The id of the dragged element
20225      * @return {Boolean} isValid True if the invalid drop should proceed, else false to cancel
20226      */
20227     beforeInvalidDrop : function(target, e, id){
20228         return true;
20229     },
20230
20231     // private
20232     handleMouseDown : function(e){
20233         if(this.dragging) {
20234             return;
20235         }
20236         var data = this.getDragData(e);
20237         if(data && this.onBeforeDrag(data, e) !== false){
20238             this.dragData = data;
20239             this.proxy.stop();
20240             Roo.dd.DragSource.superclass.handleMouseDown.apply(this, arguments);
20241         } 
20242     },
20243
20244     /**
20245      * An empty function by default, but provided so that you can perform a custom action before the initial
20246      * drag event begins and optionally cancel it.
20247      * @param {Object} data An object containing arbitrary data to be shared with drop targets
20248      * @param {Event} e The event object
20249      * @return {Boolean} isValid True if the drag event is valid, else false to cancel
20250      */
20251     onBeforeDrag : function(data, e){
20252         return true;
20253     },
20254
20255     /**
20256      * An empty function by default, but provided so that you can perform a custom action once the initial
20257      * drag event has begun.  The drag cannot be canceled from this function.
20258      * @param {Number} x The x position of the click on the dragged object
20259      * @param {Number} y The y position of the click on the dragged object
20260      */
20261     onStartDrag : Roo.emptyFn,
20262
20263     // private - YUI override
20264     startDrag : function(x, y){
20265         this.proxy.reset();
20266         this.dragging = true;
20267         this.proxy.update("");
20268         this.onInitDrag(x, y);
20269         this.proxy.show();
20270     },
20271
20272     // private
20273     onInitDrag : function(x, y){
20274         var clone = this.el.dom.cloneNode(true);
20275         clone.id = Roo.id(); // prevent duplicate ids
20276         this.proxy.update(clone);
20277         this.onStartDrag(x, y);
20278         return true;
20279     },
20280
20281     /**
20282      * Returns the drag source's underlying {@link Roo.dd.StatusProxy}
20283      * @return {Roo.dd.StatusProxy} proxy The StatusProxy
20284      */
20285     getProxy : function(){
20286         return this.proxy;  
20287     },
20288
20289     /**
20290      * Hides the drag source's {@link Roo.dd.StatusProxy}
20291      */
20292     hideProxy : function(){
20293         this.proxy.hide();  
20294         this.proxy.reset(true);
20295         this.dragging = false;
20296     },
20297
20298     // private
20299     triggerCacheRefresh : function(){
20300         Roo.dd.DDM.refreshCache(this.groups);
20301     },
20302
20303     // private - override to prevent hiding
20304     b4EndDrag: function(e) {
20305     },
20306
20307     // private - override to prevent moving
20308     endDrag : function(e){
20309         this.onEndDrag(this.dragData, e);
20310     },
20311
20312     // private
20313     onEndDrag : function(data, e){
20314     },
20315     
20316     // private - pin to cursor
20317     autoOffset : function(x, y) {
20318         this.setDelta(-12, -20);
20319     }    
20320 });/*
20321  * Based on:
20322  * Ext JS Library 1.1.1
20323  * Copyright(c) 2006-2007, Ext JS, LLC.
20324  *
20325  * Originally Released Under LGPL - original licence link has changed is not relivant.
20326  *
20327  * Fork - LGPL
20328  * <script type="text/javascript">
20329  */
20330
20331
20332 /**
20333  * @class Roo.dd.DropTarget
20334  * @extends Roo.dd.DDTarget
20335  * A simple class that provides the basic implementation needed to make any element a drop target that can have
20336  * draggable items dropped onto it.  The drop has no effect until an implementation of notifyDrop is provided.
20337  * @constructor
20338  * @param {String/HTMLElement/Element} el The container element
20339  * @param {Object} config
20340  */
20341 Roo.dd.DropTarget = function(el, config){
20342     this.el = Roo.get(el);
20343     
20344     var listeners = false; ;
20345     if (config && config.listeners) {
20346         listeners= config.listeners;
20347         delete config.listeners;
20348     }
20349     Roo.apply(this, config);
20350     
20351     if(this.containerScroll){
20352         Roo.dd.ScrollManager.register(this.el);
20353     }
20354     this.addEvents( {
20355          /**
20356          * @scope Roo.dd.DropTarget
20357          */
20358          
20359          /**
20360          * @event enter
20361          * The function a {@link Roo.dd.DragSource} calls once to notify this drop target that the source is now over the
20362          * target.  This default implementation adds the CSS class specified by overClass (if any) to the drop element
20363          * and returns the dropAllowed config value.  This method should be overridden if drop validation is required.
20364          * 
20365          * IMPORTANT : it should set this.overClass and this.dropAllowed
20366          * 
20367          * @param {Roo.dd.DragSource} source The drag source that was dragged over this drop target
20368          * @param {Event} e The event
20369          * @param {Object} data An object containing arbitrary data supplied by the drag source
20370          */
20371         "enter" : true,
20372         
20373          /**
20374          * @event over
20375          * The function a {@link Roo.dd.DragSource} calls continuously while it is being dragged over the target.
20376          * This method will be called on every mouse movement while the drag source is over the drop target.
20377          * This default implementation simply returns the dropAllowed config value.
20378          * 
20379          * IMPORTANT : it should set this.dropAllowed
20380          * 
20381          * @param {Roo.dd.DragSource} source The drag source that was dragged over this drop target
20382          * @param {Event} e The event
20383          * @param {Object} data An object containing arbitrary data supplied by the drag source
20384          
20385          */
20386         "over" : true,
20387         /**
20388          * @event out
20389          * The function a {@link Roo.dd.DragSource} calls once to notify this drop target that the source has been dragged
20390          * out of the target without dropping.  This default implementation simply removes the CSS class specified by
20391          * overClass (if any) from the drop element.
20392          * 
20393          * @param {Roo.dd.DragSource} source The drag source that was dragged over this drop target
20394          * @param {Event} e The event
20395          * @param {Object} data An object containing arbitrary data supplied by the drag source
20396          */
20397          "out" : true,
20398          
20399         /**
20400          * @event drop
20401          * The function a {@link Roo.dd.DragSource} calls once to notify this drop target that the dragged item has
20402          * been dropped on it.  This method has no default implementation and returns false, so you must provide an
20403          * implementation that does something to process the drop event and returns true so that the drag source's
20404          * repair action does not run.
20405          * 
20406          * IMPORTANT : it should set this.success
20407          * 
20408          * @param {Roo.dd.DragSource} source The drag source that was dragged over this drop target
20409          * @param {Event} e The event
20410          * @param {Object} data An object containing arbitrary data supplied by the drag source
20411         */
20412          "drop" : true
20413     });
20414             
20415      
20416     Roo.dd.DropTarget.superclass.constructor.call(  this, 
20417         this.el.dom, 
20418         this.ddGroup || this.group,
20419         {
20420             isTarget: true,
20421             listeners : listeners || {} 
20422            
20423         
20424         }
20425     );
20426
20427 };
20428
20429 Roo.extend(Roo.dd.DropTarget, Roo.dd.DDTarget, {
20430     /**
20431      * @cfg {String} overClass
20432      * The CSS class applied to the drop target element while the drag source is over it (defaults to "").
20433      */
20434      /**
20435      * @cfg {String} ddGroup
20436      * The drag drop group to handle drop events for
20437      */
20438      
20439     /**
20440      * @cfg {String} dropAllowed
20441      * The CSS class returned to the drag source when drop is allowed (defaults to "x-dd-drop-ok").
20442      */
20443     dropAllowed : "x-dd-drop-ok",
20444     /**
20445      * @cfg {String} dropNotAllowed
20446      * The CSS class returned to the drag source when drop is not allowed (defaults to "x-dd-drop-nodrop").
20447      */
20448     dropNotAllowed : "x-dd-drop-nodrop",
20449     /**
20450      * @cfg {boolean} success
20451      * set this after drop listener.. 
20452      */
20453     success : false,
20454     /**
20455      * @cfg {boolean|String} valid true/false or string (ok-add/ok-sub/ok/nodrop)
20456      * if the drop point is valid for over/enter..
20457      */
20458     valid : false,
20459     // private
20460     isTarget : true,
20461
20462     // private
20463     isNotifyTarget : true,
20464     
20465     /**
20466      * @hide
20467      */
20468     notifyEnter : function(dd, e, data)
20469     {
20470         this.valid = true;
20471         this.fireEvent('enter', dd, e, data);
20472         if(this.overClass){
20473             this.el.addClass(this.overClass);
20474         }
20475         return typeof(this.valid) == 'string' ? 'x-dd-drop-' + this.valid : (
20476             this.valid ? this.dropAllowed : this.dropNotAllowed
20477         );
20478     },
20479
20480     /**
20481      * @hide
20482      */
20483     notifyOver : function(dd, e, data)
20484     {
20485         this.valid = true;
20486         this.fireEvent('over', dd, e, data);
20487         return typeof(this.valid) == 'string' ? 'x-dd-drop-' + this.valid : (
20488             this.valid ? this.dropAllowed : this.dropNotAllowed
20489         );
20490     },
20491
20492     /**
20493      * @hide
20494      */
20495     notifyOut : function(dd, e, data)
20496     {
20497         this.fireEvent('out', dd, e, data);
20498         if(this.overClass){
20499             this.el.removeClass(this.overClass);
20500         }
20501     },
20502
20503     /**
20504      * @hide
20505      */
20506     notifyDrop : function(dd, e, data)
20507     {
20508         this.success = false;
20509         this.fireEvent('drop', dd, e, data);
20510         return this.success;
20511     }
20512 });/*
20513  * Based on:
20514  * Ext JS Library 1.1.1
20515  * Copyright(c) 2006-2007, Ext JS, LLC.
20516  *
20517  * Originally Released Under LGPL - original licence link has changed is not relivant.
20518  *
20519  * Fork - LGPL
20520  * <script type="text/javascript">
20521  */
20522
20523
20524 /**
20525  * @class Roo.dd.DragZone
20526  * @extends Roo.dd.DragSource
20527  * This class provides a container DD instance that proxies for multiple child node sources.<br />
20528  * By default, this class requires that draggable child nodes are registered with {@link Roo.dd.Registry}.
20529  * @constructor
20530  * @param {String/HTMLElement/Element} el The container element
20531  * @param {Object} config
20532  */
20533 Roo.dd.DragZone = function(el, config){
20534     Roo.dd.DragZone.superclass.constructor.call(this, el, config);
20535     if(this.containerScroll){
20536         Roo.dd.ScrollManager.register(this.el);
20537     }
20538 };
20539
20540 Roo.extend(Roo.dd.DragZone, Roo.dd.DragSource, {
20541     /**
20542      * @cfg {Boolean} containerScroll True to register this container with the Scrollmanager
20543      * for auto scrolling during drag operations.
20544      */
20545     /**
20546      * @cfg {String} hlColor The color to use when visually highlighting the drag source in the afterRepair
20547      * method after a failed drop (defaults to "c3daf9" - light blue)
20548      */
20549
20550     /**
20551      * Called when a mousedown occurs in this container. Looks in {@link Roo.dd.Registry}
20552      * for a valid target to drag based on the mouse down. Override this method
20553      * to provide your own lookup logic (e.g. finding a child by class name). Make sure your returned
20554      * object has a "ddel" attribute (with an HTML Element) for other functions to work.
20555      * @param {EventObject} e The mouse down event
20556      * @return {Object} The dragData
20557      */
20558     getDragData : function(e){
20559         return Roo.dd.Registry.getHandleFromEvent(e);
20560     },
20561     
20562     /**
20563      * Called once drag threshold has been reached to initialize the proxy element. By default, it clones the
20564      * this.dragData.ddel
20565      * @param {Number} x The x position of the click on the dragged object
20566      * @param {Number} y The y position of the click on the dragged object
20567      * @return {Boolean} true to continue the drag, false to cancel
20568      */
20569     onInitDrag : function(x, y){
20570         this.proxy.update(this.dragData.ddel.cloneNode(true));
20571         this.onStartDrag(x, y);
20572         return true;
20573     },
20574     
20575     /**
20576      * Called after a repair of an invalid drop. By default, highlights this.dragData.ddel 
20577      */
20578     afterRepair : function(){
20579         if(Roo.enableFx){
20580             Roo.Element.fly(this.dragData.ddel).highlight(this.hlColor || "c3daf9");
20581         }
20582         this.dragging = false;
20583     },
20584
20585     /**
20586      * Called before a repair of an invalid drop to get the XY to animate to. By default returns
20587      * the XY of this.dragData.ddel
20588      * @param {EventObject} e The mouse up event
20589      * @return {Array} The xy location (e.g. [100, 200])
20590      */
20591     getRepairXY : function(e){
20592         return Roo.Element.fly(this.dragData.ddel).getXY();  
20593     }
20594 });/*
20595  * Based on:
20596  * Ext JS Library 1.1.1
20597  * Copyright(c) 2006-2007, Ext JS, LLC.
20598  *
20599  * Originally Released Under LGPL - original licence link has changed is not relivant.
20600  *
20601  * Fork - LGPL
20602  * <script type="text/javascript">
20603  */
20604 /**
20605  * @class Roo.dd.DropZone
20606  * @extends Roo.dd.DropTarget
20607  * This class provides a container DD instance that proxies for multiple child node targets.<br />
20608  * By default, this class requires that child nodes accepting drop are registered with {@link Roo.dd.Registry}.
20609  * @constructor
20610  * @param {String/HTMLElement/Element} el The container element
20611  * @param {Object} config
20612  */
20613 Roo.dd.DropZone = function(el, config){
20614     Roo.dd.DropZone.superclass.constructor.call(this, el, config);
20615 };
20616
20617 Roo.extend(Roo.dd.DropZone, Roo.dd.DropTarget, {
20618     /**
20619      * Returns a custom data object associated with the DOM node that is the target of the event.  By default
20620      * this looks up the event target in the {@link Roo.dd.Registry}, although you can override this method to
20621      * provide your own custom lookup.
20622      * @param {Event} e The event
20623      * @return {Object} data The custom data
20624      */
20625     getTargetFromEvent : function(e){
20626         return Roo.dd.Registry.getTargetFromEvent(e);
20627     },
20628
20629     /**
20630      * Called internally when the DropZone determines that a {@link Roo.dd.DragSource} has entered a drop node
20631      * that it has registered.  This method has no default implementation and should be overridden to provide
20632      * node-specific processing if necessary.
20633      * @param {Object} nodeData The custom data associated with the drop node (this is the same value returned from 
20634      * {@link #getTargetFromEvent} for this node)
20635      * @param {Roo.dd.DragSource} source The drag source that was dragged over this drop zone
20636      * @param {Event} e The event
20637      * @param {Object} data An object containing arbitrary data supplied by the drag source
20638      */
20639     onNodeEnter : function(n, dd, e, data){
20640         
20641     },
20642
20643     /**
20644      * Called internally while the DropZone determines that a {@link Roo.dd.DragSource} is over a drop node
20645      * that it has registered.  The default implementation returns this.dropNotAllowed, so it should be
20646      * overridden to provide the proper feedback.
20647      * @param {Object} nodeData The custom data associated with the drop node (this is the same value returned from
20648      * {@link #getTargetFromEvent} for this node)
20649      * @param {Roo.dd.DragSource} source The drag source that was dragged over this drop zone
20650      * @param {Event} e The event
20651      * @param {Object} data An object containing arbitrary data supplied by the drag source
20652      * @return {String} status The CSS class that communicates the drop status back to the source so that the
20653      * underlying {@link Roo.dd.StatusProxy} can be updated
20654      */
20655     onNodeOver : function(n, dd, e, data){
20656         return this.dropAllowed;
20657     },
20658
20659     /**
20660      * Called internally when the DropZone determines that a {@link Roo.dd.DragSource} has been dragged out of
20661      * the drop node without dropping.  This method has no default implementation and should be overridden to provide
20662      * node-specific processing if necessary.
20663      * @param {Object} nodeData The custom data associated with the drop node (this is the same value returned from
20664      * {@link #getTargetFromEvent} for this node)
20665      * @param {Roo.dd.DragSource} source The drag source that was dragged over this drop zone
20666      * @param {Event} e The event
20667      * @param {Object} data An object containing arbitrary data supplied by the drag source
20668      */
20669     onNodeOut : function(n, dd, e, data){
20670         
20671     },
20672
20673     /**
20674      * Called internally when the DropZone determines that a {@link Roo.dd.DragSource} has been dropped onto
20675      * the drop node.  The default implementation returns false, so it should be overridden to provide the
20676      * appropriate processing of the drop event and return true so that the drag source's repair action does not run.
20677      * @param {Object} nodeData The custom data associated with the drop node (this is the same value returned from
20678      * {@link #getTargetFromEvent} for this node)
20679      * @param {Roo.dd.DragSource} source The drag source that was dragged over this drop zone
20680      * @param {Event} e The event
20681      * @param {Object} data An object containing arbitrary data supplied by the drag source
20682      * @return {Boolean} True if the drop was valid, else false
20683      */
20684     onNodeDrop : function(n, dd, e, data){
20685         return false;
20686     },
20687
20688     /**
20689      * Called internally while the DropZone determines that a {@link Roo.dd.DragSource} is being dragged over it,
20690      * but not over any of its registered drop nodes.  The default implementation returns this.dropNotAllowed, so
20691      * it should be overridden to provide the proper feedback if necessary.
20692      * @param {Roo.dd.DragSource} source The drag source that was dragged over this drop zone
20693      * @param {Event} e The event
20694      * @param {Object} data An object containing arbitrary data supplied by the drag source
20695      * @return {String} status The CSS class that communicates the drop status back to the source so that the
20696      * underlying {@link Roo.dd.StatusProxy} can be updated
20697      */
20698     onContainerOver : function(dd, e, data){
20699         return this.dropNotAllowed;
20700     },
20701
20702     /**
20703      * Called internally when the DropZone determines that a {@link Roo.dd.DragSource} has been dropped on it,
20704      * but not on any of its registered drop nodes.  The default implementation returns false, so it should be
20705      * overridden to provide the appropriate processing of the drop event if you need the drop zone itself to
20706      * be able to accept drops.  It should return true when valid so that the drag source's repair action does not run.
20707      * @param {Roo.dd.DragSource} source The drag source that was dragged over this drop zone
20708      * @param {Event} e The event
20709      * @param {Object} data An object containing arbitrary data supplied by the drag source
20710      * @return {Boolean} True if the drop was valid, else false
20711      */
20712     onContainerDrop : function(dd, e, data){
20713         return false;
20714     },
20715
20716     /**
20717      * The function a {@link Roo.dd.DragSource} calls once to notify this drop zone that the source is now over
20718      * the zone.  The default implementation returns this.dropNotAllowed and expects that only registered drop
20719      * nodes can process drag drop operations, so if you need the drop zone itself to be able to process drops
20720      * you should override this method and provide a custom implementation.
20721      * @param {Roo.dd.DragSource} source The drag source that was dragged over this drop zone
20722      * @param {Event} e The event
20723      * @param {Object} data An object containing arbitrary data supplied by the drag source
20724      * @return {String} status The CSS class that communicates the drop status back to the source so that the
20725      * underlying {@link Roo.dd.StatusProxy} can be updated
20726      */
20727     notifyEnter : function(dd, e, data){
20728         return this.dropNotAllowed;
20729     },
20730
20731     /**
20732      * The function a {@link Roo.dd.DragSource} calls continuously while it is being dragged over the drop zone.
20733      * This method will be called on every mouse movement while the drag source is over the drop zone.
20734      * It will call {@link #onNodeOver} while the drag source is over a registered node, and will also automatically
20735      * delegate to the appropriate node-specific methods as necessary when the drag source enters and exits
20736      * registered nodes ({@link #onNodeEnter}, {@link #onNodeOut}). If the drag source is not currently over a
20737      * registered node, it will call {@link #onContainerOver}.
20738      * @param {Roo.dd.DragSource} source The drag source that was dragged over this drop zone
20739      * @param {Event} e The event
20740      * @param {Object} data An object containing arbitrary data supplied by the drag source
20741      * @return {String} status The CSS class that communicates the drop status back to the source so that the
20742      * underlying {@link Roo.dd.StatusProxy} can be updated
20743      */
20744     notifyOver : function(dd, e, data){
20745         var n = this.getTargetFromEvent(e);
20746         if(!n){ // not over valid drop target
20747             if(this.lastOverNode){
20748                 this.onNodeOut(this.lastOverNode, dd, e, data);
20749                 this.lastOverNode = null;
20750             }
20751             return this.onContainerOver(dd, e, data);
20752         }
20753         if(this.lastOverNode != n){
20754             if(this.lastOverNode){
20755                 this.onNodeOut(this.lastOverNode, dd, e, data);
20756             }
20757             this.onNodeEnter(n, dd, e, data);
20758             this.lastOverNode = n;
20759         }
20760         return this.onNodeOver(n, dd, e, data);
20761     },
20762
20763     /**
20764      * The function a {@link Roo.dd.DragSource} calls once to notify this drop zone that the source has been dragged
20765      * out of the zone without dropping.  If the drag source is currently over a registered node, the notification
20766      * will be delegated to {@link #onNodeOut} for node-specific handling, otherwise it will be ignored.
20767      * @param {Roo.dd.DragSource} source The drag source that was dragged over this drop target
20768      * @param {Event} e The event
20769      * @param {Object} data An object containing arbitrary data supplied by the drag zone
20770      */
20771     notifyOut : function(dd, e, data){
20772         if(this.lastOverNode){
20773             this.onNodeOut(this.lastOverNode, dd, e, data);
20774             this.lastOverNode = null;
20775         }
20776     },
20777
20778     /**
20779      * The function a {@link Roo.dd.DragSource} calls once to notify this drop zone that the dragged item has
20780      * been dropped on it.  The drag zone will look up the target node based on the event passed in, and if there
20781      * is a node registered for that event, it will delegate to {@link #onNodeDrop} for node-specific handling,
20782      * otherwise it will call {@link #onContainerDrop}.
20783      * @param {Roo.dd.DragSource} source The drag source that was dragged over this drop zone
20784      * @param {Event} e The event
20785      * @param {Object} data An object containing arbitrary data supplied by the drag source
20786      * @return {Boolean} True if the drop was valid, else false
20787      */
20788     notifyDrop : function(dd, e, data){
20789         if(this.lastOverNode){
20790             this.onNodeOut(this.lastOverNode, dd, e, data);
20791             this.lastOverNode = null;
20792         }
20793         var n = this.getTargetFromEvent(e);
20794         return n ?
20795             this.onNodeDrop(n, dd, e, data) :
20796             this.onContainerDrop(dd, e, data);
20797     },
20798
20799     // private
20800     triggerCacheRefresh : function(){
20801         Roo.dd.DDM.refreshCache(this.groups);
20802     }  
20803 });/*
20804  * Based on:
20805  * Ext JS Library 1.1.1
20806  * Copyright(c) 2006-2007, Ext JS, LLC.
20807  *
20808  * Originally Released Under LGPL - original licence link has changed is not relivant.
20809  *
20810  * Fork - LGPL
20811  * <script type="text/javascript">
20812  */
20813
20814
20815 /**
20816  * @class Roo.data.SortTypes
20817  * @singleton
20818  * Defines the default sorting (casting?) comparison functions used when sorting data.
20819  */
20820 Roo.data.SortTypes = {
20821     /**
20822      * Default sort that does nothing
20823      * @param {Mixed} s The value being converted
20824      * @return {Mixed} The comparison value
20825      */
20826     none : function(s){
20827         return s;
20828     },
20829     
20830     /**
20831      * The regular expression used to strip tags
20832      * @type {RegExp}
20833      * @property
20834      */
20835     stripTagsRE : /<\/?[^>]+>/gi,
20836     
20837     /**
20838      * Strips all HTML tags to sort on text only
20839      * @param {Mixed} s The value being converted
20840      * @return {String} The comparison value
20841      */
20842     asText : function(s){
20843         return String(s).replace(this.stripTagsRE, "");
20844     },
20845     
20846     /**
20847      * Strips all HTML tags to sort on text only - Case insensitive
20848      * @param {Mixed} s The value being converted
20849      * @return {String} The comparison value
20850      */
20851     asUCText : function(s){
20852         return String(s).toUpperCase().replace(this.stripTagsRE, "");
20853     },
20854     
20855     /**
20856      * Case insensitive string
20857      * @param {Mixed} s The value being converted
20858      * @return {String} The comparison value
20859      */
20860     asUCString : function(s) {
20861         return String(s).toUpperCase();
20862     },
20863     
20864     /**
20865      * Date sorting
20866      * @param {Mixed} s The value being converted
20867      * @return {Number} The comparison value
20868      */
20869     asDate : function(s) {
20870         if(!s){
20871             return 0;
20872         }
20873         if(s instanceof Date){
20874             return s.getTime();
20875         }
20876         return Date.parse(String(s));
20877     },
20878     
20879     /**
20880      * Float sorting
20881      * @param {Mixed} s The value being converted
20882      * @return {Float} The comparison value
20883      */
20884     asFloat : function(s) {
20885         var val = parseFloat(String(s).replace(/,/g, ""));
20886         if(isNaN(val)) val = 0;
20887         return val;
20888     },
20889     
20890     /**
20891      * Integer sorting
20892      * @param {Mixed} s The value being converted
20893      * @return {Number} The comparison value
20894      */
20895     asInt : function(s) {
20896         var val = parseInt(String(s).replace(/,/g, ""));
20897         if(isNaN(val)) val = 0;
20898         return val;
20899     }
20900 };/*
20901  * Based on:
20902  * Ext JS Library 1.1.1
20903  * Copyright(c) 2006-2007, Ext JS, LLC.
20904  *
20905  * Originally Released Under LGPL - original licence link has changed is not relivant.
20906  *
20907  * Fork - LGPL
20908  * <script type="text/javascript">
20909  */
20910
20911 /**
20912 * @class Roo.data.Record
20913  * Instances of this class encapsulate both record <em>definition</em> information, and record
20914  * <em>value</em> information for use in {@link Roo.data.Store} objects, or any code which needs
20915  * to access Records cached in an {@link Roo.data.Store} object.<br>
20916  * <p>
20917  * Constructors for this class are generated by passing an Array of field definition objects to {@link #create}.
20918  * Instances are usually only created by {@link Roo.data.Reader} implementations when processing unformatted data
20919  * objects.<br>
20920  * <p>
20921  * Record objects generated by this constructor inherit all the methods of Roo.data.Record listed below.
20922  * @constructor
20923  * This constructor should not be used to create Record objects. Instead, use the constructor generated by
20924  * {@link #create}. The parameters are the same.
20925  * @param {Array} data An associative Array of data values keyed by the field name.
20926  * @param {Object} id (Optional) The id of the record. This id should be unique, and is used by the
20927  * {@link Roo.data.Store} object which owns the Record to index its collection of Records. If
20928  * not specified an integer id is generated.
20929  */
20930 Roo.data.Record = function(data, id){
20931     this.id = (id || id === 0) ? id : ++Roo.data.Record.AUTO_ID;
20932     this.data = data;
20933 };
20934
20935 /**
20936  * Generate a constructor for a specific record layout.
20937  * @param {Array} o An Array of field definition objects which specify field names, and optionally,
20938  * data types, and a mapping for an {@link Roo.data.Reader} to extract the field's value from a data object.
20939  * Each field definition object may contain the following properties: <ul>
20940  * <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,
20941  * for example the <em>dataIndex</em> property in column definition objects passed to {@link Roo.grid.ColumnModel}</p></li>
20942  * <li><b>mapping</b> : String<p style="margin-left:1em">(Optional) A path specification for use by the {@link Roo.data.Reader} implementation
20943  * that is creating the Record to access the data value from the data object. If an {@link Roo.data.JsonReader}
20944  * is being used, then this is a string containing the javascript expression to reference the data relative to 
20945  * the record item's root. If an {@link Roo.data.XmlReader} is being used, this is an {@link Roo.DomQuery} path
20946  * to the data item relative to the record element. If the mapping expression is the same as the field name,
20947  * this may be omitted.</p></li>
20948  * <li><b>type</b> : String<p style="margin-left:1em">(Optional) The data type for conversion to displayable value. Possible values are
20949  * <ul><li>auto (Default, implies no conversion)</li>
20950  * <li>string</li>
20951  * <li>int</li>
20952  * <li>float</li>
20953  * <li>boolean</li>
20954  * <li>date</li></ul></p></li>
20955  * <li><b>sortType</b> : Mixed<p style="margin-left:1em">(Optional) A member of {@link Roo.data.SortTypes}.</p></li>
20956  * <li><b>sortDir</b> : String<p style="margin-left:1em">(Optional) Initial direction to sort. "ASC" or "DESC"</p></li>
20957  * <li><b>convert</b> : Function<p style="margin-left:1em">(Optional) A function which converts the value provided
20958  * by the Reader into an object that will be stored in the Record. It is passed the
20959  * following parameters:<ul>
20960  * <li><b>v</b> : Mixed<p style="margin-left:1em">The data value as read by the Reader.</p></li>
20961  * </ul></p></li>
20962  * <li><b>dateFormat</b> : String<p style="margin-left:1em">(Optional) A format String for the Date.parseDate function.</p></li>
20963  * </ul>
20964  * <br>usage:<br><pre><code>
20965 var TopicRecord = Roo.data.Record.create(
20966     {name: 'title', mapping: 'topic_title'},
20967     {name: 'author', mapping: 'username'},
20968     {name: 'totalPosts', mapping: 'topic_replies', type: 'int'},
20969     {name: 'lastPost', mapping: 'post_time', type: 'date'},
20970     {name: 'lastPoster', mapping: 'user2'},
20971     {name: 'excerpt', mapping: 'post_text'}
20972 );
20973
20974 var myNewRecord = new TopicRecord({
20975     title: 'Do my job please',
20976     author: 'noobie',
20977     totalPosts: 1,
20978     lastPost: new Date(),
20979     lastPoster: 'Animal',
20980     excerpt: 'No way dude!'
20981 });
20982 myStore.add(myNewRecord);
20983 </code></pre>
20984  * @method create
20985  * @static
20986  */
20987 Roo.data.Record.create = function(o){
20988     var f = function(){
20989         f.superclass.constructor.apply(this, arguments);
20990     };
20991     Roo.extend(f, Roo.data.Record);
20992     var p = f.prototype;
20993     p.fields = new Roo.util.MixedCollection(false, function(field){
20994         return field.name;
20995     });
20996     for(var i = 0, len = o.length; i < len; i++){
20997         p.fields.add(new Roo.data.Field(o[i]));
20998     }
20999     f.getField = function(name){
21000         return p.fields.get(name);  
21001     };
21002     return f;
21003 };
21004
21005 Roo.data.Record.AUTO_ID = 1000;
21006 Roo.data.Record.EDIT = 'edit';
21007 Roo.data.Record.REJECT = 'reject';
21008 Roo.data.Record.COMMIT = 'commit';
21009
21010 Roo.data.Record.prototype = {
21011     /**
21012      * Readonly flag - true if this record has been modified.
21013      * @type Boolean
21014      */
21015     dirty : false,
21016     editing : false,
21017     error: null,
21018     modified: null,
21019
21020     // private
21021     join : function(store){
21022         this.store = store;
21023     },
21024
21025     /**
21026      * Set the named field to the specified value.
21027      * @param {String} name The name of the field to set.
21028      * @param {Object} value The value to set the field to.
21029      */
21030     set : function(name, value){
21031         if(this.data[name] == value){
21032             return;
21033         }
21034         this.dirty = true;
21035         if(!this.modified){
21036             this.modified = {};
21037         }
21038         if(typeof this.modified[name] == 'undefined'){
21039             this.modified[name] = this.data[name];
21040         }
21041         this.data[name] = value;
21042         if(!this.editing && this.store){
21043             this.store.afterEdit(this);
21044         }       
21045     },
21046
21047     /**
21048      * Get the value of the named field.
21049      * @param {String} name The name of the field to get the value of.
21050      * @return {Object} The value of the field.
21051      */
21052     get : function(name){
21053         return this.data[name]; 
21054     },
21055
21056     // private
21057     beginEdit : function(){
21058         this.editing = true;
21059         this.modified = {}; 
21060     },
21061
21062     // private
21063     cancelEdit : function(){
21064         this.editing = false;
21065         delete this.modified;
21066     },
21067
21068     // private
21069     endEdit : function(){
21070         this.editing = false;
21071         if(this.dirty && this.store){
21072             this.store.afterEdit(this);
21073         }
21074     },
21075
21076     /**
21077      * Usually called by the {@link Roo.data.Store} which owns the Record.
21078      * Rejects all changes made to the Record since either creation, or the last commit operation.
21079      * Modified fields are reverted to their original values.
21080      * <p>
21081      * Developers should subscribe to the {@link Roo.data.Store#update} event to have their code notified
21082      * of reject operations.
21083      */
21084     reject : function(){
21085         var m = this.modified;
21086         for(var n in m){
21087             if(typeof m[n] != "function"){
21088                 this.data[n] = m[n];
21089             }
21090         }
21091         this.dirty = false;
21092         delete this.modified;
21093         this.editing = false;
21094         if(this.store){
21095             this.store.afterReject(this);
21096         }
21097     },
21098
21099     /**
21100      * Usually called by the {@link Roo.data.Store} which owns the Record.
21101      * Commits all changes made to the Record since either creation, or the last commit operation.
21102      * <p>
21103      * Developers should subscribe to the {@link Roo.data.Store#update} event to have their code notified
21104      * of commit operations.
21105      */
21106     commit : function(){
21107         this.dirty = false;
21108         delete this.modified;
21109         this.editing = false;
21110         if(this.store){
21111             this.store.afterCommit(this);
21112         }
21113     },
21114
21115     // private
21116     hasError : function(){
21117         return this.error != null;
21118     },
21119
21120     // private
21121     clearError : function(){
21122         this.error = null;
21123     },
21124
21125     /**
21126      * Creates a copy of this record.
21127      * @param {String} id (optional) A new record id if you don't want to use this record's id
21128      * @return {Record}
21129      */
21130     copy : function(newId) {
21131         return new this.constructor(Roo.apply({}, this.data), newId || this.id);
21132     }
21133 };/*
21134  * Based on:
21135  * Ext JS Library 1.1.1
21136  * Copyright(c) 2006-2007, Ext JS, LLC.
21137  *
21138  * Originally Released Under LGPL - original licence link has changed is not relivant.
21139  *
21140  * Fork - LGPL
21141  * <script type="text/javascript">
21142  */
21143
21144
21145
21146 /**
21147  * @class Roo.data.Store
21148  * @extends Roo.util.Observable
21149  * The Store class encapsulates a client side cache of {@link Roo.data.Record} objects which provide input data
21150  * for widgets such as the Roo.grid.Grid, or the Roo.form.ComboBox.<br>
21151  * <p>
21152  * 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
21153  * has no knowledge of the format of the data returned by the Proxy.<br>
21154  * <p>
21155  * A Store object uses its configured implementation of {@link Roo.data.DataReader} to create {@link Roo.data.Record}
21156  * instances from the data object. These records are cached and made available through accessor functions.
21157  * @constructor
21158  * Creates a new Store.
21159  * @param {Object} config A config object containing the objects needed for the Store to access data,
21160  * and read the data into Records.
21161  */
21162 Roo.data.Store = function(config){
21163     this.data = new Roo.util.MixedCollection(false);
21164     this.data.getKey = function(o){
21165         return o.id;
21166     };
21167     this.baseParams = {};
21168     // private
21169     this.paramNames = {
21170         "start" : "start",
21171         "limit" : "limit",
21172         "sort" : "sort",
21173         "dir" : "dir",
21174         "multisort" : "_multisort"
21175     };
21176
21177     if(config && config.data){
21178         this.inlineData = config.data;
21179         delete config.data;
21180     }
21181
21182     Roo.apply(this, config);
21183     
21184     if(this.reader){ // reader passed
21185         this.reader = Roo.factory(this.reader, Roo.data);
21186         this.reader.xmodule = this.xmodule || false;
21187         if(!this.recordType){
21188             this.recordType = this.reader.recordType;
21189         }
21190         if(this.reader.onMetaChange){
21191             this.reader.onMetaChange = this.onMetaChange.createDelegate(this);
21192         }
21193     }
21194
21195     if(this.recordType){
21196         this.fields = this.recordType.prototype.fields;
21197     }
21198     this.modified = [];
21199
21200     this.addEvents({
21201         /**
21202          * @event datachanged
21203          * Fires when the data cache has changed, and a widget which is using this Store
21204          * as a Record cache should refresh its view.
21205          * @param {Store} this
21206          */
21207         datachanged : true,
21208         /**
21209          * @event metachange
21210          * Fires when this store's reader provides new metadata (fields). This is currently only support for JsonReaders.
21211          * @param {Store} this
21212          * @param {Object} meta The JSON metadata
21213          */
21214         metachange : true,
21215         /**
21216          * @event add
21217          * Fires when Records have been added to the Store
21218          * @param {Store} this
21219          * @param {Roo.data.Record[]} records The array of Records added
21220          * @param {Number} index The index at which the record(s) were added
21221          */
21222         add : true,
21223         /**
21224          * @event remove
21225          * Fires when a Record has been removed from the Store
21226          * @param {Store} this
21227          * @param {Roo.data.Record} record The Record that was removed
21228          * @param {Number} index The index at which the record was removed
21229          */
21230         remove : true,
21231         /**
21232          * @event update
21233          * Fires when a Record has been updated
21234          * @param {Store} this
21235          * @param {Roo.data.Record} record The Record that was updated
21236          * @param {String} operation The update operation being performed.  Value may be one of:
21237          * <pre><code>
21238  Roo.data.Record.EDIT
21239  Roo.data.Record.REJECT
21240  Roo.data.Record.COMMIT
21241          * </code></pre>
21242          */
21243         update : true,
21244         /**
21245          * @event clear
21246          * Fires when the data cache has been cleared.
21247          * @param {Store} this
21248          */
21249         clear : true,
21250         /**
21251          * @event beforeload
21252          * Fires before a request is made for a new data object.  If the beforeload handler returns false
21253          * the load action will be canceled.
21254          * @param {Store} this
21255          * @param {Object} options The loading options that were specified (see {@link #load} for details)
21256          */
21257         beforeload : true,
21258         /**
21259          * @event beforeloadadd
21260          * Fires after a new set of Records has been loaded.
21261          * @param {Store} this
21262          * @param {Roo.data.Record[]} records The Records that were loaded
21263          * @param {Object} options The loading options that were specified (see {@link #load} for details)
21264          */
21265         beforeloadadd : true,
21266         /**
21267          * @event load
21268          * Fires after a new set of Records has been loaded, before they are added to the store.
21269          * @param {Store} this
21270          * @param {Roo.data.Record[]} records The Records that were loaded
21271          * @param {Object} options The loading options that were specified (see {@link #load} for details)
21272          * @params {Object} return from reader
21273          */
21274         load : true,
21275         /**
21276          * @event loadexception
21277          * Fires if an exception occurs in the Proxy during loading.
21278          * Called with the signature of the Proxy's "loadexception" event.
21279          * If you return Json { data: [] , success: false, .... } then this will be thrown with the following args
21280          * 
21281          * @param {Proxy} 
21282          * @param {Object} return from JsonData.reader() - success, totalRecords, records
21283          * @param {Object} load options 
21284          * @param {Object} jsonData from your request (normally this contains the Exception)
21285          */
21286         loadexception : true
21287     });
21288     
21289     if(this.proxy){
21290         this.proxy = Roo.factory(this.proxy, Roo.data);
21291         this.proxy.xmodule = this.xmodule || false;
21292         this.relayEvents(this.proxy,  ["loadexception"]);
21293     }
21294     this.sortToggle = {};
21295     this.sortOrder = []; // array of order of sorting - updated by grid if multisort is enabled.
21296
21297     Roo.data.Store.superclass.constructor.call(this);
21298
21299     if(this.inlineData){
21300         this.loadData(this.inlineData);
21301         delete this.inlineData;
21302     }
21303 };
21304
21305 Roo.extend(Roo.data.Store, Roo.util.Observable, {
21306      /**
21307     * @cfg {boolean} isLocal   flag if data is locally available (and can be always looked up
21308     * without a remote query - used by combo/forms at present.
21309     */
21310     
21311     /**
21312     * @cfg {Roo.data.DataProxy} proxy The Proxy object which provides access to a data object.
21313     */
21314     /**
21315     * @cfg {Array} data Inline data to be loaded when the store is initialized.
21316     */
21317     /**
21318     * @cfg {Roo.data.Reader} reader The Reader object which processes the data object and returns
21319     * an Array of Roo.data.record objects which are cached keyed by their <em>id</em> property.
21320     */
21321     /**
21322     * @cfg {Object} baseParams An object containing properties which are to be sent as parameters
21323     * on any HTTP request
21324     */
21325     /**
21326     * @cfg {Object} sortInfo A config object in the format: {field: "fieldName", direction: "ASC|DESC"}
21327     */
21328     /**
21329     * @cfg {Boolean} multiSort enable multi column sorting (sort is based on the order of columns, remote only at present)
21330     */
21331     multiSort: false,
21332     /**
21333     * @cfg {boolean} remoteSort True if sorting is to be handled by requesting the Proxy to provide a refreshed
21334     * version of the data object in sorted order, as opposed to sorting the Record cache in place (defaults to false).
21335     */
21336     remoteSort : false,
21337
21338     /**
21339     * @cfg {boolean} pruneModifiedRecords True to clear all modified record information each time the store is
21340      * loaded or when a record is removed. (defaults to false).
21341     */
21342     pruneModifiedRecords : false,
21343
21344     // private
21345     lastOptions : null,
21346
21347     /**
21348      * Add Records to the Store and fires the add event.
21349      * @param {Roo.data.Record[]} records An Array of Roo.data.Record objects to add to the cache.
21350      */
21351     add : function(records){
21352         records = [].concat(records);
21353         for(var i = 0, len = records.length; i < len; i++){
21354             records[i].join(this);
21355         }
21356         var index = this.data.length;
21357         this.data.addAll(records);
21358         this.fireEvent("add", this, records, index);
21359     },
21360
21361     /**
21362      * Remove a Record from the Store and fires the remove event.
21363      * @param {Ext.data.Record} record The Roo.data.Record object to remove from the cache.
21364      */
21365     remove : function(record){
21366         var index = this.data.indexOf(record);
21367         this.data.removeAt(index);
21368         if(this.pruneModifiedRecords){
21369             this.modified.remove(record);
21370         }
21371         this.fireEvent("remove", this, record, index);
21372     },
21373
21374     /**
21375      * Remove all Records from the Store and fires the clear event.
21376      */
21377     removeAll : function(){
21378         this.data.clear();
21379         if(this.pruneModifiedRecords){
21380             this.modified = [];
21381         }
21382         this.fireEvent("clear", this);
21383     },
21384
21385     /**
21386      * Inserts Records to the Store at the given index and fires the add event.
21387      * @param {Number} index The start index at which to insert the passed Records.
21388      * @param {Roo.data.Record[]} records An Array of Roo.data.Record objects to add to the cache.
21389      */
21390     insert : function(index, records){
21391         records = [].concat(records);
21392         for(var i = 0, len = records.length; i < len; i++){
21393             this.data.insert(index, records[i]);
21394             records[i].join(this);
21395         }
21396         this.fireEvent("add", this, records, index);
21397     },
21398
21399     /**
21400      * Get the index within the cache of the passed Record.
21401      * @param {Roo.data.Record} record The Roo.data.Record object to to find.
21402      * @return {Number} The index of the passed Record. Returns -1 if not found.
21403      */
21404     indexOf : function(record){
21405         return this.data.indexOf(record);
21406     },
21407
21408     /**
21409      * Get the index within the cache of the Record with the passed id.
21410      * @param {String} id The id of the Record to find.
21411      * @return {Number} The index of the Record. Returns -1 if not found.
21412      */
21413     indexOfId : function(id){
21414         return this.data.indexOfKey(id);
21415     },
21416
21417     /**
21418      * Get the Record with the specified id.
21419      * @param {String} id The id of the Record to find.
21420      * @return {Roo.data.Record} The Record with the passed id. Returns undefined if not found.
21421      */
21422     getById : function(id){
21423         return this.data.key(id);
21424     },
21425
21426     /**
21427      * Get the Record at the specified index.
21428      * @param {Number} index The index of the Record to find.
21429      * @return {Roo.data.Record} The Record at the passed index. Returns undefined if not found.
21430      */
21431     getAt : function(index){
21432         return this.data.itemAt(index);
21433     },
21434
21435     /**
21436      * Returns a range of Records between specified indices.
21437      * @param {Number} startIndex (optional) The starting index (defaults to 0)
21438      * @param {Number} endIndex (optional) The ending index (defaults to the last Record in the Store)
21439      * @return {Roo.data.Record[]} An array of Records
21440      */
21441     getRange : function(start, end){
21442         return this.data.getRange(start, end);
21443     },
21444
21445     // private
21446     storeOptions : function(o){
21447         o = Roo.apply({}, o);
21448         delete o.callback;
21449         delete o.scope;
21450         this.lastOptions = o;
21451     },
21452
21453     /**
21454      * Loads the Record cache from the configured Proxy using the configured Reader.
21455      * <p>
21456      * If using remote paging, then the first load call must specify the <em>start</em>
21457      * and <em>limit</em> properties in the options.params property to establish the initial
21458      * position within the dataset, and the number of Records to cache on each read from the Proxy.
21459      * <p>
21460      * <strong>It is important to note that for remote data sources, loading is asynchronous,
21461      * and this call will return before the new data has been loaded. Perform any post-processing
21462      * in a callback function, or in a "load" event handler.</strong>
21463      * <p>
21464      * @param {Object} options An object containing properties which control loading options:<ul>
21465      * <li>params {Object} An object containing properties to pass as HTTP parameters to a remote data source.</li>
21466      * <li>callback {Function} A function to be called after the Records have been loaded. The callback is
21467      * passed the following arguments:<ul>
21468      * <li>r : Roo.data.Record[]</li>
21469      * <li>options: Options object from the load call</li>
21470      * <li>success: Boolean success indicator</li></ul></li>
21471      * <li>scope {Object} Scope with which to call the callback (defaults to the Store object)</li>
21472      * <li>add {Boolean} indicator to append loaded records rather than replace the current cache.</li>
21473      * </ul>
21474      */
21475     load : function(options){
21476         options = options || {};
21477         if(this.fireEvent("beforeload", this, options) !== false){
21478             this.storeOptions(options);
21479             var p = Roo.apply(options.params || {}, this.baseParams);
21480             // if meta was not loaded from remote source.. try requesting it.
21481             if (!this.reader.metaFromRemote) {
21482                 p._requestMeta = 1;
21483             }
21484             if(this.sortInfo && this.remoteSort){
21485                 var pn = this.paramNames;
21486                 p[pn["sort"]] = this.sortInfo.field;
21487                 p[pn["dir"]] = this.sortInfo.direction;
21488             }
21489             if (this.multiSort) {
21490                 var pn = this.paramNames;
21491                 p[pn["multisort"]] = Roo.encode( { sort : this.sortToggle, order: this.sortOrder });
21492             }
21493             
21494             this.proxy.load(p, this.reader, this.loadRecords, this, options);
21495         }
21496     },
21497
21498     /**
21499      * Reloads the Record cache from the configured Proxy using the configured Reader and
21500      * the options from the last load operation performed.
21501      * @param {Object} options (optional) An object containing properties which may override the options
21502      * used in the last load operation. See {@link #load} for details (defaults to null, in which case
21503      * the most recently used options are reused).
21504      */
21505     reload : function(options){
21506         this.load(Roo.applyIf(options||{}, this.lastOptions));
21507     },
21508
21509     // private
21510     // Called as a callback by the Reader during a load operation.
21511     loadRecords : function(o, options, success){
21512         if(!o || success === false){
21513             if(success !== false){
21514                 this.fireEvent("load", this, [], options, o);
21515             }
21516             if(options.callback){
21517                 options.callback.call(options.scope || this, [], options, false);
21518             }
21519             return;
21520         }
21521         // if data returned failure - throw an exception.
21522         if (o.success === false) {
21523             // show a message if no listener is registered.
21524             if (!this.hasListener('loadexception') && typeof(o.raw.errorMsg) != 'undefined') {
21525                     Roo.MessageBox.alert("Error loading",o.raw.errorMsg);
21526             }
21527             // loadmask wil be hooked into this..
21528             this.fireEvent("loadexception", this, o, options, o.raw.errorMsg);
21529             return;
21530         }
21531         var r = o.records, t = o.totalRecords || r.length;
21532         
21533         this.fireEvent("beforeloadadd", this, r, options, o);
21534         
21535         if(!options || options.add !== true){
21536             if(this.pruneModifiedRecords){
21537                 this.modified = [];
21538             }
21539             for(var i = 0, len = r.length; i < len; i++){
21540                 r[i].join(this);
21541             }
21542             if(this.snapshot){
21543                 this.data = this.snapshot;
21544                 delete this.snapshot;
21545             }
21546             this.data.clear();
21547             this.data.addAll(r);
21548             this.totalLength = t;
21549             this.applySort();
21550             this.fireEvent("datachanged", this);
21551         }else{
21552             this.totalLength = Math.max(t, this.data.length+r.length);
21553             this.add(r);
21554         }
21555         this.fireEvent("load", this, r, options, o);
21556         if(options.callback){
21557             options.callback.call(options.scope || this, r, options, true);
21558         }
21559     },
21560
21561
21562     /**
21563      * Loads data from a passed data block. A Reader which understands the format of the data
21564      * must have been configured in the constructor.
21565      * @param {Object} data The data block from which to read the Records.  The format of the data expected
21566      * is dependent on the type of Reader that is configured and should correspond to that Reader's readRecords parameter.
21567      * @param {Boolean} append (Optional) True to append the new Records rather than replace the existing cache.
21568      */
21569     loadData : function(o, append){
21570         var r = this.reader.readRecords(o);
21571         this.loadRecords(r, {add: append}, true);
21572     },
21573
21574     /**
21575      * Gets the number of cached records.
21576      * <p>
21577      * <em>If using paging, this may not be the total size of the dataset. If the data object
21578      * used by the Reader contains the dataset size, then the getTotalCount() function returns
21579      * the data set size</em>
21580      */
21581     getCount : function(){
21582         return this.data.length || 0;
21583     },
21584
21585     /**
21586      * Gets the total number of records in the dataset as returned by the server.
21587      * <p>
21588      * <em>If using paging, for this to be accurate, the data object used by the Reader must contain
21589      * the dataset size</em>
21590      */
21591     getTotalCount : function(){
21592         return this.totalLength || 0;
21593     },
21594
21595     /**
21596      * Returns the sort state of the Store as an object with two properties:
21597      * <pre><code>
21598  field {String} The name of the field by which the Records are sorted
21599  direction {String} The sort order, "ASC" or "DESC"
21600      * </code></pre>
21601      */
21602     getSortState : function(){
21603         return this.sortInfo;
21604     },
21605
21606     // private
21607     applySort : function(){
21608         if(this.sortInfo && !this.remoteSort){
21609             var s = this.sortInfo, f = s.field;
21610             var st = this.fields.get(f).sortType;
21611             var fn = function(r1, r2){
21612                 var v1 = st(r1.data[f]), v2 = st(r2.data[f]);
21613                 return v1 > v2 ? 1 : (v1 < v2 ? -1 : 0);
21614             };
21615             this.data.sort(s.direction, fn);
21616             if(this.snapshot && this.snapshot != this.data){
21617                 this.snapshot.sort(s.direction, fn);
21618             }
21619         }
21620     },
21621
21622     /**
21623      * Sets the default sort column and order to be used by the next load operation.
21624      * @param {String} fieldName The name of the field to sort by.
21625      * @param {String} dir (optional) The sort order, "ASC" or "DESC" (defaults to "ASC")
21626      */
21627     setDefaultSort : function(field, dir){
21628         this.sortInfo = {field: field, direction: dir ? dir.toUpperCase() : "ASC"};
21629     },
21630
21631     /**
21632      * Sort the Records.
21633      * If remote sorting is used, the sort is performed on the server, and the cache is
21634      * reloaded. If local sorting is used, the cache is sorted internally.
21635      * @param {String} fieldName The name of the field to sort by.
21636      * @param {String} dir (optional) The sort order, "ASC" or "DESC" (defaults to "ASC")
21637      */
21638     sort : function(fieldName, dir){
21639         var f = this.fields.get(fieldName);
21640         if(!dir){
21641             this.sortToggle[f.name] = this.sortToggle[f.name] || f.sortDir;
21642             
21643             if(this.multiSort || (this.sortInfo && this.sortInfo.field == f.name) ){ // toggle sort dir
21644                 dir = (this.sortToggle[f.name] || "ASC").toggle("ASC", "DESC");
21645             }else{
21646                 dir = f.sortDir;
21647             }
21648         }
21649         this.sortToggle[f.name] = dir;
21650         this.sortInfo = {field: f.name, direction: dir};
21651         if(!this.remoteSort){
21652             this.applySort();
21653             this.fireEvent("datachanged", this);
21654         }else{
21655             this.load(this.lastOptions);
21656         }
21657     },
21658
21659     /**
21660      * Calls the specified function for each of the Records in the cache.
21661      * @param {Function} fn The function to call. The Record is passed as the first parameter.
21662      * Returning <em>false</em> aborts and exits the iteration.
21663      * @param {Object} scope (optional) The scope in which to call the function (defaults to the Record).
21664      */
21665     each : function(fn, scope){
21666         this.data.each(fn, scope);
21667     },
21668
21669     /**
21670      * Gets all records modified since the last commit.  Modified records are persisted across load operations
21671      * (e.g., during paging).
21672      * @return {Roo.data.Record[]} An array of Records containing outstanding modifications.
21673      */
21674     getModifiedRecords : function(){
21675         return this.modified;
21676     },
21677
21678     // private
21679     createFilterFn : function(property, value, anyMatch){
21680         if(!value.exec){ // not a regex
21681             value = String(value);
21682             if(value.length == 0){
21683                 return false;
21684             }
21685             value = new RegExp((anyMatch === true ? '' : '^') + Roo.escapeRe(value), "i");
21686         }
21687         return function(r){
21688             return value.test(r.data[property]);
21689         };
21690     },
21691
21692     /**
21693      * Sums the value of <i>property</i> for each record between start and end and returns the result.
21694      * @param {String} property A field on your records
21695      * @param {Number} start The record index to start at (defaults to 0)
21696      * @param {Number} end The last record index to include (defaults to length - 1)
21697      * @return {Number} The sum
21698      */
21699     sum : function(property, start, end){
21700         var rs = this.data.items, v = 0;
21701         start = start || 0;
21702         end = (end || end === 0) ? end : rs.length-1;
21703
21704         for(var i = start; i <= end; i++){
21705             v += (rs[i].data[property] || 0);
21706         }
21707         return v;
21708     },
21709
21710     /**
21711      * Filter the records by a specified property.
21712      * @param {String} field A field on your records
21713      * @param {String/RegExp} value Either a string that the field
21714      * should start with or a RegExp to test against the field
21715      * @param {Boolean} anyMatch True to match any part not just the beginning
21716      */
21717     filter : function(property, value, anyMatch){
21718         var fn = this.createFilterFn(property, value, anyMatch);
21719         return fn ? this.filterBy(fn) : this.clearFilter();
21720     },
21721
21722     /**
21723      * Filter by a function. The specified function will be called with each
21724      * record in this data source. If the function returns true the record is included,
21725      * otherwise it is filtered.
21726      * @param {Function} fn The function to be called, it will receive 2 args (record, id)
21727      * @param {Object} scope (optional) The scope of the function (defaults to this)
21728      */
21729     filterBy : function(fn, scope){
21730         this.snapshot = this.snapshot || this.data;
21731         this.data = this.queryBy(fn, scope||this);
21732         this.fireEvent("datachanged", this);
21733     },
21734
21735     /**
21736      * Query the records by a specified property.
21737      * @param {String} field A field on your records
21738      * @param {String/RegExp} value Either a string that the field
21739      * should start with or a RegExp to test against the field
21740      * @param {Boolean} anyMatch True to match any part not just the beginning
21741      * @return {MixedCollection} Returns an Roo.util.MixedCollection of the matched records
21742      */
21743     query : function(property, value, anyMatch){
21744         var fn = this.createFilterFn(property, value, anyMatch);
21745         return fn ? this.queryBy(fn) : this.data.clone();
21746     },
21747
21748     /**
21749      * Query by a function. The specified function will be called with each
21750      * record in this data source. If the function returns true the record is included
21751      * in the results.
21752      * @param {Function} fn The function to be called, it will receive 2 args (record, id)
21753      * @param {Object} scope (optional) The scope of the function (defaults to this)
21754       @return {MixedCollection} Returns an Roo.util.MixedCollection of the matched records
21755      **/
21756     queryBy : function(fn, scope){
21757         var data = this.snapshot || this.data;
21758         return data.filterBy(fn, scope||this);
21759     },
21760
21761     /**
21762      * Collects unique values for a particular dataIndex from this store.
21763      * @param {String} dataIndex The property to collect
21764      * @param {Boolean} allowNull (optional) Pass true to allow null, undefined or empty string values
21765      * @param {Boolean} bypassFilter (optional) Pass true to collect from all records, even ones which are filtered
21766      * @return {Array} An array of the unique values
21767      **/
21768     collect : function(dataIndex, allowNull, bypassFilter){
21769         var d = (bypassFilter === true && this.snapshot) ?
21770                 this.snapshot.items : this.data.items;
21771         var v, sv, r = [], l = {};
21772         for(var i = 0, len = d.length; i < len; i++){
21773             v = d[i].data[dataIndex];
21774             sv = String(v);
21775             if((allowNull || !Roo.isEmpty(v)) && !l[sv]){
21776                 l[sv] = true;
21777                 r[r.length] = v;
21778             }
21779         }
21780         return r;
21781     },
21782
21783     /**
21784      * Revert to a view of the Record cache with no filtering applied.
21785      * @param {Boolean} suppressEvent If true the filter is cleared silently without notifying listeners
21786      */
21787     clearFilter : function(suppressEvent){
21788         if(this.snapshot && this.snapshot != this.data){
21789             this.data = this.snapshot;
21790             delete this.snapshot;
21791             if(suppressEvent !== true){
21792                 this.fireEvent("datachanged", this);
21793             }
21794         }
21795     },
21796
21797     // private
21798     afterEdit : function(record){
21799         if(this.modified.indexOf(record) == -1){
21800             this.modified.push(record);
21801         }
21802         this.fireEvent("update", this, record, Roo.data.Record.EDIT);
21803     },
21804     
21805     // private
21806     afterReject : function(record){
21807         this.modified.remove(record);
21808         this.fireEvent("update", this, record, Roo.data.Record.REJECT);
21809     },
21810
21811     // private
21812     afterCommit : function(record){
21813         this.modified.remove(record);
21814         this.fireEvent("update", this, record, Roo.data.Record.COMMIT);
21815     },
21816
21817     /**
21818      * Commit all Records with outstanding changes. To handle updates for changes, subscribe to the
21819      * Store's "update" event, and perform updating when the third parameter is Roo.data.Record.COMMIT.
21820      */
21821     commitChanges : function(){
21822         var m = this.modified.slice(0);
21823         this.modified = [];
21824         for(var i = 0, len = m.length; i < len; i++){
21825             m[i].commit();
21826         }
21827     },
21828
21829     /**
21830      * Cancel outstanding changes on all changed records.
21831      */
21832     rejectChanges : function(){
21833         var m = this.modified.slice(0);
21834         this.modified = [];
21835         for(var i = 0, len = m.length; i < len; i++){
21836             m[i].reject();
21837         }
21838     },
21839
21840     onMetaChange : function(meta, rtype, o){
21841         this.recordType = rtype;
21842         this.fields = rtype.prototype.fields;
21843         delete this.snapshot;
21844         this.sortInfo = meta.sortInfo || this.sortInfo;
21845         this.modified = [];
21846         this.fireEvent('metachange', this, this.reader.meta);
21847     },
21848     
21849     moveIndex : function(data, type)
21850     {
21851         var index = this.indexOf(data);
21852         
21853         var newIndex = index + type;
21854         
21855         this.remove(data);
21856         
21857         this.insert(newIndex, data);
21858         
21859     }
21860 });/*
21861  * Based on:
21862  * Ext JS Library 1.1.1
21863  * Copyright(c) 2006-2007, Ext JS, LLC.
21864  *
21865  * Originally Released Under LGPL - original licence link has changed is not relivant.
21866  *
21867  * Fork - LGPL
21868  * <script type="text/javascript">
21869  */
21870
21871 /**
21872  * @class Roo.data.SimpleStore
21873  * @extends Roo.data.Store
21874  * Small helper class to make creating Stores from Array data easier.
21875  * @cfg {Number} id The array index of the record id. Leave blank to auto generate ids.
21876  * @cfg {Array} fields An array of field definition objects, or field name strings.
21877  * @cfg {Array} data The multi-dimensional array of data
21878  * @constructor
21879  * @param {Object} config
21880  */
21881 Roo.data.SimpleStore = function(config){
21882     Roo.data.SimpleStore.superclass.constructor.call(this, {
21883         isLocal : true,
21884         reader: new Roo.data.ArrayReader({
21885                 id: config.id
21886             },
21887             Roo.data.Record.create(config.fields)
21888         ),
21889         proxy : new Roo.data.MemoryProxy(config.data)
21890     });
21891     this.load();
21892 };
21893 Roo.extend(Roo.data.SimpleStore, Roo.data.Store);/*
21894  * Based on:
21895  * Ext JS Library 1.1.1
21896  * Copyright(c) 2006-2007, Ext JS, LLC.
21897  *
21898  * Originally Released Under LGPL - original licence link has changed is not relivant.
21899  *
21900  * Fork - LGPL
21901  * <script type="text/javascript">
21902  */
21903
21904 /**
21905 /**
21906  * @extends Roo.data.Store
21907  * @class Roo.data.JsonStore
21908  * Small helper class to make creating Stores for JSON data easier. <br/>
21909 <pre><code>
21910 var store = new Roo.data.JsonStore({
21911     url: 'get-images.php',
21912     root: 'images',
21913     fields: ['name', 'url', {name:'size', type: 'float'}, {name:'lastmod', type:'date'}]
21914 });
21915 </code></pre>
21916  * <b>Note: Although they are not listed, this class inherits all of the config options of Store,
21917  * JsonReader and HttpProxy (unless inline data is provided).</b>
21918  * @cfg {Array} fields An array of field definition objects, or field name strings.
21919  * @constructor
21920  * @param {Object} config
21921  */
21922 Roo.data.JsonStore = function(c){
21923     Roo.data.JsonStore.superclass.constructor.call(this, Roo.apply(c, {
21924         proxy: !c.data ? new Roo.data.HttpProxy({url: c.url}) : undefined,
21925         reader: new Roo.data.JsonReader(c, c.fields)
21926     }));
21927 };
21928 Roo.extend(Roo.data.JsonStore, Roo.data.Store);/*
21929  * Based on:
21930  * Ext JS Library 1.1.1
21931  * Copyright(c) 2006-2007, Ext JS, LLC.
21932  *
21933  * Originally Released Under LGPL - original licence link has changed is not relivant.
21934  *
21935  * Fork - LGPL
21936  * <script type="text/javascript">
21937  */
21938
21939  
21940 Roo.data.Field = function(config){
21941     if(typeof config == "string"){
21942         config = {name: config};
21943     }
21944     Roo.apply(this, config);
21945     
21946     if(!this.type){
21947         this.type = "auto";
21948     }
21949     
21950     var st = Roo.data.SortTypes;
21951     // named sortTypes are supported, here we look them up
21952     if(typeof this.sortType == "string"){
21953         this.sortType = st[this.sortType];
21954     }
21955     
21956     // set default sortType for strings and dates
21957     if(!this.sortType){
21958         switch(this.type){
21959             case "string":
21960                 this.sortType = st.asUCString;
21961                 break;
21962             case "date":
21963                 this.sortType = st.asDate;
21964                 break;
21965             default:
21966                 this.sortType = st.none;
21967         }
21968     }
21969
21970     // define once
21971     var stripRe = /[\$,%]/g;
21972
21973     // prebuilt conversion function for this field, instead of
21974     // switching every time we're reading a value
21975     if(!this.convert){
21976         var cv, dateFormat = this.dateFormat;
21977         switch(this.type){
21978             case "":
21979             case "auto":
21980             case undefined:
21981                 cv = function(v){ return v; };
21982                 break;
21983             case "string":
21984                 cv = function(v){ return (v === undefined || v === null) ? '' : String(v); };
21985                 break;
21986             case "int":
21987                 cv = function(v){
21988                     return v !== undefined && v !== null && v !== '' ?
21989                            parseInt(String(v).replace(stripRe, ""), 10) : '';
21990                     };
21991                 break;
21992             case "float":
21993                 cv = function(v){
21994                     return v !== undefined && v !== null && v !== '' ?
21995                            parseFloat(String(v).replace(stripRe, ""), 10) : ''; 
21996                     };
21997                 break;
21998             case "bool":
21999             case "boolean":
22000                 cv = function(v){ return v === true || v === "true" || v == 1; };
22001                 break;
22002             case "date":
22003                 cv = function(v){
22004                     if(!v){
22005                         return '';
22006                     }
22007                     if(v instanceof Date){
22008                         return v;
22009                     }
22010                     if(dateFormat){
22011                         if(dateFormat == "timestamp"){
22012                             return new Date(v*1000);
22013                         }
22014                         return Date.parseDate(v, dateFormat);
22015                     }
22016                     var parsed = Date.parse(v);
22017                     return parsed ? new Date(parsed) : null;
22018                 };
22019              break;
22020             
22021         }
22022         this.convert = cv;
22023     }
22024 };
22025
22026 Roo.data.Field.prototype = {
22027     dateFormat: null,
22028     defaultValue: "",
22029     mapping: null,
22030     sortType : null,
22031     sortDir : "ASC"
22032 };/*
22033  * Based on:
22034  * Ext JS Library 1.1.1
22035  * Copyright(c) 2006-2007, Ext JS, LLC.
22036  *
22037  * Originally Released Under LGPL - original licence link has changed is not relivant.
22038  *
22039  * Fork - LGPL
22040  * <script type="text/javascript">
22041  */
22042  
22043 // Base class for reading structured data from a data source.  This class is intended to be
22044 // extended (see ArrayReader, JsonReader and XmlReader) and should not be created directly.
22045
22046 /**
22047  * @class Roo.data.DataReader
22048  * Base class for reading structured data from a data source.  This class is intended to be
22049  * extended (see {Roo.data.ArrayReader}, {Roo.data.JsonReader} and {Roo.data.XmlReader}) and should not be created directly.
22050  */
22051
22052 Roo.data.DataReader = function(meta, recordType){
22053     
22054     this.meta = meta;
22055     
22056     this.recordType = recordType instanceof Array ? 
22057         Roo.data.Record.create(recordType) : recordType;
22058 };
22059
22060 Roo.data.DataReader.prototype = {
22061      /**
22062      * Create an empty record
22063      * @param {Object} data (optional) - overlay some values
22064      * @return {Roo.data.Record} record created.
22065      */
22066     newRow :  function(d) {
22067         var da =  {};
22068         this.recordType.prototype.fields.each(function(c) {
22069             switch( c.type) {
22070                 case 'int' : da[c.name] = 0; break;
22071                 case 'date' : da[c.name] = new Date(); break;
22072                 case 'float' : da[c.name] = 0.0; break;
22073                 case 'boolean' : da[c.name] = false; break;
22074                 default : da[c.name] = ""; break;
22075             }
22076             
22077         });
22078         return new this.recordType(Roo.apply(da, d));
22079     }
22080     
22081 };/*
22082  * Based on:
22083  * Ext JS Library 1.1.1
22084  * Copyright(c) 2006-2007, Ext JS, LLC.
22085  *
22086  * Originally Released Under LGPL - original licence link has changed is not relivant.
22087  *
22088  * Fork - LGPL
22089  * <script type="text/javascript">
22090  */
22091
22092 /**
22093  * @class Roo.data.DataProxy
22094  * @extends Roo.data.Observable
22095  * This class is an abstract base class for implementations which provide retrieval of
22096  * unformatted data objects.<br>
22097  * <p>
22098  * DataProxy implementations are usually used in conjunction with an implementation of Roo.data.DataReader
22099  * (of the appropriate type which knows how to parse the data object) to provide a block of
22100  * {@link Roo.data.Records} to an {@link Roo.data.Store}.<br>
22101  * <p>
22102  * Custom implementations must implement the load method as described in
22103  * {@link Roo.data.HttpProxy#load}.
22104  */
22105 Roo.data.DataProxy = function(){
22106     this.addEvents({
22107         /**
22108          * @event beforeload
22109          * Fires before a network request is made to retrieve a data object.
22110          * @param {Object} This DataProxy object.
22111          * @param {Object} params The params parameter to the load function.
22112          */
22113         beforeload : true,
22114         /**
22115          * @event load
22116          * Fires before the load method's callback is called.
22117          * @param {Object} This DataProxy object.
22118          * @param {Object} o The data object.
22119          * @param {Object} arg The callback argument object passed to the load function.
22120          */
22121         load : true,
22122         /**
22123          * @event loadexception
22124          * Fires if an Exception occurs during data retrieval.
22125          * @param {Object} This DataProxy object.
22126          * @param {Object} o The data object.
22127          * @param {Object} arg The callback argument object passed to the load function.
22128          * @param {Object} e The Exception.
22129          */
22130         loadexception : true
22131     });
22132     Roo.data.DataProxy.superclass.constructor.call(this);
22133 };
22134
22135 Roo.extend(Roo.data.DataProxy, Roo.util.Observable);
22136
22137     /**
22138      * @cfg {void} listeners (Not available) Constructor blocks listeners from being set
22139      */
22140 /*
22141  * Based on:
22142  * Ext JS Library 1.1.1
22143  * Copyright(c) 2006-2007, Ext JS, LLC.
22144  *
22145  * Originally Released Under LGPL - original licence link has changed is not relivant.
22146  *
22147  * Fork - LGPL
22148  * <script type="text/javascript">
22149  */
22150 /**
22151  * @class Roo.data.MemoryProxy
22152  * An implementation of Roo.data.DataProxy that simply passes the data specified in its constructor
22153  * to the Reader when its load method is called.
22154  * @constructor
22155  * @param {Object} data The data object which the Reader uses to construct a block of Roo.data.Records.
22156  */
22157 Roo.data.MemoryProxy = function(data){
22158     if (data.data) {
22159         data = data.data;
22160     }
22161     Roo.data.MemoryProxy.superclass.constructor.call(this);
22162     this.data = data;
22163 };
22164
22165 Roo.extend(Roo.data.MemoryProxy, Roo.data.DataProxy, {
22166     /**
22167      * Load data from the requested source (in this case an in-memory
22168      * data object passed to the constructor), read the data object into
22169      * a block of Roo.data.Records using the passed Roo.data.DataReader implementation, and
22170      * process that block using the passed callback.
22171      * @param {Object} params This parameter is not used by the MemoryProxy class.
22172      * @param {Roo.data.DataReader} reader The Reader object which converts the data
22173      * object into a block of Roo.data.Records.
22174      * @param {Function} callback The function into which to pass the block of Roo.data.records.
22175      * The function must be passed <ul>
22176      * <li>The Record block object</li>
22177      * <li>The "arg" argument from the load function</li>
22178      * <li>A boolean success indicator</li>
22179      * </ul>
22180      * @param {Object} scope The scope in which to call the callback
22181      * @param {Object} arg An optional argument which is passed to the callback as its second parameter.
22182      */
22183     load : function(params, reader, callback, scope, arg){
22184         params = params || {};
22185         var result;
22186         try {
22187             result = reader.readRecords(this.data);
22188         }catch(e){
22189             this.fireEvent("loadexception", this, arg, null, e);
22190             callback.call(scope, null, arg, false);
22191             return;
22192         }
22193         callback.call(scope, result, arg, true);
22194     },
22195     
22196     // private
22197     update : function(params, records){
22198         
22199     }
22200 });/*
22201  * Based on:
22202  * Ext JS Library 1.1.1
22203  * Copyright(c) 2006-2007, Ext JS, LLC.
22204  *
22205  * Originally Released Under LGPL - original licence link has changed is not relivant.
22206  *
22207  * Fork - LGPL
22208  * <script type="text/javascript">
22209  */
22210 /**
22211  * @class Roo.data.HttpProxy
22212  * @extends Roo.data.DataProxy
22213  * An implementation of {@link Roo.data.DataProxy} that reads a data object from an {@link Roo.data.Connection} object
22214  * configured to reference a certain URL.<br><br>
22215  * <p>
22216  * <em>Note that this class cannot be used to retrieve data from a domain other than the domain
22217  * from which the running page was served.<br><br>
22218  * <p>
22219  * For cross-domain access to remote data, use an {@link Roo.data.ScriptTagProxy}.</em><br><br>
22220  * <p>
22221  * Be aware that to enable the browser to parse an XML document, the server must set
22222  * the Content-Type header in the HTTP response to "text/xml".
22223  * @constructor
22224  * @param {Object} conn Connection config options to add to each request (e.g. {url: 'foo.php'} or
22225  * an {@link Roo.data.Connection} object.  If a Connection config is passed, the singleton {@link Roo.Ajax} object
22226  * will be used to make the request.
22227  */
22228 Roo.data.HttpProxy = function(conn){
22229     Roo.data.HttpProxy.superclass.constructor.call(this);
22230     // is conn a conn config or a real conn?
22231     this.conn = conn;
22232     this.useAjax = !conn || !conn.events;
22233   
22234 };
22235
22236 Roo.extend(Roo.data.HttpProxy, Roo.data.DataProxy, {
22237     // thse are take from connection...
22238     
22239     /**
22240      * @cfg {String} url (Optional) The default URL to be used for requests to the server. (defaults to undefined)
22241      */
22242     /**
22243      * @cfg {Object} extraParams (Optional) An object containing properties which are used as
22244      * extra parameters to each request made by this object. (defaults to undefined)
22245      */
22246     /**
22247      * @cfg {Object} defaultHeaders (Optional) An object containing request headers which are added
22248      *  to each request made by this object. (defaults to undefined)
22249      */
22250     /**
22251      * @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)
22252      */
22253     /**
22254      * @cfg {Number} timeout (Optional) The timeout in milliseconds to be used for requests. (defaults to 30000)
22255      */
22256      /**
22257      * @cfg {Boolean} autoAbort (Optional) Whether this request should abort any pending requests. (defaults to false)
22258      * @type Boolean
22259      */
22260   
22261
22262     /**
22263      * @cfg {Boolean} disableCaching (Optional) True to add a unique cache-buster param to GET requests. (defaults to true)
22264      * @type Boolean
22265      */
22266     /**
22267      * Return the {@link Roo.data.Connection} object being used by this Proxy.
22268      * @return {Connection} The Connection object. This object may be used to subscribe to events on
22269      * a finer-grained basis than the DataProxy events.
22270      */
22271     getConnection : function(){
22272         return this.useAjax ? Roo.Ajax : this.conn;
22273     },
22274
22275     /**
22276      * Load data from the configured {@link Roo.data.Connection}, read the data object into
22277      * a block of Roo.data.Records using the passed {@link Roo.data.DataReader} implementation, and
22278      * process that block using the passed callback.
22279      * @param {Object} params An object containing properties which are to be used as HTTP parameters
22280      * for the request to the remote server.
22281      * @param {Roo.data.DataReader} reader The Reader object which converts the data
22282      * object into a block of Roo.data.Records.
22283      * @param {Function} callback The function into which to pass the block of Roo.data.Records.
22284      * The function must be passed <ul>
22285      * <li>The Record block object</li>
22286      * <li>The "arg" argument from the load function</li>
22287      * <li>A boolean success indicator</li>
22288      * </ul>
22289      * @param {Object} scope The scope in which to call the callback
22290      * @param {Object} arg An optional argument which is passed to the callback as its second parameter.
22291      */
22292     load : function(params, reader, callback, scope, arg){
22293         if(this.fireEvent("beforeload", this, params) !== false){
22294             var  o = {
22295                 params : params || {},
22296                 request: {
22297                     callback : callback,
22298                     scope : scope,
22299                     arg : arg
22300                 },
22301                 reader: reader,
22302                 callback : this.loadResponse,
22303                 scope: this
22304             };
22305             if(this.useAjax){
22306                 Roo.applyIf(o, this.conn);
22307                 if(this.activeRequest){
22308                     Roo.Ajax.abort(this.activeRequest);
22309                 }
22310                 this.activeRequest = Roo.Ajax.request(o);
22311             }else{
22312                 this.conn.request(o);
22313             }
22314         }else{
22315             callback.call(scope||this, null, arg, false);
22316         }
22317     },
22318
22319     // private
22320     loadResponse : function(o, success, response){
22321         delete this.activeRequest;
22322         if(!success){
22323             this.fireEvent("loadexception", this, o, response);
22324             o.request.callback.call(o.request.scope, null, o.request.arg, false);
22325             return;
22326         }
22327         var result;
22328         try {
22329             result = o.reader.read(response);
22330         }catch(e){
22331             this.fireEvent("loadexception", this, o, response, e);
22332             o.request.callback.call(o.request.scope, null, o.request.arg, false);
22333             return;
22334         }
22335         
22336         this.fireEvent("load", this, o, o.request.arg);
22337         o.request.callback.call(o.request.scope, result, o.request.arg, true);
22338     },
22339
22340     // private
22341     update : function(dataSet){
22342
22343     },
22344
22345     // private
22346     updateResponse : function(dataSet){
22347
22348     }
22349 });/*
22350  * Based on:
22351  * Ext JS Library 1.1.1
22352  * Copyright(c) 2006-2007, Ext JS, LLC.
22353  *
22354  * Originally Released Under LGPL - original licence link has changed is not relivant.
22355  *
22356  * Fork - LGPL
22357  * <script type="text/javascript">
22358  */
22359
22360 /**
22361  * @class Roo.data.ScriptTagProxy
22362  * An implementation of Roo.data.DataProxy that reads a data object from a URL which may be in a domain
22363  * other than the originating domain of the running page.<br><br>
22364  * <p>
22365  * <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
22366  * of the running page, you must use this class, rather than DataProxy.</em><br><br>
22367  * <p>
22368  * The content passed back from a server resource requested by a ScriptTagProxy is executable JavaScript
22369  * source code that is used as the source inside a &lt;script> tag.<br><br>
22370  * <p>
22371  * In order for the browser to process the returned data, the server must wrap the data object
22372  * with a call to a callback function, the name of which is passed as a parameter by the ScriptTagProxy.
22373  * Below is a Java example for a servlet which returns data for either a ScriptTagProxy, or an HttpProxy
22374  * depending on whether the callback name was passed:
22375  * <p>
22376  * <pre><code>
22377 boolean scriptTag = false;
22378 String cb = request.getParameter("callback");
22379 if (cb != null) {
22380     scriptTag = true;
22381     response.setContentType("text/javascript");
22382 } else {
22383     response.setContentType("application/x-json");
22384 }
22385 Writer out = response.getWriter();
22386 if (scriptTag) {
22387     out.write(cb + "(");
22388 }
22389 out.print(dataBlock.toJsonString());
22390 if (scriptTag) {
22391     out.write(");");
22392 }
22393 </pre></code>
22394  *
22395  * @constructor
22396  * @param {Object} config A configuration object.
22397  */
22398 Roo.data.ScriptTagProxy = function(config){
22399     Roo.data.ScriptTagProxy.superclass.constructor.call(this);
22400     Roo.apply(this, config);
22401     this.head = document.getElementsByTagName("head")[0];
22402 };
22403
22404 Roo.data.ScriptTagProxy.TRANS_ID = 1000;
22405
22406 Roo.extend(Roo.data.ScriptTagProxy, Roo.data.DataProxy, {
22407     /**
22408      * @cfg {String} url The URL from which to request the data object.
22409      */
22410     /**
22411      * @cfg {Number} timeout (Optional) The number of milliseconds to wait for a response. Defaults to 30 seconds.
22412      */
22413     timeout : 30000,
22414     /**
22415      * @cfg {String} callbackParam (Optional) The name of the parameter to pass to the server which tells
22416      * the server the name of the callback function set up by the load call to process the returned data object.
22417      * Defaults to "callback".<p>The server-side processing must read this parameter value, and generate
22418      * javascript output which calls this named function passing the data object as its only parameter.
22419      */
22420     callbackParam : "callback",
22421     /**
22422      *  @cfg {Boolean} nocache (Optional) Defaults to true. Disable cacheing by adding a unique parameter
22423      * name to the request.
22424      */
22425     nocache : true,
22426
22427     /**
22428      * Load data from the configured URL, read the data object into
22429      * a block of Roo.data.Records using the passed Roo.data.DataReader implementation, and
22430      * process that block using the passed callback.
22431      * @param {Object} params An object containing properties which are to be used as HTTP parameters
22432      * for the request to the remote server.
22433      * @param {Roo.data.DataReader} reader The Reader object which converts the data
22434      * object into a block of Roo.data.Records.
22435      * @param {Function} callback The function into which to pass the block of Roo.data.Records.
22436      * The function must be passed <ul>
22437      * <li>The Record block object</li>
22438      * <li>The "arg" argument from the load function</li>
22439      * <li>A boolean success indicator</li>
22440      * </ul>
22441      * @param {Object} scope The scope in which to call the callback
22442      * @param {Object} arg An optional argument which is passed to the callback as its second parameter.
22443      */
22444     load : function(params, reader, callback, scope, arg){
22445         if(this.fireEvent("beforeload", this, params) !== false){
22446
22447             var p = Roo.urlEncode(Roo.apply(params, this.extraParams));
22448
22449             var url = this.url;
22450             url += (url.indexOf("?") != -1 ? "&" : "?") + p;
22451             if(this.nocache){
22452                 url += "&_dc=" + (new Date().getTime());
22453             }
22454             var transId = ++Roo.data.ScriptTagProxy.TRANS_ID;
22455             var trans = {
22456                 id : transId,
22457                 cb : "stcCallback"+transId,
22458                 scriptId : "stcScript"+transId,
22459                 params : params,
22460                 arg : arg,
22461                 url : url,
22462                 callback : callback,
22463                 scope : scope,
22464                 reader : reader
22465             };
22466             var conn = this;
22467
22468             window[trans.cb] = function(o){
22469                 conn.handleResponse(o, trans);
22470             };
22471
22472             url += String.format("&{0}={1}", this.callbackParam, trans.cb);
22473
22474             if(this.autoAbort !== false){
22475                 this.abort();
22476             }
22477
22478             trans.timeoutId = this.handleFailure.defer(this.timeout, this, [trans]);
22479
22480             var script = document.createElement("script");
22481             script.setAttribute("src", url);
22482             script.setAttribute("type", "text/javascript");
22483             script.setAttribute("id", trans.scriptId);
22484             this.head.appendChild(script);
22485
22486             this.trans = trans;
22487         }else{
22488             callback.call(scope||this, null, arg, false);
22489         }
22490     },
22491
22492     // private
22493     isLoading : function(){
22494         return this.trans ? true : false;
22495     },
22496
22497     /**
22498      * Abort the current server request.
22499      */
22500     abort : function(){
22501         if(this.isLoading()){
22502             this.destroyTrans(this.trans);
22503         }
22504     },
22505
22506     // private
22507     destroyTrans : function(trans, isLoaded){
22508         this.head.removeChild(document.getElementById(trans.scriptId));
22509         clearTimeout(trans.timeoutId);
22510         if(isLoaded){
22511             window[trans.cb] = undefined;
22512             try{
22513                 delete window[trans.cb];
22514             }catch(e){}
22515         }else{
22516             // if hasn't been loaded, wait for load to remove it to prevent script error
22517             window[trans.cb] = function(){
22518                 window[trans.cb] = undefined;
22519                 try{
22520                     delete window[trans.cb];
22521                 }catch(e){}
22522             };
22523         }
22524     },
22525
22526     // private
22527     handleResponse : function(o, trans){
22528         this.trans = false;
22529         this.destroyTrans(trans, true);
22530         var result;
22531         try {
22532             result = trans.reader.readRecords(o);
22533         }catch(e){
22534             this.fireEvent("loadexception", this, o, trans.arg, e);
22535             trans.callback.call(trans.scope||window, null, trans.arg, false);
22536             return;
22537         }
22538         this.fireEvent("load", this, o, trans.arg);
22539         trans.callback.call(trans.scope||window, result, trans.arg, true);
22540     },
22541
22542     // private
22543     handleFailure : function(trans){
22544         this.trans = false;
22545         this.destroyTrans(trans, false);
22546         this.fireEvent("loadexception", this, null, trans.arg);
22547         trans.callback.call(trans.scope||window, null, trans.arg, false);
22548     }
22549 });/*
22550  * Based on:
22551  * Ext JS Library 1.1.1
22552  * Copyright(c) 2006-2007, Ext JS, LLC.
22553  *
22554  * Originally Released Under LGPL - original licence link has changed is not relivant.
22555  *
22556  * Fork - LGPL
22557  * <script type="text/javascript">
22558  */
22559
22560 /**
22561  * @class Roo.data.JsonReader
22562  * @extends Roo.data.DataReader
22563  * Data reader class to create an Array of Roo.data.Record objects from a JSON response
22564  * based on mappings in a provided Roo.data.Record constructor.
22565  * 
22566  * The default behaviour of a store is to send ?_requestMeta=1, unless the class has recieved 'metaData' property
22567  * in the reply previously. 
22568  * 
22569  * <p>
22570  * Example code:
22571  * <pre><code>
22572 var RecordDef = Roo.data.Record.create([
22573     {name: 'name', mapping: 'name'},     // "mapping" property not needed if it's the same as "name"
22574     {name: 'occupation'}                 // This field will use "occupation" as the mapping.
22575 ]);
22576 var myReader = new Roo.data.JsonReader({
22577     totalProperty: "results",    // The property which contains the total dataset size (optional)
22578     root: "rows",                // The property which contains an Array of row objects
22579     id: "id"                     // The property within each row object that provides an ID for the record (optional)
22580 }, RecordDef);
22581 </code></pre>
22582  * <p>
22583  * This would consume a JSON file like this:
22584  * <pre><code>
22585 { 'results': 2, 'rows': [
22586     { 'id': 1, 'name': 'Bill', occupation: 'Gardener' },
22587     { 'id': 2, 'name': 'Ben', occupation: 'Horticulturalist' } ]
22588 }
22589 </code></pre>
22590  * @cfg {String} totalProperty Name of the property from which to retrieve the total number of records
22591  * in the dataset. This is only needed if the whole dataset is not passed in one go, but is being
22592  * paged from the remote server.
22593  * @cfg {String} successProperty Name of the property from which to retrieve the success attribute used by forms.
22594  * @cfg {String} root name of the property which contains the Array of row objects.
22595  * @cfg {String} id Name of the property within a row object that contains a record identifier value.
22596  * @constructor
22597  * Create a new JsonReader
22598  * @param {Object} meta Metadata configuration options
22599  * @param {Object} recordType Either an Array of field definition objects,
22600  * or an {@link Roo.data.Record} object created using {@link Roo.data.Record#create}.
22601  */
22602 Roo.data.JsonReader = function(meta, recordType){
22603     
22604     meta = meta || {};
22605     // set some defaults:
22606     Roo.applyIf(meta, {
22607         totalProperty: 'total',
22608         successProperty : 'success',
22609         root : 'data',
22610         id : 'id'
22611     });
22612     
22613     Roo.data.JsonReader.superclass.constructor.call(this, meta, recordType||meta.fields);
22614 };
22615 Roo.extend(Roo.data.JsonReader, Roo.data.DataReader, {
22616     
22617     /**
22618      * @prop {Boolean} metaFromRemote  - if the meta data was loaded from the remote source.
22619      * Used by Store query builder to append _requestMeta to params.
22620      * 
22621      */
22622     metaFromRemote : false,
22623     /**
22624      * This method is only used by a DataProxy which has retrieved data from a remote server.
22625      * @param {Object} response The XHR object which contains the JSON data in its responseText.
22626      * @return {Object} data A data block which is used by an Roo.data.Store object as
22627      * a cache of Roo.data.Records.
22628      */
22629     read : function(response){
22630         var json = response.responseText;
22631        
22632         var o = /* eval:var:o */ eval("("+json+")");
22633         if(!o) {
22634             throw {message: "JsonReader.read: Json object not found"};
22635         }
22636         
22637         if(o.metaData){
22638             
22639             delete this.ef;
22640             this.metaFromRemote = true;
22641             this.meta = o.metaData;
22642             this.recordType = Roo.data.Record.create(o.metaData.fields);
22643             this.onMetaChange(this.meta, this.recordType, o);
22644         }
22645         return this.readRecords(o);
22646     },
22647
22648     // private function a store will implement
22649     onMetaChange : function(meta, recordType, o){
22650
22651     },
22652
22653     /**
22654          * @ignore
22655          */
22656     simpleAccess: function(obj, subsc) {
22657         return obj[subsc];
22658     },
22659
22660         /**
22661          * @ignore
22662          */
22663     getJsonAccessor: function(){
22664         var re = /[\[\.]/;
22665         return function(expr) {
22666             try {
22667                 return(re.test(expr))
22668                     ? new Function("obj", "return obj." + expr)
22669                     : function(obj){
22670                         return obj[expr];
22671                     };
22672             } catch(e){}
22673             return Roo.emptyFn;
22674         };
22675     }(),
22676
22677     /**
22678      * Create a data block containing Roo.data.Records from an XML document.
22679      * @param {Object} o An object which contains an Array of row objects in the property specified
22680      * in the config as 'root, and optionally a property, specified in the config as 'totalProperty'
22681      * which contains the total size of the dataset.
22682      * @return {Object} data A data block which is used by an Roo.data.Store object as
22683      * a cache of Roo.data.Records.
22684      */
22685     readRecords : function(o){
22686         /**
22687          * After any data loads, the raw JSON data is available for further custom processing.
22688          * @type Object
22689          */
22690         this.o = o;
22691         var s = this.meta, Record = this.recordType,
22692             f = Record.prototype.fields, fi = f.items, fl = f.length;
22693
22694 //      Generate extraction functions for the totalProperty, the root, the id, and for each field
22695         if (!this.ef) {
22696             if(s.totalProperty) {
22697                     this.getTotal = this.getJsonAccessor(s.totalProperty);
22698                 }
22699                 if(s.successProperty) {
22700                     this.getSuccess = this.getJsonAccessor(s.successProperty);
22701                 }
22702                 this.getRoot = s.root ? this.getJsonAccessor(s.root) : function(p){return p;};
22703                 if (s.id) {
22704                         var g = this.getJsonAccessor(s.id);
22705                         this.getId = function(rec) {
22706                                 var r = g(rec);
22707                                 return (r === undefined || r === "") ? null : r;
22708                         };
22709                 } else {
22710                         this.getId = function(){return null;};
22711                 }
22712             this.ef = [];
22713             for(var jj = 0; jj < fl; jj++){
22714                 f = fi[jj];
22715                 var map = (f.mapping !== undefined && f.mapping !== null) ? f.mapping : f.name;
22716                 this.ef[jj] = this.getJsonAccessor(map);
22717             }
22718         }
22719
22720         var root = this.getRoot(o), c = root.length, totalRecords = c, success = true;
22721         if(s.totalProperty){
22722             var vt = parseInt(this.getTotal(o), 10);
22723             if(!isNaN(vt)){
22724                 totalRecords = vt;
22725             }
22726         }
22727         if(s.successProperty){
22728             var vs = this.getSuccess(o);
22729             if(vs === false || vs === 'false'){
22730                 success = false;
22731             }
22732         }
22733         var records = [];
22734             for(var i = 0; i < c; i++){
22735                     var n = root[i];
22736                 var values = {};
22737                 var id = this.getId(n);
22738                 for(var j = 0; j < fl; j++){
22739                     f = fi[j];
22740                 var v = this.ef[j](n);
22741                 if (!f.convert) {
22742                     Roo.log('missing convert for ' + f.name);
22743                     Roo.log(f);
22744                     continue;
22745                 }
22746                 values[f.name] = f.convert((v !== undefined) ? v : f.defaultValue);
22747                 }
22748                 var record = new Record(values, id);
22749                 record.json = n;
22750                 records[i] = record;
22751             }
22752             return {
22753             raw : o,
22754                 success : success,
22755                 records : records,
22756                 totalRecords : totalRecords
22757             };
22758     }
22759 });/*
22760  * Based on:
22761  * Ext JS Library 1.1.1
22762  * Copyright(c) 2006-2007, Ext JS, LLC.
22763  *
22764  * Originally Released Under LGPL - original licence link has changed is not relivant.
22765  *
22766  * Fork - LGPL
22767  * <script type="text/javascript">
22768  */
22769
22770 /**
22771  * @class Roo.data.XmlReader
22772  * @extends Roo.data.DataReader
22773  * Data reader class to create an Array of {@link Roo.data.Record} objects from an XML document
22774  * based on mappings in a provided Roo.data.Record constructor.<br><br>
22775  * <p>
22776  * <em>Note that in order for the browser to parse a returned XML document, the Content-Type
22777  * header in the HTTP response must be set to "text/xml".</em>
22778  * <p>
22779  * Example code:
22780  * <pre><code>
22781 var RecordDef = Roo.data.Record.create([
22782    {name: 'name', mapping: 'name'},     // "mapping" property not needed if it's the same as "name"
22783    {name: 'occupation'}                 // This field will use "occupation" as the mapping.
22784 ]);
22785 var myReader = new Roo.data.XmlReader({
22786    totalRecords: "results", // The element which contains the total dataset size (optional)
22787    record: "row",           // The repeated element which contains row information
22788    id: "id"                 // The element within the row that provides an ID for the record (optional)
22789 }, RecordDef);
22790 </code></pre>
22791  * <p>
22792  * This would consume an XML file like this:
22793  * <pre><code>
22794 &lt;?xml?>
22795 &lt;dataset>
22796  &lt;results>2&lt;/results>
22797  &lt;row>
22798    &lt;id>1&lt;/id>
22799    &lt;name>Bill&lt;/name>
22800    &lt;occupation>Gardener&lt;/occupation>
22801  &lt;/row>
22802  &lt;row>
22803    &lt;id>2&lt;/id>
22804    &lt;name>Ben&lt;/name>
22805    &lt;occupation>Horticulturalist&lt;/occupation>
22806  &lt;/row>
22807 &lt;/dataset>
22808 </code></pre>
22809  * @cfg {String} totalRecords The DomQuery path from which to retrieve the total number of records
22810  * in the dataset. This is only needed if the whole dataset is not passed in one go, but is being
22811  * paged from the remote server.
22812  * @cfg {String} record The DomQuery path to the repeated element which contains record information.
22813  * @cfg {String} success The DomQuery path to the success attribute used by forms.
22814  * @cfg {String} id The DomQuery path relative from the record element to the element that contains
22815  * a record identifier value.
22816  * @constructor
22817  * Create a new XmlReader
22818  * @param {Object} meta Metadata configuration options
22819  * @param {Mixed} recordType The definition of the data record type to produce.  This can be either a valid
22820  * Record subclass created with {@link Roo.data.Record#create}, or an array of objects with which to call
22821  * Roo.data.Record.create.  See the {@link Roo.data.Record} class for more details.
22822  */
22823 Roo.data.XmlReader = function(meta, recordType){
22824     meta = meta || {};
22825     Roo.data.XmlReader.superclass.constructor.call(this, meta, recordType||meta.fields);
22826 };
22827 Roo.extend(Roo.data.XmlReader, Roo.data.DataReader, {
22828     /**
22829      * This method is only used by a DataProxy which has retrieved data from a remote server.
22830          * @param {Object} response The XHR object which contains the parsed XML document.  The response is expected
22831          * to contain a method called 'responseXML' that returns an XML document object.
22832      * @return {Object} records A data block which is used by an {@link Roo.data.Store} as
22833      * a cache of Roo.data.Records.
22834      */
22835     read : function(response){
22836         var doc = response.responseXML;
22837         if(!doc) {
22838             throw {message: "XmlReader.read: XML Document not available"};
22839         }
22840         return this.readRecords(doc);
22841     },
22842
22843     /**
22844      * Create a data block containing Roo.data.Records from an XML document.
22845          * @param {Object} doc A parsed XML document.
22846      * @return {Object} records A data block which is used by an {@link Roo.data.Store} as
22847      * a cache of Roo.data.Records.
22848      */
22849     readRecords : function(doc){
22850         /**
22851          * After any data loads/reads, the raw XML Document is available for further custom processing.
22852          * @type XMLDocument
22853          */
22854         this.xmlData = doc;
22855         var root = doc.documentElement || doc;
22856         var q = Roo.DomQuery;
22857         var recordType = this.recordType, fields = recordType.prototype.fields;
22858         var sid = this.meta.id;
22859         var totalRecords = 0, success = true;
22860         if(this.meta.totalRecords){
22861             totalRecords = q.selectNumber(this.meta.totalRecords, root, 0);
22862         }
22863         
22864         if(this.meta.success){
22865             var sv = q.selectValue(this.meta.success, root, true);
22866             success = sv !== false && sv !== 'false';
22867         }
22868         var records = [];
22869         var ns = q.select(this.meta.record, root);
22870         for(var i = 0, len = ns.length; i < len; i++) {
22871                 var n = ns[i];
22872                 var values = {};
22873                 var id = sid ? q.selectValue(sid, n) : undefined;
22874                 for(var j = 0, jlen = fields.length; j < jlen; j++){
22875                     var f = fields.items[j];
22876                 var v = q.selectValue(f.mapping || f.name, n, f.defaultValue);
22877                     v = f.convert(v);
22878                     values[f.name] = v;
22879                 }
22880                 var record = new recordType(values, id);
22881                 record.node = n;
22882                 records[records.length] = record;
22883             }
22884
22885             return {
22886                 success : success,
22887                 records : records,
22888                 totalRecords : totalRecords || records.length
22889             };
22890     }
22891 });/*
22892  * Based on:
22893  * Ext JS Library 1.1.1
22894  * Copyright(c) 2006-2007, Ext JS, LLC.
22895  *
22896  * Originally Released Under LGPL - original licence link has changed is not relivant.
22897  *
22898  * Fork - LGPL
22899  * <script type="text/javascript">
22900  */
22901
22902 /**
22903  * @class Roo.data.ArrayReader
22904  * @extends Roo.data.DataReader
22905  * Data reader class to create an Array of Roo.data.Record objects from an Array.
22906  * Each element of that Array represents a row of data fields. The
22907  * fields are pulled into a Record object using as a subscript, the <em>mapping</em> property
22908  * of the field definition if it exists, or the field's ordinal position in the definition.<br>
22909  * <p>
22910  * Example code:.
22911  * <pre><code>
22912 var RecordDef = Roo.data.Record.create([
22913     {name: 'name', mapping: 1},         // "mapping" only needed if an "id" field is present which
22914     {name: 'occupation', mapping: 2}    // precludes using the ordinal position as the index.
22915 ]);
22916 var myReader = new Roo.data.ArrayReader({
22917     id: 0                     // The subscript within row Array that provides an ID for the Record (optional)
22918 }, RecordDef);
22919 </code></pre>
22920  * <p>
22921  * This would consume an Array like this:
22922  * <pre><code>
22923 [ [1, 'Bill', 'Gardener'], [2, 'Ben', 'Horticulturalist'] ]
22924   </code></pre>
22925  * @cfg {String} id (optional) The subscript within row Array that provides an ID for the Record
22926  * @constructor
22927  * Create a new JsonReader
22928  * @param {Object} meta Metadata configuration options.
22929  * @param {Object} recordType Either an Array of field definition objects
22930  * as specified to {@link Roo.data.Record#create},
22931  * or an {@link Roo.data.Record} object
22932  * created using {@link Roo.data.Record#create}.
22933  */
22934 Roo.data.ArrayReader = function(meta, recordType){
22935     Roo.data.ArrayReader.superclass.constructor.call(this, meta, recordType);
22936 };
22937
22938 Roo.extend(Roo.data.ArrayReader, Roo.data.JsonReader, {
22939     /**
22940      * Create a data block containing Roo.data.Records from an XML document.
22941      * @param {Object} o An Array of row objects which represents the dataset.
22942      * @return {Object} data A data block which is used by an Roo.data.Store object as
22943      * a cache of Roo.data.Records.
22944      */
22945     readRecords : function(o){
22946         var sid = this.meta ? this.meta.id : null;
22947         var recordType = this.recordType, fields = recordType.prototype.fields;
22948         var records = [];
22949         var root = o;
22950             for(var i = 0; i < root.length; i++){
22951                     var n = root[i];
22952                 var values = {};
22953                 var id = ((sid || sid === 0) && n[sid] !== undefined && n[sid] !== "" ? n[sid] : null);
22954                 for(var j = 0, jlen = fields.length; j < jlen; j++){
22955                 var f = fields.items[j];
22956                 var k = f.mapping !== undefined && f.mapping !== null ? f.mapping : j;
22957                 var v = n[k] !== undefined ? n[k] : f.defaultValue;
22958                 v = f.convert(v);
22959                 values[f.name] = v;
22960             }
22961                 var record = new recordType(values, id);
22962                 record.json = n;
22963                 records[records.length] = record;
22964             }
22965             return {
22966                 records : records,
22967                 totalRecords : records.length
22968             };
22969     }
22970 });/*
22971  * Based on:
22972  * Ext JS Library 1.1.1
22973  * Copyright(c) 2006-2007, Ext JS, LLC.
22974  *
22975  * Originally Released Under LGPL - original licence link has changed is not relivant.
22976  *
22977  * Fork - LGPL
22978  * <script type="text/javascript">
22979  */
22980
22981
22982 /**
22983  * @class Roo.data.Tree
22984  * @extends Roo.util.Observable
22985  * Represents a tree data structure and bubbles all the events for its nodes. The nodes
22986  * in the tree have most standard DOM functionality.
22987  * @constructor
22988  * @param {Node} root (optional) The root node
22989  */
22990 Roo.data.Tree = function(root){
22991    this.nodeHash = {};
22992    /**
22993     * The root node for this tree
22994     * @type Node
22995     */
22996    this.root = null;
22997    if(root){
22998        this.setRootNode(root);
22999    }
23000    this.addEvents({
23001        /**
23002         * @event append
23003         * Fires when a new child node is appended to a node in this tree.
23004         * @param {Tree} tree The owner tree
23005         * @param {Node} parent The parent node
23006         * @param {Node} node The newly appended node
23007         * @param {Number} index The index of the newly appended node
23008         */
23009        "append" : true,
23010        /**
23011         * @event remove
23012         * Fires when a child node is removed from a node in this tree.
23013         * @param {Tree} tree The owner tree
23014         * @param {Node} parent The parent node
23015         * @param {Node} node The child node removed
23016         */
23017        "remove" : true,
23018        /**
23019         * @event move
23020         * Fires when a node is moved to a new location in the tree
23021         * @param {Tree} tree The owner tree
23022         * @param {Node} node The node moved
23023         * @param {Node} oldParent The old parent of this node
23024         * @param {Node} newParent The new parent of this node
23025         * @param {Number} index The index it was moved to
23026         */
23027        "move" : true,
23028        /**
23029         * @event insert
23030         * Fires when a new child node is inserted in a node in this tree.
23031         * @param {Tree} tree The owner tree
23032         * @param {Node} parent The parent node
23033         * @param {Node} node The child node inserted
23034         * @param {Node} refNode The child node the node was inserted before
23035         */
23036        "insert" : true,
23037        /**
23038         * @event beforeappend
23039         * Fires before a new child is appended to a node in this tree, return false to cancel the append.
23040         * @param {Tree} tree The owner tree
23041         * @param {Node} parent The parent node
23042         * @param {Node} node The child node to be appended
23043         */
23044        "beforeappend" : true,
23045        /**
23046         * @event beforeremove
23047         * Fires before a child is removed from a node in this tree, return false to cancel the remove.
23048         * @param {Tree} tree The owner tree
23049         * @param {Node} parent The parent node
23050         * @param {Node} node The child node to be removed
23051         */
23052        "beforeremove" : true,
23053        /**
23054         * @event beforemove
23055         * Fires before a node is moved to a new location in the tree. Return false to cancel the move.
23056         * @param {Tree} tree The owner tree
23057         * @param {Node} node The node being moved
23058         * @param {Node} oldParent The parent of the node
23059         * @param {Node} newParent The new parent the node is moving to
23060         * @param {Number} index The index it is being moved to
23061         */
23062        "beforemove" : true,
23063        /**
23064         * @event beforeinsert
23065         * Fires before a new child is inserted in a node in this tree, return false to cancel the insert.
23066         * @param {Tree} tree The owner tree
23067         * @param {Node} parent The parent node
23068         * @param {Node} node The child node to be inserted
23069         * @param {Node} refNode The child node the node is being inserted before
23070         */
23071        "beforeinsert" : true
23072    });
23073
23074     Roo.data.Tree.superclass.constructor.call(this);
23075 };
23076
23077 Roo.extend(Roo.data.Tree, Roo.util.Observable, {
23078     pathSeparator: "/",
23079
23080     proxyNodeEvent : function(){
23081         return this.fireEvent.apply(this, arguments);
23082     },
23083
23084     /**
23085      * Returns the root node for this tree.
23086      * @return {Node}
23087      */
23088     getRootNode : function(){
23089         return this.root;
23090     },
23091
23092     /**
23093      * Sets the root node for this tree.
23094      * @param {Node} node
23095      * @return {Node}
23096      */
23097     setRootNode : function(node){
23098         this.root = node;
23099         node.ownerTree = this;
23100         node.isRoot = true;
23101         this.registerNode(node);
23102         return node;
23103     },
23104
23105     /**
23106      * Gets a node in this tree by its id.
23107      * @param {String} id
23108      * @return {Node}
23109      */
23110     getNodeById : function(id){
23111         return this.nodeHash[id];
23112     },
23113
23114     registerNode : function(node){
23115         this.nodeHash[node.id] = node;
23116     },
23117
23118     unregisterNode : function(node){
23119         delete this.nodeHash[node.id];
23120     },
23121
23122     toString : function(){
23123         return "[Tree"+(this.id?" "+this.id:"")+"]";
23124     }
23125 });
23126
23127 /**
23128  * @class Roo.data.Node
23129  * @extends Roo.util.Observable
23130  * @cfg {Boolean} leaf true if this node is a leaf and does not have children
23131  * @cfg {String} id The id for this node. If one is not specified, one is generated.
23132  * @constructor
23133  * @param {Object} attributes The attributes/config for the node
23134  */
23135 Roo.data.Node = function(attributes){
23136     /**
23137      * The attributes supplied for the node. You can use this property to access any custom attributes you supplied.
23138      * @type {Object}
23139      */
23140     this.attributes = attributes || {};
23141     this.leaf = this.attributes.leaf;
23142     /**
23143      * The node id. @type String
23144      */
23145     this.id = this.attributes.id;
23146     if(!this.id){
23147         this.id = Roo.id(null, "ynode-");
23148         this.attributes.id = this.id;
23149     }
23150      
23151     
23152     /**
23153      * All child nodes of this node. @type Array
23154      */
23155     this.childNodes = [];
23156     if(!this.childNodes.indexOf){ // indexOf is a must
23157         this.childNodes.indexOf = function(o){
23158             for(var i = 0, len = this.length; i < len; i++){
23159                 if(this[i] == o) {
23160                     return i;
23161                 }
23162             }
23163             return -1;
23164         };
23165     }
23166     /**
23167      * The parent node for this node. @type Node
23168      */
23169     this.parentNode = null;
23170     /**
23171      * The first direct child node of this node, or null if this node has no child nodes. @type Node
23172      */
23173     this.firstChild = null;
23174     /**
23175      * The last direct child node of this node, or null if this node has no child nodes. @type Node
23176      */
23177     this.lastChild = null;
23178     /**
23179      * The node immediately preceding this node in the tree, or null if there is no sibling node. @type Node
23180      */
23181     this.previousSibling = null;
23182     /**
23183      * The node immediately following this node in the tree, or null if there is no sibling node. @type Node
23184      */
23185     this.nextSibling = null;
23186
23187     this.addEvents({
23188        /**
23189         * @event append
23190         * Fires when a new child node is appended
23191         * @param {Tree} tree The owner tree
23192         * @param {Node} this This node
23193         * @param {Node} node The newly appended node
23194         * @param {Number} index The index of the newly appended node
23195         */
23196        "append" : true,
23197        /**
23198         * @event remove
23199         * Fires when a child node is removed
23200         * @param {Tree} tree The owner tree
23201         * @param {Node} this This node
23202         * @param {Node} node The removed node
23203         */
23204        "remove" : true,
23205        /**
23206         * @event move
23207         * Fires when this node is moved to a new location in the tree
23208         * @param {Tree} tree The owner tree
23209         * @param {Node} this This node
23210         * @param {Node} oldParent The old parent of this node
23211         * @param {Node} newParent The new parent of this node
23212         * @param {Number} index The index it was moved to
23213         */
23214        "move" : true,
23215        /**
23216         * @event insert
23217         * Fires when a new child node is inserted.
23218         * @param {Tree} tree The owner tree
23219         * @param {Node} this This node
23220         * @param {Node} node The child node inserted
23221         * @param {Node} refNode The child node the node was inserted before
23222         */
23223        "insert" : true,
23224        /**
23225         * @event beforeappend
23226         * Fires before a new child is appended, return false to cancel the append.
23227         * @param {Tree} tree The owner tree
23228         * @param {Node} this This node
23229         * @param {Node} node The child node to be appended
23230         */
23231        "beforeappend" : true,
23232        /**
23233         * @event beforeremove
23234         * Fires before a child is removed, return false to cancel the remove.
23235         * @param {Tree} tree The owner tree
23236         * @param {Node} this This node
23237         * @param {Node} node The child node to be removed
23238         */
23239        "beforeremove" : true,
23240        /**
23241         * @event beforemove
23242         * Fires before this node is moved to a new location in the tree. Return false to cancel the move.
23243         * @param {Tree} tree The owner tree
23244         * @param {Node} this This node
23245         * @param {Node} oldParent The parent of this node
23246         * @param {Node} newParent The new parent this node is moving to
23247         * @param {Number} index The index it is being moved to
23248         */
23249        "beforemove" : true,
23250        /**
23251         * @event beforeinsert
23252         * Fires before a new child is inserted, return false to cancel the insert.
23253         * @param {Tree} tree The owner tree
23254         * @param {Node} this This node
23255         * @param {Node} node The child node to be inserted
23256         * @param {Node} refNode The child node the node is being inserted before
23257         */
23258        "beforeinsert" : true
23259    });
23260     this.listeners = this.attributes.listeners;
23261     Roo.data.Node.superclass.constructor.call(this);
23262 };
23263
23264 Roo.extend(Roo.data.Node, Roo.util.Observable, {
23265     fireEvent : function(evtName){
23266         // first do standard event for this node
23267         if(Roo.data.Node.superclass.fireEvent.apply(this, arguments) === false){
23268             return false;
23269         }
23270         // then bubble it up to the tree if the event wasn't cancelled
23271         var ot = this.getOwnerTree();
23272         if(ot){
23273             if(ot.proxyNodeEvent.apply(ot, arguments) === false){
23274                 return false;
23275             }
23276         }
23277         return true;
23278     },
23279
23280     /**
23281      * Returns true if this node is a leaf
23282      * @return {Boolean}
23283      */
23284     isLeaf : function(){
23285         return this.leaf === true;
23286     },
23287
23288     // private
23289     setFirstChild : function(node){
23290         this.firstChild = node;
23291     },
23292
23293     //private
23294     setLastChild : function(node){
23295         this.lastChild = node;
23296     },
23297
23298
23299     /**
23300      * Returns true if this node is the last child of its parent
23301      * @return {Boolean}
23302      */
23303     isLast : function(){
23304        return (!this.parentNode ? true : this.parentNode.lastChild == this);
23305     },
23306
23307     /**
23308      * Returns true if this node is the first child of its parent
23309      * @return {Boolean}
23310      */
23311     isFirst : function(){
23312        return (!this.parentNode ? true : this.parentNode.firstChild == this);
23313     },
23314
23315     hasChildNodes : function(){
23316         return !this.isLeaf() && this.childNodes.length > 0;
23317     },
23318
23319     /**
23320      * Insert node(s) as the last child node of this node.
23321      * @param {Node/Array} node The node or Array of nodes to append
23322      * @return {Node} The appended node if single append, or null if an array was passed
23323      */
23324     appendChild : function(node){
23325         var multi = false;
23326         if(node instanceof Array){
23327             multi = node;
23328         }else if(arguments.length > 1){
23329             multi = arguments;
23330         }
23331         // if passed an array or multiple args do them one by one
23332         if(multi){
23333             for(var i = 0, len = multi.length; i < len; i++) {
23334                 this.appendChild(multi[i]);
23335             }
23336         }else{
23337             if(this.fireEvent("beforeappend", this.ownerTree, this, node) === false){
23338                 return false;
23339             }
23340             var index = this.childNodes.length;
23341             var oldParent = node.parentNode;
23342             // it's a move, make sure we move it cleanly
23343             if(oldParent){
23344                 if(node.fireEvent("beforemove", node.getOwnerTree(), node, oldParent, this, index) === false){
23345                     return false;
23346                 }
23347                 oldParent.removeChild(node);
23348             }
23349             index = this.childNodes.length;
23350             if(index == 0){
23351                 this.setFirstChild(node);
23352             }
23353             this.childNodes.push(node);
23354             node.parentNode = this;
23355             var ps = this.childNodes[index-1];
23356             if(ps){
23357                 node.previousSibling = ps;
23358                 ps.nextSibling = node;
23359             }else{
23360                 node.previousSibling = null;
23361             }
23362             node.nextSibling = null;
23363             this.setLastChild(node);
23364             node.setOwnerTree(this.getOwnerTree());
23365             this.fireEvent("append", this.ownerTree, this, node, index);
23366             if(oldParent){
23367                 node.fireEvent("move", this.ownerTree, node, oldParent, this, index);
23368             }
23369             return node;
23370         }
23371     },
23372
23373     /**
23374      * Removes a child node from this node.
23375      * @param {Node} node The node to remove
23376      * @return {Node} The removed node
23377      */
23378     removeChild : function(node){
23379         var index = this.childNodes.indexOf(node);
23380         if(index == -1){
23381             return false;
23382         }
23383         if(this.fireEvent("beforeremove", this.ownerTree, this, node) === false){
23384             return false;
23385         }
23386
23387         // remove it from childNodes collection
23388         this.childNodes.splice(index, 1);
23389
23390         // update siblings
23391         if(node.previousSibling){
23392             node.previousSibling.nextSibling = node.nextSibling;
23393         }
23394         if(node.nextSibling){
23395             node.nextSibling.previousSibling = node.previousSibling;
23396         }
23397
23398         // update child refs
23399         if(this.firstChild == node){
23400             this.setFirstChild(node.nextSibling);
23401         }
23402         if(this.lastChild == node){
23403             this.setLastChild(node.previousSibling);
23404         }
23405
23406         node.setOwnerTree(null);
23407         // clear any references from the node
23408         node.parentNode = null;
23409         node.previousSibling = null;
23410         node.nextSibling = null;
23411         this.fireEvent("remove", this.ownerTree, this, node);
23412         return node;
23413     },
23414
23415     /**
23416      * Inserts the first node before the second node in this nodes childNodes collection.
23417      * @param {Node} node The node to insert
23418      * @param {Node} refNode The node to insert before (if null the node is appended)
23419      * @return {Node} The inserted node
23420      */
23421     insertBefore : function(node, refNode){
23422         if(!refNode){ // like standard Dom, refNode can be null for append
23423             return this.appendChild(node);
23424         }
23425         // nothing to do
23426         if(node == refNode){
23427             return false;
23428         }
23429
23430         if(this.fireEvent("beforeinsert", this.ownerTree, this, node, refNode) === false){
23431             return false;
23432         }
23433         var index = this.childNodes.indexOf(refNode);
23434         var oldParent = node.parentNode;
23435         var refIndex = index;
23436
23437         // when moving internally, indexes will change after remove
23438         if(oldParent == this && this.childNodes.indexOf(node) < index){
23439             refIndex--;
23440         }
23441
23442         // it's a move, make sure we move it cleanly
23443         if(oldParent){
23444             if(node.fireEvent("beforemove", node.getOwnerTree(), node, oldParent, this, index, refNode) === false){
23445                 return false;
23446             }
23447             oldParent.removeChild(node);
23448         }
23449         if(refIndex == 0){
23450             this.setFirstChild(node);
23451         }
23452         this.childNodes.splice(refIndex, 0, node);
23453         node.parentNode = this;
23454         var ps = this.childNodes[refIndex-1];
23455         if(ps){
23456             node.previousSibling = ps;
23457             ps.nextSibling = node;
23458         }else{
23459             node.previousSibling = null;
23460         }
23461         node.nextSibling = refNode;
23462         refNode.previousSibling = node;
23463         node.setOwnerTree(this.getOwnerTree());
23464         this.fireEvent("insert", this.ownerTree, this, node, refNode);
23465         if(oldParent){
23466             node.fireEvent("move", this.ownerTree, node, oldParent, this, refIndex, refNode);
23467         }
23468         return node;
23469     },
23470
23471     /**
23472      * Returns the child node at the specified index.
23473      * @param {Number} index
23474      * @return {Node}
23475      */
23476     item : function(index){
23477         return this.childNodes[index];
23478     },
23479
23480     /**
23481      * Replaces one child node in this node with another.
23482      * @param {Node} newChild The replacement node
23483      * @param {Node} oldChild The node to replace
23484      * @return {Node} The replaced node
23485      */
23486     replaceChild : function(newChild, oldChild){
23487         this.insertBefore(newChild, oldChild);
23488         this.removeChild(oldChild);
23489         return oldChild;
23490     },
23491
23492     /**
23493      * Returns the index of a child node
23494      * @param {Node} node
23495      * @return {Number} The index of the node or -1 if it was not found
23496      */
23497     indexOf : function(child){
23498         return this.childNodes.indexOf(child);
23499     },
23500
23501     /**
23502      * Returns the tree this node is in.
23503      * @return {Tree}
23504      */
23505     getOwnerTree : function(){
23506         // if it doesn't have one, look for one
23507         if(!this.ownerTree){
23508             var p = this;
23509             while(p){
23510                 if(p.ownerTree){
23511                     this.ownerTree = p.ownerTree;
23512                     break;
23513                 }
23514                 p = p.parentNode;
23515             }
23516         }
23517         return this.ownerTree;
23518     },
23519
23520     /**
23521      * Returns depth of this node (the root node has a depth of 0)
23522      * @return {Number}
23523      */
23524     getDepth : function(){
23525         var depth = 0;
23526         var p = this;
23527         while(p.parentNode){
23528             ++depth;
23529             p = p.parentNode;
23530         }
23531         return depth;
23532     },
23533
23534     // private
23535     setOwnerTree : function(tree){
23536         // if it's move, we need to update everyone
23537         if(tree != this.ownerTree){
23538             if(this.ownerTree){
23539                 this.ownerTree.unregisterNode(this);
23540             }
23541             this.ownerTree = tree;
23542             var cs = this.childNodes;
23543             for(var i = 0, len = cs.length; i < len; i++) {
23544                 cs[i].setOwnerTree(tree);
23545             }
23546             if(tree){
23547                 tree.registerNode(this);
23548             }
23549         }
23550     },
23551
23552     /**
23553      * Returns the path for this node. The path can be used to expand or select this node programmatically.
23554      * @param {String} attr (optional) The attr to use for the path (defaults to the node's id)
23555      * @return {String} The path
23556      */
23557     getPath : function(attr){
23558         attr = attr || "id";
23559         var p = this.parentNode;
23560         var b = [this.attributes[attr]];
23561         while(p){
23562             b.unshift(p.attributes[attr]);
23563             p = p.parentNode;
23564         }
23565         var sep = this.getOwnerTree().pathSeparator;
23566         return sep + b.join(sep);
23567     },
23568
23569     /**
23570      * Bubbles up the tree from this node, calling the specified function with each node. The scope (<i>this</i>) of
23571      * function call will be the scope provided or the current node. The arguments to the function
23572      * will be the args provided or the current node. If the function returns false at any point,
23573      * the bubble is stopped.
23574      * @param {Function} fn The function to call
23575      * @param {Object} scope (optional) The scope of the function (defaults to current node)
23576      * @param {Array} args (optional) The args to call the function with (default to passing the current node)
23577      */
23578     bubble : function(fn, scope, args){
23579         var p = this;
23580         while(p){
23581             if(fn.call(scope || p, args || p) === false){
23582                 break;
23583             }
23584             p = p.parentNode;
23585         }
23586     },
23587
23588     /**
23589      * Cascades down the tree from this node, calling the specified function with each node. The scope (<i>this</i>) of
23590      * function call will be the scope provided or the current node. The arguments to the function
23591      * will be the args provided or the current node. If the function returns false at any point,
23592      * the cascade is stopped on that branch.
23593      * @param {Function} fn The function to call
23594      * @param {Object} scope (optional) The scope of the function (defaults to current node)
23595      * @param {Array} args (optional) The args to call the function with (default to passing the current node)
23596      */
23597     cascade : function(fn, scope, args){
23598         if(fn.call(scope || this, args || this) !== false){
23599             var cs = this.childNodes;
23600             for(var i = 0, len = cs.length; i < len; i++) {
23601                 cs[i].cascade(fn, scope, args);
23602             }
23603         }
23604     },
23605
23606     /**
23607      * Interates the child nodes of this node, calling the specified function with each node. The scope (<i>this</i>) of
23608      * function call will be the scope provided or the current node. The arguments to the function
23609      * will be the args provided or the current node. If the function returns false at any point,
23610      * the iteration stops.
23611      * @param {Function} fn The function to call
23612      * @param {Object} scope (optional) The scope of the function (defaults to current node)
23613      * @param {Array} args (optional) The args to call the function with (default to passing the current node)
23614      */
23615     eachChild : function(fn, scope, args){
23616         var cs = this.childNodes;
23617         for(var i = 0, len = cs.length; i < len; i++) {
23618                 if(fn.call(scope || this, args || cs[i]) === false){
23619                     break;
23620                 }
23621         }
23622     },
23623
23624     /**
23625      * Finds the first child that has the attribute with the specified value.
23626      * @param {String} attribute The attribute name
23627      * @param {Mixed} value The value to search for
23628      * @return {Node} The found child or null if none was found
23629      */
23630     findChild : function(attribute, value){
23631         var cs = this.childNodes;
23632         for(var i = 0, len = cs.length; i < len; i++) {
23633                 if(cs[i].attributes[attribute] == value){
23634                     return cs[i];
23635                 }
23636         }
23637         return null;
23638     },
23639
23640     /**
23641      * Finds the first child by a custom function. The child matches if the function passed
23642      * returns true.
23643      * @param {Function} fn
23644      * @param {Object} scope (optional)
23645      * @return {Node} The found child or null if none was found
23646      */
23647     findChildBy : function(fn, scope){
23648         var cs = this.childNodes;
23649         for(var i = 0, len = cs.length; i < len; i++) {
23650                 if(fn.call(scope||cs[i], cs[i]) === true){
23651                     return cs[i];
23652                 }
23653         }
23654         return null;
23655     },
23656
23657     /**
23658      * Sorts this nodes children using the supplied sort function
23659      * @param {Function} fn
23660      * @param {Object} scope (optional)
23661      */
23662     sort : function(fn, scope){
23663         var cs = this.childNodes;
23664         var len = cs.length;
23665         if(len > 0){
23666             var sortFn = scope ? function(){fn.apply(scope, arguments);} : fn;
23667             cs.sort(sortFn);
23668             for(var i = 0; i < len; i++){
23669                 var n = cs[i];
23670                 n.previousSibling = cs[i-1];
23671                 n.nextSibling = cs[i+1];
23672                 if(i == 0){
23673                     this.setFirstChild(n);
23674                 }
23675                 if(i == len-1){
23676                     this.setLastChild(n);
23677                 }
23678             }
23679         }
23680     },
23681
23682     /**
23683      * Returns true if this node is an ancestor (at any point) of the passed node.
23684      * @param {Node} node
23685      * @return {Boolean}
23686      */
23687     contains : function(node){
23688         return node.isAncestor(this);
23689     },
23690
23691     /**
23692      * Returns true if the passed node is an ancestor (at any point) of this node.
23693      * @param {Node} node
23694      * @return {Boolean}
23695      */
23696     isAncestor : function(node){
23697         var p = this.parentNode;
23698         while(p){
23699             if(p == node){
23700                 return true;
23701             }
23702             p = p.parentNode;
23703         }
23704         return false;
23705     },
23706
23707     toString : function(){
23708         return "[Node"+(this.id?" "+this.id:"")+"]";
23709     }
23710 });/*
23711  * Based on:
23712  * Ext JS Library 1.1.1
23713  * Copyright(c) 2006-2007, Ext JS, LLC.
23714  *
23715  * Originally Released Under LGPL - original licence link has changed is not relivant.
23716  *
23717  * Fork - LGPL
23718  * <script type="text/javascript">
23719  */
23720  (function(){ 
23721 /**
23722  * @class Roo.Layer
23723  * @extends Roo.Element
23724  * An extended {@link Roo.Element} object that supports a shadow and shim, constrain to viewport and
23725  * automatic maintaining of shadow/shim positions.
23726  * @cfg {Boolean} shim False to disable the iframe shim in browsers which need one (defaults to true)
23727  * @cfg {String/Boolean} shadow True to create a shadow element with default class "x-layer-shadow", or
23728  * you can pass a string with a CSS class name. False turns off the shadow.
23729  * @cfg {Object} dh DomHelper object config to create element with (defaults to {tag: "div", cls: "x-layer"}).
23730  * @cfg {Boolean} constrain False to disable constrain to viewport (defaults to true)
23731  * @cfg {String} cls CSS class to add to the element
23732  * @cfg {Number} zindex Starting z-index (defaults to 11000)
23733  * @cfg {Number} shadowOffset Number of pixels to offset the shadow (defaults to 3)
23734  * @constructor
23735  * @param {Object} config An object with config options.
23736  * @param {String/HTMLElement} existingEl (optional) Uses an existing DOM element. If the element is not found it creates it.
23737  */
23738
23739 Roo.Layer = function(config, existingEl){
23740     config = config || {};
23741     var dh = Roo.DomHelper;
23742     var cp = config.parentEl, pel = cp ? Roo.getDom(cp) : document.body;
23743     if(existingEl){
23744         this.dom = Roo.getDom(existingEl);
23745     }
23746     if(!this.dom){
23747         var o = config.dh || {tag: "div", cls: "x-layer"};
23748         this.dom = dh.append(pel, o);
23749     }
23750     if(config.cls){
23751         this.addClass(config.cls);
23752     }
23753     this.constrain = config.constrain !== false;
23754     this.visibilityMode = Roo.Element.VISIBILITY;
23755     if(config.id){
23756         this.id = this.dom.id = config.id;
23757     }else{
23758         this.id = Roo.id(this.dom);
23759     }
23760     this.zindex = config.zindex || this.getZIndex();
23761     this.position("absolute", this.zindex);
23762     if(config.shadow){
23763         this.shadowOffset = config.shadowOffset || 4;
23764         this.shadow = new Roo.Shadow({
23765             offset : this.shadowOffset,
23766             mode : config.shadow
23767         });
23768     }else{
23769         this.shadowOffset = 0;
23770     }
23771     this.useShim = config.shim !== false && Roo.useShims;
23772     this.useDisplay = config.useDisplay;
23773     this.hide();
23774 };
23775
23776 var supr = Roo.Element.prototype;
23777
23778 // shims are shared among layer to keep from having 100 iframes
23779 var shims = [];
23780
23781 Roo.extend(Roo.Layer, Roo.Element, {
23782
23783     getZIndex : function(){
23784         return this.zindex || parseInt(this.getStyle("z-index"), 10) || 11000;
23785     },
23786
23787     getShim : function(){
23788         if(!this.useShim){
23789             return null;
23790         }
23791         if(this.shim){
23792             return this.shim;
23793         }
23794         var shim = shims.shift();
23795         if(!shim){
23796             shim = this.createShim();
23797             shim.enableDisplayMode('block');
23798             shim.dom.style.display = 'none';
23799             shim.dom.style.visibility = 'visible';
23800         }
23801         var pn = this.dom.parentNode;
23802         if(shim.dom.parentNode != pn){
23803             pn.insertBefore(shim.dom, this.dom);
23804         }
23805         shim.setStyle('z-index', this.getZIndex()-2);
23806         this.shim = shim;
23807         return shim;
23808     },
23809
23810     hideShim : function(){
23811         if(this.shim){
23812             this.shim.setDisplayed(false);
23813             shims.push(this.shim);
23814             delete this.shim;
23815         }
23816     },
23817
23818     disableShadow : function(){
23819         if(this.shadow){
23820             this.shadowDisabled = true;
23821             this.shadow.hide();
23822             this.lastShadowOffset = this.shadowOffset;
23823             this.shadowOffset = 0;
23824         }
23825     },
23826
23827     enableShadow : function(show){
23828         if(this.shadow){
23829             this.shadowDisabled = false;
23830             this.shadowOffset = this.lastShadowOffset;
23831             delete this.lastShadowOffset;
23832             if(show){
23833                 this.sync(true);
23834             }
23835         }
23836     },
23837
23838     // private
23839     // this code can execute repeatedly in milliseconds (i.e. during a drag) so
23840     // code size was sacrificed for effeciency (e.g. no getBox/setBox, no XY calls)
23841     sync : function(doShow){
23842         var sw = this.shadow;
23843         if(!this.updating && this.isVisible() && (sw || this.useShim)){
23844             var sh = this.getShim();
23845
23846             var w = this.getWidth(),
23847                 h = this.getHeight();
23848
23849             var l = this.getLeft(true),
23850                 t = this.getTop(true);
23851
23852             if(sw && !this.shadowDisabled){
23853                 if(doShow && !sw.isVisible()){
23854                     sw.show(this);
23855                 }else{
23856                     sw.realign(l, t, w, h);
23857                 }
23858                 if(sh){
23859                     if(doShow){
23860                        sh.show();
23861                     }
23862                     // fit the shim behind the shadow, so it is shimmed too
23863                     var a = sw.adjusts, s = sh.dom.style;
23864                     s.left = (Math.min(l, l+a.l))+"px";
23865                     s.top = (Math.min(t, t+a.t))+"px";
23866                     s.width = (w+a.w)+"px";
23867                     s.height = (h+a.h)+"px";
23868                 }
23869             }else if(sh){
23870                 if(doShow){
23871                    sh.show();
23872                 }
23873                 sh.setSize(w, h);
23874                 sh.setLeftTop(l, t);
23875             }
23876             
23877         }
23878     },
23879
23880     // private
23881     destroy : function(){
23882         this.hideShim();
23883         if(this.shadow){
23884             this.shadow.hide();
23885         }
23886         this.removeAllListeners();
23887         var pn = this.dom.parentNode;
23888         if(pn){
23889             pn.removeChild(this.dom);
23890         }
23891         Roo.Element.uncache(this.id);
23892     },
23893
23894     remove : function(){
23895         this.destroy();
23896     },
23897
23898     // private
23899     beginUpdate : function(){
23900         this.updating = true;
23901     },
23902
23903     // private
23904     endUpdate : function(){
23905         this.updating = false;
23906         this.sync(true);
23907     },
23908
23909     // private
23910     hideUnders : function(negOffset){
23911         if(this.shadow){
23912             this.shadow.hide();
23913         }
23914         this.hideShim();
23915     },
23916
23917     // private
23918     constrainXY : function(){
23919         if(this.constrain){
23920             var vw = Roo.lib.Dom.getViewWidth(),
23921                 vh = Roo.lib.Dom.getViewHeight();
23922             var s = Roo.get(document).getScroll();
23923
23924             var xy = this.getXY();
23925             var x = xy[0], y = xy[1];   
23926             var w = this.dom.offsetWidth+this.shadowOffset, h = this.dom.offsetHeight+this.shadowOffset;
23927             // only move it if it needs it
23928             var moved = false;
23929             // first validate right/bottom
23930             if((x + w) > vw+s.left){
23931                 x = vw - w - this.shadowOffset;
23932                 moved = true;
23933             }
23934             if((y + h) > vh+s.top){
23935                 y = vh - h - this.shadowOffset;
23936                 moved = true;
23937             }
23938             // then make sure top/left isn't negative
23939             if(x < s.left){
23940                 x = s.left;
23941                 moved = true;
23942             }
23943             if(y < s.top){
23944                 y = s.top;
23945                 moved = true;
23946             }
23947             if(moved){
23948                 if(this.avoidY){
23949                     var ay = this.avoidY;
23950                     if(y <= ay && (y+h) >= ay){
23951                         y = ay-h-5;   
23952                     }
23953                 }
23954                 xy = [x, y];
23955                 this.storeXY(xy);
23956                 supr.setXY.call(this, xy);
23957                 this.sync();
23958             }
23959         }
23960     },
23961
23962     isVisible : function(){
23963         return this.visible;    
23964     },
23965
23966     // private
23967     showAction : function(){
23968         this.visible = true; // track visibility to prevent getStyle calls
23969         if(this.useDisplay === true){
23970             this.setDisplayed("");
23971         }else if(this.lastXY){
23972             supr.setXY.call(this, this.lastXY);
23973         }else if(this.lastLT){
23974             supr.setLeftTop.call(this, this.lastLT[0], this.lastLT[1]);
23975         }
23976     },
23977
23978     // private
23979     hideAction : function(){
23980         this.visible = false;
23981         if(this.useDisplay === true){
23982             this.setDisplayed(false);
23983         }else{
23984             this.setLeftTop(-10000,-10000);
23985         }
23986     },
23987
23988     // overridden Element method
23989     setVisible : function(v, a, d, c, e){
23990         if(v){
23991             this.showAction();
23992         }
23993         if(a && v){
23994             var cb = function(){
23995                 this.sync(true);
23996                 if(c){
23997                     c();
23998                 }
23999             }.createDelegate(this);
24000             supr.setVisible.call(this, true, true, d, cb, e);
24001         }else{
24002             if(!v){
24003                 this.hideUnders(true);
24004             }
24005             var cb = c;
24006             if(a){
24007                 cb = function(){
24008                     this.hideAction();
24009                     if(c){
24010                         c();
24011                     }
24012                 }.createDelegate(this);
24013             }
24014             supr.setVisible.call(this, v, a, d, cb, e);
24015             if(v){
24016                 this.sync(true);
24017             }else if(!a){
24018                 this.hideAction();
24019             }
24020         }
24021     },
24022
24023     storeXY : function(xy){
24024         delete this.lastLT;
24025         this.lastXY = xy;
24026     },
24027
24028     storeLeftTop : function(left, top){
24029         delete this.lastXY;
24030         this.lastLT = [left, top];
24031     },
24032
24033     // private
24034     beforeFx : function(){
24035         this.beforeAction();
24036         return Roo.Layer.superclass.beforeFx.apply(this, arguments);
24037     },
24038
24039     // private
24040     afterFx : function(){
24041         Roo.Layer.superclass.afterFx.apply(this, arguments);
24042         this.sync(this.isVisible());
24043     },
24044
24045     // private
24046     beforeAction : function(){
24047         if(!this.updating && this.shadow){
24048             this.shadow.hide();
24049         }
24050     },
24051
24052     // overridden Element method
24053     setLeft : function(left){
24054         this.storeLeftTop(left, this.getTop(true));
24055         supr.setLeft.apply(this, arguments);
24056         this.sync();
24057     },
24058
24059     setTop : function(top){
24060         this.storeLeftTop(this.getLeft(true), top);
24061         supr.setTop.apply(this, arguments);
24062         this.sync();
24063     },
24064
24065     setLeftTop : function(left, top){
24066         this.storeLeftTop(left, top);
24067         supr.setLeftTop.apply(this, arguments);
24068         this.sync();
24069     },
24070
24071     setXY : function(xy, a, d, c, e){
24072         this.fixDisplay();
24073         this.beforeAction();
24074         this.storeXY(xy);
24075         var cb = this.createCB(c);
24076         supr.setXY.call(this, xy, a, d, cb, e);
24077         if(!a){
24078             cb();
24079         }
24080     },
24081
24082     // private
24083     createCB : function(c){
24084         var el = this;
24085         return function(){
24086             el.constrainXY();
24087             el.sync(true);
24088             if(c){
24089                 c();
24090             }
24091         };
24092     },
24093
24094     // overridden Element method
24095     setX : function(x, a, d, c, e){
24096         this.setXY([x, this.getY()], a, d, c, e);
24097     },
24098
24099     // overridden Element method
24100     setY : function(y, a, d, c, e){
24101         this.setXY([this.getX(), y], a, d, c, e);
24102     },
24103
24104     // overridden Element method
24105     setSize : function(w, h, a, d, c, e){
24106         this.beforeAction();
24107         var cb = this.createCB(c);
24108         supr.setSize.call(this, w, h, a, d, cb, e);
24109         if(!a){
24110             cb();
24111         }
24112     },
24113
24114     // overridden Element method
24115     setWidth : function(w, a, d, c, e){
24116         this.beforeAction();
24117         var cb = this.createCB(c);
24118         supr.setWidth.call(this, w, a, d, cb, e);
24119         if(!a){
24120             cb();
24121         }
24122     },
24123
24124     // overridden Element method
24125     setHeight : function(h, a, d, c, e){
24126         this.beforeAction();
24127         var cb = this.createCB(c);
24128         supr.setHeight.call(this, h, a, d, cb, e);
24129         if(!a){
24130             cb();
24131         }
24132     },
24133
24134     // overridden Element method
24135     setBounds : function(x, y, w, h, a, d, c, e){
24136         this.beforeAction();
24137         var cb = this.createCB(c);
24138         if(!a){
24139             this.storeXY([x, y]);
24140             supr.setXY.call(this, [x, y]);
24141             supr.setSize.call(this, w, h, a, d, cb, e);
24142             cb();
24143         }else{
24144             supr.setBounds.call(this, x, y, w, h, a, d, cb, e);
24145         }
24146         return this;
24147     },
24148     
24149     /**
24150      * Sets the z-index of this layer and adjusts any shadow and shim z-indexes. The layer z-index is automatically
24151      * incremented by two more than the value passed in so that it always shows above any shadow or shim (the shadow
24152      * element, if any, will be assigned z-index + 1, and the shim element, if any, will be assigned the unmodified z-index).
24153      * @param {Number} zindex The new z-index to set
24154      * @return {this} The Layer
24155      */
24156     setZIndex : function(zindex){
24157         this.zindex = zindex;
24158         this.setStyle("z-index", zindex + 2);
24159         if(this.shadow){
24160             this.shadow.setZIndex(zindex + 1);
24161         }
24162         if(this.shim){
24163             this.shim.setStyle("z-index", zindex);
24164         }
24165     }
24166 });
24167 })();/*
24168  * Based on:
24169  * Ext JS Library 1.1.1
24170  * Copyright(c) 2006-2007, Ext JS, LLC.
24171  *
24172  * Originally Released Under LGPL - original licence link has changed is not relivant.
24173  *
24174  * Fork - LGPL
24175  * <script type="text/javascript">
24176  */
24177
24178
24179 /**
24180  * @class Roo.Shadow
24181  * Simple class that can provide a shadow effect for any element.  Note that the element MUST be absolutely positioned,
24182  * and the shadow does not provide any shimming.  This should be used only in simple cases -- for more advanced
24183  * functionality that can also provide the same shadow effect, see the {@link Roo.Layer} class.
24184  * @constructor
24185  * Create a new Shadow
24186  * @param {Object} config The config object
24187  */
24188 Roo.Shadow = function(config){
24189     Roo.apply(this, config);
24190     if(typeof this.mode != "string"){
24191         this.mode = this.defaultMode;
24192     }
24193     var o = this.offset, a = {h: 0};
24194     var rad = Math.floor(this.offset/2);
24195     switch(this.mode.toLowerCase()){ // all this hideous nonsense calculates the various offsets for shadows
24196         case "drop":
24197             a.w = 0;
24198             a.l = a.t = o;
24199             a.t -= 1;
24200             if(Roo.isIE){
24201                 a.l -= this.offset + rad;
24202                 a.t -= this.offset + rad;
24203                 a.w -= rad;
24204                 a.h -= rad;
24205                 a.t += 1;
24206             }
24207         break;
24208         case "sides":
24209             a.w = (o*2);
24210             a.l = -o;
24211             a.t = o-1;
24212             if(Roo.isIE){
24213                 a.l -= (this.offset - rad);
24214                 a.t -= this.offset + rad;
24215                 a.l += 1;
24216                 a.w -= (this.offset - rad)*2;
24217                 a.w -= rad + 1;
24218                 a.h -= 1;
24219             }
24220         break;
24221         case "frame":
24222             a.w = a.h = (o*2);
24223             a.l = a.t = -o;
24224             a.t += 1;
24225             a.h -= 2;
24226             if(Roo.isIE){
24227                 a.l -= (this.offset - rad);
24228                 a.t -= (this.offset - rad);
24229                 a.l += 1;
24230                 a.w -= (this.offset + rad + 1);
24231                 a.h -= (this.offset + rad);
24232                 a.h += 1;
24233             }
24234         break;
24235     };
24236
24237     this.adjusts = a;
24238 };
24239
24240 Roo.Shadow.prototype = {
24241     /**
24242      * @cfg {String} mode
24243      * The shadow display mode.  Supports the following options:<br />
24244      * sides: Shadow displays on both sides and bottom only<br />
24245      * frame: Shadow displays equally on all four sides<br />
24246      * drop: Traditional bottom-right drop shadow (default)
24247      */
24248     /**
24249      * @cfg {String} offset
24250      * The number of pixels to offset the shadow from the element (defaults to 4)
24251      */
24252     offset: 4,
24253
24254     // private
24255     defaultMode: "drop",
24256
24257     /**
24258      * Displays the shadow under the target element
24259      * @param {String/HTMLElement/Element} targetEl The id or element under which the shadow should display
24260      */
24261     show : function(target){
24262         target = Roo.get(target);
24263         if(!this.el){
24264             this.el = Roo.Shadow.Pool.pull();
24265             if(this.el.dom.nextSibling != target.dom){
24266                 this.el.insertBefore(target);
24267             }
24268         }
24269         this.el.setStyle("z-index", this.zIndex || parseInt(target.getStyle("z-index"), 10)-1);
24270         if(Roo.isIE){
24271             this.el.dom.style.filter="progid:DXImageTransform.Microsoft.alpha(opacity=50) progid:DXImageTransform.Microsoft.Blur(pixelradius="+(this.offset)+")";
24272         }
24273         this.realign(
24274             target.getLeft(true),
24275             target.getTop(true),
24276             target.getWidth(),
24277             target.getHeight()
24278         );
24279         this.el.dom.style.display = "block";
24280     },
24281
24282     /**
24283      * Returns true if the shadow is visible, else false
24284      */
24285     isVisible : function(){
24286         return this.el ? true : false;  
24287     },
24288
24289     /**
24290      * Direct alignment when values are already available. Show must be called at least once before
24291      * calling this method to ensure it is initialized.
24292      * @param {Number} left The target element left position
24293      * @param {Number} top The target element top position
24294      * @param {Number} width The target element width
24295      * @param {Number} height The target element height
24296      */
24297     realign : function(l, t, w, h){
24298         if(!this.el){
24299             return;
24300         }
24301         var a = this.adjusts, d = this.el.dom, s = d.style;
24302         var iea = 0;
24303         s.left = (l+a.l)+"px";
24304         s.top = (t+a.t)+"px";
24305         var sw = (w+a.w), sh = (h+a.h), sws = sw +"px", shs = sh + "px";
24306  
24307         if(s.width != sws || s.height != shs){
24308             s.width = sws;
24309             s.height = shs;
24310             if(!Roo.isIE){
24311                 var cn = d.childNodes;
24312                 var sww = Math.max(0, (sw-12))+"px";
24313                 cn[0].childNodes[1].style.width = sww;
24314                 cn[1].childNodes[1].style.width = sww;
24315                 cn[2].childNodes[1].style.width = sww;
24316                 cn[1].style.height = Math.max(0, (sh-12))+"px";
24317             }
24318         }
24319     },
24320
24321     /**
24322      * Hides this shadow
24323      */
24324     hide : function(){
24325         if(this.el){
24326             this.el.dom.style.display = "none";
24327             Roo.Shadow.Pool.push(this.el);
24328             delete this.el;
24329         }
24330     },
24331
24332     /**
24333      * Adjust the z-index of this shadow
24334      * @param {Number} zindex The new z-index
24335      */
24336     setZIndex : function(z){
24337         this.zIndex = z;
24338         if(this.el){
24339             this.el.setStyle("z-index", z);
24340         }
24341     }
24342 };
24343
24344 // Private utility class that manages the internal Shadow cache
24345 Roo.Shadow.Pool = function(){
24346     var p = [];
24347     var markup = Roo.isIE ?
24348                  '<div class="x-ie-shadow"></div>' :
24349                  '<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>';
24350     return {
24351         pull : function(){
24352             var sh = p.shift();
24353             if(!sh){
24354                 sh = Roo.get(Roo.DomHelper.insertHtml("beforeBegin", document.body.firstChild, markup));
24355                 sh.autoBoxAdjust = false;
24356             }
24357             return sh;
24358         },
24359
24360         push : function(sh){
24361             p.push(sh);
24362         }
24363     };
24364 }();/*
24365  * Based on:
24366  * Ext JS Library 1.1.1
24367  * Copyright(c) 2006-2007, Ext JS, LLC.
24368  *
24369  * Originally Released Under LGPL - original licence link has changed is not relivant.
24370  *
24371  * Fork - LGPL
24372  * <script type="text/javascript">
24373  */
24374
24375
24376 /**
24377  * @class Roo.SplitBar
24378  * @extends Roo.util.Observable
24379  * Creates draggable splitter bar functionality from two elements (element to be dragged and element to be resized).
24380  * <br><br>
24381  * Usage:
24382  * <pre><code>
24383 var split = new Roo.SplitBar("elementToDrag", "elementToSize",
24384                    Roo.SplitBar.HORIZONTAL, Roo.SplitBar.LEFT);
24385 split.setAdapter(new Roo.SplitBar.AbsoluteLayoutAdapter("container"));
24386 split.minSize = 100;
24387 split.maxSize = 600;
24388 split.animate = true;
24389 split.on('moved', splitterMoved);
24390 </code></pre>
24391  * @constructor
24392  * Create a new SplitBar
24393  * @param {String/HTMLElement/Roo.Element} dragElement The element to be dragged and act as the SplitBar. 
24394  * @param {String/HTMLElement/Roo.Element} resizingElement The element to be resized based on where the SplitBar element is dragged 
24395  * @param {Number} orientation (optional) Either Roo.SplitBar.HORIZONTAL or Roo.SplitBar.VERTICAL. (Defaults to HORIZONTAL)
24396  * @param {Number} placement (optional) Either Roo.SplitBar.LEFT or Roo.SplitBar.RIGHT for horizontal or  
24397                         Roo.SplitBar.TOP or Roo.SplitBar.BOTTOM for vertical. (By default, this is determined automatically by the initial
24398                         position of the SplitBar).
24399  */
24400 Roo.SplitBar = function(dragElement, resizingElement, orientation, placement, existingProxy){
24401     
24402     /** @private */
24403     this.el = Roo.get(dragElement, true);
24404     this.el.dom.unselectable = "on";
24405     /** @private */
24406     this.resizingEl = Roo.get(resizingElement, true);
24407
24408     /**
24409      * @private
24410      * The orientation of the split. Either Roo.SplitBar.HORIZONTAL or Roo.SplitBar.VERTICAL. (Defaults to HORIZONTAL)
24411      * Note: If this is changed after creating the SplitBar, the placement property must be manually updated
24412      * @type Number
24413      */
24414     this.orientation = orientation || Roo.SplitBar.HORIZONTAL;
24415     
24416     /**
24417      * The minimum size of the resizing element. (Defaults to 0)
24418      * @type Number
24419      */
24420     this.minSize = 0;
24421     
24422     /**
24423      * The maximum size of the resizing element. (Defaults to 2000)
24424      * @type Number
24425      */
24426     this.maxSize = 2000;
24427     
24428     /**
24429      * Whether to animate the transition to the new size
24430      * @type Boolean
24431      */
24432     this.animate = false;
24433     
24434     /**
24435      * Whether to create a transparent shim that overlays the page when dragging, enables dragging across iframes.
24436      * @type Boolean
24437      */
24438     this.useShim = false;
24439     
24440     /** @private */
24441     this.shim = null;
24442     
24443     if(!existingProxy){
24444         /** @private */
24445         this.proxy = Roo.SplitBar.createProxy(this.orientation);
24446     }else{
24447         this.proxy = Roo.get(existingProxy).dom;
24448     }
24449     /** @private */
24450     this.dd = new Roo.dd.DDProxy(this.el.dom.id, "XSplitBars", {dragElId : this.proxy.id});
24451     
24452     /** @private */
24453     this.dd.b4StartDrag = this.onStartProxyDrag.createDelegate(this);
24454     
24455     /** @private */
24456     this.dd.endDrag = this.onEndProxyDrag.createDelegate(this);
24457     
24458     /** @private */
24459     this.dragSpecs = {};
24460     
24461     /**
24462      * @private The adapter to use to positon and resize elements
24463      */
24464     this.adapter = new Roo.SplitBar.BasicLayoutAdapter();
24465     this.adapter.init(this);
24466     
24467     if(this.orientation == Roo.SplitBar.HORIZONTAL){
24468         /** @private */
24469         this.placement = placement || (this.el.getX() > this.resizingEl.getX() ? Roo.SplitBar.LEFT : Roo.SplitBar.RIGHT);
24470         this.el.addClass("x-splitbar-h");
24471     }else{
24472         /** @private */
24473         this.placement = placement || (this.el.getY() > this.resizingEl.getY() ? Roo.SplitBar.TOP : Roo.SplitBar.BOTTOM);
24474         this.el.addClass("x-splitbar-v");
24475     }
24476     
24477     this.addEvents({
24478         /**
24479          * @event resize
24480          * Fires when the splitter is moved (alias for {@link #event-moved})
24481          * @param {Roo.SplitBar} this
24482          * @param {Number} newSize the new width or height
24483          */
24484         "resize" : true,
24485         /**
24486          * @event moved
24487          * Fires when the splitter is moved
24488          * @param {Roo.SplitBar} this
24489          * @param {Number} newSize the new width or height
24490          */
24491         "moved" : true,
24492         /**
24493          * @event beforeresize
24494          * Fires before the splitter is dragged
24495          * @param {Roo.SplitBar} this
24496          */
24497         "beforeresize" : true,
24498
24499         "beforeapply" : true
24500     });
24501
24502     Roo.util.Observable.call(this);
24503 };
24504
24505 Roo.extend(Roo.SplitBar, Roo.util.Observable, {
24506     onStartProxyDrag : function(x, y){
24507         this.fireEvent("beforeresize", this);
24508         if(!this.overlay){
24509             var o = Roo.DomHelper.insertFirst(document.body,  {cls: "x-drag-overlay", html: "&#160;"}, true);
24510             o.unselectable();
24511             o.enableDisplayMode("block");
24512             // all splitbars share the same overlay
24513             Roo.SplitBar.prototype.overlay = o;
24514         }
24515         this.overlay.setSize(Roo.lib.Dom.getViewWidth(true), Roo.lib.Dom.getViewHeight(true));
24516         this.overlay.show();
24517         Roo.get(this.proxy).setDisplayed("block");
24518         var size = this.adapter.getElementSize(this);
24519         this.activeMinSize = this.getMinimumSize();;
24520         this.activeMaxSize = this.getMaximumSize();;
24521         var c1 = size - this.activeMinSize;
24522         var c2 = Math.max(this.activeMaxSize - size, 0);
24523         if(this.orientation == Roo.SplitBar.HORIZONTAL){
24524             this.dd.resetConstraints();
24525             this.dd.setXConstraint(
24526                 this.placement == Roo.SplitBar.LEFT ? c1 : c2, 
24527                 this.placement == Roo.SplitBar.LEFT ? c2 : c1
24528             );
24529             this.dd.setYConstraint(0, 0);
24530         }else{
24531             this.dd.resetConstraints();
24532             this.dd.setXConstraint(0, 0);
24533             this.dd.setYConstraint(
24534                 this.placement == Roo.SplitBar.TOP ? c1 : c2, 
24535                 this.placement == Roo.SplitBar.TOP ? c2 : c1
24536             );
24537          }
24538         this.dragSpecs.startSize = size;
24539         this.dragSpecs.startPoint = [x, y];
24540         Roo.dd.DDProxy.prototype.b4StartDrag.call(this.dd, x, y);
24541     },
24542     
24543     /** 
24544      * @private Called after the drag operation by the DDProxy
24545      */
24546     onEndProxyDrag : function(e){
24547         Roo.get(this.proxy).setDisplayed(false);
24548         var endPoint = Roo.lib.Event.getXY(e);
24549         if(this.overlay){
24550             this.overlay.hide();
24551         }
24552         var newSize;
24553         if(this.orientation == Roo.SplitBar.HORIZONTAL){
24554             newSize = this.dragSpecs.startSize + 
24555                 (this.placement == Roo.SplitBar.LEFT ?
24556                     endPoint[0] - this.dragSpecs.startPoint[0] :
24557                     this.dragSpecs.startPoint[0] - endPoint[0]
24558                 );
24559         }else{
24560             newSize = this.dragSpecs.startSize + 
24561                 (this.placement == Roo.SplitBar.TOP ?
24562                     endPoint[1] - this.dragSpecs.startPoint[1] :
24563                     this.dragSpecs.startPoint[1] - endPoint[1]
24564                 );
24565         }
24566         newSize = Math.min(Math.max(newSize, this.activeMinSize), this.activeMaxSize);
24567         if(newSize != this.dragSpecs.startSize){
24568             if(this.fireEvent('beforeapply', this, newSize) !== false){
24569                 this.adapter.setElementSize(this, newSize);
24570                 this.fireEvent("moved", this, newSize);
24571                 this.fireEvent("resize", this, newSize);
24572             }
24573         }
24574     },
24575     
24576     /**
24577      * Get the adapter this SplitBar uses
24578      * @return The adapter object
24579      */
24580     getAdapter : function(){
24581         return this.adapter;
24582     },
24583     
24584     /**
24585      * Set the adapter this SplitBar uses
24586      * @param {Object} adapter A SplitBar adapter object
24587      */
24588     setAdapter : function(adapter){
24589         this.adapter = adapter;
24590         this.adapter.init(this);
24591     },
24592     
24593     /**
24594      * Gets the minimum size for the resizing element
24595      * @return {Number} The minimum size
24596      */
24597     getMinimumSize : function(){
24598         return this.minSize;
24599     },
24600     
24601     /**
24602      * Sets the minimum size for the resizing element
24603      * @param {Number} minSize The minimum size
24604      */
24605     setMinimumSize : function(minSize){
24606         this.minSize = minSize;
24607     },
24608     
24609     /**
24610      * Gets the maximum size for the resizing element
24611      * @return {Number} The maximum size
24612      */
24613     getMaximumSize : function(){
24614         return this.maxSize;
24615     },
24616     
24617     /**
24618      * Sets the maximum size for the resizing element
24619      * @param {Number} maxSize The maximum size
24620      */
24621     setMaximumSize : function(maxSize){
24622         this.maxSize = maxSize;
24623     },
24624     
24625     /**
24626      * Sets the initialize size for the resizing element
24627      * @param {Number} size The initial size
24628      */
24629     setCurrentSize : function(size){
24630         var oldAnimate = this.animate;
24631         this.animate = false;
24632         this.adapter.setElementSize(this, size);
24633         this.animate = oldAnimate;
24634     },
24635     
24636     /**
24637      * Destroy this splitbar. 
24638      * @param {Boolean} removeEl True to remove the element
24639      */
24640     destroy : function(removeEl){
24641         if(this.shim){
24642             this.shim.remove();
24643         }
24644         this.dd.unreg();
24645         this.proxy.parentNode.removeChild(this.proxy);
24646         if(removeEl){
24647             this.el.remove();
24648         }
24649     }
24650 });
24651
24652 /**
24653  * @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.
24654  */
24655 Roo.SplitBar.createProxy = function(dir){
24656     var proxy = new Roo.Element(document.createElement("div"));
24657     proxy.unselectable();
24658     var cls = 'x-splitbar-proxy';
24659     proxy.addClass(cls + ' ' + (dir == Roo.SplitBar.HORIZONTAL ? cls +'-h' : cls + '-v'));
24660     document.body.appendChild(proxy.dom);
24661     return proxy.dom;
24662 };
24663
24664 /** 
24665  * @class Roo.SplitBar.BasicLayoutAdapter
24666  * Default Adapter. It assumes the splitter and resizing element are not positioned
24667  * elements and only gets/sets the width of the element. Generally used for table based layouts.
24668  */
24669 Roo.SplitBar.BasicLayoutAdapter = function(){
24670 };
24671
24672 Roo.SplitBar.BasicLayoutAdapter.prototype = {
24673     // do nothing for now
24674     init : function(s){
24675     
24676     },
24677     /**
24678      * Called before drag operations to get the current size of the resizing element. 
24679      * @param {Roo.SplitBar} s The SplitBar using this adapter
24680      */
24681      getElementSize : function(s){
24682         if(s.orientation == Roo.SplitBar.HORIZONTAL){
24683             return s.resizingEl.getWidth();
24684         }else{
24685             return s.resizingEl.getHeight();
24686         }
24687     },
24688     
24689     /**
24690      * Called after drag operations to set the size of the resizing element.
24691      * @param {Roo.SplitBar} s The SplitBar using this adapter
24692      * @param {Number} newSize The new size to set
24693      * @param {Function} onComplete A function to be invoked when resizing is complete
24694      */
24695     setElementSize : function(s, newSize, onComplete){
24696         if(s.orientation == Roo.SplitBar.HORIZONTAL){
24697             if(!s.animate){
24698                 s.resizingEl.setWidth(newSize);
24699                 if(onComplete){
24700                     onComplete(s, newSize);
24701                 }
24702             }else{
24703                 s.resizingEl.setWidth(newSize, true, .1, onComplete, 'easeOut');
24704             }
24705         }else{
24706             
24707             if(!s.animate){
24708                 s.resizingEl.setHeight(newSize);
24709                 if(onComplete){
24710                     onComplete(s, newSize);
24711                 }
24712             }else{
24713                 s.resizingEl.setHeight(newSize, true, .1, onComplete, 'easeOut');
24714             }
24715         }
24716     }
24717 };
24718
24719 /** 
24720  *@class Roo.SplitBar.AbsoluteLayoutAdapter
24721  * @extends Roo.SplitBar.BasicLayoutAdapter
24722  * Adapter that  moves the splitter element to align with the resized sizing element. 
24723  * Used with an absolute positioned SplitBar.
24724  * @param {String/HTMLElement/Roo.Element} container The container that wraps around the absolute positioned content. If it's
24725  * document.body, make sure you assign an id to the body element.
24726  */
24727 Roo.SplitBar.AbsoluteLayoutAdapter = function(container){
24728     this.basic = new Roo.SplitBar.BasicLayoutAdapter();
24729     this.container = Roo.get(container);
24730 };
24731
24732 Roo.SplitBar.AbsoluteLayoutAdapter.prototype = {
24733     init : function(s){
24734         this.basic.init(s);
24735     },
24736     
24737     getElementSize : function(s){
24738         return this.basic.getElementSize(s);
24739     },
24740     
24741     setElementSize : function(s, newSize, onComplete){
24742         this.basic.setElementSize(s, newSize, this.moveSplitter.createDelegate(this, [s]));
24743     },
24744     
24745     moveSplitter : function(s){
24746         var yes = Roo.SplitBar;
24747         switch(s.placement){
24748             case yes.LEFT:
24749                 s.el.setX(s.resizingEl.getRight());
24750                 break;
24751             case yes.RIGHT:
24752                 s.el.setStyle("right", (this.container.getWidth() - s.resizingEl.getLeft()) + "px");
24753                 break;
24754             case yes.TOP:
24755                 s.el.setY(s.resizingEl.getBottom());
24756                 break;
24757             case yes.BOTTOM:
24758                 s.el.setY(s.resizingEl.getTop() - s.el.getHeight());
24759                 break;
24760         }
24761     }
24762 };
24763
24764 /**
24765  * Orientation constant - Create a vertical SplitBar
24766  * @static
24767  * @type Number
24768  */
24769 Roo.SplitBar.VERTICAL = 1;
24770
24771 /**
24772  * Orientation constant - Create a horizontal SplitBar
24773  * @static
24774  * @type Number
24775  */
24776 Roo.SplitBar.HORIZONTAL = 2;
24777
24778 /**
24779  * Placement constant - The resizing element is to the left of the splitter element
24780  * @static
24781  * @type Number
24782  */
24783 Roo.SplitBar.LEFT = 1;
24784
24785 /**
24786  * Placement constant - The resizing element is to the right of the splitter element
24787  * @static
24788  * @type Number
24789  */
24790 Roo.SplitBar.RIGHT = 2;
24791
24792 /**
24793  * Placement constant - The resizing element is positioned above the splitter element
24794  * @static
24795  * @type Number
24796  */
24797 Roo.SplitBar.TOP = 3;
24798
24799 /**
24800  * Placement constant - The resizing element is positioned under splitter element
24801  * @static
24802  * @type Number
24803  */
24804 Roo.SplitBar.BOTTOM = 4;
24805 /*
24806  * Based on:
24807  * Ext JS Library 1.1.1
24808  * Copyright(c) 2006-2007, Ext JS, LLC.
24809  *
24810  * Originally Released Under LGPL - original licence link has changed is not relivant.
24811  *
24812  * Fork - LGPL
24813  * <script type="text/javascript">
24814  */
24815
24816 /**
24817  * @class Roo.View
24818  * @extends Roo.util.Observable
24819  * Create a "View" for an element based on a data model or UpdateManager and the supplied DomHelper template. 
24820  * This class also supports single and multi selection modes. <br>
24821  * Create a data model bound view:
24822  <pre><code>
24823  var store = new Roo.data.Store(...);
24824
24825  var view = new Roo.View({
24826     el : "my-element",
24827     tpl : '&lt;div id="{0}"&gt;{2} - {1}&lt;/div&gt;', // auto create template
24828  
24829     singleSelect: true,
24830     selectedClass: "ydataview-selected",
24831     store: store
24832  });
24833
24834  // listen for node click?
24835  view.on("click", function(vw, index, node, e){
24836  alert('Node "' + node.id + '" at index: ' + index + " was clicked.");
24837  });
24838
24839  // load XML data
24840  dataModel.load("foobar.xml");
24841  </code></pre>
24842  For an example of creating a JSON/UpdateManager view, see {@link Roo.JsonView}.
24843  * <br><br>
24844  * <b>Note: The root of your template must be a single node. Table/row implementations may work but are not supported due to
24845  * IE"s limited insertion support with tables and Opera"s faulty event bubbling.</b>
24846  * 
24847  * Note: old style constructor is still suported (container, template, config)
24848  * 
24849  * @constructor
24850  * Create a new View
24851  * @param {Object} config The config object
24852  * 
24853  */
24854 Roo.View = function(config, depreciated_tpl, depreciated_config){
24855     
24856     this.parent = false;
24857     
24858     if (typeof(depreciated_tpl) == 'undefined') {
24859         // new way.. - universal constructor.
24860         Roo.apply(this, config);
24861         this.el  = Roo.get(this.el);
24862     } else {
24863         // old format..
24864         this.el  = Roo.get(config);
24865         this.tpl = depreciated_tpl;
24866         Roo.apply(this, depreciated_config);
24867     }
24868     this.wrapEl  = this.el.wrap().wrap();
24869     ///this.el = this.wrapEla.appendChild(document.createElement("div"));
24870     
24871     
24872     if(typeof(this.tpl) == "string"){
24873         this.tpl = new Roo.Template(this.tpl);
24874     } else {
24875         // support xtype ctors..
24876         this.tpl = new Roo.factory(this.tpl, Roo);
24877     }
24878     
24879     
24880     this.tpl.compile();
24881     
24882     /** @private */
24883     this.addEvents({
24884         /**
24885          * @event beforeclick
24886          * Fires before a click is processed. Returns false to cancel the default action.
24887          * @param {Roo.View} this
24888          * @param {Number} index The index of the target node
24889          * @param {HTMLElement} node The target node
24890          * @param {Roo.EventObject} e The raw event object
24891          */
24892             "beforeclick" : true,
24893         /**
24894          * @event click
24895          * Fires when a template node is clicked.
24896          * @param {Roo.View} this
24897          * @param {Number} index The index of the target node
24898          * @param {HTMLElement} node The target node
24899          * @param {Roo.EventObject} e The raw event object
24900          */
24901             "click" : true,
24902         /**
24903          * @event dblclick
24904          * Fires when a template node is double clicked.
24905          * @param {Roo.View} this
24906          * @param {Number} index The index of the target node
24907          * @param {HTMLElement} node The target node
24908          * @param {Roo.EventObject} e The raw event object
24909          */
24910             "dblclick" : true,
24911         /**
24912          * @event contextmenu
24913          * Fires when a template node is right clicked.
24914          * @param {Roo.View} this
24915          * @param {Number} index The index of the target node
24916          * @param {HTMLElement} node The target node
24917          * @param {Roo.EventObject} e The raw event object
24918          */
24919             "contextmenu" : true,
24920         /**
24921          * @event selectionchange
24922          * Fires when the selected nodes change.
24923          * @param {Roo.View} this
24924          * @param {Array} selections Array of the selected nodes
24925          */
24926             "selectionchange" : true,
24927     
24928         /**
24929          * @event beforeselect
24930          * Fires before a selection is made. If any handlers return false, the selection is cancelled.
24931          * @param {Roo.View} this
24932          * @param {HTMLElement} node The node to be selected
24933          * @param {Array} selections Array of currently selected nodes
24934          */
24935             "beforeselect" : true,
24936         /**
24937          * @event preparedata
24938          * Fires on every row to render, to allow you to change the data.
24939          * @param {Roo.View} this
24940          * @param {Object} data to be rendered (change this)
24941          */
24942           "preparedata" : true
24943           
24944           
24945         });
24946
24947
24948
24949     this.el.on({
24950         "click": this.onClick,
24951         "dblclick": this.onDblClick,
24952         "contextmenu": this.onContextMenu,
24953         scope:this
24954     });
24955
24956     this.selections = [];
24957     this.nodes = [];
24958     this.cmp = new Roo.CompositeElementLite([]);
24959     if(this.store){
24960         this.store = Roo.factory(this.store, Roo.data);
24961         this.setStore(this.store, true);
24962     }
24963     
24964     if ( this.footer && this.footer.xtype) {
24965            
24966          var fctr = this.wrapEl.appendChild(document.createElement("div"));
24967         
24968         this.footer.dataSource = this.store
24969         this.footer.container = fctr;
24970         this.footer = Roo.factory(this.footer, Roo);
24971         fctr.insertFirst(this.el);
24972         
24973         // this is a bit insane - as the paging toolbar seems to detach the el..
24974 //        dom.parentNode.parentNode.parentNode
24975          // they get detached?
24976     }
24977     
24978     
24979     Roo.View.superclass.constructor.call(this);
24980     
24981     
24982 };
24983
24984 Roo.extend(Roo.View, Roo.util.Observable, {
24985     
24986      /**
24987      * @cfg {Roo.data.Store} store Data store to load data from.
24988      */
24989     store : false,
24990     
24991     /**
24992      * @cfg {String|Roo.Element} el The container element.
24993      */
24994     el : '',
24995     
24996     /**
24997      * @cfg {String|Roo.Template} tpl The template used by this View 
24998      */
24999     tpl : false,
25000     /**
25001      * @cfg {String} dataName the named area of the template to use as the data area
25002      *                          Works with domtemplates roo-name="name"
25003      */
25004     dataName: false,
25005     /**
25006      * @cfg {String} selectedClass The css class to add to selected nodes
25007      */
25008     selectedClass : "x-view-selected",
25009      /**
25010      * @cfg {String} emptyText The empty text to show when nothing is loaded.
25011      */
25012     emptyText : "",
25013     
25014     /**
25015      * @cfg {String} text to display on mask (default Loading)
25016      */
25017     mask : false,
25018     /**
25019      * @cfg {Boolean} multiSelect Allow multiple selection
25020      */
25021     multiSelect : false,
25022     /**
25023      * @cfg {Boolean} singleSelect Allow single selection
25024      */
25025     singleSelect:  false,
25026     
25027     /**
25028      * @cfg {Boolean} toggleSelect - selecting 
25029      */
25030     toggleSelect : false,
25031     
25032     /**
25033      * @cfg {Boolean} tickable - selecting 
25034      */
25035     tickable : false,
25036     
25037     /**
25038      * Returns the element this view is bound to.
25039      * @return {Roo.Element}
25040      */
25041     getEl : function(){
25042         return this.wrapEl;
25043     },
25044     
25045     
25046
25047     /**
25048      * Refreshes the view. - called by datachanged on the store. - do not call directly.
25049      */
25050     refresh : function(){
25051         Roo.log('refresh');
25052         var t = this.tpl;
25053         
25054         // if we are using something like 'domtemplate', then
25055         // the what gets used is:
25056         // t.applySubtemplate(NAME, data, wrapping data..)
25057         // the outer template then get' applied with
25058         //     the store 'extra data'
25059         // and the body get's added to the
25060         //      roo-name="data" node?
25061         //      <span class='roo-tpl-{name}'></span> ?????
25062         
25063         
25064         
25065         this.clearSelections();
25066         this.el.update("");
25067         var html = [];
25068         var records = this.store.getRange();
25069         if(records.length < 1) {
25070             
25071             // is this valid??  = should it render a template??
25072             
25073             this.el.update(this.emptyText);
25074             return;
25075         }
25076         var el = this.el;
25077         if (this.dataName) {
25078             this.el.update(t.apply(this.store.meta)); //????
25079             el = this.el.child('.roo-tpl-' + this.dataName);
25080         }
25081         
25082         for(var i = 0, len = records.length; i < len; i++){
25083             var data = this.prepareData(records[i].data, i, records[i]);
25084             this.fireEvent("preparedata", this, data, i, records[i]);
25085             
25086             var d = Roo.apply({}, data);
25087             
25088             if(this.tickable){
25089                 Roo.apply(d, {'roo-id' : Roo.id()});
25090                 
25091                 var _this = this;
25092             
25093                 Roo.each(this.parent.item, function(item){
25094                     if(item[_this.parent.valueField] != data[_this.parent.valueField]){
25095                         return;
25096                     }
25097                     Roo.apply(d, {'roo-data-checked' : 'checked'});
25098                 });
25099             }
25100             
25101             html[html.length] = Roo.util.Format.trim(
25102                 this.dataName ?
25103                     t.applySubtemplate(this.dataName, d, this.store.meta) :
25104                     t.apply(d)
25105             );
25106         }
25107         
25108         
25109         
25110         el.update(html.join(""));
25111         this.nodes = el.dom.childNodes;
25112         this.updateIndexes(0);
25113     },
25114     
25115
25116     /**
25117      * Function to override to reformat the data that is sent to
25118      * the template for each node.
25119      * DEPRICATED - use the preparedata event handler.
25120      * @param {Array/Object} data The raw data (array of colData for a data model bound view or
25121      * a JSON object for an UpdateManager bound view).
25122      */
25123     prepareData : function(data, index, record)
25124     {
25125         this.fireEvent("preparedata", this, data, index, record);
25126         return data;
25127     },
25128
25129     onUpdate : function(ds, record){
25130          Roo.log('on update');   
25131         this.clearSelections();
25132         var index = this.store.indexOf(record);
25133         var n = this.nodes[index];
25134         this.tpl.insertBefore(n, this.prepareData(record.data, index, record));
25135         n.parentNode.removeChild(n);
25136         this.updateIndexes(index, index);
25137     },
25138
25139     
25140     
25141 // --------- FIXME     
25142     onAdd : function(ds, records, index)
25143     {
25144         Roo.log(['on Add', ds, records, index] );        
25145         this.clearSelections();
25146         if(this.nodes.length == 0){
25147             this.refresh();
25148             return;
25149         }
25150         var n = this.nodes[index];
25151         for(var i = 0, len = records.length; i < len; i++){
25152             var d = this.prepareData(records[i].data, i, records[i]);
25153             if(n){
25154                 this.tpl.insertBefore(n, d);
25155             }else{
25156                 
25157                 this.tpl.append(this.el, d);
25158             }
25159         }
25160         this.updateIndexes(index);
25161     },
25162
25163     onRemove : function(ds, record, index){
25164         Roo.log('onRemove');
25165         this.clearSelections();
25166         var el = this.dataName  ?
25167             this.el.child('.roo-tpl-' + this.dataName) :
25168             this.el; 
25169         
25170         el.dom.removeChild(this.nodes[index]);
25171         this.updateIndexes(index);
25172     },
25173
25174     /**
25175      * Refresh an individual node.
25176      * @param {Number} index
25177      */
25178     refreshNode : function(index){
25179         this.onUpdate(this.store, this.store.getAt(index));
25180     },
25181
25182     updateIndexes : function(startIndex, endIndex){
25183         var ns = this.nodes;
25184         startIndex = startIndex || 0;
25185         endIndex = endIndex || ns.length - 1;
25186         for(var i = startIndex; i <= endIndex; i++){
25187             ns[i].nodeIndex = i;
25188         }
25189     },
25190
25191     /**
25192      * Changes the data store this view uses and refresh the view.
25193      * @param {Store} store
25194      */
25195     setStore : function(store, initial){
25196         if(!initial && this.store){
25197             this.store.un("datachanged", this.refresh);
25198             this.store.un("add", this.onAdd);
25199             this.store.un("remove", this.onRemove);
25200             this.store.un("update", this.onUpdate);
25201             this.store.un("clear", this.refresh);
25202             this.store.un("beforeload", this.onBeforeLoad);
25203             this.store.un("load", this.onLoad);
25204             this.store.un("loadexception", this.onLoad);
25205         }
25206         if(store){
25207           
25208             store.on("datachanged", this.refresh, this);
25209             store.on("add", this.onAdd, this);
25210             store.on("remove", this.onRemove, this);
25211             store.on("update", this.onUpdate, this);
25212             store.on("clear", this.refresh, this);
25213             store.on("beforeload", this.onBeforeLoad, this);
25214             store.on("load", this.onLoad, this);
25215             store.on("loadexception", this.onLoad, this);
25216         }
25217         
25218         if(store){
25219             this.refresh();
25220         }
25221     },
25222     /**
25223      * onbeforeLoad - masks the loading area.
25224      *
25225      */
25226     onBeforeLoad : function(store,opts)
25227     {
25228          Roo.log('onBeforeLoad');   
25229         if (!opts.add) {
25230             this.el.update("");
25231         }
25232         this.el.mask(this.mask ? this.mask : "Loading" ); 
25233     },
25234     onLoad : function ()
25235     {
25236         this.el.unmask();
25237     },
25238     
25239
25240     /**
25241      * Returns the template node the passed child belongs to or null if it doesn't belong to one.
25242      * @param {HTMLElement} node
25243      * @return {HTMLElement} The template node
25244      */
25245     findItemFromChild : function(node){
25246         var el = this.dataName  ?
25247             this.el.child('.roo-tpl-' + this.dataName,true) :
25248             this.el.dom; 
25249         
25250         if(!node || node.parentNode == el){
25251                     return node;
25252             }
25253             var p = node.parentNode;
25254             while(p && p != el){
25255             if(p.parentNode == el){
25256                 return p;
25257             }
25258             p = p.parentNode;
25259         }
25260             return null;
25261     },
25262
25263     /** @ignore */
25264     onClick : function(e){
25265         var item = this.findItemFromChild(e.getTarget());
25266         if(item){
25267             var index = this.indexOf(item);
25268             if(this.onItemClick(item, index, e) !== false){
25269                 this.fireEvent("click", this, index, item, e);
25270             }
25271         }else{
25272             this.clearSelections();
25273         }
25274     },
25275
25276     /** @ignore */
25277     onContextMenu : function(e){
25278         var item = this.findItemFromChild(e.getTarget());
25279         if(item){
25280             this.fireEvent("contextmenu", this, this.indexOf(item), item, e);
25281         }
25282     },
25283
25284     /** @ignore */
25285     onDblClick : function(e){
25286         var item = this.findItemFromChild(e.getTarget());
25287         if(item){
25288             this.fireEvent("dblclick", this, this.indexOf(item), item, e);
25289         }
25290     },
25291
25292     onItemClick : function(item, index, e)
25293     {
25294         if(this.fireEvent("beforeclick", this, index, item, e) === false){
25295             return false;
25296         }
25297         if (this.toggleSelect) {
25298             var m = this.isSelected(item) ? 'unselect' : 'select';
25299             Roo.log(m);
25300             var _t = this;
25301             _t[m](item, true, false);
25302             return true;
25303         }
25304         if(this.multiSelect || this.singleSelect){
25305             if(this.multiSelect && e.shiftKey && this.lastSelection){
25306                 this.select(this.getNodes(this.indexOf(this.lastSelection), index), false);
25307             }else{
25308                 this.select(item, this.multiSelect && e.ctrlKey);
25309                 this.lastSelection = item;
25310             }
25311             
25312             if(!this.tickable){
25313                 e.preventDefault();
25314             }
25315             
25316         }
25317         return true;
25318     },
25319
25320     /**
25321      * Get the number of selected nodes.
25322      * @return {Number}
25323      */
25324     getSelectionCount : function(){
25325         return this.selections.length;
25326     },
25327
25328     /**
25329      * Get the currently selected nodes.
25330      * @return {Array} An array of HTMLElements
25331      */
25332     getSelectedNodes : function(){
25333         return this.selections;
25334     },
25335
25336     /**
25337      * Get the indexes of the selected nodes.
25338      * @return {Array}
25339      */
25340     getSelectedIndexes : function(){
25341         var indexes = [], s = this.selections;
25342         for(var i = 0, len = s.length; i < len; i++){
25343             indexes.push(s[i].nodeIndex);
25344         }
25345         return indexes;
25346     },
25347
25348     /**
25349      * Clear all selections
25350      * @param {Boolean} suppressEvent (optional) true to skip firing of the selectionchange event
25351      */
25352     clearSelections : function(suppressEvent){
25353         if(this.nodes && (this.multiSelect || this.singleSelect) && this.selections.length > 0){
25354             this.cmp.elements = this.selections;
25355             this.cmp.removeClass(this.selectedClass);
25356             this.selections = [];
25357             if(!suppressEvent){
25358                 this.fireEvent("selectionchange", this, this.selections);
25359             }
25360         }
25361     },
25362
25363     /**
25364      * Returns true if the passed node is selected
25365      * @param {HTMLElement/Number} node The node or node index
25366      * @return {Boolean}
25367      */
25368     isSelected : function(node){
25369         var s = this.selections;
25370         if(s.length < 1){
25371             return false;
25372         }
25373         node = this.getNode(node);
25374         return s.indexOf(node) !== -1;
25375     },
25376
25377     /**
25378      * Selects nodes.
25379      * @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
25380      * @param {Boolean} keepExisting (optional) true to keep existing selections
25381      * @param {Boolean} suppressEvent (optional) true to skip firing of the selectionchange vent
25382      */
25383     select : function(nodeInfo, keepExisting, suppressEvent){
25384         if(nodeInfo instanceof Array){
25385             if(!keepExisting){
25386                 this.clearSelections(true);
25387             }
25388             for(var i = 0, len = nodeInfo.length; i < len; i++){
25389                 this.select(nodeInfo[i], true, true);
25390             }
25391             return;
25392         } 
25393         var node = this.getNode(nodeInfo);
25394         if(!node || this.isSelected(node)){
25395             return; // already selected.
25396         }
25397         if(!keepExisting){
25398             this.clearSelections(true);
25399         }
25400         if(this.fireEvent("beforeselect", this, node, this.selections) !== false){
25401             Roo.fly(node).addClass(this.selectedClass);
25402             this.selections.push(node);
25403             if(!suppressEvent){
25404                 this.fireEvent("selectionchange", this, this.selections);
25405             }
25406         }
25407         
25408         
25409     },
25410       /**
25411      * Unselects nodes.
25412      * @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
25413      * @param {Boolean} keepExisting (optional) true IGNORED (for campatibility with select)
25414      * @param {Boolean} suppressEvent (optional) true to skip firing of the selectionchange vent
25415      */
25416     unselect : function(nodeInfo, keepExisting, suppressEvent)
25417     {
25418         if(nodeInfo instanceof Array){
25419             Roo.each(this.selections, function(s) {
25420                 this.unselect(s, nodeInfo);
25421             }, this);
25422             return;
25423         }
25424         var node = this.getNode(nodeInfo);
25425         if(!node || !this.isSelected(node)){
25426             Roo.log("not selected");
25427             return; // not selected.
25428         }
25429         // fireevent???
25430         var ns = [];
25431         Roo.each(this.selections, function(s) {
25432             if (s == node ) {
25433                 Roo.fly(node).removeClass(this.selectedClass);
25434
25435                 return;
25436             }
25437             ns.push(s);
25438         },this);
25439         
25440         this.selections= ns;
25441         this.fireEvent("selectionchange", this, this.selections);
25442     },
25443
25444     /**
25445      * Gets a template node.
25446      * @param {HTMLElement/String/Number} nodeInfo An HTMLElement template node, index of a template node or the id of a template node
25447      * @return {HTMLElement} The node or null if it wasn't found
25448      */
25449     getNode : function(nodeInfo){
25450         if(typeof nodeInfo == "string"){
25451             return document.getElementById(nodeInfo);
25452         }else if(typeof nodeInfo == "number"){
25453             return this.nodes[nodeInfo];
25454         }
25455         return nodeInfo;
25456     },
25457
25458     /**
25459      * Gets a range template nodes.
25460      * @param {Number} startIndex
25461      * @param {Number} endIndex
25462      * @return {Array} An array of nodes
25463      */
25464     getNodes : function(start, end){
25465         var ns = this.nodes;
25466         start = start || 0;
25467         end = typeof end == "undefined" ? ns.length - 1 : end;
25468         var nodes = [];
25469         if(start <= end){
25470             for(var i = start; i <= end; i++){
25471                 nodes.push(ns[i]);
25472             }
25473         } else{
25474             for(var i = start; i >= end; i--){
25475                 nodes.push(ns[i]);
25476             }
25477         }
25478         return nodes;
25479     },
25480
25481     /**
25482      * Finds the index of the passed node
25483      * @param {HTMLElement/String/Number} nodeInfo An HTMLElement template node, index of a template node or the id of a template node
25484      * @return {Number} The index of the node or -1
25485      */
25486     indexOf : function(node){
25487         node = this.getNode(node);
25488         if(typeof node.nodeIndex == "number"){
25489             return node.nodeIndex;
25490         }
25491         var ns = this.nodes;
25492         for(var i = 0, len = ns.length; i < len; i++){
25493             if(ns[i] == node){
25494                 return i;
25495             }
25496         }
25497         return -1;
25498     }
25499 });
25500 /*
25501  * Based on:
25502  * Ext JS Library 1.1.1
25503  * Copyright(c) 2006-2007, Ext JS, LLC.
25504  *
25505  * Originally Released Under LGPL - original licence link has changed is not relivant.
25506  *
25507  * Fork - LGPL
25508  * <script type="text/javascript">
25509  */
25510
25511 /**
25512  * @class Roo.JsonView
25513  * @extends Roo.View
25514  * Shortcut class to create a JSON + {@link Roo.UpdateManager} template view. Usage:
25515 <pre><code>
25516 var view = new Roo.JsonView({
25517     container: "my-element",
25518     tpl: '&lt;div id="{id}"&gt;{foo} - {bar}&lt;/div&gt;', // auto create template
25519     multiSelect: true, 
25520     jsonRoot: "data" 
25521 });
25522
25523 // listen for node click?
25524 view.on("click", function(vw, index, node, e){
25525     alert('Node "' + node.id + '" at index: ' + index + " was clicked.");
25526 });
25527
25528 // direct load of JSON data
25529 view.load("foobar.php");
25530
25531 // Example from my blog list
25532 var tpl = new Roo.Template(
25533     '&lt;div class="entry"&gt;' +
25534     '&lt;a class="entry-title" href="{link}"&gt;{title}&lt;/a&gt;' +
25535     "&lt;h4&gt;{date} by {author} | {comments} Comments&lt;/h4&gt;{description}" +
25536     "&lt;/div&gt;&lt;hr /&gt;"
25537 );
25538
25539 var moreView = new Roo.JsonView({
25540     container :  "entry-list", 
25541     template : tpl,
25542     jsonRoot: "posts"
25543 });
25544 moreView.on("beforerender", this.sortEntries, this);
25545 moreView.load({
25546     url: "/blog/get-posts.php",
25547     params: "allposts=true",
25548     text: "Loading Blog Entries..."
25549 });
25550 </code></pre>
25551
25552 * Note: old code is supported with arguments : (container, template, config)
25553
25554
25555  * @constructor
25556  * Create a new JsonView
25557  * 
25558  * @param {Object} config The config object
25559  * 
25560  */
25561 Roo.JsonView = function(config, depreciated_tpl, depreciated_config){
25562     
25563     
25564     Roo.JsonView.superclass.constructor.call(this, config, depreciated_tpl, depreciated_config);
25565
25566     var um = this.el.getUpdateManager();
25567     um.setRenderer(this);
25568     um.on("update", this.onLoad, this);
25569     um.on("failure", this.onLoadException, this);
25570
25571     /**
25572      * @event beforerender
25573      * Fires before rendering of the downloaded JSON data.
25574      * @param {Roo.JsonView} this
25575      * @param {Object} data The JSON data loaded
25576      */
25577     /**
25578      * @event load
25579      * Fires when data is loaded.
25580      * @param {Roo.JsonView} this
25581      * @param {Object} data The JSON data loaded
25582      * @param {Object} response The raw Connect response object
25583      */
25584     /**
25585      * @event loadexception
25586      * Fires when loading fails.
25587      * @param {Roo.JsonView} this
25588      * @param {Object} response The raw Connect response object
25589      */
25590     this.addEvents({
25591         'beforerender' : true,
25592         'load' : true,
25593         'loadexception' : true
25594     });
25595 };
25596 Roo.extend(Roo.JsonView, Roo.View, {
25597     /**
25598      * @type {String} The root property in the loaded JSON object that contains the data
25599      */
25600     jsonRoot : "",
25601
25602     /**
25603      * Refreshes the view.
25604      */
25605     refresh : function(){
25606         this.clearSelections();
25607         this.el.update("");
25608         var html = [];
25609         var o = this.jsonData;
25610         if(o && o.length > 0){
25611             for(var i = 0, len = o.length; i < len; i++){
25612                 var data = this.prepareData(o[i], i, o);
25613                 html[html.length] = this.tpl.apply(data);
25614             }
25615         }else{
25616             html.push(this.emptyText);
25617         }
25618         this.el.update(html.join(""));
25619         this.nodes = this.el.dom.childNodes;
25620         this.updateIndexes(0);
25621     },
25622
25623     /**
25624      * 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.
25625      * @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:
25626      <pre><code>
25627      view.load({
25628          url: "your-url.php",
25629          params: {param1: "foo", param2: "bar"}, // or a URL encoded string
25630          callback: yourFunction,
25631          scope: yourObject, //(optional scope)
25632          discardUrl: false,
25633          nocache: false,
25634          text: "Loading...",
25635          timeout: 30,
25636          scripts: false
25637      });
25638      </code></pre>
25639      * The only required property is <i>url</i>. The optional properties <i>nocache</i>, <i>text</i> and <i>scripts</i>
25640      * 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.
25641      * @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}
25642      * @param {Function} callback (optional) Callback when transaction is complete - called with signature (oElement, bSuccess)
25643      * @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.
25644      */
25645     load : function(){
25646         var um = this.el.getUpdateManager();
25647         um.update.apply(um, arguments);
25648     },
25649
25650     render : function(el, response){
25651         this.clearSelections();
25652         this.el.update("");
25653         var o;
25654         try{
25655             o = Roo.util.JSON.decode(response.responseText);
25656             if(this.jsonRoot){
25657                 
25658                 o = o[this.jsonRoot];
25659             }
25660         } catch(e){
25661         }
25662         /**
25663          * The current JSON data or null
25664          */
25665         this.jsonData = o;
25666         this.beforeRender();
25667         this.refresh();
25668     },
25669
25670 /**
25671  * Get the number of records in the current JSON dataset
25672  * @return {Number}
25673  */
25674     getCount : function(){
25675         return this.jsonData ? this.jsonData.length : 0;
25676     },
25677
25678 /**
25679  * Returns the JSON object for the specified node(s)
25680  * @param {HTMLElement/Array} node The node or an array of nodes
25681  * @return {Object/Array} If you pass in an array, you get an array back, otherwise
25682  * you get the JSON object for the node
25683  */
25684     getNodeData : function(node){
25685         if(node instanceof Array){
25686             var data = [];
25687             for(var i = 0, len = node.length; i < len; i++){
25688                 data.push(this.getNodeData(node[i]));
25689             }
25690             return data;
25691         }
25692         return this.jsonData[this.indexOf(node)] || null;
25693     },
25694
25695     beforeRender : function(){
25696         this.snapshot = this.jsonData;
25697         if(this.sortInfo){
25698             this.sort.apply(this, this.sortInfo);
25699         }
25700         this.fireEvent("beforerender", this, this.jsonData);
25701     },
25702
25703     onLoad : function(el, o){
25704         this.fireEvent("load", this, this.jsonData, o);
25705     },
25706
25707     onLoadException : function(el, o){
25708         this.fireEvent("loadexception", this, o);
25709     },
25710
25711 /**
25712  * Filter the data by a specific property.
25713  * @param {String} property A property on your JSON objects
25714  * @param {String/RegExp} value Either string that the property values
25715  * should start with, or a RegExp to test against the property
25716  */
25717     filter : function(property, value){
25718         if(this.jsonData){
25719             var data = [];
25720             var ss = this.snapshot;
25721             if(typeof value == "string"){
25722                 var vlen = value.length;
25723                 if(vlen == 0){
25724                     this.clearFilter();
25725                     return;
25726                 }
25727                 value = value.toLowerCase();
25728                 for(var i = 0, len = ss.length; i < len; i++){
25729                     var o = ss[i];
25730                     if(o[property].substr(0, vlen).toLowerCase() == value){
25731                         data.push(o);
25732                     }
25733                 }
25734             } else if(value.exec){ // regex?
25735                 for(var i = 0, len = ss.length; i < len; i++){
25736                     var o = ss[i];
25737                     if(value.test(o[property])){
25738                         data.push(o);
25739                     }
25740                 }
25741             } else{
25742                 return;
25743             }
25744             this.jsonData = data;
25745             this.refresh();
25746         }
25747     },
25748
25749 /**
25750  * Filter by a function. The passed function will be called with each
25751  * object in the current dataset. If the function returns true the value is kept,
25752  * otherwise it is filtered.
25753  * @param {Function} fn
25754  * @param {Object} scope (optional) The scope of the function (defaults to this JsonView)
25755  */
25756     filterBy : function(fn, scope){
25757         if(this.jsonData){
25758             var data = [];
25759             var ss = this.snapshot;
25760             for(var i = 0, len = ss.length; i < len; i++){
25761                 var o = ss[i];
25762                 if(fn.call(scope || this, o)){
25763                     data.push(o);
25764                 }
25765             }
25766             this.jsonData = data;
25767             this.refresh();
25768         }
25769     },
25770
25771 /**
25772  * Clears the current filter.
25773  */
25774     clearFilter : function(){
25775         if(this.snapshot && this.jsonData != this.snapshot){
25776             this.jsonData = this.snapshot;
25777             this.refresh();
25778         }
25779     },
25780
25781
25782 /**
25783  * Sorts the data for this view and refreshes it.
25784  * @param {String} property A property on your JSON objects to sort on
25785  * @param {String} direction (optional) "desc" or "asc" (defaults to "asc")
25786  * @param {Function} sortType (optional) A function to call to convert the data to a sortable value.
25787  */
25788     sort : function(property, dir, sortType){
25789         this.sortInfo = Array.prototype.slice.call(arguments, 0);
25790         if(this.jsonData){
25791             var p = property;
25792             var dsc = dir && dir.toLowerCase() == "desc";
25793             var f = function(o1, o2){
25794                 var v1 = sortType ? sortType(o1[p]) : o1[p];
25795                 var v2 = sortType ? sortType(o2[p]) : o2[p];
25796                 ;
25797                 if(v1 < v2){
25798                     return dsc ? +1 : -1;
25799                 } else if(v1 > v2){
25800                     return dsc ? -1 : +1;
25801                 } else{
25802                     return 0;
25803                 }
25804             };
25805             this.jsonData.sort(f);
25806             this.refresh();
25807             if(this.jsonData != this.snapshot){
25808                 this.snapshot.sort(f);
25809             }
25810         }
25811     }
25812 });/*
25813  * Based on:
25814  * Ext JS Library 1.1.1
25815  * Copyright(c) 2006-2007, Ext JS, LLC.
25816  *
25817  * Originally Released Under LGPL - original licence link has changed is not relivant.
25818  *
25819  * Fork - LGPL
25820  * <script type="text/javascript">
25821  */
25822  
25823
25824 /**
25825  * @class Roo.ColorPalette
25826  * @extends Roo.Component
25827  * Simple color palette class for choosing colors.  The palette can be rendered to any container.<br />
25828  * Here's an example of typical usage:
25829  * <pre><code>
25830 var cp = new Roo.ColorPalette({value:'993300'});  // initial selected color
25831 cp.render('my-div');
25832
25833 cp.on('select', function(palette, selColor){
25834     // do something with selColor
25835 });
25836 </code></pre>
25837  * @constructor
25838  * Create a new ColorPalette
25839  * @param {Object} config The config object
25840  */
25841 Roo.ColorPalette = function(config){
25842     Roo.ColorPalette.superclass.constructor.call(this, config);
25843     this.addEvents({
25844         /**
25845              * @event select
25846              * Fires when a color is selected
25847              * @param {ColorPalette} this
25848              * @param {String} color The 6-digit color hex code (without the # symbol)
25849              */
25850         select: true
25851     });
25852
25853     if(this.handler){
25854         this.on("select", this.handler, this.scope, true);
25855     }
25856 };
25857 Roo.extend(Roo.ColorPalette, Roo.Component, {
25858     /**
25859      * @cfg {String} itemCls
25860      * The CSS class to apply to the containing element (defaults to "x-color-palette")
25861      */
25862     itemCls : "x-color-palette",
25863     /**
25864      * @cfg {String} value
25865      * The initial color to highlight (should be a valid 6-digit color hex code without the # symbol).  Note that
25866      * the hex codes are case-sensitive.
25867      */
25868     value : null,
25869     clickEvent:'click',
25870     // private
25871     ctype: "Roo.ColorPalette",
25872
25873     /**
25874      * @cfg {Boolean} allowReselect If set to true then reselecting a color that is already selected fires the selection event
25875      */
25876     allowReselect : false,
25877
25878     /**
25879      * <p>An array of 6-digit color hex code strings (without the # symbol).  This array can contain any number
25880      * of colors, and each hex code should be unique.  The width of the palette is controlled via CSS by adjusting
25881      * the width property of the 'x-color-palette' class (or assigning a custom class), so you can balance the number
25882      * of colors with the width setting until the box is symmetrical.</p>
25883      * <p>You can override individual colors if needed:</p>
25884      * <pre><code>
25885 var cp = new Roo.ColorPalette();
25886 cp.colors[0] = "FF0000";  // change the first box to red
25887 </code></pre>
25888
25889 Or you can provide a custom array of your own for complete control:
25890 <pre><code>
25891 var cp = new Roo.ColorPalette();
25892 cp.colors = ["000000", "993300", "333300"];
25893 </code></pre>
25894      * @type Array
25895      */
25896     colors : [
25897         "000000", "993300", "333300", "003300", "003366", "000080", "333399", "333333",
25898         "800000", "FF6600", "808000", "008000", "008080", "0000FF", "666699", "808080",
25899         "FF0000", "FF9900", "99CC00", "339966", "33CCCC", "3366FF", "800080", "969696",
25900         "FF00FF", "FFCC00", "FFFF00", "00FF00", "00FFFF", "00CCFF", "993366", "C0C0C0",
25901         "FF99CC", "FFCC99", "FFFF99", "CCFFCC", "CCFFFF", "99CCFF", "CC99FF", "FFFFFF"
25902     ],
25903
25904     // private
25905     onRender : function(container, position){
25906         var t = new Roo.MasterTemplate(
25907             '<tpl><a href="#" class="color-{0}" hidefocus="on"><em><span style="background:#{0}" unselectable="on">&#160;</span></em></a></tpl>'
25908         );
25909         var c = this.colors;
25910         for(var i = 0, len = c.length; i < len; i++){
25911             t.add([c[i]]);
25912         }
25913         var el = document.createElement("div");
25914         el.className = this.itemCls;
25915         t.overwrite(el);
25916         container.dom.insertBefore(el, position);
25917         this.el = Roo.get(el);
25918         this.el.on(this.clickEvent, this.handleClick,  this, {delegate: "a"});
25919         if(this.clickEvent != 'click'){
25920             this.el.on('click', Roo.emptyFn,  this, {delegate: "a", preventDefault:true});
25921         }
25922     },
25923
25924     // private
25925     afterRender : function(){
25926         Roo.ColorPalette.superclass.afterRender.call(this);
25927         if(this.value){
25928             var s = this.value;
25929             this.value = null;
25930             this.select(s);
25931         }
25932     },
25933
25934     // private
25935     handleClick : function(e, t){
25936         e.preventDefault();
25937         if(!this.disabled){
25938             var c = t.className.match(/(?:^|\s)color-(.{6})(?:\s|$)/)[1];
25939             this.select(c.toUpperCase());
25940         }
25941     },
25942
25943     /**
25944      * Selects the specified color in the palette (fires the select event)
25945      * @param {String} color A valid 6-digit color hex code (# will be stripped if included)
25946      */
25947     select : function(color){
25948         color = color.replace("#", "");
25949         if(color != this.value || this.allowReselect){
25950             var el = this.el;
25951             if(this.value){
25952                 el.child("a.color-"+this.value).removeClass("x-color-palette-sel");
25953             }
25954             el.child("a.color-"+color).addClass("x-color-palette-sel");
25955             this.value = color;
25956             this.fireEvent("select", this, color);
25957         }
25958     }
25959 });/*
25960  * Based on:
25961  * Ext JS Library 1.1.1
25962  * Copyright(c) 2006-2007, Ext JS, LLC.
25963  *
25964  * Originally Released Under LGPL - original licence link has changed is not relivant.
25965  *
25966  * Fork - LGPL
25967  * <script type="text/javascript">
25968  */
25969  
25970 /**
25971  * @class Roo.DatePicker
25972  * @extends Roo.Component
25973  * Simple date picker class.
25974  * @constructor
25975  * Create a new DatePicker
25976  * @param {Object} config The config object
25977  */
25978 Roo.DatePicker = function(config){
25979     Roo.DatePicker.superclass.constructor.call(this, config);
25980
25981     this.value = config && config.value ?
25982                  config.value.clearTime() : new Date().clearTime();
25983
25984     this.addEvents({
25985         /**
25986              * @event select
25987              * Fires when a date is selected
25988              * @param {DatePicker} this
25989              * @param {Date} date The selected date
25990              */
25991         'select': true,
25992         /**
25993              * @event monthchange
25994              * Fires when the displayed month changes 
25995              * @param {DatePicker} this
25996              * @param {Date} date The selected month
25997              */
25998         'monthchange': true
25999     });
26000
26001     if(this.handler){
26002         this.on("select", this.handler,  this.scope || this);
26003     }
26004     // build the disabledDatesRE
26005     if(!this.disabledDatesRE && this.disabledDates){
26006         var dd = this.disabledDates;
26007         var re = "(?:";
26008         for(var i = 0; i < dd.length; i++){
26009             re += dd[i];
26010             if(i != dd.length-1) re += "|";
26011         }
26012         this.disabledDatesRE = new RegExp(re + ")");
26013     }
26014 };
26015
26016 Roo.extend(Roo.DatePicker, Roo.Component, {
26017     /**
26018      * @cfg {String} todayText
26019      * The text to display on the button that selects the current date (defaults to "Today")
26020      */
26021     todayText : "Today",
26022     /**
26023      * @cfg {String} okText
26024      * The text to display on the ok button
26025      */
26026     okText : "&#160;OK&#160;", // &#160; to give the user extra clicking room
26027     /**
26028      * @cfg {String} cancelText
26029      * The text to display on the cancel button
26030      */
26031     cancelText : "Cancel",
26032     /**
26033      * @cfg {String} todayTip
26034      * The tooltip to display for the button that selects the current date (defaults to "{current date} (Spacebar)")
26035      */
26036     todayTip : "{0} (Spacebar)",
26037     /**
26038      * @cfg {Date} minDate
26039      * Minimum allowable date (JavaScript date object, defaults to null)
26040      */
26041     minDate : null,
26042     /**
26043      * @cfg {Date} maxDate
26044      * Maximum allowable date (JavaScript date object, defaults to null)
26045      */
26046     maxDate : null,
26047     /**
26048      * @cfg {String} minText
26049      * The error text to display if the minDate validation fails (defaults to "This date is before the minimum date")
26050      */
26051     minText : "This date is before the minimum date",
26052     /**
26053      * @cfg {String} maxText
26054      * The error text to display if the maxDate validation fails (defaults to "This date is after the maximum date")
26055      */
26056     maxText : "This date is after the maximum date",
26057     /**
26058      * @cfg {String} format
26059      * The default date format string which can be overriden for localization support.  The format must be
26060      * valid according to {@link Date#parseDate} (defaults to 'm/d/y').
26061      */
26062     format : "m/d/y",
26063     /**
26064      * @cfg {Array} disabledDays
26065      * An array of days to disable, 0-based. For example, [0, 6] disables Sunday and Saturday (defaults to null).
26066      */
26067     disabledDays : null,
26068     /**
26069      * @cfg {String} disabledDaysText
26070      * The tooltip to display when the date falls on a disabled day (defaults to "")
26071      */
26072     disabledDaysText : "",
26073     /**
26074      * @cfg {RegExp} disabledDatesRE
26075      * JavaScript regular expression used to disable a pattern of dates (defaults to null)
26076      */
26077     disabledDatesRE : null,
26078     /**
26079      * @cfg {String} disabledDatesText
26080      * The tooltip text to display when the date falls on a disabled date (defaults to "")
26081      */
26082     disabledDatesText : "",
26083     /**
26084      * @cfg {Boolean} constrainToViewport
26085      * True to constrain the date picker to the viewport (defaults to true)
26086      */
26087     constrainToViewport : true,
26088     /**
26089      * @cfg {Array} monthNames
26090      * An array of textual month names which can be overriden for localization support (defaults to Date.monthNames)
26091      */
26092     monthNames : Date.monthNames,
26093     /**
26094      * @cfg {Array} dayNames
26095      * An array of textual day names which can be overriden for localization support (defaults to Date.dayNames)
26096      */
26097     dayNames : Date.dayNames,
26098     /**
26099      * @cfg {String} nextText
26100      * The next month navigation button tooltip (defaults to 'Next Month (Control+Right)')
26101      */
26102     nextText: 'Next Month (Control+Right)',
26103     /**
26104      * @cfg {String} prevText
26105      * The previous month navigation button tooltip (defaults to 'Previous Month (Control+Left)')
26106      */
26107     prevText: 'Previous Month (Control+Left)',
26108     /**
26109      * @cfg {String} monthYearText
26110      * The header month selector tooltip (defaults to 'Choose a month (Control+Up/Down to move years)')
26111      */
26112     monthYearText: 'Choose a month (Control+Up/Down to move years)',
26113     /**
26114      * @cfg {Number} startDay
26115      * Day index at which the week should begin, 0-based (defaults to 0, which is Sunday)
26116      */
26117     startDay : 0,
26118     /**
26119      * @cfg {Bool} showClear
26120      * Show a clear button (usefull for date form elements that can be blank.)
26121      */
26122     
26123     showClear: false,
26124     
26125     /**
26126      * Sets the value of the date field
26127      * @param {Date} value The date to set
26128      */
26129     setValue : function(value){
26130         var old = this.value;
26131         
26132         if (typeof(value) == 'string') {
26133          
26134             value = Date.parseDate(value, this.format);
26135         }
26136         if (!value) {
26137             value = new Date();
26138         }
26139         
26140         this.value = value.clearTime(true);
26141         if(this.el){
26142             this.update(this.value);
26143         }
26144     },
26145
26146     /**
26147      * Gets the current selected value of the date field
26148      * @return {Date} The selected date
26149      */
26150     getValue : function(){
26151         return this.value;
26152     },
26153
26154     // private
26155     focus : function(){
26156         if(this.el){
26157             this.update(this.activeDate);
26158         }
26159     },
26160
26161     // privateval
26162     onRender : function(container, position){
26163         
26164         var m = [
26165              '<table cellspacing="0">',
26166                 '<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>',
26167                 '<tr><td colspan="3"><table class="x-date-inner" cellspacing="0"><thead><tr>'];
26168         var dn = this.dayNames;
26169         for(var i = 0; i < 7; i++){
26170             var d = this.startDay+i;
26171             if(d > 6){
26172                 d = d-7;
26173             }
26174             m.push("<th><span>", dn[d].substr(0,1), "</span></th>");
26175         }
26176         m[m.length] = "</tr></thead><tbody><tr>";
26177         for(var i = 0; i < 42; i++) {
26178             if(i % 7 == 0 && i != 0){
26179                 m[m.length] = "</tr><tr>";
26180             }
26181             m[m.length] = '<td><a href="#" hidefocus="on" class="x-date-date" tabIndex="1"><em><span></span></em></a></td>';
26182         }
26183         m[m.length] = '</tr></tbody></table></td></tr><tr>'+
26184             '<td colspan="3" class="x-date-bottom" align="center"></td></tr></table><div class="x-date-mp"></div>';
26185
26186         var el = document.createElement("div");
26187         el.className = "x-date-picker";
26188         el.innerHTML = m.join("");
26189
26190         container.dom.insertBefore(el, position);
26191
26192         this.el = Roo.get(el);
26193         this.eventEl = Roo.get(el.firstChild);
26194
26195         new Roo.util.ClickRepeater(this.el.child("td.x-date-left a"), {
26196             handler: this.showPrevMonth,
26197             scope: this,
26198             preventDefault:true,
26199             stopDefault:true
26200         });
26201
26202         new Roo.util.ClickRepeater(this.el.child("td.x-date-right a"), {
26203             handler: this.showNextMonth,
26204             scope: this,
26205             preventDefault:true,
26206             stopDefault:true
26207         });
26208
26209         this.eventEl.on("mousewheel", this.handleMouseWheel,  this);
26210
26211         this.monthPicker = this.el.down('div.x-date-mp');
26212         this.monthPicker.enableDisplayMode('block');
26213         
26214         var kn = new Roo.KeyNav(this.eventEl, {
26215             "left" : function(e){
26216                 e.ctrlKey ?
26217                     this.showPrevMonth() :
26218                     this.update(this.activeDate.add("d", -1));
26219             },
26220
26221             "right" : function(e){
26222                 e.ctrlKey ?
26223                     this.showNextMonth() :
26224                     this.update(this.activeDate.add("d", 1));
26225             },
26226
26227             "up" : function(e){
26228                 e.ctrlKey ?
26229                     this.showNextYear() :
26230                     this.update(this.activeDate.add("d", -7));
26231             },
26232
26233             "down" : function(e){
26234                 e.ctrlKey ?
26235                     this.showPrevYear() :
26236                     this.update(this.activeDate.add("d", 7));
26237             },
26238
26239             "pageUp" : function(e){
26240                 this.showNextMonth();
26241             },
26242
26243             "pageDown" : function(e){
26244                 this.showPrevMonth();
26245             },
26246
26247             "enter" : function(e){
26248                 e.stopPropagation();
26249                 return true;
26250             },
26251
26252             scope : this
26253         });
26254
26255         this.eventEl.on("click", this.handleDateClick,  this, {delegate: "a.x-date-date"});
26256
26257         this.eventEl.addKeyListener(Roo.EventObject.SPACE, this.selectToday,  this);
26258
26259         this.el.unselectable();
26260         
26261         this.cells = this.el.select("table.x-date-inner tbody td");
26262         this.textNodes = this.el.query("table.x-date-inner tbody span");
26263
26264         this.mbtn = new Roo.Button(this.el.child("td.x-date-middle", true), {
26265             text: "&#160;",
26266             tooltip: this.monthYearText
26267         });
26268
26269         this.mbtn.on('click', this.showMonthPicker, this);
26270         this.mbtn.el.child(this.mbtn.menuClassTarget).addClass("x-btn-with-menu");
26271
26272
26273         var today = (new Date()).dateFormat(this.format);
26274         
26275         var baseTb = new Roo.Toolbar(this.el.child("td.x-date-bottom", true));
26276         if (this.showClear) {
26277             baseTb.add( new Roo.Toolbar.Fill());
26278         }
26279         baseTb.add({
26280             text: String.format(this.todayText, today),
26281             tooltip: String.format(this.todayTip, today),
26282             handler: this.selectToday,
26283             scope: this
26284         });
26285         
26286         //var todayBtn = new Roo.Button(this.el.child("td.x-date-bottom", true), {
26287             
26288         //});
26289         if (this.showClear) {
26290             
26291             baseTb.add( new Roo.Toolbar.Fill());
26292             baseTb.add({
26293                 text: '&#160;',
26294                 cls: 'x-btn-icon x-btn-clear',
26295                 handler: function() {
26296                     //this.value = '';
26297                     this.fireEvent("select", this, '');
26298                 },
26299                 scope: this
26300             });
26301         }
26302         
26303         
26304         if(Roo.isIE){
26305             this.el.repaint();
26306         }
26307         this.update(this.value);
26308     },
26309
26310     createMonthPicker : function(){
26311         if(!this.monthPicker.dom.firstChild){
26312             var buf = ['<table border="0" cellspacing="0">'];
26313             for(var i = 0; i < 6; i++){
26314                 buf.push(
26315                     '<tr><td class="x-date-mp-month"><a href="#">', this.monthNames[i].substr(0, 3), '</a></td>',
26316                     '<td class="x-date-mp-month x-date-mp-sep"><a href="#">', this.monthNames[i+6].substr(0, 3), '</a></td>',
26317                     i == 0 ?
26318                     '<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>' :
26319                     '<td class="x-date-mp-year"><a href="#"></a></td><td class="x-date-mp-year"><a href="#"></a></td></tr>'
26320                 );
26321             }
26322             buf.push(
26323                 '<tr class="x-date-mp-btns"><td colspan="4"><button type="button" class="x-date-mp-ok">',
26324                     this.okText,
26325                     '</button><button type="button" class="x-date-mp-cancel">',
26326                     this.cancelText,
26327                     '</button></td></tr>',
26328                 '</table>'
26329             );
26330             this.monthPicker.update(buf.join(''));
26331             this.monthPicker.on('click', this.onMonthClick, this);
26332             this.monthPicker.on('dblclick', this.onMonthDblClick, this);
26333
26334             this.mpMonths = this.monthPicker.select('td.x-date-mp-month');
26335             this.mpYears = this.monthPicker.select('td.x-date-mp-year');
26336
26337             this.mpMonths.each(function(m, a, i){
26338                 i += 1;
26339                 if((i%2) == 0){
26340                     m.dom.xmonth = 5 + Math.round(i * .5);
26341                 }else{
26342                     m.dom.xmonth = Math.round((i-1) * .5);
26343                 }
26344             });
26345         }
26346     },
26347
26348     showMonthPicker : function(){
26349         this.createMonthPicker();
26350         var size = this.el.getSize();
26351         this.monthPicker.setSize(size);
26352         this.monthPicker.child('table').setSize(size);
26353
26354         this.mpSelMonth = (this.activeDate || this.value).getMonth();
26355         this.updateMPMonth(this.mpSelMonth);
26356         this.mpSelYear = (this.activeDate || this.value).getFullYear();
26357         this.updateMPYear(this.mpSelYear);
26358
26359         this.monthPicker.slideIn('t', {duration:.2});
26360     },
26361
26362     updateMPYear : function(y){
26363         this.mpyear = y;
26364         var ys = this.mpYears.elements;
26365         for(var i = 1; i <= 10; i++){
26366             var td = ys[i-1], y2;
26367             if((i%2) == 0){
26368                 y2 = y + Math.round(i * .5);
26369                 td.firstChild.innerHTML = y2;
26370                 td.xyear = y2;
26371             }else{
26372                 y2 = y - (5-Math.round(i * .5));
26373                 td.firstChild.innerHTML = y2;
26374                 td.xyear = y2;
26375             }
26376             this.mpYears.item(i-1)[y2 == this.mpSelYear ? 'addClass' : 'removeClass']('x-date-mp-sel');
26377         }
26378     },
26379
26380     updateMPMonth : function(sm){
26381         this.mpMonths.each(function(m, a, i){
26382             m[m.dom.xmonth == sm ? 'addClass' : 'removeClass']('x-date-mp-sel');
26383         });
26384     },
26385
26386     selectMPMonth: function(m){
26387         
26388     },
26389
26390     onMonthClick : function(e, t){
26391         e.stopEvent();
26392         var el = new Roo.Element(t), pn;
26393         if(el.is('button.x-date-mp-cancel')){
26394             this.hideMonthPicker();
26395         }
26396         else if(el.is('button.x-date-mp-ok')){
26397             this.update(new Date(this.mpSelYear, this.mpSelMonth, (this.activeDate || this.value).getDate()));
26398             this.hideMonthPicker();
26399         }
26400         else if(pn = el.up('td.x-date-mp-month', 2)){
26401             this.mpMonths.removeClass('x-date-mp-sel');
26402             pn.addClass('x-date-mp-sel');
26403             this.mpSelMonth = pn.dom.xmonth;
26404         }
26405         else if(pn = el.up('td.x-date-mp-year', 2)){
26406             this.mpYears.removeClass('x-date-mp-sel');
26407             pn.addClass('x-date-mp-sel');
26408             this.mpSelYear = pn.dom.xyear;
26409         }
26410         else if(el.is('a.x-date-mp-prev')){
26411             this.updateMPYear(this.mpyear-10);
26412         }
26413         else if(el.is('a.x-date-mp-next')){
26414             this.updateMPYear(this.mpyear+10);
26415         }
26416     },
26417
26418     onMonthDblClick : function(e, t){
26419         e.stopEvent();
26420         var el = new Roo.Element(t), pn;
26421         if(pn = el.up('td.x-date-mp-month', 2)){
26422             this.update(new Date(this.mpSelYear, pn.dom.xmonth, (this.activeDate || this.value).getDate()));
26423             this.hideMonthPicker();
26424         }
26425         else if(pn = el.up('td.x-date-mp-year', 2)){
26426             this.update(new Date(pn.dom.xyear, this.mpSelMonth, (this.activeDate || this.value).getDate()));
26427             this.hideMonthPicker();
26428         }
26429     },
26430
26431     hideMonthPicker : function(disableAnim){
26432         if(this.monthPicker){
26433             if(disableAnim === true){
26434                 this.monthPicker.hide();
26435             }else{
26436                 this.monthPicker.slideOut('t', {duration:.2});
26437             }
26438         }
26439     },
26440
26441     // private
26442     showPrevMonth : function(e){
26443         this.update(this.activeDate.add("mo", -1));
26444     },
26445
26446     // private
26447     showNextMonth : function(e){
26448         this.update(this.activeDate.add("mo", 1));
26449     },
26450
26451     // private
26452     showPrevYear : function(){
26453         this.update(this.activeDate.add("y", -1));
26454     },
26455
26456     // private
26457     showNextYear : function(){
26458         this.update(this.activeDate.add("y", 1));
26459     },
26460
26461     // private
26462     handleMouseWheel : function(e){
26463         var delta = e.getWheelDelta();
26464         if(delta > 0){
26465             this.showPrevMonth();
26466             e.stopEvent();
26467         } else if(delta < 0){
26468             this.showNextMonth();
26469             e.stopEvent();
26470         }
26471     },
26472
26473     // private
26474     handleDateClick : function(e, t){
26475         e.stopEvent();
26476         if(t.dateValue && !Roo.fly(t.parentNode).hasClass("x-date-disabled")){
26477             this.setValue(new Date(t.dateValue));
26478             this.fireEvent("select", this, this.value);
26479         }
26480     },
26481
26482     // private
26483     selectToday : function(){
26484         this.setValue(new Date().clearTime());
26485         this.fireEvent("select", this, this.value);
26486     },
26487
26488     // private
26489     update : function(date)
26490     {
26491         var vd = this.activeDate;
26492         this.activeDate = date;
26493         if(vd && this.el){
26494             var t = date.getTime();
26495             if(vd.getMonth() == date.getMonth() && vd.getFullYear() == date.getFullYear()){
26496                 this.cells.removeClass("x-date-selected");
26497                 this.cells.each(function(c){
26498                    if(c.dom.firstChild.dateValue == t){
26499                        c.addClass("x-date-selected");
26500                        setTimeout(function(){
26501                             try{c.dom.firstChild.focus();}catch(e){}
26502                        }, 50);
26503                        return false;
26504                    }
26505                 });
26506                 return;
26507             }
26508         }
26509         
26510         var days = date.getDaysInMonth();
26511         var firstOfMonth = date.getFirstDateOfMonth();
26512         var startingPos = firstOfMonth.getDay()-this.startDay;
26513
26514         if(startingPos <= this.startDay){
26515             startingPos += 7;
26516         }
26517
26518         var pm = date.add("mo", -1);
26519         var prevStart = pm.getDaysInMonth()-startingPos;
26520
26521         var cells = this.cells.elements;
26522         var textEls = this.textNodes;
26523         days += startingPos;
26524
26525         // convert everything to numbers so it's fast
26526         var day = 86400000;
26527         var d = (new Date(pm.getFullYear(), pm.getMonth(), prevStart)).clearTime();
26528         var today = new Date().clearTime().getTime();
26529         var sel = date.clearTime().getTime();
26530         var min = this.minDate ? this.minDate.clearTime() : Number.NEGATIVE_INFINITY;
26531         var max = this.maxDate ? this.maxDate.clearTime() : Number.POSITIVE_INFINITY;
26532         var ddMatch = this.disabledDatesRE;
26533         var ddText = this.disabledDatesText;
26534         var ddays = this.disabledDays ? this.disabledDays.join("") : false;
26535         var ddaysText = this.disabledDaysText;
26536         var format = this.format;
26537
26538         var setCellClass = function(cal, cell){
26539             cell.title = "";
26540             var t = d.getTime();
26541             cell.firstChild.dateValue = t;
26542             if(t == today){
26543                 cell.className += " x-date-today";
26544                 cell.title = cal.todayText;
26545             }
26546             if(t == sel){
26547                 cell.className += " x-date-selected";
26548                 setTimeout(function(){
26549                     try{cell.firstChild.focus();}catch(e){}
26550                 }, 50);
26551             }
26552             // disabling
26553             if(t < min) {
26554                 cell.className = " x-date-disabled";
26555                 cell.title = cal.minText;
26556                 return;
26557             }
26558             if(t > max) {
26559                 cell.className = " x-date-disabled";
26560                 cell.title = cal.maxText;
26561                 return;
26562             }
26563             if(ddays){
26564                 if(ddays.indexOf(d.getDay()) != -1){
26565                     cell.title = ddaysText;
26566                     cell.className = " x-date-disabled";
26567                 }
26568             }
26569             if(ddMatch && format){
26570                 var fvalue = d.dateFormat(format);
26571                 if(ddMatch.test(fvalue)){
26572                     cell.title = ddText.replace("%0", fvalue);
26573                     cell.className = " x-date-disabled";
26574                 }
26575             }
26576         };
26577
26578         var i = 0;
26579         for(; i < startingPos; i++) {
26580             textEls[i].innerHTML = (++prevStart);
26581             d.setDate(d.getDate()+1);
26582             cells[i].className = "x-date-prevday";
26583             setCellClass(this, cells[i]);
26584         }
26585         for(; i < days; i++){
26586             intDay = i - startingPos + 1;
26587             textEls[i].innerHTML = (intDay);
26588             d.setDate(d.getDate()+1);
26589             cells[i].className = "x-date-active";
26590             setCellClass(this, cells[i]);
26591         }
26592         var extraDays = 0;
26593         for(; i < 42; i++) {
26594              textEls[i].innerHTML = (++extraDays);
26595              d.setDate(d.getDate()+1);
26596              cells[i].className = "x-date-nextday";
26597              setCellClass(this, cells[i]);
26598         }
26599
26600         this.mbtn.setText(this.monthNames[date.getMonth()] + " " + date.getFullYear());
26601         this.fireEvent('monthchange', this, date);
26602         
26603         if(!this.internalRender){
26604             var main = this.el.dom.firstChild;
26605             var w = main.offsetWidth;
26606             this.el.setWidth(w + this.el.getBorderWidth("lr"));
26607             Roo.fly(main).setWidth(w);
26608             this.internalRender = true;
26609             // opera does not respect the auto grow header center column
26610             // then, after it gets a width opera refuses to recalculate
26611             // without a second pass
26612             if(Roo.isOpera && !this.secondPass){
26613                 main.rows[0].cells[1].style.width = (w - (main.rows[0].cells[0].offsetWidth+main.rows[0].cells[2].offsetWidth)) + "px";
26614                 this.secondPass = true;
26615                 this.update.defer(10, this, [date]);
26616             }
26617         }
26618         
26619         
26620     }
26621 });        /*
26622  * Based on:
26623  * Ext JS Library 1.1.1
26624  * Copyright(c) 2006-2007, Ext JS, LLC.
26625  *
26626  * Originally Released Under LGPL - original licence link has changed is not relivant.
26627  *
26628  * Fork - LGPL
26629  * <script type="text/javascript">
26630  */
26631 /**
26632  * @class Roo.TabPanel
26633  * @extends Roo.util.Observable
26634  * A lightweight tab container.
26635  * <br><br>
26636  * Usage:
26637  * <pre><code>
26638 // basic tabs 1, built from existing content
26639 var tabs = new Roo.TabPanel("tabs1");
26640 tabs.addTab("script", "View Script");
26641 tabs.addTab("markup", "View Markup");
26642 tabs.activate("script");
26643
26644 // more advanced tabs, built from javascript
26645 var jtabs = new Roo.TabPanel("jtabs");
26646 jtabs.addTab("jtabs-1", "Normal Tab", "My content was added during construction.");
26647
26648 // set up the UpdateManager
26649 var tab2 = jtabs.addTab("jtabs-2", "Ajax Tab 1");
26650 var updater = tab2.getUpdateManager();
26651 updater.setDefaultUrl("ajax1.htm");
26652 tab2.on('activate', updater.refresh, updater, true);
26653
26654 // Use setUrl for Ajax loading
26655 var tab3 = jtabs.addTab("jtabs-3", "Ajax Tab 2");
26656 tab3.setUrl("ajax2.htm", null, true);
26657
26658 // Disabled tab
26659 var tab4 = jtabs.addTab("tabs1-5", "Disabled Tab", "Can't see me cause I'm disabled");
26660 tab4.disable();
26661
26662 jtabs.activate("jtabs-1");
26663  * </code></pre>
26664  * @constructor
26665  * Create a new TabPanel.
26666  * @param {String/HTMLElement/Roo.Element} container The id, DOM element or Roo.Element container where this TabPanel is to be rendered.
26667  * @param {Object/Boolean} config Config object to set any properties for this TabPanel, or true to render the tabs on the bottom.
26668  */
26669 Roo.TabPanel = function(container, config){
26670     /**
26671     * The container element for this TabPanel.
26672     * @type Roo.Element
26673     */
26674     this.el = Roo.get(container, true);
26675     if(config){
26676         if(typeof config == "boolean"){
26677             this.tabPosition = config ? "bottom" : "top";
26678         }else{
26679             Roo.apply(this, config);
26680         }
26681     }
26682     if(this.tabPosition == "bottom"){
26683         this.bodyEl = Roo.get(this.createBody(this.el.dom));
26684         this.el.addClass("x-tabs-bottom");
26685     }
26686     this.stripWrap = Roo.get(this.createStrip(this.el.dom), true);
26687     this.stripEl = Roo.get(this.createStripList(this.stripWrap.dom), true);
26688     this.stripBody = Roo.get(this.stripWrap.dom.firstChild.firstChild, true);
26689     if(Roo.isIE){
26690         Roo.fly(this.stripWrap.dom.firstChild).setStyle("overflow-x", "hidden");
26691     }
26692     if(this.tabPosition != "bottom"){
26693         /** The body element that contains {@link Roo.TabPanelItem} bodies. +
26694          * @type Roo.Element
26695          */
26696         this.bodyEl = Roo.get(this.createBody(this.el.dom));
26697         this.el.addClass("x-tabs-top");
26698     }
26699     this.items = [];
26700
26701     this.bodyEl.setStyle("position", "relative");
26702
26703     this.active = null;
26704     this.activateDelegate = this.activate.createDelegate(this);
26705
26706     this.addEvents({
26707         /**
26708          * @event tabchange
26709          * Fires when the active tab changes
26710          * @param {Roo.TabPanel} this
26711          * @param {Roo.TabPanelItem} activePanel The new active tab
26712          */
26713         "tabchange": true,
26714         /**
26715          * @event beforetabchange
26716          * Fires before the active tab changes, set cancel to true on the "e" parameter to cancel the change
26717          * @param {Roo.TabPanel} this
26718          * @param {Object} e Set cancel to true on this object to cancel the tab change
26719          * @param {Roo.TabPanelItem} tab The tab being changed to
26720          */
26721         "beforetabchange" : true
26722     });
26723
26724     Roo.EventManager.onWindowResize(this.onResize, this);
26725     this.cpad = this.el.getPadding("lr");
26726     this.hiddenCount = 0;
26727
26728
26729     // toolbar on the tabbar support...
26730     if (this.toolbar) {
26731         var tcfg = this.toolbar;
26732         tcfg.container = this.stripEl.child('td.x-tab-strip-toolbar');  
26733         this.toolbar = new Roo.Toolbar(tcfg);
26734         if (Roo.isSafari) {
26735             var tbl = tcfg.container.child('table', true);
26736             tbl.setAttribute('width', '100%');
26737         }
26738         
26739     }
26740    
26741
26742
26743     Roo.TabPanel.superclass.constructor.call(this);
26744 };
26745
26746 Roo.extend(Roo.TabPanel, Roo.util.Observable, {
26747     /*
26748      *@cfg {String} tabPosition "top" or "bottom" (defaults to "top")
26749      */
26750     tabPosition : "top",
26751     /*
26752      *@cfg {Number} currentTabWidth The width of the current tab (defaults to 0)
26753      */
26754     currentTabWidth : 0,
26755     /*
26756      *@cfg {Number} minTabWidth The minimum width of a tab (defaults to 40) (ignored if {@link #resizeTabs} is not true)
26757      */
26758     minTabWidth : 40,
26759     /*
26760      *@cfg {Number} maxTabWidth The maximum width of a tab (defaults to 250) (ignored if {@link #resizeTabs} is not true)
26761      */
26762     maxTabWidth : 250,
26763     /*
26764      *@cfg {Number} preferredTabWidth The preferred (default) width of a tab (defaults to 175) (ignored if {@link #resizeTabs} is not true)
26765      */
26766     preferredTabWidth : 175,
26767     /*
26768      *@cfg {Boolean} resizeTabs True to enable dynamic tab resizing (defaults to false)
26769      */
26770     resizeTabs : false,
26771     /*
26772      *@cfg {Boolean} monitorResize Set this to true to turn on window resize monitoring (ignored if {@link #resizeTabs} is not true) (defaults to true)
26773      */
26774     monitorResize : true,
26775     /*
26776      *@cfg {Object} toolbar xtype description of toolbar to show at the right of the tab bar. 
26777      */
26778     toolbar : false,
26779
26780     /**
26781      * Creates a new {@link Roo.TabPanelItem} by looking for an existing element with the provided id -- if it's not found it creates one.
26782      * @param {String} id The id of the div to use <b>or create</b>
26783      * @param {String} text The text for the tab
26784      * @param {String} content (optional) Content to put in the TabPanelItem body
26785      * @param {Boolean} closable (optional) True to create a close icon on the tab
26786      * @return {Roo.TabPanelItem} The created TabPanelItem
26787      */
26788     addTab : function(id, text, content, closable){
26789         var item = new Roo.TabPanelItem(this, id, text, closable);
26790         this.addTabItem(item);
26791         if(content){
26792             item.setContent(content);
26793         }
26794         return item;
26795     },
26796
26797     /**
26798      * Returns the {@link Roo.TabPanelItem} with the specified id/index
26799      * @param {String/Number} id The id or index of the TabPanelItem to fetch.
26800      * @return {Roo.TabPanelItem}
26801      */
26802     getTab : function(id){
26803         return this.items[id];
26804     },
26805
26806     /**
26807      * Hides the {@link Roo.TabPanelItem} with the specified id/index
26808      * @param {String/Number} id The id or index of the TabPanelItem to hide.
26809      */
26810     hideTab : function(id){
26811         var t = this.items[id];
26812         if(!t.isHidden()){
26813            t.setHidden(true);
26814            this.hiddenCount++;
26815            this.autoSizeTabs();
26816         }
26817     },
26818
26819     /**
26820      * "Unhides" the {@link Roo.TabPanelItem} with the specified id/index.
26821      * @param {String/Number} id The id or index of the TabPanelItem to unhide.
26822      */
26823     unhideTab : function(id){
26824         var t = this.items[id];
26825         if(t.isHidden()){
26826            t.setHidden(false);
26827            this.hiddenCount--;
26828            this.autoSizeTabs();
26829         }
26830     },
26831
26832     /**
26833      * Adds an existing {@link Roo.TabPanelItem}.
26834      * @param {Roo.TabPanelItem} item The TabPanelItem to add
26835      */
26836     addTabItem : function(item){
26837         this.items[item.id] = item;
26838         this.items.push(item);
26839         if(this.resizeTabs){
26840            item.setWidth(this.currentTabWidth || this.preferredTabWidth);
26841            this.autoSizeTabs();
26842         }else{
26843             item.autoSize();
26844         }
26845     },
26846
26847     /**
26848      * Removes a {@link Roo.TabPanelItem}.
26849      * @param {String/Number} id The id or index of the TabPanelItem to remove.
26850      */
26851     removeTab : function(id){
26852         var items = this.items;
26853         var tab = items[id];
26854         if(!tab) { return; }
26855         var index = items.indexOf(tab);
26856         if(this.active == tab && items.length > 1){
26857             var newTab = this.getNextAvailable(index);
26858             if(newTab) {
26859                 newTab.activate();
26860             }
26861         }
26862         this.stripEl.dom.removeChild(tab.pnode.dom);
26863         if(tab.bodyEl.dom.parentNode == this.bodyEl.dom){ // if it was moved already prevent error
26864             this.bodyEl.dom.removeChild(tab.bodyEl.dom);
26865         }
26866         items.splice(index, 1);
26867         delete this.items[tab.id];
26868         tab.fireEvent("close", tab);
26869         tab.purgeListeners();
26870         this.autoSizeTabs();
26871     },
26872
26873     getNextAvailable : function(start){
26874         var items = this.items;
26875         var index = start;
26876         // look for a next tab that will slide over to
26877         // replace the one being removed
26878         while(index < items.length){
26879             var item = items[++index];
26880             if(item && !item.isHidden()){
26881                 return item;
26882             }
26883         }
26884         // if one isn't found select the previous tab (on the left)
26885         index = start;
26886         while(index >= 0){
26887             var item = items[--index];
26888             if(item && !item.isHidden()){
26889                 return item;
26890             }
26891         }
26892         return null;
26893     },
26894
26895     /**
26896      * Disables a {@link Roo.TabPanelItem}. It cannot be the active tab, if it is this call is ignored.
26897      * @param {String/Number} id The id or index of the TabPanelItem to disable.
26898      */
26899     disableTab : function(id){
26900         var tab = this.items[id];
26901         if(tab && this.active != tab){
26902             tab.disable();
26903         }
26904     },
26905
26906     /**
26907      * Enables a {@link Roo.TabPanelItem} that is disabled.
26908      * @param {String/Number} id The id or index of the TabPanelItem to enable.
26909      */
26910     enableTab : function(id){
26911         var tab = this.items[id];
26912         tab.enable();
26913     },
26914
26915     /**
26916      * Activates a {@link Roo.TabPanelItem}. The currently active one will be deactivated.
26917      * @param {String/Number} id The id or index of the TabPanelItem to activate.
26918      * @return {Roo.TabPanelItem} The TabPanelItem.
26919      */
26920     activate : function(id){
26921         var tab = this.items[id];
26922         if(!tab){
26923             return null;
26924         }
26925         if(tab == this.active || tab.disabled){
26926             return tab;
26927         }
26928         var e = {};
26929         this.fireEvent("beforetabchange", this, e, tab);
26930         if(e.cancel !== true && !tab.disabled){
26931             if(this.active){
26932                 this.active.hide();
26933             }
26934             this.active = this.items[id];
26935             this.active.show();
26936             this.fireEvent("tabchange", this, this.active);
26937         }
26938         return tab;
26939     },
26940
26941     /**
26942      * Gets the active {@link Roo.TabPanelItem}.
26943      * @return {Roo.TabPanelItem} The active TabPanelItem or null if none are active.
26944      */
26945     getActiveTab : function(){
26946         return this.active;
26947     },
26948
26949     /**
26950      * Updates the tab body element to fit the height of the container element
26951      * for overflow scrolling
26952      * @param {Number} targetHeight (optional) Override the starting height from the elements height
26953      */
26954     syncHeight : function(targetHeight){
26955         var height = (targetHeight || this.el.getHeight())-this.el.getBorderWidth("tb")-this.el.getPadding("tb");
26956         var bm = this.bodyEl.getMargins();
26957         var newHeight = height-(this.stripWrap.getHeight()||0)-(bm.top+bm.bottom);
26958         this.bodyEl.setHeight(newHeight);
26959         return newHeight;
26960     },
26961
26962     onResize : function(){
26963         if(this.monitorResize){
26964             this.autoSizeTabs();
26965         }
26966     },
26967
26968     /**
26969      * Disables tab resizing while tabs are being added (if {@link #resizeTabs} is false this does nothing)
26970      */
26971     beginUpdate : function(){
26972         this.updating = true;
26973     },
26974
26975     /**
26976      * Stops an update and resizes the tabs (if {@link #resizeTabs} is false this does nothing)
26977      */
26978     endUpdate : function(){
26979         this.updating = false;
26980         this.autoSizeTabs();
26981     },
26982
26983     /**
26984      * Manual call to resize the tabs (if {@link #resizeTabs} is false this does nothing)
26985      */
26986     autoSizeTabs : function(){
26987         var count = this.items.length;
26988         var vcount = count - this.hiddenCount;
26989         if(!this.resizeTabs || count < 1 || vcount < 1 || this.updating) return;
26990         var w = Math.max(this.el.getWidth() - this.cpad, 10);
26991         var availWidth = Math.floor(w / vcount);
26992         var b = this.stripBody;
26993         if(b.getWidth() > w){
26994             var tabs = this.items;
26995             this.setTabWidth(Math.max(availWidth, this.minTabWidth)-2);
26996             if(availWidth < this.minTabWidth){
26997                 /*if(!this.sleft){    // incomplete scrolling code
26998                     this.createScrollButtons();
26999                 }
27000                 this.showScroll();
27001                 this.stripClip.setWidth(w - (this.sleft.getWidth()+this.sright.getWidth()));*/
27002             }
27003         }else{
27004             if(this.currentTabWidth < this.preferredTabWidth){
27005                 this.setTabWidth(Math.min(availWidth, this.preferredTabWidth)-2);
27006             }
27007         }
27008     },
27009
27010     /**
27011      * Returns the number of tabs in this TabPanel.
27012      * @return {Number}
27013      */
27014      getCount : function(){
27015          return this.items.length;
27016      },
27017
27018     /**
27019      * Resizes all the tabs to the passed width
27020      * @param {Number} The new width
27021      */
27022     setTabWidth : function(width){
27023         this.currentTabWidth = width;
27024         for(var i = 0, len = this.items.length; i < len; i++) {
27025                 if(!this.items[i].isHidden())this.items[i].setWidth(width);
27026         }
27027     },
27028
27029     /**
27030      * Destroys this TabPanel
27031      * @param {Boolean} removeEl (optional) True to remove the element from the DOM as well (defaults to undefined)
27032      */
27033     destroy : function(removeEl){
27034         Roo.EventManager.removeResizeListener(this.onResize, this);
27035         for(var i = 0, len = this.items.length; i < len; i++){
27036             this.items[i].purgeListeners();
27037         }
27038         if(removeEl === true){
27039             this.el.update("");
27040             this.el.remove();
27041         }
27042     }
27043 });
27044
27045 /**
27046  * @class Roo.TabPanelItem
27047  * @extends Roo.util.Observable
27048  * Represents an individual item (tab plus body) in a TabPanel.
27049  * @param {Roo.TabPanel} tabPanel The {@link Roo.TabPanel} this TabPanelItem belongs to
27050  * @param {String} id The id of this TabPanelItem
27051  * @param {String} text The text for the tab of this TabPanelItem
27052  * @param {Boolean} closable True to allow this TabPanelItem to be closable (defaults to false)
27053  */
27054 Roo.TabPanelItem = function(tabPanel, id, text, closable){
27055     /**
27056      * The {@link Roo.TabPanel} this TabPanelItem belongs to
27057      * @type Roo.TabPanel
27058      */
27059     this.tabPanel = tabPanel;
27060     /**
27061      * The id for this TabPanelItem
27062      * @type String
27063      */
27064     this.id = id;
27065     /** @private */
27066     this.disabled = false;
27067     /** @private */
27068     this.text = text;
27069     /** @private */
27070     this.loaded = false;
27071     this.closable = closable;
27072
27073     /**
27074      * The body element for this TabPanelItem.
27075      * @type Roo.Element
27076      */
27077     this.bodyEl = Roo.get(tabPanel.createItemBody(tabPanel.bodyEl.dom, id));
27078     this.bodyEl.setVisibilityMode(Roo.Element.VISIBILITY);
27079     this.bodyEl.setStyle("display", "block");
27080     this.bodyEl.setStyle("zoom", "1");
27081     this.hideAction();
27082
27083     var els = tabPanel.createStripElements(tabPanel.stripEl.dom, text, closable);
27084     /** @private */
27085     this.el = Roo.get(els.el, true);
27086     this.inner = Roo.get(els.inner, true);
27087     this.textEl = Roo.get(this.el.dom.firstChild.firstChild.firstChild, true);
27088     this.pnode = Roo.get(els.el.parentNode, true);
27089     this.el.on("mousedown", this.onTabMouseDown, this);
27090     this.el.on("click", this.onTabClick, this);
27091     /** @private */
27092     if(closable){
27093         var c = Roo.get(els.close, true);
27094         c.dom.title = this.closeText;
27095         c.addClassOnOver("close-over");
27096         c.on("click", this.closeClick, this);
27097      }
27098
27099     this.addEvents({
27100          /**
27101          * @event activate
27102          * Fires when this tab becomes the active tab.
27103          * @param {Roo.TabPanel} tabPanel The parent TabPanel
27104          * @param {Roo.TabPanelItem} this
27105          */
27106         "activate": true,
27107         /**
27108          * @event beforeclose
27109          * Fires before this tab is closed. To cancel the close, set cancel to true on e (e.cancel = true).
27110          * @param {Roo.TabPanelItem} this
27111          * @param {Object} e Set cancel to true on this object to cancel the close.
27112          */
27113         "beforeclose": true,
27114         /**
27115          * @event close
27116          * Fires when this tab is closed.
27117          * @param {Roo.TabPanelItem} this
27118          */
27119          "close": true,
27120         /**
27121          * @event deactivate
27122          * Fires when this tab is no longer the active tab.
27123          * @param {Roo.TabPanel} tabPanel The parent TabPanel
27124          * @param {Roo.TabPanelItem} this
27125          */
27126          "deactivate" : true
27127     });
27128     this.hidden = false;
27129
27130     Roo.TabPanelItem.superclass.constructor.call(this);
27131 };
27132
27133 Roo.extend(Roo.TabPanelItem, Roo.util.Observable, {
27134     purgeListeners : function(){
27135        Roo.util.Observable.prototype.purgeListeners.call(this);
27136        this.el.removeAllListeners();
27137     },
27138     /**
27139      * Shows this TabPanelItem -- this <b>does not</b> deactivate the currently active TabPanelItem.
27140      */
27141     show : function(){
27142         this.pnode.addClass("on");
27143         this.showAction();
27144         if(Roo.isOpera){
27145             this.tabPanel.stripWrap.repaint();
27146         }
27147         this.fireEvent("activate", this.tabPanel, this);
27148     },
27149
27150     /**
27151      * Returns true if this tab is the active tab.
27152      * @return {Boolean}
27153      */
27154     isActive : function(){
27155         return this.tabPanel.getActiveTab() == this;
27156     },
27157
27158     /**
27159      * Hides this TabPanelItem -- if you don't activate another TabPanelItem this could look odd.
27160      */
27161     hide : function(){
27162         this.pnode.removeClass("on");
27163         this.hideAction();
27164         this.fireEvent("deactivate", this.tabPanel, this);
27165     },
27166
27167     hideAction : function(){
27168         this.bodyEl.hide();
27169         this.bodyEl.setStyle("position", "absolute");
27170         this.bodyEl.setLeft("-20000px");
27171         this.bodyEl.setTop("-20000px");
27172     },
27173
27174     showAction : function(){
27175         this.bodyEl.setStyle("position", "relative");
27176         this.bodyEl.setTop("");
27177         this.bodyEl.setLeft("");
27178         this.bodyEl.show();
27179     },
27180
27181     /**
27182      * Set the tooltip for the tab.
27183      * @param {String} tooltip The tab's tooltip
27184      */
27185     setTooltip : function(text){
27186         if(Roo.QuickTips && Roo.QuickTips.isEnabled()){
27187             this.textEl.dom.qtip = text;
27188             this.textEl.dom.removeAttribute('title');
27189         }else{
27190             this.textEl.dom.title = text;
27191         }
27192     },
27193
27194     onTabClick : function(e){
27195         e.preventDefault();
27196         this.tabPanel.activate(this.id);
27197     },
27198
27199     onTabMouseDown : function(e){
27200         e.preventDefault();
27201         this.tabPanel.activate(this.id);
27202     },
27203
27204     getWidth : function(){
27205         return this.inner.getWidth();
27206     },
27207
27208     setWidth : function(width){
27209         var iwidth = width - this.pnode.getPadding("lr");
27210         this.inner.setWidth(iwidth);
27211         this.textEl.setWidth(iwidth-this.inner.getPadding("lr"));
27212         this.pnode.setWidth(width);
27213     },
27214
27215     /**
27216      * Show or hide the tab
27217      * @param {Boolean} hidden True to hide or false to show.
27218      */
27219     setHidden : function(hidden){
27220         this.hidden = hidden;
27221         this.pnode.setStyle("display", hidden ? "none" : "");
27222     },
27223
27224     /**
27225      * Returns true if this tab is "hidden"
27226      * @return {Boolean}
27227      */
27228     isHidden : function(){
27229         return this.hidden;
27230     },
27231
27232     /**
27233      * Returns the text for this tab
27234      * @return {String}
27235      */
27236     getText : function(){
27237         return this.text;
27238     },
27239
27240     autoSize : function(){
27241         //this.el.beginMeasure();
27242         this.textEl.setWidth(1);
27243         /*
27244          *  #2804 [new] Tabs in Roojs
27245          *  increase the width by 2-4 pixels to prevent the ellipssis showing in chrome
27246          */
27247         this.setWidth(this.textEl.dom.scrollWidth+this.pnode.getPadding("lr")+this.inner.getPadding("lr") + 2);
27248         //this.el.endMeasure();
27249     },
27250
27251     /**
27252      * Sets the text for the tab (Note: this also sets the tooltip text)
27253      * @param {String} text The tab's text and tooltip
27254      */
27255     setText : function(text){
27256         this.text = text;
27257         this.textEl.update(text);
27258         this.setTooltip(text);
27259         if(!this.tabPanel.resizeTabs){
27260             this.autoSize();
27261         }
27262     },
27263     /**
27264      * Activates this TabPanelItem -- this <b>does</b> deactivate the currently active TabPanelItem.
27265      */
27266     activate : function(){
27267         this.tabPanel.activate(this.id);
27268     },
27269
27270     /**
27271      * Disables this TabPanelItem -- this does nothing if this is the active TabPanelItem.
27272      */
27273     disable : function(){
27274         if(this.tabPanel.active != this){
27275             this.disabled = true;
27276             this.pnode.addClass("disabled");
27277         }
27278     },
27279
27280     /**
27281      * Enables this TabPanelItem if it was previously disabled.
27282      */
27283     enable : function(){
27284         this.disabled = false;
27285         this.pnode.removeClass("disabled");
27286     },
27287
27288     /**
27289      * Sets the content for this TabPanelItem.
27290      * @param {String} content The content
27291      * @param {Boolean} loadScripts true to look for and load scripts
27292      */
27293     setContent : function(content, loadScripts){
27294         this.bodyEl.update(content, loadScripts);
27295     },
27296
27297     /**
27298      * Gets the {@link Roo.UpdateManager} for the body of this TabPanelItem. Enables you to perform Ajax updates.
27299      * @return {Roo.UpdateManager} The UpdateManager
27300      */
27301     getUpdateManager : function(){
27302         return this.bodyEl.getUpdateManager();
27303     },
27304
27305     /**
27306      * Set a URL to be used to load the content for this TabPanelItem.
27307      * @param {String/Function} url The URL to load the content from, or a function to call to get the URL
27308      * @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)
27309      * @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)
27310      * @return {Roo.UpdateManager} The UpdateManager
27311      */
27312     setUrl : function(url, params, loadOnce){
27313         if(this.refreshDelegate){
27314             this.un('activate', this.refreshDelegate);
27315         }
27316         this.refreshDelegate = this._handleRefresh.createDelegate(this, [url, params, loadOnce]);
27317         this.on("activate", this.refreshDelegate);
27318         return this.bodyEl.getUpdateManager();
27319     },
27320
27321     /** @private */
27322     _handleRefresh : function(url, params, loadOnce){
27323         if(!loadOnce || !this.loaded){
27324             var updater = this.bodyEl.getUpdateManager();
27325             updater.update(url, params, this._setLoaded.createDelegate(this));
27326         }
27327     },
27328
27329     /**
27330      *   Forces a content refresh from the URL specified in the {@link #setUrl} method.
27331      *   Will fail silently if the setUrl method has not been called.
27332      *   This does not activate the panel, just updates its content.
27333      */
27334     refresh : function(){
27335         if(this.refreshDelegate){
27336            this.loaded = false;
27337            this.refreshDelegate();
27338         }
27339     },
27340
27341     /** @private */
27342     _setLoaded : function(){
27343         this.loaded = true;
27344     },
27345
27346     /** @private */
27347     closeClick : function(e){
27348         var o = {};
27349         e.stopEvent();
27350         this.fireEvent("beforeclose", this, o);
27351         if(o.cancel !== true){
27352             this.tabPanel.removeTab(this.id);
27353         }
27354     },
27355     /**
27356      * The text displayed in the tooltip for the close icon.
27357      * @type String
27358      */
27359     closeText : "Close this tab"
27360 });
27361
27362 /** @private */
27363 Roo.TabPanel.prototype.createStrip = function(container){
27364     var strip = document.createElement("div");
27365     strip.className = "x-tabs-wrap";
27366     container.appendChild(strip);
27367     return strip;
27368 };
27369 /** @private */
27370 Roo.TabPanel.prototype.createStripList = function(strip){
27371     // div wrapper for retard IE
27372     // returns the "tr" element.
27373     strip.innerHTML = '<div class="x-tabs-strip-wrap">'+
27374         '<table class="x-tabs-strip" cellspacing="0" cellpadding="0" border="0"><tbody><tr>'+
27375         '<td class="x-tab-strip-toolbar"></td></tr></tbody></table></div>';
27376     return strip.firstChild.firstChild.firstChild.firstChild;
27377 };
27378 /** @private */
27379 Roo.TabPanel.prototype.createBody = function(container){
27380     var body = document.createElement("div");
27381     Roo.id(body, "tab-body");
27382     Roo.fly(body).addClass("x-tabs-body");
27383     container.appendChild(body);
27384     return body;
27385 };
27386 /** @private */
27387 Roo.TabPanel.prototype.createItemBody = function(bodyEl, id){
27388     var body = Roo.getDom(id);
27389     if(!body){
27390         body = document.createElement("div");
27391         body.id = id;
27392     }
27393     Roo.fly(body).addClass("x-tabs-item-body");
27394     bodyEl.insertBefore(body, bodyEl.firstChild);
27395     return body;
27396 };
27397 /** @private */
27398 Roo.TabPanel.prototype.createStripElements = function(stripEl, text, closable){
27399     var td = document.createElement("td");
27400     stripEl.insertBefore(td, stripEl.childNodes[stripEl.childNodes.length-1]);
27401     //stripEl.appendChild(td);
27402     if(closable){
27403         td.className = "x-tabs-closable";
27404         if(!this.closeTpl){
27405             this.closeTpl = new Roo.Template(
27406                '<a href="#" class="x-tabs-right"><span class="x-tabs-left"><em class="x-tabs-inner">' +
27407                '<span unselectable="on"' + (this.disableTooltips ? '' : ' title="{text}"') +' class="x-tabs-text">{text}</span>' +
27408                '<div unselectable="on" class="close-icon">&#160;</div></em></span></a>'
27409             );
27410         }
27411         var el = this.closeTpl.overwrite(td, {"text": text});
27412         var close = el.getElementsByTagName("div")[0];
27413         var inner = el.getElementsByTagName("em")[0];
27414         return {"el": el, "close": close, "inner": inner};
27415     } else {
27416         if(!this.tabTpl){
27417             this.tabTpl = new Roo.Template(
27418                '<a href="#" class="x-tabs-right"><span class="x-tabs-left"><em class="x-tabs-inner">' +
27419                '<span unselectable="on"' + (this.disableTooltips ? '' : ' title="{text}"') +' class="x-tabs-text">{text}</span></em></span></a>'
27420             );
27421         }
27422         var el = this.tabTpl.overwrite(td, {"text": text});
27423         var inner = el.getElementsByTagName("em")[0];
27424         return {"el": el, "inner": inner};
27425     }
27426 };/*
27427  * Based on:
27428  * Ext JS Library 1.1.1
27429  * Copyright(c) 2006-2007, Ext JS, LLC.
27430  *
27431  * Originally Released Under LGPL - original licence link has changed is not relivant.
27432  *
27433  * Fork - LGPL
27434  * <script type="text/javascript">
27435  */
27436
27437 /**
27438  * @class Roo.Button
27439  * @extends Roo.util.Observable
27440  * Simple Button class
27441  * @cfg {String} text The button text
27442  * @cfg {String} icon The path to an image to display in the button (the image will be set as the background-image
27443  * CSS property of the button by default, so if you want a mixed icon/text button, set cls:"x-btn-text-icon")
27444  * @cfg {Function} handler A function called when the button is clicked (can be used instead of click event)
27445  * @cfg {Object} scope The scope of the handler
27446  * @cfg {Number} minWidth The minimum width for this button (used to give a set of buttons a common width)
27447  * @cfg {String/Object} tooltip The tooltip for the button - can be a string or QuickTips config object
27448  * @cfg {Boolean} hidden True to start hidden (defaults to false)
27449  * @cfg {Boolean} disabled True to start disabled (defaults to false)
27450  * @cfg {Boolean} pressed True to start pressed (only if enableToggle = true)
27451  * @cfg {String} toggleGroup The group this toggle button is a member of (only 1 per group can be pressed, only
27452    applies if enableToggle = true)
27453  * @cfg {String/HTMLElement/Element} renderTo The element to append the button to
27454  * @cfg {Boolean/Object} repeat True to repeat fire the click event while the mouse is down. This can also be
27455   an {@link Roo.util.ClickRepeater} config object (defaults to false).
27456  * @constructor
27457  * Create a new button
27458  * @param {Object} config The config object
27459  */
27460 Roo.Button = function(renderTo, config)
27461 {
27462     if (!config) {
27463         config = renderTo;
27464         renderTo = config.renderTo || false;
27465     }
27466     
27467     Roo.apply(this, config);
27468     this.addEvents({
27469         /**
27470              * @event click
27471              * Fires when this button is clicked
27472              * @param {Button} this
27473              * @param {EventObject} e The click event
27474              */
27475             "click" : true,
27476         /**
27477              * @event toggle
27478              * Fires when the "pressed" state of this button changes (only if enableToggle = true)
27479              * @param {Button} this
27480              * @param {Boolean} pressed
27481              */
27482             "toggle" : true,
27483         /**
27484              * @event mouseover
27485              * Fires when the mouse hovers over the button
27486              * @param {Button} this
27487              * @param {Event} e The event object
27488              */
27489         'mouseover' : true,
27490         /**
27491              * @event mouseout
27492              * Fires when the mouse exits the button
27493              * @param {Button} this
27494              * @param {Event} e The event object
27495              */
27496         'mouseout': true,
27497          /**
27498              * @event render
27499              * Fires when the button is rendered
27500              * @param {Button} this
27501              */
27502         'render': true
27503     });
27504     if(this.menu){
27505         this.menu = Roo.menu.MenuMgr.get(this.menu);
27506     }
27507     // register listeners first!!  - so render can be captured..
27508     Roo.util.Observable.call(this);
27509     if(renderTo){
27510         this.render(renderTo);
27511     }
27512     
27513   
27514 };
27515
27516 Roo.extend(Roo.Button, Roo.util.Observable, {
27517     /**
27518      * 
27519      */
27520     
27521     /**
27522      * Read-only. True if this button is hidden
27523      * @type Boolean
27524      */
27525     hidden : false,
27526     /**
27527      * Read-only. True if this button is disabled
27528      * @type Boolean
27529      */
27530     disabled : false,
27531     /**
27532      * Read-only. True if this button is pressed (only if enableToggle = true)
27533      * @type Boolean
27534      */
27535     pressed : false,
27536
27537     /**
27538      * @cfg {Number} tabIndex 
27539      * The DOM tabIndex for this button (defaults to undefined)
27540      */
27541     tabIndex : undefined,
27542
27543     /**
27544      * @cfg {Boolean} enableToggle
27545      * True to enable pressed/not pressed toggling (defaults to false)
27546      */
27547     enableToggle: false,
27548     /**
27549      * @cfg {Mixed} menu
27550      * Standard menu attribute consisting of a reference to a menu object, a menu id or a menu config blob (defaults to undefined).
27551      */
27552     menu : undefined,
27553     /**
27554      * @cfg {String} menuAlign
27555      * The position to align the menu to (see {@link Roo.Element#alignTo} for more details, defaults to 'tl-bl?').
27556      */
27557     menuAlign : "tl-bl?",
27558
27559     /**
27560      * @cfg {String} iconCls
27561      * A css class which sets a background image to be used as the icon for this button (defaults to undefined).
27562      */
27563     iconCls : undefined,
27564     /**
27565      * @cfg {String} type
27566      * The button's type, corresponding to the DOM input element type attribute.  Either "submit," "reset" or "button" (default).
27567      */
27568     type : 'button',
27569
27570     // private
27571     menuClassTarget: 'tr',
27572
27573     /**
27574      * @cfg {String} clickEvent
27575      * The type of event to map to the button's event handler (defaults to 'click')
27576      */
27577     clickEvent : 'click',
27578
27579     /**
27580      * @cfg {Boolean} handleMouseEvents
27581      * False to disable visual cues on mouseover, mouseout and mousedown (defaults to true)
27582      */
27583     handleMouseEvents : true,
27584
27585     /**
27586      * @cfg {String} tooltipType
27587      * The type of tooltip to use. Either "qtip" (default) for QuickTips or "title" for title attribute.
27588      */
27589     tooltipType : 'qtip',
27590
27591     /**
27592      * @cfg {String} cls
27593      * A CSS class to apply to the button's main element.
27594      */
27595     
27596     /**
27597      * @cfg {Roo.Template} template (Optional)
27598      * An {@link Roo.Template} with which to create the Button's main element. This Template must
27599      * contain numeric substitution parameter 0 if it is to display the tRoo property. Changing the template could
27600      * require code modifications if required elements (e.g. a button) aren't present.
27601      */
27602
27603     // private
27604     render : function(renderTo){
27605         var btn;
27606         if(this.hideParent){
27607             this.parentEl = Roo.get(renderTo);
27608         }
27609         if(!this.dhconfig){
27610             if(!this.template){
27611                 if(!Roo.Button.buttonTemplate){
27612                     // hideous table template
27613                     Roo.Button.buttonTemplate = new Roo.Template(
27614                         '<table border="0" cellpadding="0" cellspacing="0" class="x-btn-wrap"><tbody><tr>',
27615                         '<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>',
27616                         "</tr></tbody></table>");
27617                 }
27618                 this.template = Roo.Button.buttonTemplate;
27619             }
27620             btn = this.template.append(renderTo, [this.text || '&#160;', this.type], true);
27621             var btnEl = btn.child("button:first");
27622             btnEl.on('focus', this.onFocus, this);
27623             btnEl.on('blur', this.onBlur, this);
27624             if(this.cls){
27625                 btn.addClass(this.cls);
27626             }
27627             if(this.icon){
27628                 btnEl.setStyle('background-image', 'url(' +this.icon +')');
27629             }
27630             if(this.iconCls){
27631                 btnEl.addClass(this.iconCls);
27632                 if(!this.cls){
27633                     btn.addClass(this.text ? 'x-btn-text-icon' : 'x-btn-icon');
27634                 }
27635             }
27636             if(this.tabIndex !== undefined){
27637                 btnEl.dom.tabIndex = this.tabIndex;
27638             }
27639             if(this.tooltip){
27640                 if(typeof this.tooltip == 'object'){
27641                     Roo.QuickTips.tips(Roo.apply({
27642                           target: btnEl.id
27643                     }, this.tooltip));
27644                 } else {
27645                     btnEl.dom[this.tooltipType] = this.tooltip;
27646                 }
27647             }
27648         }else{
27649             btn = Roo.DomHelper.append(Roo.get(renderTo).dom, this.dhconfig, true);
27650         }
27651         this.el = btn;
27652         if(this.id){
27653             this.el.dom.id = this.el.id = this.id;
27654         }
27655         if(this.menu){
27656             this.el.child(this.menuClassTarget).addClass("x-btn-with-menu");
27657             this.menu.on("show", this.onMenuShow, this);
27658             this.menu.on("hide", this.onMenuHide, this);
27659         }
27660         btn.addClass("x-btn");
27661         if(Roo.isIE && !Roo.isIE7){
27662             this.autoWidth.defer(1, this);
27663         }else{
27664             this.autoWidth();
27665         }
27666         if(this.handleMouseEvents){
27667             btn.on("mouseover", this.onMouseOver, this);
27668             btn.on("mouseout", this.onMouseOut, this);
27669             btn.on("mousedown", this.onMouseDown, this);
27670         }
27671         btn.on(this.clickEvent, this.onClick, this);
27672         //btn.on("mouseup", this.onMouseUp, this);
27673         if(this.hidden){
27674             this.hide();
27675         }
27676         if(this.disabled){
27677             this.disable();
27678         }
27679         Roo.ButtonToggleMgr.register(this);
27680         if(this.pressed){
27681             this.el.addClass("x-btn-pressed");
27682         }
27683         if(this.repeat){
27684             var repeater = new Roo.util.ClickRepeater(btn,
27685                 typeof this.repeat == "object" ? this.repeat : {}
27686             );
27687             repeater.on("click", this.onClick,  this);
27688         }
27689         
27690         this.fireEvent('render', this);
27691         
27692     },
27693     /**
27694      * Returns the button's underlying element
27695      * @return {Roo.Element} The element
27696      */
27697     getEl : function(){
27698         return this.el;  
27699     },
27700     
27701     /**
27702      * Destroys this Button and removes any listeners.
27703      */
27704     destroy : function(){
27705         Roo.ButtonToggleMgr.unregister(this);
27706         this.el.removeAllListeners();
27707         this.purgeListeners();
27708         this.el.remove();
27709     },
27710
27711     // private
27712     autoWidth : function(){
27713         if(this.el){
27714             this.el.setWidth("auto");
27715             if(Roo.isIE7 && Roo.isStrict){
27716                 var ib = this.el.child('button');
27717                 if(ib && ib.getWidth() > 20){
27718                     ib.clip();
27719                     ib.setWidth(Roo.util.TextMetrics.measure(ib, this.text).width+ib.getFrameWidth('lr'));
27720                 }
27721             }
27722             if(this.minWidth){
27723                 if(this.hidden){
27724                     this.el.beginMeasure();
27725                 }
27726                 if(this.el.getWidth() < this.minWidth){
27727                     this.el.setWidth(this.minWidth);
27728                 }
27729                 if(this.hidden){
27730                     this.el.endMeasure();
27731                 }
27732             }
27733         }
27734     },
27735
27736     /**
27737      * Assigns this button's click handler
27738      * @param {Function} handler The function to call when the button is clicked
27739      * @param {Object} scope (optional) Scope for the function passed in
27740      */
27741     setHandler : function(handler, scope){
27742         this.handler = handler;
27743         this.scope = scope;  
27744     },
27745     
27746     /**
27747      * Sets this button's text
27748      * @param {String} text The button text
27749      */
27750     setText : function(text){
27751         this.text = text;
27752         if(this.el){
27753             this.el.child("td.x-btn-center button.x-btn-text").update(text);
27754         }
27755         this.autoWidth();
27756     },
27757     
27758     /**
27759      * Gets the text for this button
27760      * @return {String} The button text
27761      */
27762     getText : function(){
27763         return this.text;  
27764     },
27765     
27766     /**
27767      * Show this button
27768      */
27769     show: function(){
27770         this.hidden = false;
27771         if(this.el){
27772             this[this.hideParent? 'parentEl' : 'el'].setStyle("display", "");
27773         }
27774     },
27775     
27776     /**
27777      * Hide this button
27778      */
27779     hide: function(){
27780         this.hidden = true;
27781         if(this.el){
27782             this[this.hideParent? 'parentEl' : 'el'].setStyle("display", "none");
27783         }
27784     },
27785     
27786     /**
27787      * Convenience function for boolean show/hide
27788      * @param {Boolean} visible True to show, false to hide
27789      */
27790     setVisible: function(visible){
27791         if(visible) {
27792             this.show();
27793         }else{
27794             this.hide();
27795         }
27796     },
27797     
27798     /**
27799      * If a state it passed, it becomes the pressed state otherwise the current state is toggled.
27800      * @param {Boolean} state (optional) Force a particular state
27801      */
27802     toggle : function(state){
27803         state = state === undefined ? !this.pressed : state;
27804         if(state != this.pressed){
27805             if(state){
27806                 this.el.addClass("x-btn-pressed");
27807                 this.pressed = true;
27808                 this.fireEvent("toggle", this, true);
27809             }else{
27810                 this.el.removeClass("x-btn-pressed");
27811                 this.pressed = false;
27812                 this.fireEvent("toggle", this, false);
27813             }
27814             if(this.toggleHandler){
27815                 this.toggleHandler.call(this.scope || this, this, state);
27816             }
27817         }
27818     },
27819     
27820     /**
27821      * Focus the button
27822      */
27823     focus : function(){
27824         this.el.child('button:first').focus();
27825     },
27826     
27827     /**
27828      * Disable this button
27829      */
27830     disable : function(){
27831         if(this.el){
27832             this.el.addClass("x-btn-disabled");
27833         }
27834         this.disabled = true;
27835     },
27836     
27837     /**
27838      * Enable this button
27839      */
27840     enable : function(){
27841         if(this.el){
27842             this.el.removeClass("x-btn-disabled");
27843         }
27844         this.disabled = false;
27845     },
27846
27847     /**
27848      * Convenience function for boolean enable/disable
27849      * @param {Boolean} enabled True to enable, false to disable
27850      */
27851     setDisabled : function(v){
27852         this[v !== true ? "enable" : "disable"]();
27853     },
27854
27855     // private
27856     onClick : function(e){
27857         if(e){
27858             e.preventDefault();
27859         }
27860         if(e.button != 0){
27861             return;
27862         }
27863         if(!this.disabled){
27864             if(this.enableToggle){
27865                 this.toggle();
27866             }
27867             if(this.menu && !this.menu.isVisible()){
27868                 this.menu.show(this.el, this.menuAlign);
27869             }
27870             this.fireEvent("click", this, e);
27871             if(this.handler){
27872                 this.el.removeClass("x-btn-over");
27873                 this.handler.call(this.scope || this, this, e);
27874             }
27875         }
27876     },
27877     // private
27878     onMouseOver : function(e){
27879         if(!this.disabled){
27880             this.el.addClass("x-btn-over");
27881             this.fireEvent('mouseover', this, e);
27882         }
27883     },
27884     // private
27885     onMouseOut : function(e){
27886         if(!e.within(this.el,  true)){
27887             this.el.removeClass("x-btn-over");
27888             this.fireEvent('mouseout', this, e);
27889         }
27890     },
27891     // private
27892     onFocus : function(e){
27893         if(!this.disabled){
27894             this.el.addClass("x-btn-focus");
27895         }
27896     },
27897     // private
27898     onBlur : function(e){
27899         this.el.removeClass("x-btn-focus");
27900     },
27901     // private
27902     onMouseDown : function(e){
27903         if(!this.disabled && e.button == 0){
27904             this.el.addClass("x-btn-click");
27905             Roo.get(document).on('mouseup', this.onMouseUp, this);
27906         }
27907     },
27908     // private
27909     onMouseUp : function(e){
27910         if(e.button == 0){
27911             this.el.removeClass("x-btn-click");
27912             Roo.get(document).un('mouseup', this.onMouseUp, this);
27913         }
27914     },
27915     // private
27916     onMenuShow : function(e){
27917         this.el.addClass("x-btn-menu-active");
27918     },
27919     // private
27920     onMenuHide : function(e){
27921         this.el.removeClass("x-btn-menu-active");
27922     }   
27923 });
27924
27925 // Private utility class used by Button
27926 Roo.ButtonToggleMgr = function(){
27927    var groups = {};
27928    
27929    function toggleGroup(btn, state){
27930        if(state){
27931            var g = groups[btn.toggleGroup];
27932            for(var i = 0, l = g.length; i < l; i++){
27933                if(g[i] != btn){
27934                    g[i].toggle(false);
27935                }
27936            }
27937        }
27938    }
27939    
27940    return {
27941        register : function(btn){
27942            if(!btn.toggleGroup){
27943                return;
27944            }
27945            var g = groups[btn.toggleGroup];
27946            if(!g){
27947                g = groups[btn.toggleGroup] = [];
27948            }
27949            g.push(btn);
27950            btn.on("toggle", toggleGroup);
27951        },
27952        
27953        unregister : function(btn){
27954            if(!btn.toggleGroup){
27955                return;
27956            }
27957            var g = groups[btn.toggleGroup];
27958            if(g){
27959                g.remove(btn);
27960                btn.un("toggle", toggleGroup);
27961            }
27962        }
27963    };
27964 }();/*
27965  * Based on:
27966  * Ext JS Library 1.1.1
27967  * Copyright(c) 2006-2007, Ext JS, LLC.
27968  *
27969  * Originally Released Under LGPL - original licence link has changed is not relivant.
27970  *
27971  * Fork - LGPL
27972  * <script type="text/javascript">
27973  */
27974  
27975 /**
27976  * @class Roo.SplitButton
27977  * @extends Roo.Button
27978  * A split button that provides a built-in dropdown arrow that can fire an event separately from the default
27979  * click event of the button.  Typically this would be used to display a dropdown menu that provides additional
27980  * options to the primary button action, but any custom handler can provide the arrowclick implementation.
27981  * @cfg {Function} arrowHandler A function called when the arrow button is clicked (can be used instead of click event)
27982  * @cfg {String} arrowTooltip The title attribute of the arrow
27983  * @constructor
27984  * Create a new menu button
27985  * @param {String/HTMLElement/Element} renderTo The element to append the button to
27986  * @param {Object} config The config object
27987  */
27988 Roo.SplitButton = function(renderTo, config){
27989     Roo.SplitButton.superclass.constructor.call(this, renderTo, config);
27990     /**
27991      * @event arrowclick
27992      * Fires when this button's arrow is clicked
27993      * @param {SplitButton} this
27994      * @param {EventObject} e The click event
27995      */
27996     this.addEvents({"arrowclick":true});
27997 };
27998
27999 Roo.extend(Roo.SplitButton, Roo.Button, {
28000     render : function(renderTo){
28001         // this is one sweet looking template!
28002         var tpl = new Roo.Template(
28003             '<table cellspacing="0" class="x-btn-menu-wrap x-btn"><tr><td>',
28004             '<table cellspacing="0" class="x-btn-wrap x-btn-menu-text-wrap"><tbody>',
28005             '<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>',
28006             "</tbody></table></td><td>",
28007             '<table cellspacing="0" class="x-btn-wrap x-btn-menu-arrow-wrap"><tbody>',
28008             '<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>',
28009             "</tbody></table></td></tr></table>"
28010         );
28011         var btn = tpl.append(renderTo, [this.text, this.type], true);
28012         var btnEl = btn.child("button");
28013         if(this.cls){
28014             btn.addClass(this.cls);
28015         }
28016         if(this.icon){
28017             btnEl.setStyle('background-image', 'url(' +this.icon +')');
28018         }
28019         if(this.iconCls){
28020             btnEl.addClass(this.iconCls);
28021             if(!this.cls){
28022                 btn.addClass(this.text ? 'x-btn-text-icon' : 'x-btn-icon');
28023             }
28024         }
28025         this.el = btn;
28026         if(this.handleMouseEvents){
28027             btn.on("mouseover", this.onMouseOver, this);
28028             btn.on("mouseout", this.onMouseOut, this);
28029             btn.on("mousedown", this.onMouseDown, this);
28030             btn.on("mouseup", this.onMouseUp, this);
28031         }
28032         btn.on(this.clickEvent, this.onClick, this);
28033         if(this.tooltip){
28034             if(typeof this.tooltip == 'object'){
28035                 Roo.QuickTips.tips(Roo.apply({
28036                       target: btnEl.id
28037                 }, this.tooltip));
28038             } else {
28039                 btnEl.dom[this.tooltipType] = this.tooltip;
28040             }
28041         }
28042         if(this.arrowTooltip){
28043             btn.child("button:nth(2)").dom[this.tooltipType] = this.arrowTooltip;
28044         }
28045         if(this.hidden){
28046             this.hide();
28047         }
28048         if(this.disabled){
28049             this.disable();
28050         }
28051         if(this.pressed){
28052             this.el.addClass("x-btn-pressed");
28053         }
28054         if(Roo.isIE && !Roo.isIE7){
28055             this.autoWidth.defer(1, this);
28056         }else{
28057             this.autoWidth();
28058         }
28059         if(this.menu){
28060             this.menu.on("show", this.onMenuShow, this);
28061             this.menu.on("hide", this.onMenuHide, this);
28062         }
28063         this.fireEvent('render', this);
28064     },
28065
28066     // private
28067     autoWidth : function(){
28068         if(this.el){
28069             var tbl = this.el.child("table:first");
28070             var tbl2 = this.el.child("table:last");
28071             this.el.setWidth("auto");
28072             tbl.setWidth("auto");
28073             if(Roo.isIE7 && Roo.isStrict){
28074                 var ib = this.el.child('button:first');
28075                 if(ib && ib.getWidth() > 20){
28076                     ib.clip();
28077                     ib.setWidth(Roo.util.TextMetrics.measure(ib, this.text).width+ib.getFrameWidth('lr'));
28078                 }
28079             }
28080             if(this.minWidth){
28081                 if(this.hidden){
28082                     this.el.beginMeasure();
28083                 }
28084                 if((tbl.getWidth()+tbl2.getWidth()) < this.minWidth){
28085                     tbl.setWidth(this.minWidth-tbl2.getWidth());
28086                 }
28087                 if(this.hidden){
28088                     this.el.endMeasure();
28089                 }
28090             }
28091             this.el.setWidth(tbl.getWidth()+tbl2.getWidth());
28092         } 
28093     },
28094     /**
28095      * Sets this button's click handler
28096      * @param {Function} handler The function to call when the button is clicked
28097      * @param {Object} scope (optional) Scope for the function passed above
28098      */
28099     setHandler : function(handler, scope){
28100         this.handler = handler;
28101         this.scope = scope;  
28102     },
28103     
28104     /**
28105      * Sets this button's arrow click handler
28106      * @param {Function} handler The function to call when the arrow is clicked
28107      * @param {Object} scope (optional) Scope for the function passed above
28108      */
28109     setArrowHandler : function(handler, scope){
28110         this.arrowHandler = handler;
28111         this.scope = scope;  
28112     },
28113     
28114     /**
28115      * Focus the button
28116      */
28117     focus : function(){
28118         if(this.el){
28119             this.el.child("button:first").focus();
28120         }
28121     },
28122
28123     // private
28124     onClick : function(e){
28125         e.preventDefault();
28126         if(!this.disabled){
28127             if(e.getTarget(".x-btn-menu-arrow-wrap")){
28128                 if(this.menu && !this.menu.isVisible()){
28129                     this.menu.show(this.el, this.menuAlign);
28130                 }
28131                 this.fireEvent("arrowclick", this, e);
28132                 if(this.arrowHandler){
28133                     this.arrowHandler.call(this.scope || this, this, e);
28134                 }
28135             }else{
28136                 this.fireEvent("click", this, e);
28137                 if(this.handler){
28138                     this.handler.call(this.scope || this, this, e);
28139                 }
28140             }
28141         }
28142     },
28143     // private
28144     onMouseDown : function(e){
28145         if(!this.disabled){
28146             Roo.fly(e.getTarget("table")).addClass("x-btn-click");
28147         }
28148     },
28149     // private
28150     onMouseUp : function(e){
28151         Roo.fly(e.getTarget("table")).removeClass("x-btn-click");
28152     }   
28153 });
28154
28155
28156 // backwards compat
28157 Roo.MenuButton = Roo.SplitButton;/*
28158  * Based on:
28159  * Ext JS Library 1.1.1
28160  * Copyright(c) 2006-2007, Ext JS, LLC.
28161  *
28162  * Originally Released Under LGPL - original licence link has changed is not relivant.
28163  *
28164  * Fork - LGPL
28165  * <script type="text/javascript">
28166  */
28167
28168 /**
28169  * @class Roo.Toolbar
28170  * Basic Toolbar class.
28171  * @constructor
28172  * Creates a new Toolbar
28173  * @param {Object} container The config object
28174  */ 
28175 Roo.Toolbar = function(container, buttons, config)
28176 {
28177     /// old consturctor format still supported..
28178     if(container instanceof Array){ // omit the container for later rendering
28179         buttons = container;
28180         config = buttons;
28181         container = null;
28182     }
28183     if (typeof(container) == 'object' && container.xtype) {
28184         config = container;
28185         container = config.container;
28186         buttons = config.buttons || []; // not really - use items!!
28187     }
28188     var xitems = [];
28189     if (config && config.items) {
28190         xitems = config.items;
28191         delete config.items;
28192     }
28193     Roo.apply(this, config);
28194     this.buttons = buttons;
28195     
28196     if(container){
28197         this.render(container);
28198     }
28199     this.xitems = xitems;
28200     Roo.each(xitems, function(b) {
28201         this.add(b);
28202     }, this);
28203     
28204 };
28205
28206 Roo.Toolbar.prototype = {
28207     /**
28208      * @cfg {Array} items
28209      * array of button configs or elements to add (will be converted to a MixedCollection)
28210      */
28211     
28212     /**
28213      * @cfg {String/HTMLElement/Element} container
28214      * The id or element that will contain the toolbar
28215      */
28216     // private
28217     render : function(ct){
28218         this.el = Roo.get(ct);
28219         if(this.cls){
28220             this.el.addClass(this.cls);
28221         }
28222         // using a table allows for vertical alignment
28223         // 100% width is needed by Safari...
28224         this.el.update('<div class="x-toolbar x-small-editor"><table cellspacing="0"><tr></tr></table></div>');
28225         this.tr = this.el.child("tr", true);
28226         var autoId = 0;
28227         this.items = new Roo.util.MixedCollection(false, function(o){
28228             return o.id || ("item" + (++autoId));
28229         });
28230         if(this.buttons){
28231             this.add.apply(this, this.buttons);
28232             delete this.buttons;
28233         }
28234     },
28235
28236     /**
28237      * Adds element(s) to the toolbar -- this function takes a variable number of 
28238      * arguments of mixed type and adds them to the toolbar.
28239      * @param {Mixed} arg1 The following types of arguments are all valid:<br />
28240      * <ul>
28241      * <li>{@link Roo.Toolbar.Button} config: A valid button config object (equivalent to {@link #addButton})</li>
28242      * <li>HtmlElement: Any standard HTML element (equivalent to {@link #addElement})</li>
28243      * <li>Field: Any form field (equivalent to {@link #addField})</li>
28244      * <li>Item: Any subclass of {@link Roo.Toolbar.Item} (equivalent to {@link #addItem})</li>
28245      * <li>String: Any generic string (gets wrapped in a {@link Roo.Toolbar.TextItem}, equivalent to {@link #addText}).
28246      * Note that there are a few special strings that are treated differently as explained nRoo.</li>
28247      * <li>'separator' or '-': Creates a separator element (equivalent to {@link #addSeparator})</li>
28248      * <li>' ': Creates a spacer element (equivalent to {@link #addSpacer})</li>
28249      * <li>'->': Creates a fill element (equivalent to {@link #addFill})</li>
28250      * </ul>
28251      * @param {Mixed} arg2
28252      * @param {Mixed} etc.
28253      */
28254     add : function(){
28255         var a = arguments, l = a.length;
28256         for(var i = 0; i < l; i++){
28257             this._add(a[i]);
28258         }
28259     },
28260     // private..
28261     _add : function(el) {
28262         
28263         if (el.xtype) {
28264             el = Roo.factory(el, typeof(Roo.Toolbar[el.xtype]) == 'undefined' ? Roo.form : Roo.Toolbar);
28265         }
28266         
28267         if (el.applyTo){ // some kind of form field
28268             return this.addField(el);
28269         } 
28270         if (el.render){ // some kind of Toolbar.Item
28271             return this.addItem(el);
28272         }
28273         if (typeof el == "string"){ // string
28274             if(el == "separator" || el == "-"){
28275                 return this.addSeparator();
28276             }
28277             if (el == " "){
28278                 return this.addSpacer();
28279             }
28280             if(el == "->"){
28281                 return this.addFill();
28282             }
28283             return this.addText(el);
28284             
28285         }
28286         if(el.tagName){ // element
28287             return this.addElement(el);
28288         }
28289         if(typeof el == "object"){ // must be button config?
28290             return this.addButton(el);
28291         }
28292         // and now what?!?!
28293         return false;
28294         
28295     },
28296     
28297     /**
28298      * Add an Xtype element
28299      * @param {Object} xtype Xtype Object
28300      * @return {Object} created Object
28301      */
28302     addxtype : function(e){
28303         return this.add(e);  
28304     },
28305     
28306     /**
28307      * Returns the Element for this toolbar.
28308      * @return {Roo.Element}
28309      */
28310     getEl : function(){
28311         return this.el;  
28312     },
28313     
28314     /**
28315      * Adds a separator
28316      * @return {Roo.Toolbar.Item} The separator item
28317      */
28318     addSeparator : function(){
28319         return this.addItem(new Roo.Toolbar.Separator());
28320     },
28321
28322     /**
28323      * Adds a spacer element
28324      * @return {Roo.Toolbar.Spacer} The spacer item
28325      */
28326     addSpacer : function(){
28327         return this.addItem(new Roo.Toolbar.Spacer());
28328     },
28329
28330     /**
28331      * Adds a fill element that forces subsequent additions to the right side of the toolbar
28332      * @return {Roo.Toolbar.Fill} The fill item
28333      */
28334     addFill : function(){
28335         return this.addItem(new Roo.Toolbar.Fill());
28336     },
28337
28338     /**
28339      * Adds any standard HTML element to the toolbar
28340      * @param {String/HTMLElement/Element} el The element or id of the element to add
28341      * @return {Roo.Toolbar.Item} The element's item
28342      */
28343     addElement : function(el){
28344         return this.addItem(new Roo.Toolbar.Item(el));
28345     },
28346     /**
28347      * Collection of items on the toolbar.. (only Toolbar Items, so use fields to retrieve fields)
28348      * @type Roo.util.MixedCollection  
28349      */
28350     items : false,
28351      
28352     /**
28353      * Adds any Toolbar.Item or subclass
28354      * @param {Roo.Toolbar.Item} item
28355      * @return {Roo.Toolbar.Item} The item
28356      */
28357     addItem : function(item){
28358         var td = this.nextBlock();
28359         item.render(td);
28360         this.items.add(item);
28361         return item;
28362     },
28363     
28364     /**
28365      * Adds a button (or buttons). See {@link Roo.Toolbar.Button} for more info on the config.
28366      * @param {Object/Array} config A button config or array of configs
28367      * @return {Roo.Toolbar.Button/Array}
28368      */
28369     addButton : function(config){
28370         if(config instanceof Array){
28371             var buttons = [];
28372             for(var i = 0, len = config.length; i < len; i++) {
28373                 buttons.push(this.addButton(config[i]));
28374             }
28375             return buttons;
28376         }
28377         var b = config;
28378         if(!(config instanceof Roo.Toolbar.Button)){
28379             b = config.split ?
28380                 new Roo.Toolbar.SplitButton(config) :
28381                 new Roo.Toolbar.Button(config);
28382         }
28383         var td = this.nextBlock();
28384         b.render(td);
28385         this.items.add(b);
28386         return b;
28387     },
28388     
28389     /**
28390      * Adds text to the toolbar
28391      * @param {String} text The text to add
28392      * @return {Roo.Toolbar.Item} The element's item
28393      */
28394     addText : function(text){
28395         return this.addItem(new Roo.Toolbar.TextItem(text));
28396     },
28397     
28398     /**
28399      * Inserts any {@link Roo.Toolbar.Item}/{@link Roo.Toolbar.Button} at the specified index.
28400      * @param {Number} index The index where the item is to be inserted
28401      * @param {Object/Roo.Toolbar.Item/Roo.Toolbar.Button (may be Array)} item The button, or button config object to be inserted.
28402      * @return {Roo.Toolbar.Button/Item}
28403      */
28404     insertButton : function(index, item){
28405         if(item instanceof Array){
28406             var buttons = [];
28407             for(var i = 0, len = item.length; i < len; i++) {
28408                buttons.push(this.insertButton(index + i, item[i]));
28409             }
28410             return buttons;
28411         }
28412         if (!(item instanceof Roo.Toolbar.Button)){
28413            item = new Roo.Toolbar.Button(item);
28414         }
28415         var td = document.createElement("td");
28416         this.tr.insertBefore(td, this.tr.childNodes[index]);
28417         item.render(td);
28418         this.items.insert(index, item);
28419         return item;
28420     },
28421     
28422     /**
28423      * Adds a new element to the toolbar from the passed {@link Roo.DomHelper} config.
28424      * @param {Object} config
28425      * @return {Roo.Toolbar.Item} The element's item
28426      */
28427     addDom : function(config, returnEl){
28428         var td = this.nextBlock();
28429         Roo.DomHelper.overwrite(td, config);
28430         var ti = new Roo.Toolbar.Item(td.firstChild);
28431         ti.render(td);
28432         this.items.add(ti);
28433         return ti;
28434     },
28435
28436     /**
28437      * Collection of fields on the toolbar.. usefull for quering (value is false if there are no fields)
28438      * @type Roo.util.MixedCollection  
28439      */
28440     fields : false,
28441     
28442     /**
28443      * Adds a dynamically rendered Roo.form field (TextField, ComboBox, etc).
28444      * Note: the field should not have been rendered yet. For a field that has already been
28445      * rendered, use {@link #addElement}.
28446      * @param {Roo.form.Field} field
28447      * @return {Roo.ToolbarItem}
28448      */
28449      
28450       
28451     addField : function(field) {
28452         if (!this.fields) {
28453             var autoId = 0;
28454             this.fields = new Roo.util.MixedCollection(false, function(o){
28455                 return o.id || ("item" + (++autoId));
28456             });
28457
28458         }
28459         
28460         var td = this.nextBlock();
28461         field.render(td);
28462         var ti = new Roo.Toolbar.Item(td.firstChild);
28463         ti.render(td);
28464         this.items.add(ti);
28465         this.fields.add(field);
28466         return ti;
28467     },
28468     /**
28469      * Hide the toolbar
28470      * @method hide
28471      */
28472      
28473       
28474     hide : function()
28475     {
28476         this.el.child('div').setVisibilityMode(Roo.Element.DISPLAY);
28477         this.el.child('div').hide();
28478     },
28479     /**
28480      * Show the toolbar
28481      * @method show
28482      */
28483     show : function()
28484     {
28485         this.el.child('div').show();
28486     },
28487       
28488     // private
28489     nextBlock : function(){
28490         var td = document.createElement("td");
28491         this.tr.appendChild(td);
28492         return td;
28493     },
28494
28495     // private
28496     destroy : function(){
28497         if(this.items){ // rendered?
28498             Roo.destroy.apply(Roo, this.items.items);
28499         }
28500         if(this.fields){ // rendered?
28501             Roo.destroy.apply(Roo, this.fields.items);
28502         }
28503         Roo.Element.uncache(this.el, this.tr);
28504     }
28505 };
28506
28507 /**
28508  * @class Roo.Toolbar.Item
28509  * The base class that other classes should extend in order to get some basic common toolbar item functionality.
28510  * @constructor
28511  * Creates a new Item
28512  * @param {HTMLElement} el 
28513  */
28514 Roo.Toolbar.Item = function(el){
28515     this.el = Roo.getDom(el);
28516     this.id = Roo.id(this.el);
28517     this.hidden = false;
28518 };
28519
28520 Roo.Toolbar.Item.prototype = {
28521     
28522     /**
28523      * Get this item's HTML Element
28524      * @return {HTMLElement}
28525      */
28526     getEl : function(){
28527        return this.el;  
28528     },
28529
28530     // private
28531     render : function(td){
28532         this.td = td;
28533         td.appendChild(this.el);
28534     },
28535     
28536     /**
28537      * Removes and destroys this item.
28538      */
28539     destroy : function(){
28540         this.td.parentNode.removeChild(this.td);
28541     },
28542     
28543     /**
28544      * Shows this item.
28545      */
28546     show: function(){
28547         this.hidden = false;
28548         this.td.style.display = "";
28549     },
28550     
28551     /**
28552      * Hides this item.
28553      */
28554     hide: function(){
28555         this.hidden = true;
28556         this.td.style.display = "none";
28557     },
28558     
28559     /**
28560      * Convenience function for boolean show/hide.
28561      * @param {Boolean} visible true to show/false to hide
28562      */
28563     setVisible: function(visible){
28564         if(visible) {
28565             this.show();
28566         }else{
28567             this.hide();
28568         }
28569     },
28570     
28571     /**
28572      * Try to focus this item.
28573      */
28574     focus : function(){
28575         Roo.fly(this.el).focus();
28576     },
28577     
28578     /**
28579      * Disables this item.
28580      */
28581     disable : function(){
28582         Roo.fly(this.td).addClass("x-item-disabled");
28583         this.disabled = true;
28584         this.el.disabled = true;
28585     },
28586     
28587     /**
28588      * Enables this item.
28589      */
28590     enable : function(){
28591         Roo.fly(this.td).removeClass("x-item-disabled");
28592         this.disabled = false;
28593         this.el.disabled = false;
28594     }
28595 };
28596
28597
28598 /**
28599  * @class Roo.Toolbar.Separator
28600  * @extends Roo.Toolbar.Item
28601  * A simple toolbar separator class
28602  * @constructor
28603  * Creates a new Separator
28604  */
28605 Roo.Toolbar.Separator = function(){
28606     var s = document.createElement("span");
28607     s.className = "ytb-sep";
28608     Roo.Toolbar.Separator.superclass.constructor.call(this, s);
28609 };
28610 Roo.extend(Roo.Toolbar.Separator, Roo.Toolbar.Item, {
28611     enable:Roo.emptyFn,
28612     disable:Roo.emptyFn,
28613     focus:Roo.emptyFn
28614 });
28615
28616 /**
28617  * @class Roo.Toolbar.Spacer
28618  * @extends Roo.Toolbar.Item
28619  * A simple element that adds extra horizontal space to a toolbar.
28620  * @constructor
28621  * Creates a new Spacer
28622  */
28623 Roo.Toolbar.Spacer = function(){
28624     var s = document.createElement("div");
28625     s.className = "ytb-spacer";
28626     Roo.Toolbar.Spacer.superclass.constructor.call(this, s);
28627 };
28628 Roo.extend(Roo.Toolbar.Spacer, Roo.Toolbar.Item, {
28629     enable:Roo.emptyFn,
28630     disable:Roo.emptyFn,
28631     focus:Roo.emptyFn
28632 });
28633
28634 /**
28635  * @class Roo.Toolbar.Fill
28636  * @extends Roo.Toolbar.Spacer
28637  * A simple element that adds a greedy (100% width) horizontal space to a toolbar.
28638  * @constructor
28639  * Creates a new Spacer
28640  */
28641 Roo.Toolbar.Fill = Roo.extend(Roo.Toolbar.Spacer, {
28642     // private
28643     render : function(td){
28644         td.style.width = '100%';
28645         Roo.Toolbar.Fill.superclass.render.call(this, td);
28646     }
28647 });
28648
28649 /**
28650  * @class Roo.Toolbar.TextItem
28651  * @extends Roo.Toolbar.Item
28652  * A simple class that renders text directly into a toolbar.
28653  * @constructor
28654  * Creates a new TextItem
28655  * @param {String} text
28656  */
28657 Roo.Toolbar.TextItem = function(text){
28658     if (typeof(text) == 'object') {
28659         text = text.text;
28660     }
28661     var s = document.createElement("span");
28662     s.className = "ytb-text";
28663     s.innerHTML = text;
28664     Roo.Toolbar.TextItem.superclass.constructor.call(this, s);
28665 };
28666 Roo.extend(Roo.Toolbar.TextItem, Roo.Toolbar.Item, {
28667     enable:Roo.emptyFn,
28668     disable:Roo.emptyFn,
28669     focus:Roo.emptyFn
28670 });
28671
28672 /**
28673  * @class Roo.Toolbar.Button
28674  * @extends Roo.Button
28675  * A button that renders into a toolbar.
28676  * @constructor
28677  * Creates a new Button
28678  * @param {Object} config A standard {@link Roo.Button} config object
28679  */
28680 Roo.Toolbar.Button = function(config){
28681     Roo.Toolbar.Button.superclass.constructor.call(this, null, config);
28682 };
28683 Roo.extend(Roo.Toolbar.Button, Roo.Button, {
28684     render : function(td){
28685         this.td = td;
28686         Roo.Toolbar.Button.superclass.render.call(this, td);
28687     },
28688     
28689     /**
28690      * Removes and destroys this button
28691      */
28692     destroy : function(){
28693         Roo.Toolbar.Button.superclass.destroy.call(this);
28694         this.td.parentNode.removeChild(this.td);
28695     },
28696     
28697     /**
28698      * Shows this button
28699      */
28700     show: function(){
28701         this.hidden = false;
28702         this.td.style.display = "";
28703     },
28704     
28705     /**
28706      * Hides this button
28707      */
28708     hide: function(){
28709         this.hidden = true;
28710         this.td.style.display = "none";
28711     },
28712
28713     /**
28714      * Disables this item
28715      */
28716     disable : function(){
28717         Roo.fly(this.td).addClass("x-item-disabled");
28718         this.disabled = true;
28719     },
28720
28721     /**
28722      * Enables this item
28723      */
28724     enable : function(){
28725         Roo.fly(this.td).removeClass("x-item-disabled");
28726         this.disabled = false;
28727     }
28728 });
28729 // backwards compat
28730 Roo.ToolbarButton = Roo.Toolbar.Button;
28731
28732 /**
28733  * @class Roo.Toolbar.SplitButton
28734  * @extends Roo.SplitButton
28735  * A menu button that renders into a toolbar.
28736  * @constructor
28737  * Creates a new SplitButton
28738  * @param {Object} config A standard {@link Roo.SplitButton} config object
28739  */
28740 Roo.Toolbar.SplitButton = function(config){
28741     Roo.Toolbar.SplitButton.superclass.constructor.call(this, null, config);
28742 };
28743 Roo.extend(Roo.Toolbar.SplitButton, Roo.SplitButton, {
28744     render : function(td){
28745         this.td = td;
28746         Roo.Toolbar.SplitButton.superclass.render.call(this, td);
28747     },
28748     
28749     /**
28750      * Removes and destroys this button
28751      */
28752     destroy : function(){
28753         Roo.Toolbar.SplitButton.superclass.destroy.call(this);
28754         this.td.parentNode.removeChild(this.td);
28755     },
28756     
28757     /**
28758      * Shows this button
28759      */
28760     show: function(){
28761         this.hidden = false;
28762         this.td.style.display = "";
28763     },
28764     
28765     /**
28766      * Hides this button
28767      */
28768     hide: function(){
28769         this.hidden = true;
28770         this.td.style.display = "none";
28771     }
28772 });
28773
28774 // backwards compat
28775 Roo.Toolbar.MenuButton = Roo.Toolbar.SplitButton;/*
28776  * Based on:
28777  * Ext JS Library 1.1.1
28778  * Copyright(c) 2006-2007, Ext JS, LLC.
28779  *
28780  * Originally Released Under LGPL - original licence link has changed is not relivant.
28781  *
28782  * Fork - LGPL
28783  * <script type="text/javascript">
28784  */
28785  
28786 /**
28787  * @class Roo.PagingToolbar
28788  * @extends Roo.Toolbar
28789  * A specialized toolbar that is bound to a {@link Roo.data.Store} and provides automatic paging controls.
28790  * @constructor
28791  * Create a new PagingToolbar
28792  * @param {Object} config The config object
28793  */
28794 Roo.PagingToolbar = function(el, ds, config)
28795 {
28796     // old args format still supported... - xtype is prefered..
28797     if (typeof(el) == 'object' && el.xtype) {
28798         // created from xtype...
28799         config = el;
28800         ds = el.dataSource;
28801         el = config.container;
28802     }
28803     var items = [];
28804     if (config.items) {
28805         items = config.items;
28806         config.items = [];
28807     }
28808     
28809     Roo.PagingToolbar.superclass.constructor.call(this, el, null, config);
28810     this.ds = ds;
28811     this.cursor = 0;
28812     this.renderButtons(this.el);
28813     this.bind(ds);
28814     
28815     // supprot items array.
28816    
28817     Roo.each(items, function(e) {
28818         this.add(Roo.factory(e));
28819     },this);
28820     
28821 };
28822
28823 Roo.extend(Roo.PagingToolbar, Roo.Toolbar, {
28824     /**
28825      * @cfg {Roo.data.Store} dataSource
28826      * The underlying data store providing the paged data
28827      */
28828     /**
28829      * @cfg {String/HTMLElement/Element} container
28830      * container The id or element that will contain the toolbar
28831      */
28832     /**
28833      * @cfg {Boolean} displayInfo
28834      * True to display the displayMsg (defaults to false)
28835      */
28836     /**
28837      * @cfg {Number} pageSize
28838      * The number of records to display per page (defaults to 20)
28839      */
28840     pageSize: 20,
28841     /**
28842      * @cfg {String} displayMsg
28843      * The paging status message to display (defaults to "Displaying {start} - {end} of {total}")
28844      */
28845     displayMsg : 'Displaying {0} - {1} of {2}',
28846     /**
28847      * @cfg {String} emptyMsg
28848      * The message to display when no records are found (defaults to "No data to display")
28849      */
28850     emptyMsg : 'No data to display',
28851     /**
28852      * Customizable piece of the default paging text (defaults to "Page")
28853      * @type String
28854      */
28855     beforePageText : "Page",
28856     /**
28857      * Customizable piece of the default paging text (defaults to "of %0")
28858      * @type String
28859      */
28860     afterPageText : "of {0}",
28861     /**
28862      * Customizable piece of the default paging text (defaults to "First Page")
28863      * @type String
28864      */
28865     firstText : "First Page",
28866     /**
28867      * Customizable piece of the default paging text (defaults to "Previous Page")
28868      * @type String
28869      */
28870     prevText : "Previous Page",
28871     /**
28872      * Customizable piece of the default paging text (defaults to "Next Page")
28873      * @type String
28874      */
28875     nextText : "Next Page",
28876     /**
28877      * Customizable piece of the default paging text (defaults to "Last Page")
28878      * @type String
28879      */
28880     lastText : "Last Page",
28881     /**
28882      * Customizable piece of the default paging text (defaults to "Refresh")
28883      * @type String
28884      */
28885     refreshText : "Refresh",
28886
28887     // private
28888     renderButtons : function(el){
28889         Roo.PagingToolbar.superclass.render.call(this, el);
28890         this.first = this.addButton({
28891             tooltip: this.firstText,
28892             cls: "x-btn-icon x-grid-page-first",
28893             disabled: true,
28894             handler: this.onClick.createDelegate(this, ["first"])
28895         });
28896         this.prev = this.addButton({
28897             tooltip: this.prevText,
28898             cls: "x-btn-icon x-grid-page-prev",
28899             disabled: true,
28900             handler: this.onClick.createDelegate(this, ["prev"])
28901         });
28902         //this.addSeparator();
28903         this.add(this.beforePageText);
28904         this.field = Roo.get(this.addDom({
28905            tag: "input",
28906            type: "text",
28907            size: "3",
28908            value: "1",
28909            cls: "x-grid-page-number"
28910         }).el);
28911         this.field.on("keydown", this.onPagingKeydown, this);
28912         this.field.on("focus", function(){this.dom.select();});
28913         this.afterTextEl = this.addText(String.format(this.afterPageText, 1));
28914         this.field.setHeight(18);
28915         //this.addSeparator();
28916         this.next = this.addButton({
28917             tooltip: this.nextText,
28918             cls: "x-btn-icon x-grid-page-next",
28919             disabled: true,
28920             handler: this.onClick.createDelegate(this, ["next"])
28921         });
28922         this.last = this.addButton({
28923             tooltip: this.lastText,
28924             cls: "x-btn-icon x-grid-page-last",
28925             disabled: true,
28926             handler: this.onClick.createDelegate(this, ["last"])
28927         });
28928         //this.addSeparator();
28929         this.loading = this.addButton({
28930             tooltip: this.refreshText,
28931             cls: "x-btn-icon x-grid-loading",
28932             handler: this.onClick.createDelegate(this, ["refresh"])
28933         });
28934
28935         if(this.displayInfo){
28936             this.displayEl = Roo.fly(this.el.dom.firstChild).createChild({cls:'x-paging-info'});
28937         }
28938     },
28939
28940     // private
28941     updateInfo : function(){
28942         if(this.displayEl){
28943             var count = this.ds.getCount();
28944             var msg = count == 0 ?
28945                 this.emptyMsg :
28946                 String.format(
28947                     this.displayMsg,
28948                     this.cursor+1, this.cursor+count, this.ds.getTotalCount()    
28949                 );
28950             this.displayEl.update(msg);
28951         }
28952     },
28953
28954     // private
28955     onLoad : function(ds, r, o){
28956        this.cursor = o.params ? o.params.start : 0;
28957        var d = this.getPageData(), ap = d.activePage, ps = d.pages;
28958
28959        this.afterTextEl.el.innerHTML = String.format(this.afterPageText, d.pages);
28960        this.field.dom.value = ap;
28961        this.first.setDisabled(ap == 1);
28962        this.prev.setDisabled(ap == 1);
28963        this.next.setDisabled(ap == ps);
28964        this.last.setDisabled(ap == ps);
28965        this.loading.enable();
28966        this.updateInfo();
28967     },
28968
28969     // private
28970     getPageData : function(){
28971         var total = this.ds.getTotalCount();
28972         return {
28973             total : total,
28974             activePage : Math.ceil((this.cursor+this.pageSize)/this.pageSize),
28975             pages :  total < this.pageSize ? 1 : Math.ceil(total/this.pageSize)
28976         };
28977     },
28978
28979     // private
28980     onLoadError : function(){
28981         this.loading.enable();
28982     },
28983
28984     // private
28985     onPagingKeydown : function(e){
28986         var k = e.getKey();
28987         var d = this.getPageData();
28988         if(k == e.RETURN){
28989             var v = this.field.dom.value, pageNum;
28990             if(!v || isNaN(pageNum = parseInt(v, 10))){
28991                 this.field.dom.value = d.activePage;
28992                 return;
28993             }
28994             pageNum = Math.min(Math.max(1, pageNum), d.pages) - 1;
28995             this.ds.load({params:{start: pageNum * this.pageSize, limit: this.pageSize}});
28996             e.stopEvent();
28997         }
28998         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))
28999         {
29000           var pageNum = (k == e.HOME || (k == e.DOWN && e.ctrlKey) || (k == e.LEFT && e.ctrlKey) || (k == e.PAGEDOWN && e.ctrlKey)) ? 1 : d.pages;
29001           this.field.dom.value = pageNum;
29002           this.ds.load({params:{start: (pageNum - 1) * this.pageSize, limit: this.pageSize}});
29003           e.stopEvent();
29004         }
29005         else if(k == e.UP || k == e.RIGHT || k == e.PAGEUP || k == e.DOWN || k == e.LEFT || k == e.PAGEDOWN)
29006         {
29007           var v = this.field.dom.value, pageNum; 
29008           var increment = (e.shiftKey) ? 10 : 1;
29009           if(k == e.DOWN || k == e.LEFT || k == e.PAGEDOWN)
29010             increment *= -1;
29011           if(!v || isNaN(pageNum = parseInt(v, 10))) {
29012             this.field.dom.value = d.activePage;
29013             return;
29014           }
29015           else if(parseInt(v, 10) + increment >= 1 & parseInt(v, 10) + increment <= d.pages)
29016           {
29017             this.field.dom.value = parseInt(v, 10) + increment;
29018             pageNum = Math.min(Math.max(1, pageNum + increment), d.pages) - 1;
29019             this.ds.load({params:{start: pageNum * this.pageSize, limit: this.pageSize}});
29020           }
29021           e.stopEvent();
29022         }
29023     },
29024
29025     // private
29026     beforeLoad : function(){
29027         if(this.loading){
29028             this.loading.disable();
29029         }
29030     },
29031
29032     // private
29033     onClick : function(which){
29034         var ds = this.ds;
29035         switch(which){
29036             case "first":
29037                 ds.load({params:{start: 0, limit: this.pageSize}});
29038             break;
29039             case "prev":
29040                 ds.load({params:{start: Math.max(0, this.cursor-this.pageSize), limit: this.pageSize}});
29041             break;
29042             case "next":
29043                 ds.load({params:{start: this.cursor+this.pageSize, limit: this.pageSize}});
29044             break;
29045             case "last":
29046                 var total = ds.getTotalCount();
29047                 var extra = total % this.pageSize;
29048                 var lastStart = extra ? (total - extra) : total-this.pageSize;
29049                 ds.load({params:{start: lastStart, limit: this.pageSize}});
29050             break;
29051             case "refresh":
29052                 ds.load({params:{start: this.cursor, limit: this.pageSize}});
29053             break;
29054         }
29055     },
29056
29057     /**
29058      * Unbinds the paging toolbar from the specified {@link Roo.data.Store}
29059      * @param {Roo.data.Store} store The data store to unbind
29060      */
29061     unbind : function(ds){
29062         ds.un("beforeload", this.beforeLoad, this);
29063         ds.un("load", this.onLoad, this);
29064         ds.un("loadexception", this.onLoadError, this);
29065         ds.un("remove", this.updateInfo, this);
29066         ds.un("add", this.updateInfo, this);
29067         this.ds = undefined;
29068     },
29069
29070     /**
29071      * Binds the paging toolbar to the specified {@link Roo.data.Store}
29072      * @param {Roo.data.Store} store The data store to bind
29073      */
29074     bind : function(ds){
29075         ds.on("beforeload", this.beforeLoad, this);
29076         ds.on("load", this.onLoad, this);
29077         ds.on("loadexception", this.onLoadError, this);
29078         ds.on("remove", this.updateInfo, this);
29079         ds.on("add", this.updateInfo, this);
29080         this.ds = ds;
29081     }
29082 });/*
29083  * Based on:
29084  * Ext JS Library 1.1.1
29085  * Copyright(c) 2006-2007, Ext JS, LLC.
29086  *
29087  * Originally Released Under LGPL - original licence link has changed is not relivant.
29088  *
29089  * Fork - LGPL
29090  * <script type="text/javascript">
29091  */
29092
29093 /**
29094  * @class Roo.Resizable
29095  * @extends Roo.util.Observable
29096  * <p>Applies drag handles to an element to make it resizable. The drag handles are inserted into the element
29097  * and positioned absolute. Some elements, such as a textarea or image, don't support this. To overcome that, you can wrap
29098  * 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
29099  * the element will be wrapped for you automatically.</p>
29100  * <p>Here is the list of valid resize handles:</p>
29101  * <pre>
29102 Value   Description
29103 ------  -------------------
29104  'n'     north
29105  's'     south
29106  'e'     east
29107  'w'     west
29108  'nw'    northwest
29109  'sw'    southwest
29110  'se'    southeast
29111  'ne'    northeast
29112  'hd'    horizontal drag
29113  'all'   all
29114 </pre>
29115  * <p>Here's an example showing the creation of a typical Resizable:</p>
29116  * <pre><code>
29117 var resizer = new Roo.Resizable("element-id", {
29118     handles: 'all',
29119     minWidth: 200,
29120     minHeight: 100,
29121     maxWidth: 500,
29122     maxHeight: 400,
29123     pinned: true
29124 });
29125 resizer.on("resize", myHandler);
29126 </code></pre>
29127  * <p>To hide a particular handle, set its display to none in CSS, or through script:<br>
29128  * resizer.east.setDisplayed(false);</p>
29129  * @cfg {Boolean/String/Element} resizeChild True to resize the first child, or id/element to resize (defaults to false)
29130  * @cfg {Array/String} adjustments String "auto" or an array [width, height] with values to be <b>added</b> to the
29131  * resize operation's new size (defaults to [0, 0])
29132  * @cfg {Number} minWidth The minimum width for the element (defaults to 5)
29133  * @cfg {Number} minHeight The minimum height for the element (defaults to 5)
29134  * @cfg {Number} maxWidth The maximum width for the element (defaults to 10000)
29135  * @cfg {Number} maxHeight The maximum height for the element (defaults to 10000)
29136  * @cfg {Boolean} enabled False to disable resizing (defaults to true)
29137  * @cfg {Boolean} wrap True to wrap an element with a div if needed (required for textareas and images, defaults to false)
29138  * @cfg {Number} width The width of the element in pixels (defaults to null)
29139  * @cfg {Number} height The height of the element in pixels (defaults to null)
29140  * @cfg {Boolean} animate True to animate the resize (not compatible with dynamic sizing, defaults to false)
29141  * @cfg {Number} duration Animation duration if animate = true (defaults to .35)
29142  * @cfg {Boolean} dynamic True to resize the element while dragging instead of using a proxy (defaults to false)
29143  * @cfg {String} handles String consisting of the resize handles to display (defaults to undefined)
29144  * @cfg {Boolean} multiDirectional <b>Deprecated</b>.  The old style of adding multi-direction resize handles, deprecated
29145  * in favor of the handles config option (defaults to false)
29146  * @cfg {Boolean} disableTrackOver True to disable mouse tracking. This is only applied at config time. (defaults to false)
29147  * @cfg {String} easing Animation easing if animate = true (defaults to 'easingOutStrong')
29148  * @cfg {Number} widthIncrement The increment to snap the width resize in pixels (dynamic must be true, defaults to 0)
29149  * @cfg {Number} heightIncrement The increment to snap the height resize in pixels (dynamic must be true, defaults to 0)
29150  * @cfg {Boolean} pinned True to ensure that the resize handles are always visible, false to display them only when the
29151  * user mouses over the resizable borders. This is only applied at config time. (defaults to false)
29152  * @cfg {Boolean} preserveRatio True to preserve the original ratio between height and width during resize (defaults to false)
29153  * @cfg {Boolean} transparent True for transparent handles. This is only applied at config time. (defaults to false)
29154  * @cfg {Number} minX The minimum allowed page X for the element (only used for west resizing, defaults to 0)
29155  * @cfg {Number} minY The minimum allowed page Y for the element (only used for north resizing, defaults to 0)
29156  * @cfg {Boolean} draggable Convenience to initialize drag drop (defaults to false)
29157  * @constructor
29158  * Create a new resizable component
29159  * @param {String/HTMLElement/Roo.Element} el The id or element to resize
29160  * @param {Object} config configuration options
29161   */
29162 Roo.Resizable = function(el, config)
29163 {
29164     this.el = Roo.get(el);
29165
29166     if(config && config.wrap){
29167         config.resizeChild = this.el;
29168         this.el = this.el.wrap(typeof config.wrap == "object" ? config.wrap : {cls:"xresizable-wrap"});
29169         this.el.id = this.el.dom.id = config.resizeChild.id + "-rzwrap";
29170         this.el.setStyle("overflow", "hidden");
29171         this.el.setPositioning(config.resizeChild.getPositioning());
29172         config.resizeChild.clearPositioning();
29173         if(!config.width || !config.height){
29174             var csize = config.resizeChild.getSize();
29175             this.el.setSize(csize.width, csize.height);
29176         }
29177         if(config.pinned && !config.adjustments){
29178             config.adjustments = "auto";
29179         }
29180     }
29181
29182     this.proxy = this.el.createProxy({tag: "div", cls: "x-resizable-proxy", id: this.el.id + "-rzproxy"});
29183     this.proxy.unselectable();
29184     this.proxy.enableDisplayMode('block');
29185
29186     Roo.apply(this, config);
29187
29188     if(this.pinned){
29189         this.disableTrackOver = true;
29190         this.el.addClass("x-resizable-pinned");
29191     }
29192     // if the element isn't positioned, make it relative
29193     var position = this.el.getStyle("position");
29194     if(position != "absolute" && position != "fixed"){
29195         this.el.setStyle("position", "relative");
29196     }
29197     if(!this.handles){ // no handles passed, must be legacy style
29198         this.handles = 's,e,se';
29199         if(this.multiDirectional){
29200             this.handles += ',n,w';
29201         }
29202     }
29203     if(this.handles == "all"){
29204         this.handles = "n s e w ne nw se sw";
29205     }
29206     var hs = this.handles.split(/\s*?[,;]\s*?| /);
29207     var ps = Roo.Resizable.positions;
29208     for(var i = 0, len = hs.length; i < len; i++){
29209         if(hs[i] && ps[hs[i]]){
29210             var pos = ps[hs[i]];
29211             this[pos] = new Roo.Resizable.Handle(this, pos, this.disableTrackOver, this.transparent);
29212         }
29213     }
29214     // legacy
29215     this.corner = this.southeast;
29216     
29217     // updateBox = the box can move..
29218     if(this.handles.indexOf("n") != -1 || this.handles.indexOf("w") != -1 || this.handles.indexOf("hd") != -1) {
29219         this.updateBox = true;
29220     }
29221
29222     this.activeHandle = null;
29223
29224     if(this.resizeChild){
29225         if(typeof this.resizeChild == "boolean"){
29226             this.resizeChild = Roo.get(this.el.dom.firstChild, true);
29227         }else{
29228             this.resizeChild = Roo.get(this.resizeChild, true);
29229         }
29230     }
29231     
29232     if(this.adjustments == "auto"){
29233         var rc = this.resizeChild;
29234         var hw = this.west, he = this.east, hn = this.north, hs = this.south;
29235         if(rc && (hw || hn)){
29236             rc.position("relative");
29237             rc.setLeft(hw ? hw.el.getWidth() : 0);
29238             rc.setTop(hn ? hn.el.getHeight() : 0);
29239         }
29240         this.adjustments = [
29241             (he ? -he.el.getWidth() : 0) + (hw ? -hw.el.getWidth() : 0),
29242             (hn ? -hn.el.getHeight() : 0) + (hs ? -hs.el.getHeight() : 0) -1
29243         ];
29244     }
29245
29246     if(this.draggable){
29247         this.dd = this.dynamic ?
29248             this.el.initDD(null) : this.el.initDDProxy(null, {dragElId: this.proxy.id});
29249         this.dd.setHandleElId(this.resizeChild ? this.resizeChild.id : this.el.id);
29250     }
29251
29252     // public events
29253     this.addEvents({
29254         /**
29255          * @event beforeresize
29256          * Fired before resize is allowed. Set enabled to false to cancel resize.
29257          * @param {Roo.Resizable} this
29258          * @param {Roo.EventObject} e The mousedown event
29259          */
29260         "beforeresize" : true,
29261         /**
29262          * @event resizing
29263          * Fired a resizing.
29264          * @param {Roo.Resizable} this
29265          * @param {Number} x The new x position
29266          * @param {Number} y The new y position
29267          * @param {Number} w The new w width
29268          * @param {Number} h The new h hight
29269          * @param {Roo.EventObject} e The mouseup event
29270          */
29271         "resizing" : true,
29272         /**
29273          * @event resize
29274          * Fired after a resize.
29275          * @param {Roo.Resizable} this
29276          * @param {Number} width The new width
29277          * @param {Number} height The new height
29278          * @param {Roo.EventObject} e The mouseup event
29279          */
29280         "resize" : true
29281     });
29282
29283     if(this.width !== null && this.height !== null){
29284         this.resizeTo(this.width, this.height);
29285     }else{
29286         this.updateChildSize();
29287     }
29288     if(Roo.isIE){
29289         this.el.dom.style.zoom = 1;
29290     }
29291     Roo.Resizable.superclass.constructor.call(this);
29292 };
29293
29294 Roo.extend(Roo.Resizable, Roo.util.Observable, {
29295         resizeChild : false,
29296         adjustments : [0, 0],
29297         minWidth : 5,
29298         minHeight : 5,
29299         maxWidth : 10000,
29300         maxHeight : 10000,
29301         enabled : true,
29302         animate : false,
29303         duration : .35,
29304         dynamic : false,
29305         handles : false,
29306         multiDirectional : false,
29307         disableTrackOver : false,
29308         easing : 'easeOutStrong',
29309         widthIncrement : 0,
29310         heightIncrement : 0,
29311         pinned : false,
29312         width : null,
29313         height : null,
29314         preserveRatio : false,
29315         transparent: false,
29316         minX: 0,
29317         minY: 0,
29318         draggable: false,
29319
29320         /**
29321          * @cfg {String/HTMLElement/Element} constrainTo Constrain the resize to a particular element
29322          */
29323         constrainTo: undefined,
29324         /**
29325          * @cfg {Roo.lib.Region} resizeRegion Constrain the resize to a particular region
29326          */
29327         resizeRegion: undefined,
29328
29329
29330     /**
29331      * Perform a manual resize
29332      * @param {Number} width
29333      * @param {Number} height
29334      */
29335     resizeTo : function(width, height){
29336         this.el.setSize(width, height);
29337         this.updateChildSize();
29338         this.fireEvent("resize", this, width, height, null);
29339     },
29340
29341     // private
29342     startSizing : function(e, handle){
29343         this.fireEvent("beforeresize", this, e);
29344         if(this.enabled){ // 2nd enabled check in case disabled before beforeresize handler
29345
29346             if(!this.overlay){
29347                 this.overlay = this.el.createProxy({tag: "div", cls: "x-resizable-overlay", html: "&#160;"});
29348                 this.overlay.unselectable();
29349                 this.overlay.enableDisplayMode("block");
29350                 this.overlay.on("mousemove", this.onMouseMove, this);
29351                 this.overlay.on("mouseup", this.onMouseUp, this);
29352             }
29353             this.overlay.setStyle("cursor", handle.el.getStyle("cursor"));
29354
29355             this.resizing = true;
29356             this.startBox = this.el.getBox();
29357             this.startPoint = e.getXY();
29358             this.offsets = [(this.startBox.x + this.startBox.width) - this.startPoint[0],
29359                             (this.startBox.y + this.startBox.height) - this.startPoint[1]];
29360
29361             this.overlay.setSize(Roo.lib.Dom.getViewWidth(true), Roo.lib.Dom.getViewHeight(true));
29362             this.overlay.show();
29363
29364             if(this.constrainTo) {
29365                 var ct = Roo.get(this.constrainTo);
29366                 this.resizeRegion = ct.getRegion().adjust(
29367                     ct.getFrameWidth('t'),
29368                     ct.getFrameWidth('l'),
29369                     -ct.getFrameWidth('b'),
29370                     -ct.getFrameWidth('r')
29371                 );
29372             }
29373
29374             this.proxy.setStyle('visibility', 'hidden'); // workaround display none
29375             this.proxy.show();
29376             this.proxy.setBox(this.startBox);
29377             if(!this.dynamic){
29378                 this.proxy.setStyle('visibility', 'visible');
29379             }
29380         }
29381     },
29382
29383     // private
29384     onMouseDown : function(handle, e){
29385         if(this.enabled){
29386             e.stopEvent();
29387             this.activeHandle = handle;
29388             this.startSizing(e, handle);
29389         }
29390     },
29391
29392     // private
29393     onMouseUp : function(e){
29394         var size = this.resizeElement();
29395         this.resizing = false;
29396         this.handleOut();
29397         this.overlay.hide();
29398         this.proxy.hide();
29399         this.fireEvent("resize", this, size.width, size.height, e);
29400     },
29401
29402     // private
29403     updateChildSize : function(){
29404         
29405         if(this.resizeChild){
29406             var el = this.el;
29407             var child = this.resizeChild;
29408             var adj = this.adjustments;
29409             if(el.dom.offsetWidth){
29410                 var b = el.getSize(true);
29411                 child.setSize(b.width+adj[0], b.height+adj[1]);
29412             }
29413             // Second call here for IE
29414             // The first call enables instant resizing and
29415             // the second call corrects scroll bars if they
29416             // exist
29417             if(Roo.isIE){
29418                 setTimeout(function(){
29419                     if(el.dom.offsetWidth){
29420                         var b = el.getSize(true);
29421                         child.setSize(b.width+adj[0], b.height+adj[1]);
29422                     }
29423                 }, 10);
29424             }
29425         }
29426     },
29427
29428     // private
29429     snap : function(value, inc, min){
29430         if(!inc || !value) return value;
29431         var newValue = value;
29432         var m = value % inc;
29433         if(m > 0){
29434             if(m > (inc/2)){
29435                 newValue = value + (inc-m);
29436             }else{
29437                 newValue = value - m;
29438             }
29439         }
29440         return Math.max(min, newValue);
29441     },
29442
29443     // private
29444     resizeElement : function(){
29445         var box = this.proxy.getBox();
29446         if(this.updateBox){
29447             this.el.setBox(box, false, this.animate, this.duration, null, this.easing);
29448         }else{
29449             this.el.setSize(box.width, box.height, this.animate, this.duration, null, this.easing);
29450         }
29451         this.updateChildSize();
29452         if(!this.dynamic){
29453             this.proxy.hide();
29454         }
29455         return box;
29456     },
29457
29458     // private
29459     constrain : function(v, diff, m, mx){
29460         if(v - diff < m){
29461             diff = v - m;
29462         }else if(v - diff > mx){
29463             diff = mx - v;
29464         }
29465         return diff;
29466     },
29467
29468     // private
29469     onMouseMove : function(e){
29470         
29471         if(this.enabled){
29472             try{// try catch so if something goes wrong the user doesn't get hung
29473
29474             if(this.resizeRegion && !this.resizeRegion.contains(e.getPoint())) {
29475                 return;
29476             }
29477
29478             //var curXY = this.startPoint;
29479             var curSize = this.curSize || this.startBox;
29480             var x = this.startBox.x, y = this.startBox.y;
29481             var ox = x, oy = y;
29482             var w = curSize.width, h = curSize.height;
29483             var ow = w, oh = h;
29484             var mw = this.minWidth, mh = this.minHeight;
29485             var mxw = this.maxWidth, mxh = this.maxHeight;
29486             var wi = this.widthIncrement;
29487             var hi = this.heightIncrement;
29488
29489             var eventXY = e.getXY();
29490             var diffX = -(this.startPoint[0] - Math.max(this.minX, eventXY[0]));
29491             var diffY = -(this.startPoint[1] - Math.max(this.minY, eventXY[1]));
29492
29493             var pos = this.activeHandle.position;
29494
29495             switch(pos){
29496                 case "east":
29497                     w += diffX;
29498                     w = Math.min(Math.max(mw, w), mxw);
29499                     break;
29500              
29501                 case "south":
29502                     h += diffY;
29503                     h = Math.min(Math.max(mh, h), mxh);
29504                     break;
29505                 case "southeast":
29506                     w += diffX;
29507                     h += diffY;
29508                     w = Math.min(Math.max(mw, w), mxw);
29509                     h = Math.min(Math.max(mh, h), mxh);
29510                     break;
29511                 case "north":
29512                     diffY = this.constrain(h, diffY, mh, mxh);
29513                     y += diffY;
29514                     h -= diffY;
29515                     break;
29516                 case "hdrag":
29517                     
29518                     if (wi) {
29519                         var adiffX = Math.abs(diffX);
29520                         var sub = (adiffX % wi); // how much 
29521                         if (sub > (wi/2)) { // far enough to snap
29522                             diffX = (diffX > 0) ? diffX-sub + wi : diffX+sub - wi;
29523                         } else {
29524                             // remove difference.. 
29525                             diffX = (diffX > 0) ? diffX-sub : diffX+sub;
29526                         }
29527                     }
29528                     x += diffX;
29529                     x = Math.max(this.minX, x);
29530                     break;
29531                 case "west":
29532                     diffX = this.constrain(w, diffX, mw, mxw);
29533                     x += diffX;
29534                     w -= diffX;
29535                     break;
29536                 case "northeast":
29537                     w += diffX;
29538                     w = Math.min(Math.max(mw, w), mxw);
29539                     diffY = this.constrain(h, diffY, mh, mxh);
29540                     y += diffY;
29541                     h -= diffY;
29542                     break;
29543                 case "northwest":
29544                     diffX = this.constrain(w, diffX, mw, mxw);
29545                     diffY = this.constrain(h, diffY, mh, mxh);
29546                     y += diffY;
29547                     h -= diffY;
29548                     x += diffX;
29549                     w -= diffX;
29550                     break;
29551                case "southwest":
29552                     diffX = this.constrain(w, diffX, mw, mxw);
29553                     h += diffY;
29554                     h = Math.min(Math.max(mh, h), mxh);
29555                     x += diffX;
29556                     w -= diffX;
29557                     break;
29558             }
29559
29560             var sw = this.snap(w, wi, mw);
29561             var sh = this.snap(h, hi, mh);
29562             if(sw != w || sh != h){
29563                 switch(pos){
29564                     case "northeast":
29565                         y -= sh - h;
29566                     break;
29567                     case "north":
29568                         y -= sh - h;
29569                         break;
29570                     case "southwest":
29571                         x -= sw - w;
29572                     break;
29573                     case "west":
29574                         x -= sw - w;
29575                         break;
29576                     case "northwest":
29577                         x -= sw - w;
29578                         y -= sh - h;
29579                     break;
29580                 }
29581                 w = sw;
29582                 h = sh;
29583             }
29584
29585             if(this.preserveRatio){
29586                 switch(pos){
29587                     case "southeast":
29588                     case "east":
29589                         h = oh * (w/ow);
29590                         h = Math.min(Math.max(mh, h), mxh);
29591                         w = ow * (h/oh);
29592                        break;
29593                     case "south":
29594                         w = ow * (h/oh);
29595                         w = Math.min(Math.max(mw, w), mxw);
29596                         h = oh * (w/ow);
29597                         break;
29598                     case "northeast":
29599                         w = ow * (h/oh);
29600                         w = Math.min(Math.max(mw, w), mxw);
29601                         h = oh * (w/ow);
29602                     break;
29603                     case "north":
29604                         var tw = w;
29605                         w = ow * (h/oh);
29606                         w = Math.min(Math.max(mw, w), mxw);
29607                         h = oh * (w/ow);
29608                         x += (tw - w) / 2;
29609                         break;
29610                     case "southwest":
29611                         h = oh * (w/ow);
29612                         h = Math.min(Math.max(mh, h), mxh);
29613                         var tw = w;
29614                         w = ow * (h/oh);
29615                         x += tw - w;
29616                         break;
29617                     case "west":
29618                         var th = h;
29619                         h = oh * (w/ow);
29620                         h = Math.min(Math.max(mh, h), mxh);
29621                         y += (th - h) / 2;
29622                         var tw = w;
29623                         w = ow * (h/oh);
29624                         x += tw - w;
29625                        break;
29626                     case "northwest":
29627                         var tw = w;
29628                         var th = h;
29629                         h = oh * (w/ow);
29630                         h = Math.min(Math.max(mh, h), mxh);
29631                         w = ow * (h/oh);
29632                         y += th - h;
29633                         x += tw - w;
29634                        break;
29635
29636                 }
29637             }
29638             if (pos == 'hdrag') {
29639                 w = ow;
29640             }
29641             this.proxy.setBounds(x, y, w, h);
29642             if(this.dynamic){
29643                 this.resizeElement();
29644             }
29645             }catch(e){}
29646         }
29647         this.fireEvent("resizing", this, x, y, w, h, e);
29648     },
29649
29650     // private
29651     handleOver : function(){
29652         if(this.enabled){
29653             this.el.addClass("x-resizable-over");
29654         }
29655     },
29656
29657     // private
29658     handleOut : function(){
29659         if(!this.resizing){
29660             this.el.removeClass("x-resizable-over");
29661         }
29662     },
29663
29664     /**
29665      * Returns the element this component is bound to.
29666      * @return {Roo.Element}
29667      */
29668     getEl : function(){
29669         return this.el;
29670     },
29671
29672     /**
29673      * Returns the resizeChild element (or null).
29674      * @return {Roo.Element}
29675      */
29676     getResizeChild : function(){
29677         return this.resizeChild;
29678     },
29679     groupHandler : function()
29680     {
29681         
29682     },
29683     /**
29684      * Destroys this resizable. If the element was wrapped and
29685      * removeEl is not true then the element remains.
29686      * @param {Boolean} removeEl (optional) true to remove the element from the DOM
29687      */
29688     destroy : function(removeEl){
29689         this.proxy.remove();
29690         if(this.overlay){
29691             this.overlay.removeAllListeners();
29692             this.overlay.remove();
29693         }
29694         var ps = Roo.Resizable.positions;
29695         for(var k in ps){
29696             if(typeof ps[k] != "function" && this[ps[k]]){
29697                 var h = this[ps[k]];
29698                 h.el.removeAllListeners();
29699                 h.el.remove();
29700             }
29701         }
29702         if(removeEl){
29703             this.el.update("");
29704             this.el.remove();
29705         }
29706     }
29707 });
29708
29709 // private
29710 // hash to map config positions to true positions
29711 Roo.Resizable.positions = {
29712     n: "north", s: "south", e: "east", w: "west", se: "southeast", sw: "southwest", nw: "northwest", ne: "northeast", 
29713     hd: "hdrag"
29714 };
29715
29716 // private
29717 Roo.Resizable.Handle = function(rz, pos, disableTrackOver, transparent){
29718     if(!this.tpl){
29719         // only initialize the template if resizable is used
29720         var tpl = Roo.DomHelper.createTemplate(
29721             {tag: "div", cls: "x-resizable-handle x-resizable-handle-{0}"}
29722         );
29723         tpl.compile();
29724         Roo.Resizable.Handle.prototype.tpl = tpl;
29725     }
29726     this.position = pos;
29727     this.rz = rz;
29728     // show north drag fro topdra
29729     var handlepos = pos == 'hdrag' ? 'north' : pos;
29730     
29731     this.el = this.tpl.append(rz.el.dom, [handlepos], true);
29732     if (pos == 'hdrag') {
29733         this.el.setStyle('cursor', 'pointer');
29734     }
29735     this.el.unselectable();
29736     if(transparent){
29737         this.el.setOpacity(0);
29738     }
29739     this.el.on("mousedown", this.onMouseDown, this);
29740     if(!disableTrackOver){
29741         this.el.on("mouseover", this.onMouseOver, this);
29742         this.el.on("mouseout", this.onMouseOut, this);
29743     }
29744 };
29745
29746 // private
29747 Roo.Resizable.Handle.prototype = {
29748     afterResize : function(rz){
29749         Roo.log('after?');
29750         // do nothing
29751     },
29752     // private
29753     onMouseDown : function(e){
29754         this.rz.onMouseDown(this, e);
29755     },
29756     // private
29757     onMouseOver : function(e){
29758         this.rz.handleOver(this, e);
29759     },
29760     // private
29761     onMouseOut : function(e){
29762         this.rz.handleOut(this, e);
29763     }
29764 };/*
29765  * Based on:
29766  * Ext JS Library 1.1.1
29767  * Copyright(c) 2006-2007, Ext JS, LLC.
29768  *
29769  * Originally Released Under LGPL - original licence link has changed is not relivant.
29770  *
29771  * Fork - LGPL
29772  * <script type="text/javascript">
29773  */
29774
29775 /**
29776  * @class Roo.Editor
29777  * @extends Roo.Component
29778  * A base editor field that handles displaying/hiding on demand and has some built-in sizing and event handling logic.
29779  * @constructor
29780  * Create a new Editor
29781  * @param {Roo.form.Field} field The Field object (or descendant)
29782  * @param {Object} config The config object
29783  */
29784 Roo.Editor = function(field, config){
29785     Roo.Editor.superclass.constructor.call(this, config);
29786     this.field = field;
29787     this.addEvents({
29788         /**
29789              * @event beforestartedit
29790              * Fires when editing is initiated, but before the value changes.  Editing can be canceled by returning
29791              * false from the handler of this event.
29792              * @param {Editor} this
29793              * @param {Roo.Element} boundEl The underlying element bound to this editor
29794              * @param {Mixed} value The field value being set
29795              */
29796         "beforestartedit" : true,
29797         /**
29798              * @event startedit
29799              * Fires when this editor is displayed
29800              * @param {Roo.Element} boundEl The underlying element bound to this editor
29801              * @param {Mixed} value The starting field value
29802              */
29803         "startedit" : true,
29804         /**
29805              * @event beforecomplete
29806              * Fires after a change has been made to the field, but before the change is reflected in the underlying
29807              * field.  Saving the change to the field can be canceled by returning false from the handler of this event.
29808              * Note that if the value has not changed and ignoreNoChange = true, the editing will still end but this
29809              * event will not fire since no edit actually occurred.
29810              * @param {Editor} this
29811              * @param {Mixed} value The current field value
29812              * @param {Mixed} startValue The original field value
29813              */
29814         "beforecomplete" : true,
29815         /**
29816              * @event complete
29817              * Fires after editing is complete and any changed value has been written to the underlying field.
29818              * @param {Editor} this
29819              * @param {Mixed} value The current field value
29820              * @param {Mixed} startValue The original field value
29821              */
29822         "complete" : true,
29823         /**
29824          * @event specialkey
29825          * Fires when any key related to navigation (arrows, tab, enter, esc, etc.) is pressed.  You can check
29826          * {@link Roo.EventObject#getKey} to determine which key was pressed.
29827          * @param {Roo.form.Field} this
29828          * @param {Roo.EventObject} e The event object
29829          */
29830         "specialkey" : true
29831     });
29832 };
29833
29834 Roo.extend(Roo.Editor, Roo.Component, {
29835     /**
29836      * @cfg {Boolean/String} autosize
29837      * True for the editor to automatically adopt the size of the underlying field, "width" to adopt the width only,
29838      * or "height" to adopt the height only (defaults to false)
29839      */
29840     /**
29841      * @cfg {Boolean} revertInvalid
29842      * True to automatically revert the field value and cancel the edit when the user completes an edit and the field
29843      * validation fails (defaults to true)
29844      */
29845     /**
29846      * @cfg {Boolean} ignoreNoChange
29847      * True to skip the the edit completion process (no save, no events fired) if the user completes an edit and
29848      * the value has not changed (defaults to false).  Applies only to string values - edits for other data types
29849      * will never be ignored.
29850      */
29851     /**
29852      * @cfg {Boolean} hideEl
29853      * False to keep the bound element visible while the editor is displayed (defaults to true)
29854      */
29855     /**
29856      * @cfg {Mixed} value
29857      * The data value of the underlying field (defaults to "")
29858      */
29859     value : "",
29860     /**
29861      * @cfg {String} alignment
29862      * The position to align to (see {@link Roo.Element#alignTo} for more details, defaults to "c-c?").
29863      */
29864     alignment: "c-c?",
29865     /**
29866      * @cfg {Boolean/String} shadow "sides" for sides/bottom only, "frame" for 4-way shadow, and "drop"
29867      * for bottom-right shadow (defaults to "frame")
29868      */
29869     shadow : "frame",
29870     /**
29871      * @cfg {Boolean} constrain True to constrain the editor to the viewport
29872      */
29873     constrain : false,
29874     /**
29875      * @cfg {Boolean} completeOnEnter True to complete the edit when the enter key is pressed (defaults to false)
29876      */
29877     completeOnEnter : false,
29878     /**
29879      * @cfg {Boolean} cancelOnEsc True to cancel the edit when the escape key is pressed (defaults to false)
29880      */
29881     cancelOnEsc : false,
29882     /**
29883      * @cfg {Boolean} updateEl True to update the innerHTML of the bound element when the update completes (defaults to false)
29884      */
29885     updateEl : false,
29886
29887     // private
29888     onRender : function(ct, position){
29889         this.el = new Roo.Layer({
29890             shadow: this.shadow,
29891             cls: "x-editor",
29892             parentEl : ct,
29893             shim : this.shim,
29894             shadowOffset:4,
29895             id: this.id,
29896             constrain: this.constrain
29897         });
29898         this.el.setStyle("overflow", Roo.isGecko ? "auto" : "hidden");
29899         if(this.field.msgTarget != 'title'){
29900             this.field.msgTarget = 'qtip';
29901         }
29902         this.field.render(this.el);
29903         if(Roo.isGecko){
29904             this.field.el.dom.setAttribute('autocomplete', 'off');
29905         }
29906         this.field.on("specialkey", this.onSpecialKey, this);
29907         if(this.swallowKeys){
29908             this.field.el.swallowEvent(['keydown','keypress']);
29909         }
29910         this.field.show();
29911         this.field.on("blur", this.onBlur, this);
29912         if(this.field.grow){
29913             this.field.on("autosize", this.el.sync,  this.el, {delay:1});
29914         }
29915     },
29916
29917     onSpecialKey : function(field, e)
29918     {
29919         //Roo.log('editor onSpecialKey');
29920         if(this.completeOnEnter && e.getKey() == e.ENTER){
29921             e.stopEvent();
29922             this.completeEdit();
29923             return;
29924         }
29925         // do not fire special key otherwise it might hide close the editor...
29926         if(e.getKey() == e.ENTER){    
29927             return;
29928         }
29929         if(this.cancelOnEsc && e.getKey() == e.ESC){
29930             this.cancelEdit();
29931             return;
29932         } 
29933         this.fireEvent('specialkey', field, e);
29934     
29935     },
29936
29937     /**
29938      * Starts the editing process and shows the editor.
29939      * @param {String/HTMLElement/Element} el The element to edit
29940      * @param {String} value (optional) A value to initialize the editor with. If a value is not provided, it defaults
29941       * to the innerHTML of el.
29942      */
29943     startEdit : function(el, value){
29944         if(this.editing){
29945             this.completeEdit();
29946         }
29947         this.boundEl = Roo.get(el);
29948         var v = value !== undefined ? value : this.boundEl.dom.innerHTML;
29949         if(!this.rendered){
29950             this.render(this.parentEl || document.body);
29951         }
29952         if(this.fireEvent("beforestartedit", this, this.boundEl, v) === false){
29953             return;
29954         }
29955         this.startValue = v;
29956         this.field.setValue(v);
29957         if(this.autoSize){
29958             var sz = this.boundEl.getSize();
29959             switch(this.autoSize){
29960                 case "width":
29961                 this.setSize(sz.width,  "");
29962                 break;
29963                 case "height":
29964                 this.setSize("",  sz.height);
29965                 break;
29966                 default:
29967                 this.setSize(sz.width,  sz.height);
29968             }
29969         }
29970         this.el.alignTo(this.boundEl, this.alignment);
29971         this.editing = true;
29972         if(Roo.QuickTips){
29973             Roo.QuickTips.disable();
29974         }
29975         this.show();
29976     },
29977
29978     /**
29979      * Sets the height and width of this editor.
29980      * @param {Number} width The new width
29981      * @param {Number} height The new height
29982      */
29983     setSize : function(w, h){
29984         this.field.setSize(w, h);
29985         if(this.el){
29986             this.el.sync();
29987         }
29988     },
29989
29990     /**
29991      * Realigns the editor to the bound field based on the current alignment config value.
29992      */
29993     realign : function(){
29994         this.el.alignTo(this.boundEl, this.alignment);
29995     },
29996
29997     /**
29998      * Ends the editing process, persists the changed value to the underlying field, and hides the editor.
29999      * @param {Boolean} remainVisible Override the default behavior and keep the editor visible after edit (defaults to false)
30000      */
30001     completeEdit : function(remainVisible){
30002         if(!this.editing){
30003             return;
30004         }
30005         var v = this.getValue();
30006         if(this.revertInvalid !== false && !this.field.isValid()){
30007             v = this.startValue;
30008             this.cancelEdit(true);
30009         }
30010         if(String(v) === String(this.startValue) && this.ignoreNoChange){
30011             this.editing = false;
30012             this.hide();
30013             return;
30014         }
30015         if(this.fireEvent("beforecomplete", this, v, this.startValue) !== false){
30016             this.editing = false;
30017             if(this.updateEl && this.boundEl){
30018                 this.boundEl.update(v);
30019             }
30020             if(remainVisible !== true){
30021                 this.hide();
30022             }
30023             this.fireEvent("complete", this, v, this.startValue);
30024         }
30025     },
30026
30027     // private
30028     onShow : function(){
30029         this.el.show();
30030         if(this.hideEl !== false){
30031             this.boundEl.hide();
30032         }
30033         this.field.show();
30034         if(Roo.isIE && !this.fixIEFocus){ // IE has problems with focusing the first time
30035             this.fixIEFocus = true;
30036             this.deferredFocus.defer(50, this);
30037         }else{
30038             this.field.focus();
30039         }
30040         this.fireEvent("startedit", this.boundEl, this.startValue);
30041     },
30042
30043     deferredFocus : function(){
30044         if(this.editing){
30045             this.field.focus();
30046         }
30047     },
30048
30049     /**
30050      * Cancels the editing process and hides the editor without persisting any changes.  The field value will be
30051      * reverted to the original starting value.
30052      * @param {Boolean} remainVisible Override the default behavior and keep the editor visible after
30053      * cancel (defaults to false)
30054      */
30055     cancelEdit : function(remainVisible){
30056         if(this.editing){
30057             this.setValue(this.startValue);
30058             if(remainVisible !== true){
30059                 this.hide();
30060             }
30061         }
30062     },
30063
30064     // private
30065     onBlur : function(){
30066         if(this.allowBlur !== true && this.editing){
30067             this.completeEdit();
30068         }
30069     },
30070
30071     // private
30072     onHide : function(){
30073         if(this.editing){
30074             this.completeEdit();
30075             return;
30076         }
30077         this.field.blur();
30078         if(this.field.collapse){
30079             this.field.collapse();
30080         }
30081         this.el.hide();
30082         if(this.hideEl !== false){
30083             this.boundEl.show();
30084         }
30085         if(Roo.QuickTips){
30086             Roo.QuickTips.enable();
30087         }
30088     },
30089
30090     /**
30091      * Sets the data value of the editor
30092      * @param {Mixed} value Any valid value supported by the underlying field
30093      */
30094     setValue : function(v){
30095         this.field.setValue(v);
30096     },
30097
30098     /**
30099      * Gets the data value of the editor
30100      * @return {Mixed} The data value
30101      */
30102     getValue : function(){
30103         return this.field.getValue();
30104     }
30105 });/*
30106  * Based on:
30107  * Ext JS Library 1.1.1
30108  * Copyright(c) 2006-2007, Ext JS, LLC.
30109  *
30110  * Originally Released Under LGPL - original licence link has changed is not relivant.
30111  *
30112  * Fork - LGPL
30113  * <script type="text/javascript">
30114  */
30115  
30116 /**
30117  * @class Roo.BasicDialog
30118  * @extends Roo.util.Observable
30119  * Lightweight Dialog Class.  The code below shows the creation of a typical dialog using existing HTML markup:
30120  * <pre><code>
30121 var dlg = new Roo.BasicDialog("my-dlg", {
30122     height: 200,
30123     width: 300,
30124     minHeight: 100,
30125     minWidth: 150,
30126     modal: true,
30127     proxyDrag: true,
30128     shadow: true
30129 });
30130 dlg.addKeyListener(27, dlg.hide, dlg); // ESC can also close the dialog
30131 dlg.addButton('OK', dlg.hide, dlg);    // Could call a save function instead of hiding
30132 dlg.addButton('Cancel', dlg.hide, dlg);
30133 dlg.show();
30134 </code></pre>
30135   <b>A Dialog should always be a direct child of the body element.</b>
30136  * @cfg {Boolean/DomHelper} autoCreate True to auto create from scratch, or using a DomHelper Object (defaults to false)
30137  * @cfg {String} title Default text to display in the title bar (defaults to null)
30138  * @cfg {Number} width Width of the dialog in pixels (can also be set via CSS).  Determined by browser if unspecified.
30139  * @cfg {Number} height Height of the dialog in pixels (can also be set via CSS).  Determined by browser if unspecified.
30140  * @cfg {Number} x The default left page coordinate of the dialog (defaults to center screen)
30141  * @cfg {Number} y The default top page coordinate of the dialog (defaults to center screen)
30142  * @cfg {String/Element} animateTarget Id or element from which the dialog should animate while opening
30143  * (defaults to null with no animation)
30144  * @cfg {Boolean} resizable False to disable manual dialog resizing (defaults to true)
30145  * @cfg {String} resizeHandles Which resize handles to display - see the {@link Roo.Resizable} handles config
30146  * property for valid values (defaults to 'all')
30147  * @cfg {Number} minHeight The minimum allowable height for a resizable dialog (defaults to 80)
30148  * @cfg {Number} minWidth The minimum allowable width for a resizable dialog (defaults to 200)
30149  * @cfg {Boolean} modal True to show the dialog modally, preventing user interaction with the rest of the page (defaults to false)
30150  * @cfg {Boolean} autoScroll True to allow the dialog body contents to overflow and display scrollbars (defaults to false)
30151  * @cfg {Boolean} closable False to remove the built-in top-right corner close button (defaults to true)
30152  * @cfg {Boolean} collapsible False to remove the built-in top-right corner collapse button (defaults to true)
30153  * @cfg {Boolean} constraintoviewport True to keep the dialog constrained within the visible viewport boundaries (defaults to true)
30154  * @cfg {Boolean} syncHeightBeforeShow True to cause the dimensions to be recalculated before the dialog is shown (defaults to false)
30155  * @cfg {Boolean} draggable False to disable dragging of the dialog within the viewport (defaults to true)
30156  * @cfg {Boolean} autoTabs If true, all elements with class 'x-dlg-tab' will get automatically converted to tabs (defaults to false)
30157  * @cfg {String} tabTag The tag name of tab elements, used when autoTabs = true (defaults to 'div')
30158  * @cfg {Boolean} proxyDrag True to drag a lightweight proxy element rather than the dialog itself, used when
30159  * draggable = true (defaults to false)
30160  * @cfg {Boolean} fixedcenter True to ensure that anytime the dialog is shown or resized it gets centered (defaults to false)
30161  * @cfg {Boolean/String} shadow True or "sides" for the default effect, "frame" for 4-way shadow, and "drop" for bottom-right
30162  * shadow (defaults to false)
30163  * @cfg {Number} shadowOffset The number of pixels to offset the shadow if displayed (defaults to 5)
30164  * @cfg {String} buttonAlign Valid values are "left," "center" and "right" (defaults to "right")
30165  * @cfg {Number} minButtonWidth Minimum width of all dialog buttons (defaults to 75)
30166  * @cfg {Array} buttons Array of buttons
30167  * @cfg {Boolean} shim True to create an iframe shim that prevents selects from showing through (defaults to false)
30168  * @constructor
30169  * Create a new BasicDialog.
30170  * @param {String/HTMLElement/Roo.Element} el The container element or DOM node, or its id
30171  * @param {Object} config Configuration options
30172  */
30173 Roo.BasicDialog = function(el, config){
30174     this.el = Roo.get(el);
30175     var dh = Roo.DomHelper;
30176     if(!this.el && config && config.autoCreate){
30177         if(typeof config.autoCreate == "object"){
30178             if(!config.autoCreate.id){
30179                 config.autoCreate.id = el;
30180             }
30181             this.el = dh.append(document.body,
30182                         config.autoCreate, true);
30183         }else{
30184             this.el = dh.append(document.body,
30185                         {tag: "div", id: el, style:'visibility:hidden;'}, true);
30186         }
30187     }
30188     el = this.el;
30189     el.setDisplayed(true);
30190     el.hide = this.hideAction;
30191     this.id = el.id;
30192     el.addClass("x-dlg");
30193
30194     Roo.apply(this, config);
30195
30196     this.proxy = el.createProxy("x-dlg-proxy");
30197     this.proxy.hide = this.hideAction;
30198     this.proxy.setOpacity(.5);
30199     this.proxy.hide();
30200
30201     if(config.width){
30202         el.setWidth(config.width);
30203     }
30204     if(config.height){
30205         el.setHeight(config.height);
30206     }
30207     this.size = el.getSize();
30208     if(typeof config.x != "undefined" && typeof config.y != "undefined"){
30209         this.xy = [config.x,config.y];
30210     }else{
30211         this.xy = el.getCenterXY(true);
30212     }
30213     /** The header element @type Roo.Element */
30214     this.header = el.child("> .x-dlg-hd");
30215     /** The body element @type Roo.Element */
30216     this.body = el.child("> .x-dlg-bd");
30217     /** The footer element @type Roo.Element */
30218     this.footer = el.child("> .x-dlg-ft");
30219
30220     if(!this.header){
30221         this.header = el.createChild({tag: "div", cls:"x-dlg-hd", html: "&#160;"}, this.body ? this.body.dom : null);
30222     }
30223     if(!this.body){
30224         this.body = el.createChild({tag: "div", cls:"x-dlg-bd"});
30225     }
30226
30227     this.header.unselectable();
30228     if(this.title){
30229         this.header.update(this.title);
30230     }
30231     // this element allows the dialog to be focused for keyboard event
30232     this.focusEl = el.createChild({tag: "a", href:"#", cls:"x-dlg-focus", tabIndex:"-1"});
30233     this.focusEl.swallowEvent("click", true);
30234
30235     this.header.wrap({cls:"x-dlg-hd-right"}).wrap({cls:"x-dlg-hd-left"}, true);
30236
30237     // wrap the body and footer for special rendering
30238     this.bwrap = this.body.wrap({tag: "div", cls:"x-dlg-dlg-body"});
30239     if(this.footer){
30240         this.bwrap.dom.appendChild(this.footer.dom);
30241     }
30242
30243     this.bg = this.el.createChild({
30244         tag: "div", cls:"x-dlg-bg",
30245         html: '<div class="x-dlg-bg-left"><div class="x-dlg-bg-right"><div class="x-dlg-bg-center">&#160;</div></div></div>'
30246     });
30247     this.centerBg = this.bg.child("div.x-dlg-bg-center");
30248
30249
30250     if(this.autoScroll !== false && !this.autoTabs){
30251         this.body.setStyle("overflow", "auto");
30252     }
30253
30254     this.toolbox = this.el.createChild({cls: "x-dlg-toolbox"});
30255
30256     if(this.closable !== false){
30257         this.el.addClass("x-dlg-closable");
30258         this.close = this.toolbox.createChild({cls:"x-dlg-close"});
30259         this.close.on("click", this.closeClick, this);
30260         this.close.addClassOnOver("x-dlg-close-over");
30261     }
30262     if(this.collapsible !== false){
30263         this.collapseBtn = this.toolbox.createChild({cls:"x-dlg-collapse"});
30264         this.collapseBtn.on("click", this.collapseClick, this);
30265         this.collapseBtn.addClassOnOver("x-dlg-collapse-over");
30266         this.header.on("dblclick", this.collapseClick, this);
30267     }
30268     if(this.resizable !== false){
30269         this.el.addClass("x-dlg-resizable");
30270         this.resizer = new Roo.Resizable(el, {
30271             minWidth: this.minWidth || 80,
30272             minHeight:this.minHeight || 80,
30273             handles: this.resizeHandles || "all",
30274             pinned: true
30275         });
30276         this.resizer.on("beforeresize", this.beforeResize, this);
30277         this.resizer.on("resize", this.onResize, this);
30278     }
30279     if(this.draggable !== false){
30280         el.addClass("x-dlg-draggable");
30281         if (!this.proxyDrag) {
30282             var dd = new Roo.dd.DD(el.dom.id, "WindowDrag");
30283         }
30284         else {
30285             var dd = new Roo.dd.DDProxy(el.dom.id, "WindowDrag", {dragElId: this.proxy.id});
30286         }
30287         dd.setHandleElId(this.header.id);
30288         dd.endDrag = this.endMove.createDelegate(this);
30289         dd.startDrag = this.startMove.createDelegate(this);
30290         dd.onDrag = this.onDrag.createDelegate(this);
30291         dd.scroll = false;
30292         this.dd = dd;
30293     }
30294     if(this.modal){
30295         this.mask = dh.append(document.body, {tag: "div", cls:"x-dlg-mask"}, true);
30296         this.mask.enableDisplayMode("block");
30297         this.mask.hide();
30298         this.el.addClass("x-dlg-modal");
30299     }
30300     if(this.shadow){
30301         this.shadow = new Roo.Shadow({
30302             mode : typeof this.shadow == "string" ? this.shadow : "sides",
30303             offset : this.shadowOffset
30304         });
30305     }else{
30306         this.shadowOffset = 0;
30307     }
30308     if(Roo.useShims && this.shim !== false){
30309         this.shim = this.el.createShim();
30310         this.shim.hide = this.hideAction;
30311         this.shim.hide();
30312     }else{
30313         this.shim = false;
30314     }
30315     if(this.autoTabs){
30316         this.initTabs();
30317     }
30318     if (this.buttons) { 
30319         var bts= this.buttons;
30320         this.buttons = [];
30321         Roo.each(bts, function(b) {
30322             this.addButton(b);
30323         }, this);
30324     }
30325     
30326     
30327     this.addEvents({
30328         /**
30329          * @event keydown
30330          * Fires when a key is pressed
30331          * @param {Roo.BasicDialog} this
30332          * @param {Roo.EventObject} e
30333          */
30334         "keydown" : true,
30335         /**
30336          * @event move
30337          * Fires when this dialog is moved by the user.
30338          * @param {Roo.BasicDialog} this
30339          * @param {Number} x The new page X
30340          * @param {Number} y The new page Y
30341          */
30342         "move" : true,
30343         /**
30344          * @event resize
30345          * Fires when this dialog is resized by the user.
30346          * @param {Roo.BasicDialog} this
30347          * @param {Number} width The new width
30348          * @param {Number} height The new height
30349          */
30350         "resize" : true,
30351         /**
30352          * @event beforehide
30353          * Fires before this dialog is hidden.
30354          * @param {Roo.BasicDialog} this
30355          */
30356         "beforehide" : true,
30357         /**
30358          * @event hide
30359          * Fires when this dialog is hidden.
30360          * @param {Roo.BasicDialog} this
30361          */
30362         "hide" : true,
30363         /**
30364          * @event beforeshow
30365          * Fires before this dialog is shown.
30366          * @param {Roo.BasicDialog} this
30367          */
30368         "beforeshow" : true,
30369         /**
30370          * @event show
30371          * Fires when this dialog is shown.
30372          * @param {Roo.BasicDialog} this
30373          */
30374         "show" : true
30375     });
30376     el.on("keydown", this.onKeyDown, this);
30377     el.on("mousedown", this.toFront, this);
30378     Roo.EventManager.onWindowResize(this.adjustViewport, this, true);
30379     this.el.hide();
30380     Roo.DialogManager.register(this);
30381     Roo.BasicDialog.superclass.constructor.call(this);
30382 };
30383
30384 Roo.extend(Roo.BasicDialog, Roo.util.Observable, {
30385     shadowOffset: Roo.isIE ? 6 : 5,
30386     minHeight: 80,
30387     minWidth: 200,
30388     minButtonWidth: 75,
30389     defaultButton: null,
30390     buttonAlign: "right",
30391     tabTag: 'div',
30392     firstShow: true,
30393
30394     /**
30395      * Sets the dialog title text
30396      * @param {String} text The title text to display
30397      * @return {Roo.BasicDialog} this
30398      */
30399     setTitle : function(text){
30400         this.header.update(text);
30401         return this;
30402     },
30403
30404     // private
30405     closeClick : function(){
30406         this.hide();
30407     },
30408
30409     // private
30410     collapseClick : function(){
30411         this[this.collapsed ? "expand" : "collapse"]();
30412     },
30413
30414     /**
30415      * Collapses the dialog to its minimized state (only the title bar is visible).
30416      * Equivalent to the user clicking the collapse dialog button.
30417      */
30418     collapse : function(){
30419         if(!this.collapsed){
30420             this.collapsed = true;
30421             this.el.addClass("x-dlg-collapsed");
30422             this.restoreHeight = this.el.getHeight();
30423             this.resizeTo(this.el.getWidth(), this.header.getHeight());
30424         }
30425     },
30426
30427     /**
30428      * Expands a collapsed dialog back to its normal state.  Equivalent to the user
30429      * clicking the expand dialog button.
30430      */
30431     expand : function(){
30432         if(this.collapsed){
30433             this.collapsed = false;
30434             this.el.removeClass("x-dlg-collapsed");
30435             this.resizeTo(this.el.getWidth(), this.restoreHeight);
30436         }
30437     },
30438
30439     /**
30440      * Reinitializes the tabs component, clearing out old tabs and finding new ones.
30441      * @return {Roo.TabPanel} The tabs component
30442      */
30443     initTabs : function(){
30444         var tabs = this.getTabs();
30445         while(tabs.getTab(0)){
30446             tabs.removeTab(0);
30447         }
30448         this.el.select(this.tabTag+'.x-dlg-tab').each(function(el){
30449             var dom = el.dom;
30450             tabs.addTab(Roo.id(dom), dom.title);
30451             dom.title = "";
30452         });
30453         tabs.activate(0);
30454         return tabs;
30455     },
30456
30457     // private
30458     beforeResize : function(){
30459         this.resizer.minHeight = Math.max(this.minHeight, this.getHeaderFooterHeight(true)+40);
30460     },
30461
30462     // private
30463     onResize : function(){
30464         this.refreshSize();
30465         this.syncBodyHeight();
30466         this.adjustAssets();
30467         this.focus();
30468         this.fireEvent("resize", this, this.size.width, this.size.height);
30469     },
30470
30471     // private
30472     onKeyDown : function(e){
30473         if(this.isVisible()){
30474             this.fireEvent("keydown", this, e);
30475         }
30476     },
30477
30478     /**
30479      * Resizes the dialog.
30480      * @param {Number} width
30481      * @param {Number} height
30482      * @return {Roo.BasicDialog} this
30483      */
30484     resizeTo : function(width, height){
30485         this.el.setSize(width, height);
30486         this.size = {width: width, height: height};
30487         this.syncBodyHeight();
30488         if(this.fixedcenter){
30489             this.center();
30490         }
30491         if(this.isVisible()){
30492             this.constrainXY();
30493             this.adjustAssets();
30494         }
30495         this.fireEvent("resize", this, width, height);
30496         return this;
30497     },
30498
30499
30500     /**
30501      * Resizes the dialog to fit the specified content size.
30502      * @param {Number} width
30503      * @param {Number} height
30504      * @return {Roo.BasicDialog} this
30505      */
30506     setContentSize : function(w, h){
30507         h += this.getHeaderFooterHeight() + this.body.getMargins("tb");
30508         w += this.body.getMargins("lr") + this.bwrap.getMargins("lr") + this.centerBg.getPadding("lr");
30509         //if(!this.el.isBorderBox()){
30510             h +=  this.body.getPadding("tb") + this.bwrap.getBorderWidth("tb") + this.body.getBorderWidth("tb") + this.el.getBorderWidth("tb");
30511             w += this.body.getPadding("lr") + this.bwrap.getBorderWidth("lr") + this.body.getBorderWidth("lr") + this.bwrap.getPadding("lr") + this.el.getBorderWidth("lr");
30512         //}
30513         if(this.tabs){
30514             h += this.tabs.stripWrap.getHeight() + this.tabs.bodyEl.getMargins("tb") + this.tabs.bodyEl.getPadding("tb");
30515             w += this.tabs.bodyEl.getMargins("lr") + this.tabs.bodyEl.getPadding("lr");
30516         }
30517         this.resizeTo(w, h);
30518         return this;
30519     },
30520
30521     /**
30522      * Adds a key listener for when this dialog is displayed.  This allows you to hook in a function that will be
30523      * executed in response to a particular key being pressed while the dialog is active.
30524      * @param {Number/Array/Object} key Either the numeric key code, array of key codes or an object with the following options:
30525      *                                  {key: (number or array), shift: (true/false), ctrl: (true/false), alt: (true/false)}
30526      * @param {Function} fn The function to call
30527      * @param {Object} scope (optional) The scope of the function
30528      * @return {Roo.BasicDialog} this
30529      */
30530     addKeyListener : function(key, fn, scope){
30531         var keyCode, shift, ctrl, alt;
30532         if(typeof key == "object" && !(key instanceof Array)){
30533             keyCode = key["key"];
30534             shift = key["shift"];
30535             ctrl = key["ctrl"];
30536             alt = key["alt"];
30537         }else{
30538             keyCode = key;
30539         }
30540         var handler = function(dlg, e){
30541             if((!shift || e.shiftKey) && (!ctrl || e.ctrlKey) &&  (!alt || e.altKey)){
30542                 var k = e.getKey();
30543                 if(keyCode instanceof Array){
30544                     for(var i = 0, len = keyCode.length; i < len; i++){
30545                         if(keyCode[i] == k){
30546                           fn.call(scope || window, dlg, k, e);
30547                           return;
30548                         }
30549                     }
30550                 }else{
30551                     if(k == keyCode){
30552                         fn.call(scope || window, dlg, k, e);
30553                     }
30554                 }
30555             }
30556         };
30557         this.on("keydown", handler);
30558         return this;
30559     },
30560
30561     /**
30562      * Returns the TabPanel component (creates it if it doesn't exist).
30563      * Note: If you wish to simply check for the existence of tabs without creating them,
30564      * check for a null 'tabs' property.
30565      * @return {Roo.TabPanel} The tabs component
30566      */
30567     getTabs : function(){
30568         if(!this.tabs){
30569             this.el.addClass("x-dlg-auto-tabs");
30570             this.body.addClass(this.tabPosition == "bottom" ? "x-tabs-bottom" : "x-tabs-top");
30571             this.tabs = new Roo.TabPanel(this.body.dom, this.tabPosition == "bottom");
30572         }
30573         return this.tabs;
30574     },
30575
30576     /**
30577      * Adds a button to the footer section of the dialog.
30578      * @param {String/Object} config A string becomes the button text, an object can either be a Button config
30579      * object or a valid Roo.DomHelper element config
30580      * @param {Function} handler The function called when the button is clicked
30581      * @param {Object} scope (optional) The scope of the handler function (accepts position as a property)
30582      * @return {Roo.Button} The new button
30583      */
30584     addButton : function(config, handler, scope){
30585         var dh = Roo.DomHelper;
30586         if(!this.footer){
30587             this.footer = dh.append(this.bwrap, {tag: "div", cls:"x-dlg-ft"}, true);
30588         }
30589         if(!this.btnContainer){
30590             var tb = this.footer.createChild({
30591
30592                 cls:"x-dlg-btns x-dlg-btns-"+this.buttonAlign,
30593                 html:'<table cellspacing="0"><tbody><tr></tr></tbody></table><div class="x-clear"></div>'
30594             }, null, true);
30595             this.btnContainer = tb.firstChild.firstChild.firstChild;
30596         }
30597         var bconfig = {
30598             handler: handler,
30599             scope: scope,
30600             minWidth: this.minButtonWidth,
30601             hideParent:true
30602         };
30603         if(typeof config == "string"){
30604             bconfig.text = config;
30605         }else{
30606             if(config.tag){
30607                 bconfig.dhconfig = config;
30608             }else{
30609                 Roo.apply(bconfig, config);
30610             }
30611         }
30612         var fc = false;
30613         if ((typeof(bconfig.position) != 'undefined') && bconfig.position < this.btnContainer.childNodes.length-1) {
30614             bconfig.position = Math.max(0, bconfig.position);
30615             fc = this.btnContainer.childNodes[bconfig.position];
30616         }
30617          
30618         var btn = new Roo.Button(
30619             fc ? 
30620                 this.btnContainer.insertBefore(document.createElement("td"),fc)
30621                 : this.btnContainer.appendChild(document.createElement("td")),
30622             //Roo.get(this.btnContainer).createChild( { tag: 'td'},  fc ),
30623             bconfig
30624         );
30625         this.syncBodyHeight();
30626         if(!this.buttons){
30627             /**
30628              * Array of all the buttons that have been added to this dialog via addButton
30629              * @type Array
30630              */
30631             this.buttons = [];
30632         }
30633         this.buttons.push(btn);
30634         return btn;
30635     },
30636
30637     /**
30638      * Sets the default button to be focused when the dialog is displayed.
30639      * @param {Roo.BasicDialog.Button} btn The button object returned by {@link #addButton}
30640      * @return {Roo.BasicDialog} this
30641      */
30642     setDefaultButton : function(btn){
30643         this.defaultButton = btn;
30644         return this;
30645     },
30646
30647     // private
30648     getHeaderFooterHeight : function(safe){
30649         var height = 0;
30650         if(this.header){
30651            height += this.header.getHeight();
30652         }
30653         if(this.footer){
30654            var fm = this.footer.getMargins();
30655             height += (this.footer.getHeight()+fm.top+fm.bottom);
30656         }
30657         height += this.bwrap.getPadding("tb")+this.bwrap.getBorderWidth("tb");
30658         height += this.centerBg.getPadding("tb");
30659         return height;
30660     },
30661
30662     // private
30663     syncBodyHeight : function()
30664     {
30665         var bd = this.body, // the text
30666             cb = this.centerBg, // wrapper around bottom.. but does not seem to be used..
30667             bw = this.bwrap;
30668         var height = this.size.height - this.getHeaderFooterHeight(false);
30669         bd.setHeight(height-bd.getMargins("tb"));
30670         var hh = this.header.getHeight();
30671         var h = this.size.height-hh;
30672         cb.setHeight(h);
30673         
30674         bw.setLeftTop(cb.getPadding("l"), hh+cb.getPadding("t"));
30675         bw.setHeight(h-cb.getPadding("tb"));
30676         
30677         bw.setWidth(this.el.getWidth(true)-cb.getPadding("lr"));
30678         bd.setWidth(bw.getWidth(true));
30679         if(this.tabs){
30680             this.tabs.syncHeight();
30681             if(Roo.isIE){
30682                 this.tabs.el.repaint();
30683             }
30684         }
30685     },
30686
30687     /**
30688      * Restores the previous state of the dialog if Roo.state is configured.
30689      * @return {Roo.BasicDialog} this
30690      */
30691     restoreState : function(){
30692         var box = Roo.state.Manager.get(this.stateId || (this.el.id + "-state"));
30693         if(box && box.width){
30694             this.xy = [box.x, box.y];
30695             this.resizeTo(box.width, box.height);
30696         }
30697         return this;
30698     },
30699
30700     // private
30701     beforeShow : function(){
30702         this.expand();
30703         if(this.fixedcenter){
30704             this.xy = this.el.getCenterXY(true);
30705         }
30706         if(this.modal){
30707             Roo.get(document.body).addClass("x-body-masked");
30708             this.mask.setSize(Roo.lib.Dom.getViewWidth(true), Roo.lib.Dom.getViewHeight(true));
30709             this.mask.show();
30710         }
30711         this.constrainXY();
30712     },
30713
30714     // private
30715     animShow : function(){
30716         var b = Roo.get(this.animateTarget).getBox();
30717         this.proxy.setSize(b.width, b.height);
30718         this.proxy.setLocation(b.x, b.y);
30719         this.proxy.show();
30720         this.proxy.setBounds(this.xy[0], this.xy[1], this.size.width, this.size.height,
30721                     true, .35, this.showEl.createDelegate(this));
30722     },
30723
30724     /**
30725      * Shows the dialog.
30726      * @param {String/HTMLElement/Roo.Element} animateTarget (optional) Reset the animation target
30727      * @return {Roo.BasicDialog} this
30728      */
30729     show : function(animateTarget){
30730         if (this.fireEvent("beforeshow", this) === false){
30731             return;
30732         }
30733         if(this.syncHeightBeforeShow){
30734             this.syncBodyHeight();
30735         }else if(this.firstShow){
30736             this.firstShow = false;
30737             this.syncBodyHeight(); // sync the height on the first show instead of in the constructor
30738         }
30739         this.animateTarget = animateTarget || this.animateTarget;
30740         if(!this.el.isVisible()){
30741             this.beforeShow();
30742             if(this.animateTarget && Roo.get(this.animateTarget)){
30743                 this.animShow();
30744             }else{
30745                 this.showEl();
30746             }
30747         }
30748         return this;
30749     },
30750
30751     // private
30752     showEl : function(){
30753         this.proxy.hide();
30754         this.el.setXY(this.xy);
30755         this.el.show();
30756         this.adjustAssets(true);
30757         this.toFront();
30758         this.focus();
30759         // IE peekaboo bug - fix found by Dave Fenwick
30760         if(Roo.isIE){
30761             this.el.repaint();
30762         }
30763         this.fireEvent("show", this);
30764     },
30765
30766     /**
30767      * Focuses the dialog.  If a defaultButton is set, it will receive focus, otherwise the
30768      * dialog itself will receive focus.
30769      */
30770     focus : function(){
30771         if(this.defaultButton){
30772             this.defaultButton.focus();
30773         }else{
30774             this.focusEl.focus();
30775         }
30776     },
30777
30778     // private
30779     constrainXY : function(){
30780         if(this.constraintoviewport !== false){
30781             if(!this.viewSize){
30782                 if(this.container){
30783                     var s = this.container.getSize();
30784                     this.viewSize = [s.width, s.height];
30785                 }else{
30786                     this.viewSize = [Roo.lib.Dom.getViewWidth(),Roo.lib.Dom.getViewHeight()];
30787                 }
30788             }
30789             var s = Roo.get(this.container||document).getScroll();
30790
30791             var x = this.xy[0], y = this.xy[1];
30792             var w = this.size.width, h = this.size.height;
30793             var vw = this.viewSize[0], vh = this.viewSize[1];
30794             // only move it if it needs it
30795             var moved = false;
30796             // first validate right/bottom
30797             if(x + w > vw+s.left){
30798                 x = vw - w;
30799                 moved = true;
30800             }
30801             if(y + h > vh+s.top){
30802                 y = vh - h;
30803                 moved = true;
30804             }
30805             // then make sure top/left isn't negative
30806             if(x < s.left){
30807                 x = s.left;
30808                 moved = true;
30809             }
30810             if(y < s.top){
30811                 y = s.top;
30812                 moved = true;
30813             }
30814             if(moved){
30815                 // cache xy
30816                 this.xy = [x, y];
30817                 if(this.isVisible()){
30818                     this.el.setLocation(x, y);
30819                     this.adjustAssets();
30820                 }
30821             }
30822         }
30823     },
30824
30825     // private
30826     onDrag : function(){
30827         if(!this.proxyDrag){
30828             this.xy = this.el.getXY();
30829             this.adjustAssets();
30830         }
30831     },
30832
30833     // private
30834     adjustAssets : function(doShow){
30835         var x = this.xy[0], y = this.xy[1];
30836         var w = this.size.width, h = this.size.height;
30837         if(doShow === true){
30838             if(this.shadow){
30839                 this.shadow.show(this.el);
30840             }
30841             if(this.shim){
30842                 this.shim.show();
30843             }
30844         }
30845         if(this.shadow && this.shadow.isVisible()){
30846             this.shadow.show(this.el);
30847         }
30848         if(this.shim && this.shim.isVisible()){
30849             this.shim.setBounds(x, y, w, h);
30850         }
30851     },
30852
30853     // private
30854     adjustViewport : function(w, h){
30855         if(!w || !h){
30856             w = Roo.lib.Dom.getViewWidth();
30857             h = Roo.lib.Dom.getViewHeight();
30858         }
30859         // cache the size
30860         this.viewSize = [w, h];
30861         if(this.modal && this.mask.isVisible()){
30862             this.mask.setSize(w, h); // first make sure the mask isn't causing overflow
30863             this.mask.setSize(Roo.lib.Dom.getViewWidth(true), Roo.lib.Dom.getViewHeight(true));
30864         }
30865         if(this.isVisible()){
30866             this.constrainXY();
30867         }
30868     },
30869
30870     /**
30871      * Destroys this dialog and all its supporting elements (including any tabs, shim,
30872      * shadow, proxy, mask, etc.)  Also removes all event listeners.
30873      * @param {Boolean} removeEl (optional) true to remove the element from the DOM
30874      */
30875     destroy : function(removeEl){
30876         if(this.isVisible()){
30877             this.animateTarget = null;
30878             this.hide();
30879         }
30880         Roo.EventManager.removeResizeListener(this.adjustViewport, this);
30881         if(this.tabs){
30882             this.tabs.destroy(removeEl);
30883         }
30884         Roo.destroy(
30885              this.shim,
30886              this.proxy,
30887              this.resizer,
30888              this.close,
30889              this.mask
30890         );
30891         if(this.dd){
30892             this.dd.unreg();
30893         }
30894         if(this.buttons){
30895            for(var i = 0, len = this.buttons.length; i < len; i++){
30896                this.buttons[i].destroy();
30897            }
30898         }
30899         this.el.removeAllListeners();
30900         if(removeEl === true){
30901             this.el.update("");
30902             this.el.remove();
30903         }
30904         Roo.DialogManager.unregister(this);
30905     },
30906
30907     // private
30908     startMove : function(){
30909         if(this.proxyDrag){
30910             this.proxy.show();
30911         }
30912         if(this.constraintoviewport !== false){
30913             this.dd.constrainTo(document.body, {right: this.shadowOffset, bottom: this.shadowOffset});
30914         }
30915     },
30916
30917     // private
30918     endMove : function(){
30919         if(!this.proxyDrag){
30920             Roo.dd.DD.prototype.endDrag.apply(this.dd, arguments);
30921         }else{
30922             Roo.dd.DDProxy.prototype.endDrag.apply(this.dd, arguments);
30923             this.proxy.hide();
30924         }
30925         this.refreshSize();
30926         this.adjustAssets();
30927         this.focus();
30928         this.fireEvent("move", this, this.xy[0], this.xy[1]);
30929     },
30930
30931     /**
30932      * Brings this dialog to the front of any other visible dialogs
30933      * @return {Roo.BasicDialog} this
30934      */
30935     toFront : function(){
30936         Roo.DialogManager.bringToFront(this);
30937         return this;
30938     },
30939
30940     /**
30941      * Sends this dialog to the back (under) of any other visible dialogs
30942      * @return {Roo.BasicDialog} this
30943      */
30944     toBack : function(){
30945         Roo.DialogManager.sendToBack(this);
30946         return this;
30947     },
30948
30949     /**
30950      * Centers this dialog in the viewport
30951      * @return {Roo.BasicDialog} this
30952      */
30953     center : function(){
30954         var xy = this.el.getCenterXY(true);
30955         this.moveTo(xy[0], xy[1]);
30956         return this;
30957     },
30958
30959     /**
30960      * Moves the dialog's top-left corner to the specified point
30961      * @param {Number} x
30962      * @param {Number} y
30963      * @return {Roo.BasicDialog} this
30964      */
30965     moveTo : function(x, y){
30966         this.xy = [x,y];
30967         if(this.isVisible()){
30968             this.el.setXY(this.xy);
30969             this.adjustAssets();
30970         }
30971         return this;
30972     },
30973
30974     /**
30975      * Aligns the dialog to the specified element
30976      * @param {String/HTMLElement/Roo.Element} element The element to align to.
30977      * @param {String} position The position to align to (see {@link Roo.Element#alignTo} for more details).
30978      * @param {Array} offsets (optional) Offset the positioning by [x, y]
30979      * @return {Roo.BasicDialog} this
30980      */
30981     alignTo : function(element, position, offsets){
30982         this.xy = this.el.getAlignToXY(element, position, offsets);
30983         if(this.isVisible()){
30984             this.el.setXY(this.xy);
30985             this.adjustAssets();
30986         }
30987         return this;
30988     },
30989
30990     /**
30991      * Anchors an element to another element and realigns it when the window is resized.
30992      * @param {String/HTMLElement/Roo.Element} element The element to align to.
30993      * @param {String} position The position to align to (see {@link Roo.Element#alignTo} for more details)
30994      * @param {Array} offsets (optional) Offset the positioning by [x, y]
30995      * @param {Boolean/Number} monitorScroll (optional) true to monitor body scroll and reposition. If this parameter
30996      * is a number, it is used as the buffer delay (defaults to 50ms).
30997      * @return {Roo.BasicDialog} this
30998      */
30999     anchorTo : function(el, alignment, offsets, monitorScroll){
31000         var action = function(){
31001             this.alignTo(el, alignment, offsets);
31002         };
31003         Roo.EventManager.onWindowResize(action, this);
31004         var tm = typeof monitorScroll;
31005         if(tm != 'undefined'){
31006             Roo.EventManager.on(window, 'scroll', action, this,
31007                 {buffer: tm == 'number' ? monitorScroll : 50});
31008         }
31009         action.call(this);
31010         return this;
31011     },
31012
31013     /**
31014      * Returns true if the dialog is visible
31015      * @return {Boolean}
31016      */
31017     isVisible : function(){
31018         return this.el.isVisible();
31019     },
31020
31021     // private
31022     animHide : function(callback){
31023         var b = Roo.get(this.animateTarget).getBox();
31024         this.proxy.show();
31025         this.proxy.setBounds(this.xy[0], this.xy[1], this.size.width, this.size.height);
31026         this.el.hide();
31027         this.proxy.setBounds(b.x, b.y, b.width, b.height, true, .35,
31028                     this.hideEl.createDelegate(this, [callback]));
31029     },
31030
31031     /**
31032      * Hides the dialog.
31033      * @param {Function} callback (optional) Function to call when the dialog is hidden
31034      * @return {Roo.BasicDialog} this
31035      */
31036     hide : function(callback){
31037         if (this.fireEvent("beforehide", this) === false){
31038             return;
31039         }
31040         if(this.shadow){
31041             this.shadow.hide();
31042         }
31043         if(this.shim) {
31044           this.shim.hide();
31045         }
31046         // sometimes animateTarget seems to get set.. causing problems...
31047         // this just double checks..
31048         if(this.animateTarget && Roo.get(this.animateTarget)) {
31049            this.animHide(callback);
31050         }else{
31051             this.el.hide();
31052             this.hideEl(callback);
31053         }
31054         return this;
31055     },
31056
31057     // private
31058     hideEl : function(callback){
31059         this.proxy.hide();
31060         if(this.modal){
31061             this.mask.hide();
31062             Roo.get(document.body).removeClass("x-body-masked");
31063         }
31064         this.fireEvent("hide", this);
31065         if(typeof callback == "function"){
31066             callback();
31067         }
31068     },
31069
31070     // private
31071     hideAction : function(){
31072         this.setLeft("-10000px");
31073         this.setTop("-10000px");
31074         this.setStyle("visibility", "hidden");
31075     },
31076
31077     // private
31078     refreshSize : function(){
31079         this.size = this.el.getSize();
31080         this.xy = this.el.getXY();
31081         Roo.state.Manager.set(this.stateId || this.el.id + "-state", this.el.getBox());
31082     },
31083
31084     // private
31085     // z-index is managed by the DialogManager and may be overwritten at any time
31086     setZIndex : function(index){
31087         if(this.modal){
31088             this.mask.setStyle("z-index", index);
31089         }
31090         if(this.shim){
31091             this.shim.setStyle("z-index", ++index);
31092         }
31093         if(this.shadow){
31094             this.shadow.setZIndex(++index);
31095         }
31096         this.el.setStyle("z-index", ++index);
31097         if(this.proxy){
31098             this.proxy.setStyle("z-index", ++index);
31099         }
31100         if(this.resizer){
31101             this.resizer.proxy.setStyle("z-index", ++index);
31102         }
31103
31104         this.lastZIndex = index;
31105     },
31106
31107     /**
31108      * Returns the element for this dialog
31109      * @return {Roo.Element} The underlying dialog Element
31110      */
31111     getEl : function(){
31112         return this.el;
31113     }
31114 });
31115
31116 /**
31117  * @class Roo.DialogManager
31118  * Provides global access to BasicDialogs that have been created and
31119  * support for z-indexing (layering) multiple open dialogs.
31120  */
31121 Roo.DialogManager = function(){
31122     var list = {};
31123     var accessList = [];
31124     var front = null;
31125
31126     // private
31127     var sortDialogs = function(d1, d2){
31128         return (!d1._lastAccess || d1._lastAccess < d2._lastAccess) ? -1 : 1;
31129     };
31130
31131     // private
31132     var orderDialogs = function(){
31133         accessList.sort(sortDialogs);
31134         var seed = Roo.DialogManager.zseed;
31135         for(var i = 0, len = accessList.length; i < len; i++){
31136             var dlg = accessList[i];
31137             if(dlg){
31138                 dlg.setZIndex(seed + (i*10));
31139             }
31140         }
31141     };
31142
31143     return {
31144         /**
31145          * The starting z-index for BasicDialogs (defaults to 9000)
31146          * @type Number The z-index value
31147          */
31148         zseed : 9000,
31149
31150         // private
31151         register : function(dlg){
31152             list[dlg.id] = dlg;
31153             accessList.push(dlg);
31154         },
31155
31156         // private
31157         unregister : function(dlg){
31158             delete list[dlg.id];
31159             var i=0;
31160             var len=0;
31161             if(!accessList.indexOf){
31162                 for(  i = 0, len = accessList.length; i < len; i++){
31163                     if(accessList[i] == dlg){
31164                         accessList.splice(i, 1);
31165                         return;
31166                     }
31167                 }
31168             }else{
31169                  i = accessList.indexOf(dlg);
31170                 if(i != -1){
31171                     accessList.splice(i, 1);
31172                 }
31173             }
31174         },
31175
31176         /**
31177          * Gets a registered dialog by id
31178          * @param {String/Object} id The id of the dialog or a dialog
31179          * @return {Roo.BasicDialog} this
31180          */
31181         get : function(id){
31182             return typeof id == "object" ? id : list[id];
31183         },
31184
31185         /**
31186          * Brings the specified dialog to the front
31187          * @param {String/Object} dlg The id of the dialog or a dialog
31188          * @return {Roo.BasicDialog} this
31189          */
31190         bringToFront : function(dlg){
31191             dlg = this.get(dlg);
31192             if(dlg != front){
31193                 front = dlg;
31194                 dlg._lastAccess = new Date().getTime();
31195                 orderDialogs();
31196             }
31197             return dlg;
31198         },
31199
31200         /**
31201          * Sends the specified dialog to the back
31202          * @param {String/Object} dlg The id of the dialog or a dialog
31203          * @return {Roo.BasicDialog} this
31204          */
31205         sendToBack : function(dlg){
31206             dlg = this.get(dlg);
31207             dlg._lastAccess = -(new Date().getTime());
31208             orderDialogs();
31209             return dlg;
31210         },
31211
31212         /**
31213          * Hides all dialogs
31214          */
31215         hideAll : function(){
31216             for(var id in list){
31217                 if(list[id] && typeof list[id] != "function" && list[id].isVisible()){
31218                     list[id].hide();
31219                 }
31220             }
31221         }
31222     };
31223 }();
31224
31225 /**
31226  * @class Roo.LayoutDialog
31227  * @extends Roo.BasicDialog
31228  * Dialog which provides adjustments for working with a layout in a Dialog.
31229  * Add your necessary layout config options to the dialog's config.<br>
31230  * Example usage (including a nested layout):
31231  * <pre><code>
31232 if(!dialog){
31233     dialog = new Roo.LayoutDialog("download-dlg", {
31234         modal: true,
31235         width:600,
31236         height:450,
31237         shadow:true,
31238         minWidth:500,
31239         minHeight:350,
31240         autoTabs:true,
31241         proxyDrag:true,
31242         // layout config merges with the dialog config
31243         center:{
31244             tabPosition: "top",
31245             alwaysShowTabs: true
31246         }
31247     });
31248     dialog.addKeyListener(27, dialog.hide, dialog);
31249     dialog.setDefaultButton(dialog.addButton("Close", dialog.hide, dialog));
31250     dialog.addButton("Build It!", this.getDownload, this);
31251
31252     // we can even add nested layouts
31253     var innerLayout = new Roo.BorderLayout("dl-inner", {
31254         east: {
31255             initialSize: 200,
31256             autoScroll:true,
31257             split:true
31258         },
31259         center: {
31260             autoScroll:true
31261         }
31262     });
31263     innerLayout.beginUpdate();
31264     innerLayout.add("east", new Roo.ContentPanel("dl-details"));
31265     innerLayout.add("center", new Roo.ContentPanel("selection-panel"));
31266     innerLayout.endUpdate(true);
31267
31268     var layout = dialog.getLayout();
31269     layout.beginUpdate();
31270     layout.add("center", new Roo.ContentPanel("standard-panel",
31271                         {title: "Download the Source", fitToFrame:true}));
31272     layout.add("center", new Roo.NestedLayoutPanel(innerLayout,
31273                {title: "Build your own roo.js"}));
31274     layout.getRegion("center").showPanel(sp);
31275     layout.endUpdate();
31276 }
31277 </code></pre>
31278     * @constructor
31279     * @param {String/HTMLElement/Roo.Element} el The id of or container element, or config
31280     * @param {Object} config configuration options
31281   */
31282 Roo.LayoutDialog = function(el, cfg){
31283     
31284     var config=  cfg;
31285     if (typeof(cfg) == 'undefined') {
31286         config = Roo.apply({}, el);
31287         // not sure why we use documentElement here.. - it should always be body.
31288         // IE7 borks horribly if we use documentElement.
31289         // webkit also does not like documentElement - it creates a body element...
31290         el = Roo.get( document.body || document.documentElement ).createChild();
31291         //config.autoCreate = true;
31292     }
31293     
31294     
31295     config.autoTabs = false;
31296     Roo.LayoutDialog.superclass.constructor.call(this, el, config);
31297     this.body.setStyle({overflow:"hidden", position:"relative"});
31298     this.layout = new Roo.BorderLayout(this.body.dom, config);
31299     this.layout.monitorWindowResize = false;
31300     this.el.addClass("x-dlg-auto-layout");
31301     // fix case when center region overwrites center function
31302     this.center = Roo.BasicDialog.prototype.center;
31303     this.on("show", this.layout.layout, this.layout, true);
31304     if (config.items) {
31305         var xitems = config.items;
31306         delete config.items;
31307         Roo.each(xitems, this.addxtype, this);
31308     }
31309     
31310     
31311 };
31312 Roo.extend(Roo.LayoutDialog, Roo.BasicDialog, {
31313     /**
31314      * Ends update of the layout <strike>and resets display to none</strike>. Use standard beginUpdate/endUpdate on the layout.
31315      * @deprecated
31316      */
31317     endUpdate : function(){
31318         this.layout.endUpdate();
31319     },
31320
31321     /**
31322      * Begins an update of the layout <strike>and sets display to block and visibility to hidden</strike>. Use standard beginUpdate/endUpdate on the layout.
31323      *  @deprecated
31324      */
31325     beginUpdate : function(){
31326         this.layout.beginUpdate();
31327     },
31328
31329     /**
31330      * Get the BorderLayout for this dialog
31331      * @return {Roo.BorderLayout}
31332      */
31333     getLayout : function(){
31334         return this.layout;
31335     },
31336
31337     showEl : function(){
31338         Roo.LayoutDialog.superclass.showEl.apply(this, arguments);
31339         if(Roo.isIE7){
31340             this.layout.layout();
31341         }
31342     },
31343
31344     // private
31345     // Use the syncHeightBeforeShow config option to control this automatically
31346     syncBodyHeight : function(){
31347         Roo.LayoutDialog.superclass.syncBodyHeight.call(this);
31348         if(this.layout){this.layout.layout();}
31349     },
31350     
31351       /**
31352      * Add an xtype element (actually adds to the layout.)
31353      * @return {Object} xdata xtype object data.
31354      */
31355     
31356     addxtype : function(c) {
31357         return this.layout.addxtype(c);
31358     }
31359 });/*
31360  * Based on:
31361  * Ext JS Library 1.1.1
31362  * Copyright(c) 2006-2007, Ext JS, LLC.
31363  *
31364  * Originally Released Under LGPL - original licence link has changed is not relivant.
31365  *
31366  * Fork - LGPL
31367  * <script type="text/javascript">
31368  */
31369  
31370 /**
31371  * @class Roo.MessageBox
31372  * Utility class for generating different styles of message boxes.  The alias Roo.Msg can also be used.
31373  * Example usage:
31374  *<pre><code>
31375 // Basic alert:
31376 Roo.Msg.alert('Status', 'Changes saved successfully.');
31377
31378 // Prompt for user data:
31379 Roo.Msg.prompt('Name', 'Please enter your name:', function(btn, text){
31380     if (btn == 'ok'){
31381         // process text value...
31382     }
31383 });
31384
31385 // Show a dialog using config options:
31386 Roo.Msg.show({
31387    title:'Save Changes?',
31388    msg: 'Your are closing a tab that has unsaved changes. Would you like to save your changes?',
31389    buttons: Roo.Msg.YESNOCANCEL,
31390    fn: processResult,
31391    animEl: 'elId'
31392 });
31393 </code></pre>
31394  * @singleton
31395  */
31396 Roo.MessageBox = function(){
31397     var dlg, opt, mask, waitTimer;
31398     var bodyEl, msgEl, textboxEl, textareaEl, progressEl, pp;
31399     var buttons, activeTextEl, bwidth;
31400
31401     // private
31402     var handleButton = function(button){
31403         dlg.hide();
31404         Roo.callback(opt.fn, opt.scope||window, [button, activeTextEl.dom.value], 1);
31405     };
31406
31407     // private
31408     var handleHide = function(){
31409         if(opt && opt.cls){
31410             dlg.el.removeClass(opt.cls);
31411         }
31412         if(waitTimer){
31413             Roo.TaskMgr.stop(waitTimer);
31414             waitTimer = null;
31415         }
31416     };
31417
31418     // private
31419     var updateButtons = function(b){
31420         var width = 0;
31421         if(!b){
31422             buttons["ok"].hide();
31423             buttons["cancel"].hide();
31424             buttons["yes"].hide();
31425             buttons["no"].hide();
31426             dlg.footer.dom.style.display = 'none';
31427             return width;
31428         }
31429         dlg.footer.dom.style.display = '';
31430         for(var k in buttons){
31431             if(typeof buttons[k] != "function"){
31432                 if(b[k]){
31433                     buttons[k].show();
31434                     buttons[k].setText(typeof b[k] == "string" ? b[k] : Roo.MessageBox.buttonText[k]);
31435                     width += buttons[k].el.getWidth()+15;
31436                 }else{
31437                     buttons[k].hide();
31438                 }
31439             }
31440         }
31441         return width;
31442     };
31443
31444     // private
31445     var handleEsc = function(d, k, e){
31446         if(opt && opt.closable !== false){
31447             dlg.hide();
31448         }
31449         if(e){
31450             e.stopEvent();
31451         }
31452     };
31453
31454     return {
31455         /**
31456          * Returns a reference to the underlying {@link Roo.BasicDialog} element
31457          * @return {Roo.BasicDialog} The BasicDialog element
31458          */
31459         getDialog : function(){
31460            if(!dlg){
31461                 dlg = new Roo.BasicDialog("x-msg-box", {
31462                     autoCreate : true,
31463                     shadow: true,
31464                     draggable: true,
31465                     resizable:false,
31466                     constraintoviewport:false,
31467                     fixedcenter:true,
31468                     collapsible : false,
31469                     shim:true,
31470                     modal: true,
31471                     width:400, height:100,
31472                     buttonAlign:"center",
31473                     closeClick : function(){
31474                         if(opt && opt.buttons && opt.buttons.no && !opt.buttons.cancel){
31475                             handleButton("no");
31476                         }else{
31477                             handleButton("cancel");
31478                         }
31479                     }
31480                 });
31481                 dlg.on("hide", handleHide);
31482                 mask = dlg.mask;
31483                 dlg.addKeyListener(27, handleEsc);
31484                 buttons = {};
31485                 var bt = this.buttonText;
31486                 buttons["ok"] = dlg.addButton(bt["ok"], handleButton.createCallback("ok"));
31487                 buttons["yes"] = dlg.addButton(bt["yes"], handleButton.createCallback("yes"));
31488                 buttons["no"] = dlg.addButton(bt["no"], handleButton.createCallback("no"));
31489                 buttons["cancel"] = dlg.addButton(bt["cancel"], handleButton.createCallback("cancel"));
31490                 bodyEl = dlg.body.createChild({
31491
31492                     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>'
31493                 });
31494                 msgEl = bodyEl.dom.firstChild;
31495                 textboxEl = Roo.get(bodyEl.dom.childNodes[2]);
31496                 textboxEl.enableDisplayMode();
31497                 textboxEl.addKeyListener([10,13], function(){
31498                     if(dlg.isVisible() && opt && opt.buttons){
31499                         if(opt.buttons.ok){
31500                             handleButton("ok");
31501                         }else if(opt.buttons.yes){
31502                             handleButton("yes");
31503                         }
31504                     }
31505                 });
31506                 textareaEl = Roo.get(bodyEl.dom.childNodes[3]);
31507                 textareaEl.enableDisplayMode();
31508                 progressEl = Roo.get(bodyEl.dom.childNodes[4]);
31509                 progressEl.enableDisplayMode();
31510                 var pf = progressEl.dom.firstChild;
31511                 if (pf) {
31512                     pp = Roo.get(pf.firstChild);
31513                     pp.setHeight(pf.offsetHeight);
31514                 }
31515                 
31516             }
31517             return dlg;
31518         },
31519
31520         /**
31521          * Updates the message box body text
31522          * @param {String} text (optional) Replaces the message box element's innerHTML with the specified string (defaults to
31523          * the XHTML-compliant non-breaking space character '&amp;#160;')
31524          * @return {Roo.MessageBox} This message box
31525          */
31526         updateText : function(text){
31527             if(!dlg.isVisible() && !opt.width){
31528                 dlg.resizeTo(this.maxWidth, 100); // resize first so content is never clipped from previous shows
31529             }
31530             msgEl.innerHTML = text || '&#160;';
31531       
31532             var cw =  Math.max(msgEl.offsetWidth, msgEl.parentNode.scrollWidth);
31533             //Roo.log("guesed size: " + JSON.stringify([cw,msgEl.offsetWidth, msgEl.parentNode.scrollWidth]));
31534             var w = Math.max(
31535                     Math.min(opt.width || cw , this.maxWidth), 
31536                     Math.max(opt.minWidth || this.minWidth, bwidth)
31537             );
31538             if(opt.prompt){
31539                 activeTextEl.setWidth(w);
31540             }
31541             if(dlg.isVisible()){
31542                 dlg.fixedcenter = false;
31543             }
31544             // to big, make it scroll. = But as usual stupid IE does not support
31545             // !important..
31546             
31547             if ( bodyEl.getHeight() > (Roo.lib.Dom.getViewHeight() - 100)) {
31548                 bodyEl.setHeight ( Roo.lib.Dom.getViewHeight() - 100 );
31549                 bodyEl.dom.style.overflowY = 'auto' + ( Roo.isIE ? '' : ' !important');
31550             } else {
31551                 bodyEl.dom.style.height = '';
31552                 bodyEl.dom.style.overflowY = '';
31553             }
31554             if (cw > w) {
31555                 bodyEl.dom.style.get = 'auto' + ( Roo.isIE ? '' : ' !important');
31556             } else {
31557                 bodyEl.dom.style.overflowX = '';
31558             }
31559             
31560             dlg.setContentSize(w, bodyEl.getHeight());
31561             if(dlg.isVisible()){
31562                 dlg.fixedcenter = true;
31563             }
31564             return this;
31565         },
31566
31567         /**
31568          * Updates a progress-style message box's text and progress bar.  Only relevant on message boxes
31569          * initiated via {@link Roo.MessageBox#progress} or by calling {@link Roo.MessageBox#show} with progress: true.
31570          * @param {Number} value Any number between 0 and 1 (e.g., .5)
31571          * @param {String} text (optional) If defined, the message box's body text is replaced with the specified string (defaults to undefined)
31572          * @return {Roo.MessageBox} This message box
31573          */
31574         updateProgress : function(value, text){
31575             if(text){
31576                 this.updateText(text);
31577             }
31578             if (pp) { // weird bug on my firefox - for some reason this is not defined
31579                 pp.setWidth(Math.floor(value*progressEl.dom.firstChild.offsetWidth));
31580             }
31581             return this;
31582         },        
31583
31584         /**
31585          * Returns true if the message box is currently displayed
31586          * @return {Boolean} True if the message box is visible, else false
31587          */
31588         isVisible : function(){
31589             return dlg && dlg.isVisible();  
31590         },
31591
31592         /**
31593          * Hides the message box if it is displayed
31594          */
31595         hide : function(){
31596             if(this.isVisible()){
31597                 dlg.hide();
31598             }  
31599         },
31600
31601         /**
31602          * Displays a new message box, or reinitializes an existing message box, based on the config options
31603          * passed in. All functions (e.g. prompt, alert, etc) on MessageBox call this function internally.
31604          * The following config object properties are supported:
31605          * <pre>
31606 Property    Type             Description
31607 ----------  ---------------  ------------------------------------------------------------------------------------
31608 animEl            String/Element   An id or Element from which the message box should animate as it opens and
31609                                    closes (defaults to undefined)
31610 buttons           Object/Boolean   A button config object (e.g., Roo.MessageBox.OKCANCEL or {ok:'Foo',
31611                                    cancel:'Bar'}), or false to not show any buttons (defaults to false)
31612 closable          Boolean          False to hide the top-right close button (defaults to true).  Note that
31613                                    progress and wait dialogs will ignore this property and always hide the
31614                                    close button as they can only be closed programmatically.
31615 cls               String           A custom CSS class to apply to the message box element
31616 defaultTextHeight Number           The default height in pixels of the message box's multiline textarea if
31617                                    displayed (defaults to 75)
31618 fn                Function         A callback function to execute after closing the dialog.  The arguments to the
31619                                    function will be btn (the name of the button that was clicked, if applicable,
31620                                    e.g. "ok"), and text (the value of the active text field, if applicable).
31621                                    Progress and wait dialogs will ignore this option since they do not respond to
31622                                    user actions and can only be closed programmatically, so any required function
31623                                    should be called by the same code after it closes the dialog.
31624 icon              String           A CSS class that provides a background image to be used as an icon for
31625                                    the dialog (e.g., Roo.MessageBox.WARNING or 'custom-class', defaults to '')
31626 maxWidth          Number           The maximum width in pixels of the message box (defaults to 600)
31627 minWidth          Number           The minimum width in pixels of the message box (defaults to 100)
31628 modal             Boolean          False to allow user interaction with the page while the message box is
31629                                    displayed (defaults to true)
31630 msg               String           A string that will replace the existing message box body text (defaults
31631                                    to the XHTML-compliant non-breaking space character '&#160;')
31632 multiline         Boolean          True to prompt the user to enter multi-line text (defaults to false)
31633 progress          Boolean          True to display a progress bar (defaults to false)
31634 progressText      String           The text to display inside the progress bar if progress = true (defaults to '')
31635 prompt            Boolean          True to prompt the user to enter single-line text (defaults to false)
31636 proxyDrag         Boolean          True to display a lightweight proxy while dragging (defaults to false)
31637 title             String           The title text
31638 value             String           The string value to set into the active textbox element if displayed
31639 wait              Boolean          True to display a progress bar (defaults to false)
31640 width             Number           The width of the dialog in pixels
31641 </pre>
31642          *
31643          * Example usage:
31644          * <pre><code>
31645 Roo.Msg.show({
31646    title: 'Address',
31647    msg: 'Please enter your address:',
31648    width: 300,
31649    buttons: Roo.MessageBox.OKCANCEL,
31650    multiline: true,
31651    fn: saveAddress,
31652    animEl: 'addAddressBtn'
31653 });
31654 </code></pre>
31655          * @param {Object} config Configuration options
31656          * @return {Roo.MessageBox} This message box
31657          */
31658         show : function(options)
31659         {
31660             
31661             // this causes nightmares if you show one dialog after another
31662             // especially on callbacks..
31663              
31664             if(this.isVisible()){
31665                 
31666                 this.hide();
31667                 Roo.log("[Roo.Messagebox] Show called while message displayed:" );
31668                 Roo.log("Old Dialog Message:" +  msgEl.innerHTML );
31669                 Roo.log("New Dialog Message:" +  options.msg )
31670                 //this.alert("ERROR", "Multiple dialogs where displayed at the same time");
31671                 //throw "Roo.MessageBox ERROR : Multiple dialogs where displayed at the same time";
31672                 
31673             }
31674             var d = this.getDialog();
31675             opt = options;
31676             d.setTitle(opt.title || "&#160;");
31677             d.close.setDisplayed(opt.closable !== false);
31678             activeTextEl = textboxEl;
31679             opt.prompt = opt.prompt || (opt.multiline ? true : false);
31680             if(opt.prompt){
31681                 if(opt.multiline){
31682                     textboxEl.hide();
31683                     textareaEl.show();
31684                     textareaEl.setHeight(typeof opt.multiline == "number" ?
31685                         opt.multiline : this.defaultTextHeight);
31686                     activeTextEl = textareaEl;
31687                 }else{
31688                     textboxEl.show();
31689                     textareaEl.hide();
31690                 }
31691             }else{
31692                 textboxEl.hide();
31693                 textareaEl.hide();
31694             }
31695             progressEl.setDisplayed(opt.progress === true);
31696             this.updateProgress(0);
31697             activeTextEl.dom.value = opt.value || "";
31698             if(opt.prompt){
31699                 dlg.setDefaultButton(activeTextEl);
31700             }else{
31701                 var bs = opt.buttons;
31702                 var db = null;
31703                 if(bs && bs.ok){
31704                     db = buttons["ok"];
31705                 }else if(bs && bs.yes){
31706                     db = buttons["yes"];
31707                 }
31708                 dlg.setDefaultButton(db);
31709             }
31710             bwidth = updateButtons(opt.buttons);
31711             this.updateText(opt.msg);
31712             if(opt.cls){
31713                 d.el.addClass(opt.cls);
31714             }
31715             d.proxyDrag = opt.proxyDrag === true;
31716             d.modal = opt.modal !== false;
31717             d.mask = opt.modal !== false ? mask : false;
31718             if(!d.isVisible()){
31719                 // force it to the end of the z-index stack so it gets a cursor in FF
31720                 document.body.appendChild(dlg.el.dom);
31721                 d.animateTarget = null;
31722                 d.show(options.animEl);
31723             }
31724             return this;
31725         },
31726
31727         /**
31728          * Displays a message box with a progress bar.  This message box has no buttons and is not closeable by
31729          * the user.  You are responsible for updating the progress bar as needed via {@link Roo.MessageBox#updateProgress}
31730          * and closing the message box when the process is complete.
31731          * @param {String} title The title bar text
31732          * @param {String} msg The message box body text
31733          * @return {Roo.MessageBox} This message box
31734          */
31735         progress : function(title, msg){
31736             this.show({
31737                 title : title,
31738                 msg : msg,
31739                 buttons: false,
31740                 progress:true,
31741                 closable:false,
31742                 minWidth: this.minProgressWidth,
31743                 modal : true
31744             });
31745             return this;
31746         },
31747
31748         /**
31749          * Displays a standard read-only message box with an OK button (comparable to the basic JavaScript Window.alert).
31750          * If a callback function is passed it will be called after the user clicks the button, and the
31751          * id of the button that was clicked will be passed as the only parameter to the callback
31752          * (could also be the top-right close button).
31753          * @param {String} title The title bar text
31754          * @param {String} msg The message box body text
31755          * @param {Function} fn (optional) The callback function invoked after the message box is closed
31756          * @param {Object} scope (optional) The scope of the callback function
31757          * @return {Roo.MessageBox} This message box
31758          */
31759         alert : function(title, msg, fn, scope){
31760             this.show({
31761                 title : title,
31762                 msg : msg,
31763                 buttons: this.OK,
31764                 fn: fn,
31765                 scope : scope,
31766                 modal : true
31767             });
31768             return this;
31769         },
31770
31771         /**
31772          * Displays a message box with an infinitely auto-updating progress bar.  This can be used to block user
31773          * interaction while waiting for a long-running process to complete that does not have defined intervals.
31774          * You are responsible for closing the message box when the process is complete.
31775          * @param {String} msg The message box body text
31776          * @param {String} title (optional) The title bar text
31777          * @return {Roo.MessageBox} This message box
31778          */
31779         wait : function(msg, title){
31780             this.show({
31781                 title : title,
31782                 msg : msg,
31783                 buttons: false,
31784                 closable:false,
31785                 progress:true,
31786                 modal:true,
31787                 width:300,
31788                 wait:true
31789             });
31790             waitTimer = Roo.TaskMgr.start({
31791                 run: function(i){
31792                     Roo.MessageBox.updateProgress(((((i+20)%20)+1)*5)*.01);
31793                 },
31794                 interval: 1000
31795             });
31796             return this;
31797         },
31798
31799         /**
31800          * Displays a confirmation message box with Yes and No buttons (comparable to JavaScript's Window.confirm).
31801          * If a callback function is passed it will be called after the user clicks either button, and the id of the
31802          * button that was clicked will be passed as the only parameter to the callback (could also be the top-right close button).
31803          * @param {String} title The title bar text
31804          * @param {String} msg The message box body text
31805          * @param {Function} fn (optional) The callback function invoked after the message box is closed
31806          * @param {Object} scope (optional) The scope of the callback function
31807          * @return {Roo.MessageBox} This message box
31808          */
31809         confirm : function(title, msg, fn, scope){
31810             this.show({
31811                 title : title,
31812                 msg : msg,
31813                 buttons: this.YESNO,
31814                 fn: fn,
31815                 scope : scope,
31816                 modal : true
31817             });
31818             return this;
31819         },
31820
31821         /**
31822          * Displays a message box with OK and Cancel buttons prompting the user to enter some text (comparable to
31823          * JavaScript's Window.prompt).  The prompt can be a single-line or multi-line textbox.  If a callback function
31824          * is passed it will be called after the user clicks either button, and the id of the button that was clicked
31825          * (could also be the top-right close button) and the text that was entered will be passed as the two
31826          * parameters to the callback.
31827          * @param {String} title The title bar text
31828          * @param {String} msg The message box body text
31829          * @param {Function} fn (optional) The callback function invoked after the message box is closed
31830          * @param {Object} scope (optional) The scope of the callback function
31831          * @param {Boolean/Number} multiline (optional) True to create a multiline textbox using the defaultTextHeight
31832          * property, or the height in pixels to create the textbox (defaults to false / single-line)
31833          * @return {Roo.MessageBox} This message box
31834          */
31835         prompt : function(title, msg, fn, scope, multiline){
31836             this.show({
31837                 title : title,
31838                 msg : msg,
31839                 buttons: this.OKCANCEL,
31840                 fn: fn,
31841                 minWidth:250,
31842                 scope : scope,
31843                 prompt:true,
31844                 multiline: multiline,
31845                 modal : true
31846             });
31847             return this;
31848         },
31849
31850         /**
31851          * Button config that displays a single OK button
31852          * @type Object
31853          */
31854         OK : {ok:true},
31855         /**
31856          * Button config that displays Yes and No buttons
31857          * @type Object
31858          */
31859         YESNO : {yes:true, no:true},
31860         /**
31861          * Button config that displays OK and Cancel buttons
31862          * @type Object
31863          */
31864         OKCANCEL : {ok:true, cancel:true},
31865         /**
31866          * Button config that displays Yes, No and Cancel buttons
31867          * @type Object
31868          */
31869         YESNOCANCEL : {yes:true, no:true, cancel:true},
31870
31871         /**
31872          * The default height in pixels of the message box's multiline textarea if displayed (defaults to 75)
31873          * @type Number
31874          */
31875         defaultTextHeight : 75,
31876         /**
31877          * The maximum width in pixels of the message box (defaults to 600)
31878          * @type Number
31879          */
31880         maxWidth : 600,
31881         /**
31882          * The minimum width in pixels of the message box (defaults to 100)
31883          * @type Number
31884          */
31885         minWidth : 100,
31886         /**
31887          * The minimum width in pixels of the message box if it is a progress-style dialog.  This is useful
31888          * for setting a different minimum width than text-only dialogs may need (defaults to 250)
31889          * @type Number
31890          */
31891         minProgressWidth : 250,
31892         /**
31893          * An object containing the default button text strings that can be overriden for localized language support.
31894          * Supported properties are: ok, cancel, yes and no.
31895          * Customize the default text like so: Roo.MessageBox.buttonText.yes = "S?";
31896          * @type Object
31897          */
31898         buttonText : {
31899             ok : "OK",
31900             cancel : "Cancel",
31901             yes : "Yes",
31902             no : "No"
31903         }
31904     };
31905 }();
31906
31907 /**
31908  * Shorthand for {@link Roo.MessageBox}
31909  */
31910 Roo.Msg = Roo.MessageBox;/*
31911  * Based on:
31912  * Ext JS Library 1.1.1
31913  * Copyright(c) 2006-2007, Ext JS, LLC.
31914  *
31915  * Originally Released Under LGPL - original licence link has changed is not relivant.
31916  *
31917  * Fork - LGPL
31918  * <script type="text/javascript">
31919  */
31920 /**
31921  * @class Roo.QuickTips
31922  * Provides attractive and customizable tooltips for any element.
31923  * @singleton
31924  */
31925 Roo.QuickTips = function(){
31926     var el, tipBody, tipBodyText, tipTitle, tm, cfg, close, tagEls = {}, esc, removeCls = null, bdLeft, bdRight;
31927     var ce, bd, xy, dd;
31928     var visible = false, disabled = true, inited = false;
31929     var showProc = 1, hideProc = 1, dismissProc = 1, locks = [];
31930     
31931     var onOver = function(e){
31932         if(disabled){
31933             return;
31934         }
31935         var t = e.getTarget();
31936         if(!t || t.nodeType !== 1 || t == document || t == document.body){
31937             return;
31938         }
31939         if(ce && t == ce.el){
31940             clearTimeout(hideProc);
31941             return;
31942         }
31943         if(t && tagEls[t.id]){
31944             tagEls[t.id].el = t;
31945             showProc = show.defer(tm.showDelay, tm, [tagEls[t.id]]);
31946             return;
31947         }
31948         var ttp, et = Roo.fly(t);
31949         var ns = cfg.namespace;
31950         if(tm.interceptTitles && t.title){
31951             ttp = t.title;
31952             t.qtip = ttp;
31953             t.removeAttribute("title");
31954             e.preventDefault();
31955         }else{
31956             ttp = t.qtip || et.getAttributeNS(ns, cfg.attribute);
31957         }
31958         if(ttp){
31959             showProc = show.defer(tm.showDelay, tm, [{
31960                 el: t, 
31961                 text: ttp, 
31962                 width: et.getAttributeNS(ns, cfg.width),
31963                 autoHide: et.getAttributeNS(ns, cfg.hide) != "user",
31964                 title: et.getAttributeNS(ns, cfg.title),
31965                     cls: et.getAttributeNS(ns, cfg.cls)
31966             }]);
31967         }
31968     };
31969     
31970     var onOut = function(e){
31971         clearTimeout(showProc);
31972         var t = e.getTarget();
31973         if(t && ce && ce.el == t && (tm.autoHide && ce.autoHide !== false)){
31974             hideProc = setTimeout(hide, tm.hideDelay);
31975         }
31976     };
31977     
31978     var onMove = function(e){
31979         if(disabled){
31980             return;
31981         }
31982         xy = e.getXY();
31983         xy[1] += 18;
31984         if(tm.trackMouse && ce){
31985             el.setXY(xy);
31986         }
31987     };
31988     
31989     var onDown = function(e){
31990         clearTimeout(showProc);
31991         clearTimeout(hideProc);
31992         if(!e.within(el)){
31993             if(tm.hideOnClick){
31994                 hide();
31995                 tm.disable();
31996                 tm.enable.defer(100, tm);
31997             }
31998         }
31999     };
32000     
32001     var getPad = function(){
32002         return 2;//bdLeft.getPadding('l')+bdRight.getPadding('r');
32003     };
32004
32005     var show = function(o){
32006         if(disabled){
32007             return;
32008         }
32009         clearTimeout(dismissProc);
32010         ce = o;
32011         if(removeCls){ // in case manually hidden
32012             el.removeClass(removeCls);
32013             removeCls = null;
32014         }
32015         if(ce.cls){
32016             el.addClass(ce.cls);
32017             removeCls = ce.cls;
32018         }
32019         if(ce.title){
32020             tipTitle.update(ce.title);
32021             tipTitle.show();
32022         }else{
32023             tipTitle.update('');
32024             tipTitle.hide();
32025         }
32026         el.dom.style.width  = tm.maxWidth+'px';
32027         //tipBody.dom.style.width = '';
32028         tipBodyText.update(o.text);
32029         var p = getPad(), w = ce.width;
32030         if(!w){
32031             var td = tipBodyText.dom;
32032             var aw = Math.max(td.offsetWidth, td.clientWidth, td.scrollWidth);
32033             if(aw > tm.maxWidth){
32034                 w = tm.maxWidth;
32035             }else if(aw < tm.minWidth){
32036                 w = tm.minWidth;
32037             }else{
32038                 w = aw;
32039             }
32040         }
32041         //tipBody.setWidth(w);
32042         el.setWidth(parseInt(w, 10) + p);
32043         if(ce.autoHide === false){
32044             close.setDisplayed(true);
32045             if(dd){
32046                 dd.unlock();
32047             }
32048         }else{
32049             close.setDisplayed(false);
32050             if(dd){
32051                 dd.lock();
32052             }
32053         }
32054         if(xy){
32055             el.avoidY = xy[1]-18;
32056             el.setXY(xy);
32057         }
32058         if(tm.animate){
32059             el.setOpacity(.1);
32060             el.setStyle("visibility", "visible");
32061             el.fadeIn({callback: afterShow});
32062         }else{
32063             afterShow();
32064         }
32065     };
32066     
32067     var afterShow = function(){
32068         if(ce){
32069             el.show();
32070             esc.enable();
32071             if(tm.autoDismiss && ce.autoHide !== false){
32072                 dismissProc = setTimeout(hide, tm.autoDismissDelay);
32073             }
32074         }
32075     };
32076     
32077     var hide = function(noanim){
32078         clearTimeout(dismissProc);
32079         clearTimeout(hideProc);
32080         ce = null;
32081         if(el.isVisible()){
32082             esc.disable();
32083             if(noanim !== true && tm.animate){
32084                 el.fadeOut({callback: afterHide});
32085             }else{
32086                 afterHide();
32087             } 
32088         }
32089     };
32090     
32091     var afterHide = function(){
32092         el.hide();
32093         if(removeCls){
32094             el.removeClass(removeCls);
32095             removeCls = null;
32096         }
32097     };
32098     
32099     return {
32100         /**
32101         * @cfg {Number} minWidth
32102         * The minimum width of the quick tip (defaults to 40)
32103         */
32104        minWidth : 40,
32105         /**
32106         * @cfg {Number} maxWidth
32107         * The maximum width of the quick tip (defaults to 300)
32108         */
32109        maxWidth : 300,
32110         /**
32111         * @cfg {Boolean} interceptTitles
32112         * True to automatically use the element's DOM title value if available (defaults to false)
32113         */
32114        interceptTitles : false,
32115         /**
32116         * @cfg {Boolean} trackMouse
32117         * True to have the quick tip follow the mouse as it moves over the target element (defaults to false)
32118         */
32119        trackMouse : false,
32120         /**
32121         * @cfg {Boolean} hideOnClick
32122         * True to hide the quick tip if the user clicks anywhere in the document (defaults to true)
32123         */
32124        hideOnClick : true,
32125         /**
32126         * @cfg {Number} showDelay
32127         * Delay in milliseconds before the quick tip displays after the mouse enters the target element (defaults to 500)
32128         */
32129        showDelay : 500,
32130         /**
32131         * @cfg {Number} hideDelay
32132         * Delay in milliseconds before the quick tip hides when autoHide = true (defaults to 200)
32133         */
32134        hideDelay : 200,
32135         /**
32136         * @cfg {Boolean} autoHide
32137         * True to automatically hide the quick tip after the mouse exits the target element (defaults to true).
32138         * Used in conjunction with hideDelay.
32139         */
32140        autoHide : true,
32141         /**
32142         * @cfg {Boolean}
32143         * True to automatically hide the quick tip after a set period of time, regardless of the user's actions
32144         * (defaults to true).  Used in conjunction with autoDismissDelay.
32145         */
32146        autoDismiss : true,
32147         /**
32148         * @cfg {Number}
32149         * Delay in milliseconds before the quick tip hides when autoDismiss = true (defaults to 5000)
32150         */
32151        autoDismissDelay : 5000,
32152        /**
32153         * @cfg {Boolean} animate
32154         * True to turn on fade animation. Defaults to false (ClearType/scrollbar flicker issues in IE7).
32155         */
32156        animate : false,
32157
32158        /**
32159         * @cfg {String} title
32160         * Title text to display (defaults to '').  This can be any valid HTML markup.
32161         */
32162         title: '',
32163        /**
32164         * @cfg {String} text
32165         * Body text to display (defaults to '').  This can be any valid HTML markup.
32166         */
32167         text : '',
32168        /**
32169         * @cfg {String} cls
32170         * A CSS class to apply to the base quick tip element (defaults to '').
32171         */
32172         cls : '',
32173        /**
32174         * @cfg {Number} width
32175         * Width in pixels of the quick tip (defaults to auto).  Width will be ignored if it exceeds the bounds of
32176         * minWidth or maxWidth.
32177         */
32178         width : null,
32179
32180     /**
32181      * Initialize and enable QuickTips for first use.  This should be called once before the first attempt to access
32182      * or display QuickTips in a page.
32183      */
32184        init : function(){
32185           tm = Roo.QuickTips;
32186           cfg = tm.tagConfig;
32187           if(!inited){
32188               if(!Roo.isReady){ // allow calling of init() before onReady
32189                   Roo.onReady(Roo.QuickTips.init, Roo.QuickTips);
32190                   return;
32191               }
32192               el = new Roo.Layer({cls:"x-tip", shadow:"drop", shim: true, constrain:true, shadowOffset:4});
32193               el.fxDefaults = {stopFx: true};
32194               // maximum custom styling
32195               //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>');
32196               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>');              
32197               tipTitle = el.child('h3');
32198               tipTitle.enableDisplayMode("block");
32199               tipBody = el.child('div.x-tip-bd');
32200               tipBodyText = el.child('div.x-tip-bd-inner');
32201               //bdLeft = el.child('div.x-tip-bd-left');
32202               //bdRight = el.child('div.x-tip-bd-right');
32203               close = el.child('div.x-tip-close');
32204               close.enableDisplayMode("block");
32205               close.on("click", hide);
32206               var d = Roo.get(document);
32207               d.on("mousedown", onDown);
32208               d.on("mouseover", onOver);
32209               d.on("mouseout", onOut);
32210               d.on("mousemove", onMove);
32211               esc = d.addKeyListener(27, hide);
32212               esc.disable();
32213               if(Roo.dd.DD){
32214                   dd = el.initDD("default", null, {
32215                       onDrag : function(){
32216                           el.sync();  
32217                       }
32218                   });
32219                   dd.setHandleElId(tipTitle.id);
32220                   dd.lock();
32221               }
32222               inited = true;
32223           }
32224           this.enable(); 
32225        },
32226
32227     /**
32228      * Configures a new quick tip instance and assigns it to a target element.  The following config options
32229      * are supported:
32230      * <pre>
32231 Property    Type                   Description
32232 ----------  ---------------------  ------------------------------------------------------------------------
32233 target      Element/String/Array   An Element, id or array of ids that this quick tip should be tied to
32234      * </ul>
32235      * @param {Object} config The config object
32236      */
32237        register : function(config){
32238            var cs = config instanceof Array ? config : arguments;
32239            for(var i = 0, len = cs.length; i < len; i++) {
32240                var c = cs[i];
32241                var target = c.target;
32242                if(target){
32243                    if(target instanceof Array){
32244                        for(var j = 0, jlen = target.length; j < jlen; j++){
32245                            tagEls[target[j]] = c;
32246                        }
32247                    }else{
32248                        tagEls[typeof target == 'string' ? target : Roo.id(target)] = c;
32249                    }
32250                }
32251            }
32252        },
32253
32254     /**
32255      * Removes this quick tip from its element and destroys it.
32256      * @param {String/HTMLElement/Element} el The element from which the quick tip is to be removed.
32257      */
32258        unregister : function(el){
32259            delete tagEls[Roo.id(el)];
32260        },
32261
32262     /**
32263      * Enable this quick tip.
32264      */
32265        enable : function(){
32266            if(inited && disabled){
32267                locks.pop();
32268                if(locks.length < 1){
32269                    disabled = false;
32270                }
32271            }
32272        },
32273
32274     /**
32275      * Disable this quick tip.
32276      */
32277        disable : function(){
32278           disabled = true;
32279           clearTimeout(showProc);
32280           clearTimeout(hideProc);
32281           clearTimeout(dismissProc);
32282           if(ce){
32283               hide(true);
32284           }
32285           locks.push(1);
32286        },
32287
32288     /**
32289      * Returns true if the quick tip is enabled, else false.
32290      */
32291        isEnabled : function(){
32292             return !disabled;
32293        },
32294
32295         // private
32296        tagConfig : {
32297            namespace : "ext",
32298            attribute : "qtip",
32299            width : "width",
32300            target : "target",
32301            title : "qtitle",
32302            hide : "hide",
32303            cls : "qclass"
32304        }
32305    };
32306 }();
32307
32308 // backwards compat
32309 Roo.QuickTips.tips = Roo.QuickTips.register;/*
32310  * Based on:
32311  * Ext JS Library 1.1.1
32312  * Copyright(c) 2006-2007, Ext JS, LLC.
32313  *
32314  * Originally Released Under LGPL - original licence link has changed is not relivant.
32315  *
32316  * Fork - LGPL
32317  * <script type="text/javascript">
32318  */
32319  
32320
32321 /**
32322  * @class Roo.tree.TreePanel
32323  * @extends Roo.data.Tree
32324
32325  * @cfg {Boolean} rootVisible false to hide the root node (defaults to true)
32326  * @cfg {Boolean} lines false to disable tree lines (defaults to true)
32327  * @cfg {Boolean} enableDD true to enable drag and drop
32328  * @cfg {Boolean} enableDrag true to enable just drag
32329  * @cfg {Boolean} enableDrop true to enable just drop
32330  * @cfg {Object} dragConfig Custom config to pass to the {@link Roo.tree.TreeDragZone} instance
32331  * @cfg {Object} dropConfig Custom config to pass to the {@link Roo.tree.TreeDropZone} instance
32332  * @cfg {String} ddGroup The DD group this TreePanel belongs to
32333  * @cfg {String} ddAppendOnly True if the tree should only allow append drops (use for trees which are sorted)
32334  * @cfg {Boolean} ddScroll true to enable YUI body scrolling
32335  * @cfg {Boolean} containerScroll true to register this container with ScrollManager
32336  * @cfg {Boolean} hlDrop false to disable node highlight on drop (defaults to the value of Roo.enableFx)
32337  * @cfg {String} hlColor The color of the node highlight (defaults to C3DAF9)
32338  * @cfg {Boolean} animate true to enable animated expand/collapse (defaults to the value of Roo.enableFx)
32339  * @cfg {Boolean} singleExpand true if only 1 node per branch may be expanded
32340  * @cfg {Boolean} selModel A tree selection model to use with this TreePanel (defaults to a {@link Roo.tree.DefaultSelectionModel})
32341  * @cfg {Boolean} loader A TreeLoader for use with this TreePanel
32342  * @cfg {Object|Roo.tree.TreeEditor} editor The TreeEditor or xtype data to display when clicked.
32343  * @cfg {String} pathSeparator The token used to separate sub-paths in path strings (defaults to '/')
32344  * @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>
32345  * @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>
32346  * 
32347  * @constructor
32348  * @param {String/HTMLElement/Element} el The container element
32349  * @param {Object} config
32350  */
32351 Roo.tree.TreePanel = function(el, config){
32352     var root = false;
32353     var loader = false;
32354     if (config.root) {
32355         root = config.root;
32356         delete config.root;
32357     }
32358     if (config.loader) {
32359         loader = config.loader;
32360         delete config.loader;
32361     }
32362     
32363     Roo.apply(this, config);
32364     Roo.tree.TreePanel.superclass.constructor.call(this);
32365     this.el = Roo.get(el);
32366     this.el.addClass('x-tree');
32367     //console.log(root);
32368     if (root) {
32369         this.setRootNode( Roo.factory(root, Roo.tree));
32370     }
32371     if (loader) {
32372         this.loader = Roo.factory(loader, Roo.tree);
32373     }
32374    /**
32375     * Read-only. The id of the container element becomes this TreePanel's id.
32376     */
32377     this.id = this.el.id;
32378     this.addEvents({
32379         /**
32380         * @event beforeload
32381         * Fires before a node is loaded, return false to cancel
32382         * @param {Node} node The node being loaded
32383         */
32384         "beforeload" : true,
32385         /**
32386         * @event load
32387         * Fires when a node is loaded
32388         * @param {Node} node The node that was loaded
32389         */
32390         "load" : true,
32391         /**
32392         * @event textchange
32393         * Fires when the text for a node is changed
32394         * @param {Node} node The node
32395         * @param {String} text The new text
32396         * @param {String} oldText The old text
32397         */
32398         "textchange" : true,
32399         /**
32400         * @event beforeexpand
32401         * Fires before a node is expanded, return false to cancel.
32402         * @param {Node} node The node
32403         * @param {Boolean} deep
32404         * @param {Boolean} anim
32405         */
32406         "beforeexpand" : true,
32407         /**
32408         * @event beforecollapse
32409         * Fires before a node is collapsed, return false to cancel.
32410         * @param {Node} node The node
32411         * @param {Boolean} deep
32412         * @param {Boolean} anim
32413         */
32414         "beforecollapse" : true,
32415         /**
32416         * @event expand
32417         * Fires when a node is expanded
32418         * @param {Node} node The node
32419         */
32420         "expand" : true,
32421         /**
32422         * @event disabledchange
32423         * Fires when the disabled status of a node changes
32424         * @param {Node} node The node
32425         * @param {Boolean} disabled
32426         */
32427         "disabledchange" : true,
32428         /**
32429         * @event collapse
32430         * Fires when a node is collapsed
32431         * @param {Node} node The node
32432         */
32433         "collapse" : true,
32434         /**
32435         * @event beforeclick
32436         * Fires before click processing on a node. Return false to cancel the default action.
32437         * @param {Node} node The node
32438         * @param {Roo.EventObject} e The event object
32439         */
32440         "beforeclick":true,
32441         /**
32442         * @event checkchange
32443         * Fires when a node with a checkbox's checked property changes
32444         * @param {Node} this This node
32445         * @param {Boolean} checked
32446         */
32447         "checkchange":true,
32448         /**
32449         * @event click
32450         * Fires when a node is clicked
32451         * @param {Node} node The node
32452         * @param {Roo.EventObject} e The event object
32453         */
32454         "click":true,
32455         /**
32456         * @event dblclick
32457         * Fires when a node is double clicked
32458         * @param {Node} node The node
32459         * @param {Roo.EventObject} e The event object
32460         */
32461         "dblclick":true,
32462         /**
32463         * @event contextmenu
32464         * Fires when a node is right clicked
32465         * @param {Node} node The node
32466         * @param {Roo.EventObject} e The event object
32467         */
32468         "contextmenu":true,
32469         /**
32470         * @event beforechildrenrendered
32471         * Fires right before the child nodes for a node are rendered
32472         * @param {Node} node The node
32473         */
32474         "beforechildrenrendered":true,
32475         /**
32476         * @event startdrag
32477         * Fires when a node starts being dragged
32478         * @param {Roo.tree.TreePanel} this
32479         * @param {Roo.tree.TreeNode} node
32480         * @param {event} e The raw browser event
32481         */ 
32482        "startdrag" : true,
32483        /**
32484         * @event enddrag
32485         * Fires when a drag operation is complete
32486         * @param {Roo.tree.TreePanel} this
32487         * @param {Roo.tree.TreeNode} node
32488         * @param {event} e The raw browser event
32489         */
32490        "enddrag" : true,
32491        /**
32492         * @event dragdrop
32493         * Fires when a dragged node is dropped on a valid DD target
32494         * @param {Roo.tree.TreePanel} this
32495         * @param {Roo.tree.TreeNode} node
32496         * @param {DD} dd The dd it was dropped on
32497         * @param {event} e The raw browser event
32498         */
32499        "dragdrop" : true,
32500        /**
32501         * @event beforenodedrop
32502         * Fires when a DD object is dropped on a node in this tree for preprocessing. Return false to cancel the drop. The dropEvent
32503         * passed to handlers has the following properties:<br />
32504         * <ul style="padding:5px;padding-left:16px;">
32505         * <li>tree - The TreePanel</li>
32506         * <li>target - The node being targeted for the drop</li>
32507         * <li>data - The drag data from the drag source</li>
32508         * <li>point - The point of the drop - append, above or below</li>
32509         * <li>source - The drag source</li>
32510         * <li>rawEvent - Raw mouse event</li>
32511         * <li>dropNode - Drop node(s) provided by the source <b>OR</b> you can supply node(s)
32512         * to be inserted by setting them on this object.</li>
32513         * <li>cancel - Set this to true to cancel the drop.</li>
32514         * </ul>
32515         * @param {Object} dropEvent
32516         */
32517        "beforenodedrop" : true,
32518        /**
32519         * @event nodedrop
32520         * Fires after a DD object is dropped on a node in this tree. The dropEvent
32521         * passed to handlers has the following properties:<br />
32522         * <ul style="padding:5px;padding-left:16px;">
32523         * <li>tree - The TreePanel</li>
32524         * <li>target - The node being targeted for the drop</li>
32525         * <li>data - The drag data from the drag source</li>
32526         * <li>point - The point of the drop - append, above or below</li>
32527         * <li>source - The drag source</li>
32528         * <li>rawEvent - Raw mouse event</li>
32529         * <li>dropNode - Dropped node(s).</li>
32530         * </ul>
32531         * @param {Object} dropEvent
32532         */
32533        "nodedrop" : true,
32534         /**
32535         * @event nodedragover
32536         * Fires when a tree node is being targeted for a drag drop, return false to signal drop not allowed. The dragOverEvent
32537         * passed to handlers has the following properties:<br />
32538         * <ul style="padding:5px;padding-left:16px;">
32539         * <li>tree - The TreePanel</li>
32540         * <li>target - The node being targeted for the drop</li>
32541         * <li>data - The drag data from the drag source</li>
32542         * <li>point - The point of the drop - append, above or below</li>
32543         * <li>source - The drag source</li>
32544         * <li>rawEvent - Raw mouse event</li>
32545         * <li>dropNode - Drop node(s) provided by the source.</li>
32546         * <li>cancel - Set this to true to signal drop not allowed.</li>
32547         * </ul>
32548         * @param {Object} dragOverEvent
32549         */
32550        "nodedragover" : true
32551         
32552     });
32553     if(this.singleExpand){
32554        this.on("beforeexpand", this.restrictExpand, this);
32555     }
32556     if (this.editor) {
32557         this.editor.tree = this;
32558         this.editor = Roo.factory(this.editor, Roo.tree);
32559     }
32560     
32561     if (this.selModel) {
32562         this.selModel = Roo.factory(this.selModel, Roo.tree);
32563     }
32564    
32565 };
32566 Roo.extend(Roo.tree.TreePanel, Roo.data.Tree, {
32567     rootVisible : true,
32568     animate: Roo.enableFx,
32569     lines : true,
32570     enableDD : false,
32571     hlDrop : Roo.enableFx,
32572   
32573     renderer: false,
32574     
32575     rendererTip: false,
32576     // private
32577     restrictExpand : function(node){
32578         var p = node.parentNode;
32579         if(p){
32580             if(p.expandedChild && p.expandedChild.parentNode == p){
32581                 p.expandedChild.collapse();
32582             }
32583             p.expandedChild = node;
32584         }
32585     },
32586
32587     // private override
32588     setRootNode : function(node){
32589         Roo.tree.TreePanel.superclass.setRootNode.call(this, node);
32590         if(!this.rootVisible){
32591             node.ui = new Roo.tree.RootTreeNodeUI(node);
32592         }
32593         return node;
32594     },
32595
32596     /**
32597      * Returns the container element for this TreePanel
32598      */
32599     getEl : function(){
32600         return this.el;
32601     },
32602
32603     /**
32604      * Returns the default TreeLoader for this TreePanel
32605      */
32606     getLoader : function(){
32607         return this.loader;
32608     },
32609
32610     /**
32611      * Expand all nodes
32612      */
32613     expandAll : function(){
32614         this.root.expand(true);
32615     },
32616
32617     /**
32618      * Collapse all nodes
32619      */
32620     collapseAll : function(){
32621         this.root.collapse(true);
32622     },
32623
32624     /**
32625      * Returns the selection model used by this TreePanel
32626      */
32627     getSelectionModel : function(){
32628         if(!this.selModel){
32629             this.selModel = new Roo.tree.DefaultSelectionModel();
32630         }
32631         return this.selModel;
32632     },
32633
32634     /**
32635      * Retrieve an array of checked nodes, or an array of a specific attribute of checked nodes (e.g. "id")
32636      * @param {String} attribute (optional) Defaults to null (return the actual nodes)
32637      * @param {TreeNode} startNode (optional) The node to start from, defaults to the root
32638      * @return {Array}
32639      */
32640     getChecked : function(a, startNode){
32641         startNode = startNode || this.root;
32642         var r = [];
32643         var f = function(){
32644             if(this.attributes.checked){
32645                 r.push(!a ? this : (a == 'id' ? this.id : this.attributes[a]));
32646             }
32647         }
32648         startNode.cascade(f);
32649         return r;
32650     },
32651
32652     /**
32653      * Expands a specified path in this TreePanel. A path can be retrieved from a node with {@link Roo.data.Node#getPath}
32654      * @param {String} path
32655      * @param {String} attr (optional) The attribute used in the path (see {@link Roo.data.Node#getPath} for more info)
32656      * @param {Function} callback (optional) The callback to call when the expand is complete. The callback will be called with
32657      * (bSuccess, oLastNode) where bSuccess is if the expand was successful and oLastNode is the last node that was expanded.
32658      */
32659     expandPath : function(path, attr, callback){
32660         attr = attr || "id";
32661         var keys = path.split(this.pathSeparator);
32662         var curNode = this.root;
32663         if(curNode.attributes[attr] != keys[1]){ // invalid root
32664             if(callback){
32665                 callback(false, null);
32666             }
32667             return;
32668         }
32669         var index = 1;
32670         var f = function(){
32671             if(++index == keys.length){
32672                 if(callback){
32673                     callback(true, curNode);
32674                 }
32675                 return;
32676             }
32677             var c = curNode.findChild(attr, keys[index]);
32678             if(!c){
32679                 if(callback){
32680                     callback(false, curNode);
32681                 }
32682                 return;
32683             }
32684             curNode = c;
32685             c.expand(false, false, f);
32686         };
32687         curNode.expand(false, false, f);
32688     },
32689
32690     /**
32691      * Selects the node in this tree at the specified path. A path can be retrieved from a node with {@link Roo.data.Node#getPath}
32692      * @param {String} path
32693      * @param {String} attr (optional) The attribute used in the path (see {@link Roo.data.Node#getPath} for more info)
32694      * @param {Function} callback (optional) The callback to call when the selection is complete. The callback will be called with
32695      * (bSuccess, oSelNode) where bSuccess is if the selection was successful and oSelNode is the selected node.
32696      */
32697     selectPath : function(path, attr, callback){
32698         attr = attr || "id";
32699         var keys = path.split(this.pathSeparator);
32700         var v = keys.pop();
32701         if(keys.length > 0){
32702             var f = function(success, node){
32703                 if(success && node){
32704                     var n = node.findChild(attr, v);
32705                     if(n){
32706                         n.select();
32707                         if(callback){
32708                             callback(true, n);
32709                         }
32710                     }else if(callback){
32711                         callback(false, n);
32712                     }
32713                 }else{
32714                     if(callback){
32715                         callback(false, n);
32716                     }
32717                 }
32718             };
32719             this.expandPath(keys.join(this.pathSeparator), attr, f);
32720         }else{
32721             this.root.select();
32722             if(callback){
32723                 callback(true, this.root);
32724             }
32725         }
32726     },
32727
32728     getTreeEl : function(){
32729         return this.el;
32730     },
32731
32732     /**
32733      * Trigger rendering of this TreePanel
32734      */
32735     render : function(){
32736         if (this.innerCt) {
32737             return this; // stop it rendering more than once!!
32738         }
32739         
32740         this.innerCt = this.el.createChild({tag:"ul",
32741                cls:"x-tree-root-ct " +
32742                (this.lines ? "x-tree-lines" : "x-tree-no-lines")});
32743
32744         if(this.containerScroll){
32745             Roo.dd.ScrollManager.register(this.el);
32746         }
32747         if((this.enableDD || this.enableDrop) && !this.dropZone){
32748            /**
32749             * The dropZone used by this tree if drop is enabled
32750             * @type Roo.tree.TreeDropZone
32751             */
32752              this.dropZone = new Roo.tree.TreeDropZone(this, this.dropConfig || {
32753                ddGroup: this.ddGroup || "TreeDD", appendOnly: this.ddAppendOnly === true
32754            });
32755         }
32756         if((this.enableDD || this.enableDrag) && !this.dragZone){
32757            /**
32758             * The dragZone used by this tree if drag is enabled
32759             * @type Roo.tree.TreeDragZone
32760             */
32761             this.dragZone = new Roo.tree.TreeDragZone(this, this.dragConfig || {
32762                ddGroup: this.ddGroup || "TreeDD",
32763                scroll: this.ddScroll
32764            });
32765         }
32766         this.getSelectionModel().init(this);
32767         if (!this.root) {
32768             Roo.log("ROOT not set in tree");
32769             return this;
32770         }
32771         this.root.render();
32772         if(!this.rootVisible){
32773             this.root.renderChildren();
32774         }
32775         return this;
32776     }
32777 });/*
32778  * Based on:
32779  * Ext JS Library 1.1.1
32780  * Copyright(c) 2006-2007, Ext JS, LLC.
32781  *
32782  * Originally Released Under LGPL - original licence link has changed is not relivant.
32783  *
32784  * Fork - LGPL
32785  * <script type="text/javascript">
32786  */
32787  
32788
32789 /**
32790  * @class Roo.tree.DefaultSelectionModel
32791  * @extends Roo.util.Observable
32792  * The default single selection for a TreePanel.
32793  * @param {Object} cfg Configuration
32794  */
32795 Roo.tree.DefaultSelectionModel = function(cfg){
32796    this.selNode = null;
32797    
32798    
32799    
32800    this.addEvents({
32801        /**
32802         * @event selectionchange
32803         * Fires when the selected node changes
32804         * @param {DefaultSelectionModel} this
32805         * @param {TreeNode} node the new selection
32806         */
32807        "selectionchange" : true,
32808
32809        /**
32810         * @event beforeselect
32811         * Fires before the selected node changes, return false to cancel the change
32812         * @param {DefaultSelectionModel} this
32813         * @param {TreeNode} node the new selection
32814         * @param {TreeNode} node the old selection
32815         */
32816        "beforeselect" : true
32817    });
32818    
32819     Roo.tree.DefaultSelectionModel.superclass.constructor.call(this,cfg);
32820 };
32821
32822 Roo.extend(Roo.tree.DefaultSelectionModel, Roo.util.Observable, {
32823     init : function(tree){
32824         this.tree = tree;
32825         tree.getTreeEl().on("keydown", this.onKeyDown, this);
32826         tree.on("click", this.onNodeClick, this);
32827     },
32828     
32829     onNodeClick : function(node, e){
32830         if (e.ctrlKey && this.selNode == node)  {
32831             this.unselect(node);
32832             return;
32833         }
32834         this.select(node);
32835     },
32836     
32837     /**
32838      * Select a node.
32839      * @param {TreeNode} node The node to select
32840      * @return {TreeNode} The selected node
32841      */
32842     select : function(node){
32843         var last = this.selNode;
32844         if(last != node && this.fireEvent('beforeselect', this, node, last) !== false){
32845             if(last){
32846                 last.ui.onSelectedChange(false);
32847             }
32848             this.selNode = node;
32849             node.ui.onSelectedChange(true);
32850             this.fireEvent("selectionchange", this, node, last);
32851         }
32852         return node;
32853     },
32854     
32855     /**
32856      * Deselect a node.
32857      * @param {TreeNode} node The node to unselect
32858      */
32859     unselect : function(node){
32860         if(this.selNode == node){
32861             this.clearSelections();
32862         }    
32863     },
32864     
32865     /**
32866      * Clear all selections
32867      */
32868     clearSelections : function(){
32869         var n = this.selNode;
32870         if(n){
32871             n.ui.onSelectedChange(false);
32872             this.selNode = null;
32873             this.fireEvent("selectionchange", this, null);
32874         }
32875         return n;
32876     },
32877     
32878     /**
32879      * Get the selected node
32880      * @return {TreeNode} The selected node
32881      */
32882     getSelectedNode : function(){
32883         return this.selNode;    
32884     },
32885     
32886     /**
32887      * Returns true if the node is selected
32888      * @param {TreeNode} node The node to check
32889      * @return {Boolean}
32890      */
32891     isSelected : function(node){
32892         return this.selNode == node;  
32893     },
32894
32895     /**
32896      * Selects the node above the selected node in the tree, intelligently walking the nodes
32897      * @return TreeNode The new selection
32898      */
32899     selectPrevious : function(){
32900         var s = this.selNode || this.lastSelNode;
32901         if(!s){
32902             return null;
32903         }
32904         var ps = s.previousSibling;
32905         if(ps){
32906             if(!ps.isExpanded() || ps.childNodes.length < 1){
32907                 return this.select(ps);
32908             } else{
32909                 var lc = ps.lastChild;
32910                 while(lc && lc.isExpanded() && lc.childNodes.length > 0){
32911                     lc = lc.lastChild;
32912                 }
32913                 return this.select(lc);
32914             }
32915         } else if(s.parentNode && (this.tree.rootVisible || !s.parentNode.isRoot)){
32916             return this.select(s.parentNode);
32917         }
32918         return null;
32919     },
32920
32921     /**
32922      * Selects the node above the selected node in the tree, intelligently walking the nodes
32923      * @return TreeNode The new selection
32924      */
32925     selectNext : function(){
32926         var s = this.selNode || this.lastSelNode;
32927         if(!s){
32928             return null;
32929         }
32930         if(s.firstChild && s.isExpanded()){
32931              return this.select(s.firstChild);
32932          }else if(s.nextSibling){
32933              return this.select(s.nextSibling);
32934          }else if(s.parentNode){
32935             var newS = null;
32936             s.parentNode.bubble(function(){
32937                 if(this.nextSibling){
32938                     newS = this.getOwnerTree().selModel.select(this.nextSibling);
32939                     return false;
32940                 }
32941             });
32942             return newS;
32943          }
32944         return null;
32945     },
32946
32947     onKeyDown : function(e){
32948         var s = this.selNode || this.lastSelNode;
32949         // undesirable, but required
32950         var sm = this;
32951         if(!s){
32952             return;
32953         }
32954         var k = e.getKey();
32955         switch(k){
32956              case e.DOWN:
32957                  e.stopEvent();
32958                  this.selectNext();
32959              break;
32960              case e.UP:
32961                  e.stopEvent();
32962                  this.selectPrevious();
32963              break;
32964              case e.RIGHT:
32965                  e.preventDefault();
32966                  if(s.hasChildNodes()){
32967                      if(!s.isExpanded()){
32968                          s.expand();
32969                      }else if(s.firstChild){
32970                          this.select(s.firstChild, e);
32971                      }
32972                  }
32973              break;
32974              case e.LEFT:
32975                  e.preventDefault();
32976                  if(s.hasChildNodes() && s.isExpanded()){
32977                      s.collapse();
32978                  }else if(s.parentNode && (this.tree.rootVisible || s.parentNode != this.tree.getRootNode())){
32979                      this.select(s.parentNode, e);
32980                  }
32981              break;
32982         };
32983     }
32984 });
32985
32986 /**
32987  * @class Roo.tree.MultiSelectionModel
32988  * @extends Roo.util.Observable
32989  * Multi selection for a TreePanel.
32990  * @param {Object} cfg Configuration
32991  */
32992 Roo.tree.MultiSelectionModel = function(){
32993    this.selNodes = [];
32994    this.selMap = {};
32995    this.addEvents({
32996        /**
32997         * @event selectionchange
32998         * Fires when the selected nodes change
32999         * @param {MultiSelectionModel} this
33000         * @param {Array} nodes Array of the selected nodes
33001         */
33002        "selectionchange" : true
33003    });
33004    Roo.tree.MultiSelectionModel.superclass.constructor.call(this,cfg);
33005    
33006 };
33007
33008 Roo.extend(Roo.tree.MultiSelectionModel, Roo.util.Observable, {
33009     init : function(tree){
33010         this.tree = tree;
33011         tree.getTreeEl().on("keydown", this.onKeyDown, this);
33012         tree.on("click", this.onNodeClick, this);
33013     },
33014     
33015     onNodeClick : function(node, e){
33016         this.select(node, e, e.ctrlKey);
33017     },
33018     
33019     /**
33020      * Select a node.
33021      * @param {TreeNode} node The node to select
33022      * @param {EventObject} e (optional) An event associated with the selection
33023      * @param {Boolean} keepExisting True to retain existing selections
33024      * @return {TreeNode} The selected node
33025      */
33026     select : function(node, e, keepExisting){
33027         if(keepExisting !== true){
33028             this.clearSelections(true);
33029         }
33030         if(this.isSelected(node)){
33031             this.lastSelNode = node;
33032             return node;
33033         }
33034         this.selNodes.push(node);
33035         this.selMap[node.id] = node;
33036         this.lastSelNode = node;
33037         node.ui.onSelectedChange(true);
33038         this.fireEvent("selectionchange", this, this.selNodes);
33039         return node;
33040     },
33041     
33042     /**
33043      * Deselect a node.
33044      * @param {TreeNode} node The node to unselect
33045      */
33046     unselect : function(node){
33047         if(this.selMap[node.id]){
33048             node.ui.onSelectedChange(false);
33049             var sn = this.selNodes;
33050             var index = -1;
33051             if(sn.indexOf){
33052                 index = sn.indexOf(node);
33053             }else{
33054                 for(var i = 0, len = sn.length; i < len; i++){
33055                     if(sn[i] == node){
33056                         index = i;
33057                         break;
33058                     }
33059                 }
33060             }
33061             if(index != -1){
33062                 this.selNodes.splice(index, 1);
33063             }
33064             delete this.selMap[node.id];
33065             this.fireEvent("selectionchange", this, this.selNodes);
33066         }
33067     },
33068     
33069     /**
33070      * Clear all selections
33071      */
33072     clearSelections : function(suppressEvent){
33073         var sn = this.selNodes;
33074         if(sn.length > 0){
33075             for(var i = 0, len = sn.length; i < len; i++){
33076                 sn[i].ui.onSelectedChange(false);
33077             }
33078             this.selNodes = [];
33079             this.selMap = {};
33080             if(suppressEvent !== true){
33081                 this.fireEvent("selectionchange", this, this.selNodes);
33082             }
33083         }
33084     },
33085     
33086     /**
33087      * Returns true if the node is selected
33088      * @param {TreeNode} node The node to check
33089      * @return {Boolean}
33090      */
33091     isSelected : function(node){
33092         return this.selMap[node.id] ? true : false;  
33093     },
33094     
33095     /**
33096      * Returns an array of the selected nodes
33097      * @return {Array}
33098      */
33099     getSelectedNodes : function(){
33100         return this.selNodes;    
33101     },
33102
33103     onKeyDown : Roo.tree.DefaultSelectionModel.prototype.onKeyDown,
33104
33105     selectNext : Roo.tree.DefaultSelectionModel.prototype.selectNext,
33106
33107     selectPrevious : Roo.tree.DefaultSelectionModel.prototype.selectPrevious
33108 });/*
33109  * Based on:
33110  * Ext JS Library 1.1.1
33111  * Copyright(c) 2006-2007, Ext JS, LLC.
33112  *
33113  * Originally Released Under LGPL - original licence link has changed is not relivant.
33114  *
33115  * Fork - LGPL
33116  * <script type="text/javascript">
33117  */
33118  
33119 /**
33120  * @class Roo.tree.TreeNode
33121  * @extends Roo.data.Node
33122  * @cfg {String} text The text for this node
33123  * @cfg {Boolean} expanded true to start the node expanded
33124  * @cfg {Boolean} allowDrag false to make this node undraggable if DD is on (defaults to true)
33125  * @cfg {Boolean} allowDrop false if this node cannot be drop on
33126  * @cfg {Boolean} disabled true to start the node disabled
33127  * @cfg {String} icon The path to an icon for the node. The preferred way to do this
33128  * is to use the cls or iconCls attributes and add the icon via a CSS background image.
33129  * @cfg {String} cls A css class to be added to the node
33130  * @cfg {String} iconCls A css class to be added to the nodes icon element for applying css background images
33131  * @cfg {String} href URL of the link used for the node (defaults to #)
33132  * @cfg {String} hrefTarget target frame for the link
33133  * @cfg {String} qtip An Ext QuickTip for the node
33134  * @cfg {String} qtipCfg An Ext QuickTip config for the node (used instead of qtip)
33135  * @cfg {Boolean} singleClickExpand True for single click expand on this node
33136  * @cfg {Function} uiProvider A UI <b>class</b> to use for this node (defaults to Roo.tree.TreeNodeUI)
33137  * @cfg {Boolean} checked True to render a checked checkbox for this node, false to render an unchecked checkbox
33138  * (defaults to undefined with no checkbox rendered)
33139  * @constructor
33140  * @param {Object/String} attributes The attributes/config for the node or just a string with the text for the node
33141  */
33142 Roo.tree.TreeNode = function(attributes){
33143     attributes = attributes || {};
33144     if(typeof attributes == "string"){
33145         attributes = {text: attributes};
33146     }
33147     this.childrenRendered = false;
33148     this.rendered = false;
33149     Roo.tree.TreeNode.superclass.constructor.call(this, attributes);
33150     this.expanded = attributes.expanded === true;
33151     this.isTarget = attributes.isTarget !== false;
33152     this.draggable = attributes.draggable !== false && attributes.allowDrag !== false;
33153     this.allowChildren = attributes.allowChildren !== false && attributes.allowDrop !== false;
33154
33155     /**
33156      * Read-only. The text for this node. To change it use setText().
33157      * @type String
33158      */
33159     this.text = attributes.text;
33160     /**
33161      * True if this node is disabled.
33162      * @type Boolean
33163      */
33164     this.disabled = attributes.disabled === true;
33165
33166     this.addEvents({
33167         /**
33168         * @event textchange
33169         * Fires when the text for this node is changed
33170         * @param {Node} this This node
33171         * @param {String} text The new text
33172         * @param {String} oldText The old text
33173         */
33174         "textchange" : true,
33175         /**
33176         * @event beforeexpand
33177         * Fires before this node is expanded, return false to cancel.
33178         * @param {Node} this This node
33179         * @param {Boolean} deep
33180         * @param {Boolean} anim
33181         */
33182         "beforeexpand" : true,
33183         /**
33184         * @event beforecollapse
33185         * Fires before this node is collapsed, return false to cancel.
33186         * @param {Node} this This node
33187         * @param {Boolean} deep
33188         * @param {Boolean} anim
33189         */
33190         "beforecollapse" : true,
33191         /**
33192         * @event expand
33193         * Fires when this node is expanded
33194         * @param {Node} this This node
33195         */
33196         "expand" : true,
33197         /**
33198         * @event disabledchange
33199         * Fires when the disabled status of this node changes
33200         * @param {Node} this This node
33201         * @param {Boolean} disabled
33202         */
33203         "disabledchange" : true,
33204         /**
33205         * @event collapse
33206         * Fires when this node is collapsed
33207         * @param {Node} this This node
33208         */
33209         "collapse" : true,
33210         /**
33211         * @event beforeclick
33212         * Fires before click processing. Return false to cancel the default action.
33213         * @param {Node} this This node
33214         * @param {Roo.EventObject} e The event object
33215         */
33216         "beforeclick":true,
33217         /**
33218         * @event checkchange
33219         * Fires when a node with a checkbox's checked property changes
33220         * @param {Node} this This node
33221         * @param {Boolean} checked
33222         */
33223         "checkchange":true,
33224         /**
33225         * @event click
33226         * Fires when this node is clicked
33227         * @param {Node} this This node
33228         * @param {Roo.EventObject} e The event object
33229         */
33230         "click":true,
33231         /**
33232         * @event dblclick
33233         * Fires when this node is double clicked
33234         * @param {Node} this This node
33235         * @param {Roo.EventObject} e The event object
33236         */
33237         "dblclick":true,
33238         /**
33239         * @event contextmenu
33240         * Fires when this node is right clicked
33241         * @param {Node} this This node
33242         * @param {Roo.EventObject} e The event object
33243         */
33244         "contextmenu":true,
33245         /**
33246         * @event beforechildrenrendered
33247         * Fires right before the child nodes for this node are rendered
33248         * @param {Node} this This node
33249         */
33250         "beforechildrenrendered":true
33251     });
33252
33253     var uiClass = this.attributes.uiProvider || Roo.tree.TreeNodeUI;
33254
33255     /**
33256      * Read-only. The UI for this node
33257      * @type TreeNodeUI
33258      */
33259     this.ui = new uiClass(this);
33260     
33261     // finally support items[]
33262     if (typeof(this.attributes.items) == 'undefined' || !this.attributes.items) {
33263         return;
33264     }
33265     
33266     
33267     Roo.each(this.attributes.items, function(c) {
33268         this.appendChild(Roo.factory(c,Roo.Tree));
33269     }, this);
33270     delete this.attributes.items;
33271     
33272     
33273     
33274 };
33275 Roo.extend(Roo.tree.TreeNode, Roo.data.Node, {
33276     preventHScroll: true,
33277     /**
33278      * Returns true if this node is expanded
33279      * @return {Boolean}
33280      */
33281     isExpanded : function(){
33282         return this.expanded;
33283     },
33284
33285     /**
33286      * Returns the UI object for this node
33287      * @return {TreeNodeUI}
33288      */
33289     getUI : function(){
33290         return this.ui;
33291     },
33292
33293     // private override
33294     setFirstChild : function(node){
33295         var of = this.firstChild;
33296         Roo.tree.TreeNode.superclass.setFirstChild.call(this, node);
33297         if(this.childrenRendered && of && node != of){
33298             of.renderIndent(true, true);
33299         }
33300         if(this.rendered){
33301             this.renderIndent(true, true);
33302         }
33303     },
33304
33305     // private override
33306     setLastChild : function(node){
33307         var ol = this.lastChild;
33308         Roo.tree.TreeNode.superclass.setLastChild.call(this, node);
33309         if(this.childrenRendered && ol && node != ol){
33310             ol.renderIndent(true, true);
33311         }
33312         if(this.rendered){
33313             this.renderIndent(true, true);
33314         }
33315     },
33316
33317     // these methods are overridden to provide lazy rendering support
33318     // private override
33319     appendChild : function()
33320     {
33321         var node = Roo.tree.TreeNode.superclass.appendChild.apply(this, arguments);
33322         if(node && this.childrenRendered){
33323             node.render();
33324         }
33325         this.ui.updateExpandIcon();
33326         return node;
33327     },
33328
33329     // private override
33330     removeChild : function(node){
33331         this.ownerTree.getSelectionModel().unselect(node);
33332         Roo.tree.TreeNode.superclass.removeChild.apply(this, arguments);
33333         // if it's been rendered remove dom node
33334         if(this.childrenRendered){
33335             node.ui.remove();
33336         }
33337         if(this.childNodes.length < 1){
33338             this.collapse(false, false);
33339         }else{
33340             this.ui.updateExpandIcon();
33341         }
33342         if(!this.firstChild) {
33343             this.childrenRendered = false;
33344         }
33345         return node;
33346     },
33347
33348     // private override
33349     insertBefore : function(node, refNode){
33350         var newNode = Roo.tree.TreeNode.superclass.insertBefore.apply(this, arguments);
33351         if(newNode && refNode && this.childrenRendered){
33352             node.render();
33353         }
33354         this.ui.updateExpandIcon();
33355         return newNode;
33356     },
33357
33358     /**
33359      * Sets the text for this node
33360      * @param {String} text
33361      */
33362     setText : function(text){
33363         var oldText = this.text;
33364         this.text = text;
33365         this.attributes.text = text;
33366         if(this.rendered){ // event without subscribing
33367             this.ui.onTextChange(this, text, oldText);
33368         }
33369         this.fireEvent("textchange", this, text, oldText);
33370     },
33371
33372     /**
33373      * Triggers selection of this node
33374      */
33375     select : function(){
33376         this.getOwnerTree().getSelectionModel().select(this);
33377     },
33378
33379     /**
33380      * Triggers deselection of this node
33381      */
33382     unselect : function(){
33383         this.getOwnerTree().getSelectionModel().unselect(this);
33384     },
33385
33386     /**
33387      * Returns true if this node is selected
33388      * @return {Boolean}
33389      */
33390     isSelected : function(){
33391         return this.getOwnerTree().getSelectionModel().isSelected(this);
33392     },
33393
33394     /**
33395      * Expand this node.
33396      * @param {Boolean} deep (optional) True to expand all children as well
33397      * @param {Boolean} anim (optional) false to cancel the default animation
33398      * @param {Function} callback (optional) A callback to be called when
33399      * expanding this node completes (does not wait for deep expand to complete).
33400      * Called with 1 parameter, this node.
33401      */
33402     expand : function(deep, anim, callback){
33403         if(!this.expanded){
33404             if(this.fireEvent("beforeexpand", this, deep, anim) === false){
33405                 return;
33406             }
33407             if(!this.childrenRendered){
33408                 this.renderChildren();
33409             }
33410             this.expanded = true;
33411             if(!this.isHiddenRoot() && (this.getOwnerTree().animate && anim !== false) || anim){
33412                 this.ui.animExpand(function(){
33413                     this.fireEvent("expand", this);
33414                     if(typeof callback == "function"){
33415                         callback(this);
33416                     }
33417                     if(deep === true){
33418                         this.expandChildNodes(true);
33419                     }
33420                 }.createDelegate(this));
33421                 return;
33422             }else{
33423                 this.ui.expand();
33424                 this.fireEvent("expand", this);
33425                 if(typeof callback == "function"){
33426                     callback(this);
33427                 }
33428             }
33429         }else{
33430            if(typeof callback == "function"){
33431                callback(this);
33432            }
33433         }
33434         if(deep === true){
33435             this.expandChildNodes(true);
33436         }
33437     },
33438
33439     isHiddenRoot : function(){
33440         return this.isRoot && !this.getOwnerTree().rootVisible;
33441     },
33442
33443     /**
33444      * Collapse this node.
33445      * @param {Boolean} deep (optional) True to collapse all children as well
33446      * @param {Boolean} anim (optional) false to cancel the default animation
33447      */
33448     collapse : function(deep, anim){
33449         if(this.expanded && !this.isHiddenRoot()){
33450             if(this.fireEvent("beforecollapse", this, deep, anim) === false){
33451                 return;
33452             }
33453             this.expanded = false;
33454             if((this.getOwnerTree().animate && anim !== false) || anim){
33455                 this.ui.animCollapse(function(){
33456                     this.fireEvent("collapse", this);
33457                     if(deep === true){
33458                         this.collapseChildNodes(true);
33459                     }
33460                 }.createDelegate(this));
33461                 return;
33462             }else{
33463                 this.ui.collapse();
33464                 this.fireEvent("collapse", this);
33465             }
33466         }
33467         if(deep === true){
33468             var cs = this.childNodes;
33469             for(var i = 0, len = cs.length; i < len; i++) {
33470                 cs[i].collapse(true, false);
33471             }
33472         }
33473     },
33474
33475     // private
33476     delayedExpand : function(delay){
33477         if(!this.expandProcId){
33478             this.expandProcId = this.expand.defer(delay, this);
33479         }
33480     },
33481
33482     // private
33483     cancelExpand : function(){
33484         if(this.expandProcId){
33485             clearTimeout(this.expandProcId);
33486         }
33487         this.expandProcId = false;
33488     },
33489
33490     /**
33491      * Toggles expanded/collapsed state of the node
33492      */
33493     toggle : function(){
33494         if(this.expanded){
33495             this.collapse();
33496         }else{
33497             this.expand();
33498         }
33499     },
33500
33501     /**
33502      * Ensures all parent nodes are expanded
33503      */
33504     ensureVisible : function(callback){
33505         var tree = this.getOwnerTree();
33506         tree.expandPath(this.parentNode.getPath(), false, function(){
33507             tree.getTreeEl().scrollChildIntoView(this.ui.anchor);
33508             Roo.callback(callback);
33509         }.createDelegate(this));
33510     },
33511
33512     /**
33513      * Expand all child nodes
33514      * @param {Boolean} deep (optional) true if the child nodes should also expand their child nodes
33515      */
33516     expandChildNodes : function(deep){
33517         var cs = this.childNodes;
33518         for(var i = 0, len = cs.length; i < len; i++) {
33519                 cs[i].expand(deep);
33520         }
33521     },
33522
33523     /**
33524      * Collapse all child nodes
33525      * @param {Boolean} deep (optional) true if the child nodes should also collapse their child nodes
33526      */
33527     collapseChildNodes : function(deep){
33528         var cs = this.childNodes;
33529         for(var i = 0, len = cs.length; i < len; i++) {
33530                 cs[i].collapse(deep);
33531         }
33532     },
33533
33534     /**
33535      * Disables this node
33536      */
33537     disable : function(){
33538         this.disabled = true;
33539         this.unselect();
33540         if(this.rendered && this.ui.onDisableChange){ // event without subscribing
33541             this.ui.onDisableChange(this, true);
33542         }
33543         this.fireEvent("disabledchange", this, true);
33544     },
33545
33546     /**
33547      * Enables this node
33548      */
33549     enable : function(){
33550         this.disabled = false;
33551         if(this.rendered && this.ui.onDisableChange){ // event without subscribing
33552             this.ui.onDisableChange(this, false);
33553         }
33554         this.fireEvent("disabledchange", this, false);
33555     },
33556
33557     // private
33558     renderChildren : function(suppressEvent){
33559         if(suppressEvent !== false){
33560             this.fireEvent("beforechildrenrendered", this);
33561         }
33562         var cs = this.childNodes;
33563         for(var i = 0, len = cs.length; i < len; i++){
33564             cs[i].render(true);
33565         }
33566         this.childrenRendered = true;
33567     },
33568
33569     // private
33570     sort : function(fn, scope){
33571         Roo.tree.TreeNode.superclass.sort.apply(this, arguments);
33572         if(this.childrenRendered){
33573             var cs = this.childNodes;
33574             for(var i = 0, len = cs.length; i < len; i++){
33575                 cs[i].render(true);
33576             }
33577         }
33578     },
33579
33580     // private
33581     render : function(bulkRender){
33582         this.ui.render(bulkRender);
33583         if(!this.rendered){
33584             this.rendered = true;
33585             if(this.expanded){
33586                 this.expanded = false;
33587                 this.expand(false, false);
33588             }
33589         }
33590     },
33591
33592     // private
33593     renderIndent : function(deep, refresh){
33594         if(refresh){
33595             this.ui.childIndent = null;
33596         }
33597         this.ui.renderIndent();
33598         if(deep === true && this.childrenRendered){
33599             var cs = this.childNodes;
33600             for(var i = 0, len = cs.length; i < len; i++){
33601                 cs[i].renderIndent(true, refresh);
33602             }
33603         }
33604     }
33605 });/*
33606  * Based on:
33607  * Ext JS Library 1.1.1
33608  * Copyright(c) 2006-2007, Ext JS, LLC.
33609  *
33610  * Originally Released Under LGPL - original licence link has changed is not relivant.
33611  *
33612  * Fork - LGPL
33613  * <script type="text/javascript">
33614  */
33615  
33616 /**
33617  * @class Roo.tree.AsyncTreeNode
33618  * @extends Roo.tree.TreeNode
33619  * @cfg {TreeLoader} loader A TreeLoader to be used by this node (defaults to the loader defined on the tree)
33620  * @constructor
33621  * @param {Object/String} attributes The attributes/config for the node or just a string with the text for the node 
33622  */
33623  Roo.tree.AsyncTreeNode = function(config){
33624     this.loaded = false;
33625     this.loading = false;
33626     Roo.tree.AsyncTreeNode.superclass.constructor.apply(this, arguments);
33627     /**
33628     * @event beforeload
33629     * Fires before this node is loaded, return false to cancel
33630     * @param {Node} this This node
33631     */
33632     this.addEvents({'beforeload':true, 'load': true});
33633     /**
33634     * @event load
33635     * Fires when this node is loaded
33636     * @param {Node} this This node
33637     */
33638     /**
33639      * The loader used by this node (defaults to using the tree's defined loader)
33640      * @type TreeLoader
33641      * @property loader
33642      */
33643 };
33644 Roo.extend(Roo.tree.AsyncTreeNode, Roo.tree.TreeNode, {
33645     expand : function(deep, anim, callback){
33646         if(this.loading){ // if an async load is already running, waiting til it's done
33647             var timer;
33648             var f = function(){
33649                 if(!this.loading){ // done loading
33650                     clearInterval(timer);
33651                     this.expand(deep, anim, callback);
33652                 }
33653             }.createDelegate(this);
33654             timer = setInterval(f, 200);
33655             return;
33656         }
33657         if(!this.loaded){
33658             if(this.fireEvent("beforeload", this) === false){
33659                 return;
33660             }
33661             this.loading = true;
33662             this.ui.beforeLoad(this);
33663             var loader = this.loader || this.attributes.loader || this.getOwnerTree().getLoader();
33664             if(loader){
33665                 loader.load(this, this.loadComplete.createDelegate(this, [deep, anim, callback]));
33666                 return;
33667             }
33668         }
33669         Roo.tree.AsyncTreeNode.superclass.expand.call(this, deep, anim, callback);
33670     },
33671     
33672     /**
33673      * Returns true if this node is currently loading
33674      * @return {Boolean}
33675      */
33676     isLoading : function(){
33677         return this.loading;  
33678     },
33679     
33680     loadComplete : function(deep, anim, callback){
33681         this.loading = false;
33682         this.loaded = true;
33683         this.ui.afterLoad(this);
33684         this.fireEvent("load", this);
33685         this.expand(deep, anim, callback);
33686     },
33687     
33688     /**
33689      * Returns true if this node has been loaded
33690      * @return {Boolean}
33691      */
33692     isLoaded : function(){
33693         return this.loaded;
33694     },
33695     
33696     hasChildNodes : function(){
33697         if(!this.isLeaf() && !this.loaded){
33698             return true;
33699         }else{
33700             return Roo.tree.AsyncTreeNode.superclass.hasChildNodes.call(this);
33701         }
33702     },
33703
33704     /**
33705      * Trigger a reload for this node
33706      * @param {Function} callback
33707      */
33708     reload : function(callback){
33709         this.collapse(false, false);
33710         while(this.firstChild){
33711             this.removeChild(this.firstChild);
33712         }
33713         this.childrenRendered = false;
33714         this.loaded = false;
33715         if(this.isHiddenRoot()){
33716             this.expanded = false;
33717         }
33718         this.expand(false, false, callback);
33719     }
33720 });/*
33721  * Based on:
33722  * Ext JS Library 1.1.1
33723  * Copyright(c) 2006-2007, Ext JS, LLC.
33724  *
33725  * Originally Released Under LGPL - original licence link has changed is not relivant.
33726  *
33727  * Fork - LGPL
33728  * <script type="text/javascript">
33729  */
33730  
33731 /**
33732  * @class Roo.tree.TreeNodeUI
33733  * @constructor
33734  * @param {Object} node The node to render
33735  * The TreeNode UI implementation is separate from the
33736  * tree implementation. Unless you are customizing the tree UI,
33737  * you should never have to use this directly.
33738  */
33739 Roo.tree.TreeNodeUI = function(node){
33740     this.node = node;
33741     this.rendered = false;
33742     this.animating = false;
33743     this.emptyIcon = Roo.BLANK_IMAGE_URL;
33744 };
33745
33746 Roo.tree.TreeNodeUI.prototype = {
33747     removeChild : function(node){
33748         if(this.rendered){
33749             this.ctNode.removeChild(node.ui.getEl());
33750         }
33751     },
33752
33753     beforeLoad : function(){
33754          this.addClass("x-tree-node-loading");
33755     },
33756
33757     afterLoad : function(){
33758          this.removeClass("x-tree-node-loading");
33759     },
33760
33761     onTextChange : function(node, text, oldText){
33762         if(this.rendered){
33763             this.textNode.innerHTML = text;
33764         }
33765     },
33766
33767     onDisableChange : function(node, state){
33768         this.disabled = state;
33769         if(state){
33770             this.addClass("x-tree-node-disabled");
33771         }else{
33772             this.removeClass("x-tree-node-disabled");
33773         }
33774     },
33775
33776     onSelectedChange : function(state){
33777         if(state){
33778             this.focus();
33779             this.addClass("x-tree-selected");
33780         }else{
33781             //this.blur();
33782             this.removeClass("x-tree-selected");
33783         }
33784     },
33785
33786     onMove : function(tree, node, oldParent, newParent, index, refNode){
33787         this.childIndent = null;
33788         if(this.rendered){
33789             var targetNode = newParent.ui.getContainer();
33790             if(!targetNode){//target not rendered
33791                 this.holder = document.createElement("div");
33792                 this.holder.appendChild(this.wrap);
33793                 return;
33794             }
33795             var insertBefore = refNode ? refNode.ui.getEl() : null;
33796             if(insertBefore){
33797                 targetNode.insertBefore(this.wrap, insertBefore);
33798             }else{
33799                 targetNode.appendChild(this.wrap);
33800             }
33801             this.node.renderIndent(true);
33802         }
33803     },
33804
33805     addClass : function(cls){
33806         if(this.elNode){
33807             Roo.fly(this.elNode).addClass(cls);
33808         }
33809     },
33810
33811     removeClass : function(cls){
33812         if(this.elNode){
33813             Roo.fly(this.elNode).removeClass(cls);
33814         }
33815     },
33816
33817     remove : function(){
33818         if(this.rendered){
33819             this.holder = document.createElement("div");
33820             this.holder.appendChild(this.wrap);
33821         }
33822     },
33823
33824     fireEvent : function(){
33825         return this.node.fireEvent.apply(this.node, arguments);
33826     },
33827
33828     initEvents : function(){
33829         this.node.on("move", this.onMove, this);
33830         var E = Roo.EventManager;
33831         var a = this.anchor;
33832
33833         var el = Roo.fly(a, '_treeui');
33834
33835         if(Roo.isOpera){ // opera render bug ignores the CSS
33836             el.setStyle("text-decoration", "none");
33837         }
33838
33839         el.on("click", this.onClick, this);
33840         el.on("dblclick", this.onDblClick, this);
33841
33842         if(this.checkbox){
33843             Roo.EventManager.on(this.checkbox,
33844                     Roo.isIE ? 'click' : 'change', this.onCheckChange, this);
33845         }
33846
33847         el.on("contextmenu", this.onContextMenu, this);
33848
33849         var icon = Roo.fly(this.iconNode);
33850         icon.on("click", this.onClick, this);
33851         icon.on("dblclick", this.onDblClick, this);
33852         icon.on("contextmenu", this.onContextMenu, this);
33853         E.on(this.ecNode, "click", this.ecClick, this, true);
33854
33855         if(this.node.disabled){
33856             this.addClass("x-tree-node-disabled");
33857         }
33858         if(this.node.hidden){
33859             this.addClass("x-tree-node-disabled");
33860         }
33861         var ot = this.node.getOwnerTree();
33862         var dd = ot.enableDD || ot.enableDrag || ot.enableDrop;
33863         if(dd && (!this.node.isRoot || ot.rootVisible)){
33864             Roo.dd.Registry.register(this.elNode, {
33865                 node: this.node,
33866                 handles: this.getDDHandles(),
33867                 isHandle: false
33868             });
33869         }
33870     },
33871
33872     getDDHandles : function(){
33873         return [this.iconNode, this.textNode];
33874     },
33875
33876     hide : function(){
33877         if(this.rendered){
33878             this.wrap.style.display = "none";
33879         }
33880     },
33881
33882     show : function(){
33883         if(this.rendered){
33884             this.wrap.style.display = "";
33885         }
33886     },
33887
33888     onContextMenu : function(e){
33889         if (this.node.hasListener("contextmenu") || this.node.getOwnerTree().hasListener("contextmenu")) {
33890             e.preventDefault();
33891             this.focus();
33892             this.fireEvent("contextmenu", this.node, e);
33893         }
33894     },
33895
33896     onClick : function(e){
33897         if(this.dropping){
33898             e.stopEvent();
33899             return;
33900         }
33901         if(this.fireEvent("beforeclick", this.node, e) !== false){
33902             if(!this.disabled && this.node.attributes.href){
33903                 this.fireEvent("click", this.node, e);
33904                 return;
33905             }
33906             e.preventDefault();
33907             if(this.disabled){
33908                 return;
33909             }
33910
33911             if(this.node.attributes.singleClickExpand && !this.animating && this.node.hasChildNodes()){
33912                 this.node.toggle();
33913             }
33914
33915             this.fireEvent("click", this.node, e);
33916         }else{
33917             e.stopEvent();
33918         }
33919     },
33920
33921     onDblClick : function(e){
33922         e.preventDefault();
33923         if(this.disabled){
33924             return;
33925         }
33926         if(this.checkbox){
33927             this.toggleCheck();
33928         }
33929         if(!this.animating && this.node.hasChildNodes()){
33930             this.node.toggle();
33931         }
33932         this.fireEvent("dblclick", this.node, e);
33933     },
33934
33935     onCheckChange : function(){
33936         var checked = this.checkbox.checked;
33937         this.node.attributes.checked = checked;
33938         this.fireEvent('checkchange', this.node, checked);
33939     },
33940
33941     ecClick : function(e){
33942         if(!this.animating && this.node.hasChildNodes()){
33943             this.node.toggle();
33944         }
33945     },
33946
33947     startDrop : function(){
33948         this.dropping = true;
33949     },
33950
33951     // delayed drop so the click event doesn't get fired on a drop
33952     endDrop : function(){
33953        setTimeout(function(){
33954            this.dropping = false;
33955        }.createDelegate(this), 50);
33956     },
33957
33958     expand : function(){
33959         this.updateExpandIcon();
33960         this.ctNode.style.display = "";
33961     },
33962
33963     focus : function(){
33964         if(!this.node.preventHScroll){
33965             try{this.anchor.focus();
33966             }catch(e){}
33967         }else if(!Roo.isIE){
33968             try{
33969                 var noscroll = this.node.getOwnerTree().getTreeEl().dom;
33970                 var l = noscroll.scrollLeft;
33971                 this.anchor.focus();
33972                 noscroll.scrollLeft = l;
33973             }catch(e){}
33974         }
33975     },
33976
33977     toggleCheck : function(value){
33978         var cb = this.checkbox;
33979         if(cb){
33980             cb.checked = (value === undefined ? !cb.checked : value);
33981         }
33982     },
33983
33984     blur : function(){
33985         try{
33986             this.anchor.blur();
33987         }catch(e){}
33988     },
33989
33990     animExpand : function(callback){
33991         var ct = Roo.get(this.ctNode);
33992         ct.stopFx();
33993         if(!this.node.hasChildNodes()){
33994             this.updateExpandIcon();
33995             this.ctNode.style.display = "";
33996             Roo.callback(callback);
33997             return;
33998         }
33999         this.animating = true;
34000         this.updateExpandIcon();
34001
34002         ct.slideIn('t', {
34003            callback : function(){
34004                this.animating = false;
34005                Roo.callback(callback);
34006             },
34007             scope: this,
34008             duration: this.node.ownerTree.duration || .25
34009         });
34010     },
34011
34012     highlight : function(){
34013         var tree = this.node.getOwnerTree();
34014         Roo.fly(this.wrap).highlight(
34015             tree.hlColor || "C3DAF9",
34016             {endColor: tree.hlBaseColor}
34017         );
34018     },
34019
34020     collapse : function(){
34021         this.updateExpandIcon();
34022         this.ctNode.style.display = "none";
34023     },
34024
34025     animCollapse : function(callback){
34026         var ct = Roo.get(this.ctNode);
34027         ct.enableDisplayMode('block');
34028         ct.stopFx();
34029
34030         this.animating = true;
34031         this.updateExpandIcon();
34032
34033         ct.slideOut('t', {
34034             callback : function(){
34035                this.animating = false;
34036                Roo.callback(callback);
34037             },
34038             scope: this,
34039             duration: this.node.ownerTree.duration || .25
34040         });
34041     },
34042
34043     getContainer : function(){
34044         return this.ctNode;
34045     },
34046
34047     getEl : function(){
34048         return this.wrap;
34049     },
34050
34051     appendDDGhost : function(ghostNode){
34052         ghostNode.appendChild(this.elNode.cloneNode(true));
34053     },
34054
34055     getDDRepairXY : function(){
34056         return Roo.lib.Dom.getXY(this.iconNode);
34057     },
34058
34059     onRender : function(){
34060         this.render();
34061     },
34062
34063     render : function(bulkRender){
34064         var n = this.node, a = n.attributes;
34065         var targetNode = n.parentNode ?
34066               n.parentNode.ui.getContainer() : n.ownerTree.innerCt.dom;
34067
34068         if(!this.rendered){
34069             this.rendered = true;
34070
34071             this.renderElements(n, a, targetNode, bulkRender);
34072
34073             if(a.qtip){
34074                if(this.textNode.setAttributeNS){
34075                    this.textNode.setAttributeNS("ext", "qtip", a.qtip);
34076                    if(a.qtipTitle){
34077                        this.textNode.setAttributeNS("ext", "qtitle", a.qtipTitle);
34078                    }
34079                }else{
34080                    this.textNode.setAttribute("ext:qtip", a.qtip);
34081                    if(a.qtipTitle){
34082                        this.textNode.setAttribute("ext:qtitle", a.qtipTitle);
34083                    }
34084                }
34085             }else if(a.qtipCfg){
34086                 a.qtipCfg.target = Roo.id(this.textNode);
34087                 Roo.QuickTips.register(a.qtipCfg);
34088             }
34089             this.initEvents();
34090             if(!this.node.expanded){
34091                 this.updateExpandIcon();
34092             }
34093         }else{
34094             if(bulkRender === true) {
34095                 targetNode.appendChild(this.wrap);
34096             }
34097         }
34098     },
34099
34100     renderElements : function(n, a, targetNode, bulkRender)
34101     {
34102         // add some indent caching, this helps performance when rendering a large tree
34103         this.indentMarkup = n.parentNode ? n.parentNode.ui.getChildIndent() : '';
34104         var t = n.getOwnerTree();
34105         var txt = t.renderer ? t.renderer(n.attributes) : Roo.util.Format.htmlEncode(n.text);
34106         if (typeof(n.attributes.html) != 'undefined') {
34107             txt = n.attributes.html;
34108         }
34109         var tip = t.rendererTip ? t.rendererTip(n.attributes) : txt;
34110         var cb = typeof a.checked == 'boolean';
34111         var href = a.href ? a.href : Roo.isGecko ? "" : "#";
34112         var buf = ['<li class="x-tree-node"><div class="x-tree-node-el ', a.cls,'">',
34113             '<span class="x-tree-node-indent">',this.indentMarkup,"</span>",
34114             '<img src="', this.emptyIcon, '" class="x-tree-ec-icon" />',
34115             '<img src="', a.icon || this.emptyIcon, '" class="x-tree-node-icon',(a.icon ? " x-tree-node-inline-icon" : ""),(a.iconCls ? " "+a.iconCls : ""),'" unselectable="on" />',
34116             cb ? ('<input class="x-tree-node-cb" type="checkbox" ' + (a.checked ? 'checked="checked" />' : ' />')) : '',
34117             '<a hidefocus="on" href="',href,'" tabIndex="1" ',
34118              a.hrefTarget ? ' target="'+a.hrefTarget+'"' : "", 
34119                 '><span unselectable="on" qtip="' , tip ,'">',txt,"</span></a></div>",
34120             '<ul class="x-tree-node-ct" style="display:none;"></ul>',
34121             "</li>"];
34122
34123         if(bulkRender !== true && n.nextSibling && n.nextSibling.ui.getEl()){
34124             this.wrap = Roo.DomHelper.insertHtml("beforeBegin",
34125                                 n.nextSibling.ui.getEl(), buf.join(""));
34126         }else{
34127             this.wrap = Roo.DomHelper.insertHtml("beforeEnd", targetNode, buf.join(""));
34128         }
34129
34130         this.elNode = this.wrap.childNodes[0];
34131         this.ctNode = this.wrap.childNodes[1];
34132         var cs = this.elNode.childNodes;
34133         this.indentNode = cs[0];
34134         this.ecNode = cs[1];
34135         this.iconNode = cs[2];
34136         var index = 3;
34137         if(cb){
34138             this.checkbox = cs[3];
34139             index++;
34140         }
34141         this.anchor = cs[index];
34142         this.textNode = cs[index].firstChild;
34143     },
34144
34145     getAnchor : function(){
34146         return this.anchor;
34147     },
34148
34149     getTextEl : function(){
34150         return this.textNode;
34151     },
34152
34153     getIconEl : function(){
34154         return this.iconNode;
34155     },
34156
34157     isChecked : function(){
34158         return this.checkbox ? this.checkbox.checked : false;
34159     },
34160
34161     updateExpandIcon : function(){
34162         if(this.rendered){
34163             var n = this.node, c1, c2;
34164             var cls = n.isLast() ? "x-tree-elbow-end" : "x-tree-elbow";
34165             var hasChild = n.hasChildNodes();
34166             if(hasChild){
34167                 if(n.expanded){
34168                     cls += "-minus";
34169                     c1 = "x-tree-node-collapsed";
34170                     c2 = "x-tree-node-expanded";
34171                 }else{
34172                     cls += "-plus";
34173                     c1 = "x-tree-node-expanded";
34174                     c2 = "x-tree-node-collapsed";
34175                 }
34176                 if(this.wasLeaf){
34177                     this.removeClass("x-tree-node-leaf");
34178                     this.wasLeaf = false;
34179                 }
34180                 if(this.c1 != c1 || this.c2 != c2){
34181                     Roo.fly(this.elNode).replaceClass(c1, c2);
34182                     this.c1 = c1; this.c2 = c2;
34183                 }
34184             }else{
34185                 // this changes non-leafs into leafs if they have no children.
34186                 // it's not very rational behaviour..
34187                 
34188                 if(!this.wasLeaf && this.node.leaf){
34189                     Roo.fly(this.elNode).replaceClass("x-tree-node-expanded", "x-tree-node-leaf");
34190                     delete this.c1;
34191                     delete this.c2;
34192                     this.wasLeaf = true;
34193                 }
34194             }
34195             var ecc = "x-tree-ec-icon "+cls;
34196             if(this.ecc != ecc){
34197                 this.ecNode.className = ecc;
34198                 this.ecc = ecc;
34199             }
34200         }
34201     },
34202
34203     getChildIndent : function(){
34204         if(!this.childIndent){
34205             var buf = [];
34206             var p = this.node;
34207             while(p){
34208                 if(!p.isRoot || (p.isRoot && p.ownerTree.rootVisible)){
34209                     if(!p.isLast()) {
34210                         buf.unshift('<img src="'+this.emptyIcon+'" class="x-tree-elbow-line" />');
34211                     } else {
34212                         buf.unshift('<img src="'+this.emptyIcon+'" class="x-tree-icon" />');
34213                     }
34214                 }
34215                 p = p.parentNode;
34216             }
34217             this.childIndent = buf.join("");
34218         }
34219         return this.childIndent;
34220     },
34221
34222     renderIndent : function(){
34223         if(this.rendered){
34224             var indent = "";
34225             var p = this.node.parentNode;
34226             if(p){
34227                 indent = p.ui.getChildIndent();
34228             }
34229             if(this.indentMarkup != indent){ // don't rerender if not required
34230                 this.indentNode.innerHTML = indent;
34231                 this.indentMarkup = indent;
34232             }
34233             this.updateExpandIcon();
34234         }
34235     }
34236 };
34237
34238 Roo.tree.RootTreeNodeUI = function(){
34239     Roo.tree.RootTreeNodeUI.superclass.constructor.apply(this, arguments);
34240 };
34241 Roo.extend(Roo.tree.RootTreeNodeUI, Roo.tree.TreeNodeUI, {
34242     render : function(){
34243         if(!this.rendered){
34244             var targetNode = this.node.ownerTree.innerCt.dom;
34245             this.node.expanded = true;
34246             targetNode.innerHTML = '<div class="x-tree-root-node"></div>';
34247             this.wrap = this.ctNode = targetNode.firstChild;
34248         }
34249     },
34250     collapse : function(){
34251     },
34252     expand : function(){
34253     }
34254 });/*
34255  * Based on:
34256  * Ext JS Library 1.1.1
34257  * Copyright(c) 2006-2007, Ext JS, LLC.
34258  *
34259  * Originally Released Under LGPL - original licence link has changed is not relivant.
34260  *
34261  * Fork - LGPL
34262  * <script type="text/javascript">
34263  */
34264 /**
34265  * @class Roo.tree.TreeLoader
34266  * @extends Roo.util.Observable
34267  * A TreeLoader provides for lazy loading of an {@link Roo.tree.TreeNode}'s child
34268  * nodes from a specified URL. The response must be a javascript Array definition
34269  * who's elements are node definition objects. eg:
34270  * <pre><code>
34271 {  success : true,
34272    data :      [
34273    
34274     { 'id': 1, 'text': 'A folder Node', 'leaf': false },
34275     { 'id': 2, 'text': 'A leaf Node', 'leaf': true }
34276     ]
34277 }
34278
34279
34280 </code></pre>
34281  * <br><br>
34282  * The old style respose with just an array is still supported, but not recommended.
34283  * <br><br>
34284  *
34285  * A server request is sent, and child nodes are loaded only when a node is expanded.
34286  * The loading node's id is passed to the server under the parameter name "node" to
34287  * enable the server to produce the correct child nodes.
34288  * <br><br>
34289  * To pass extra parameters, an event handler may be attached to the "beforeload"
34290  * event, and the parameters specified in the TreeLoader's baseParams property:
34291  * <pre><code>
34292     myTreeLoader.on("beforeload", function(treeLoader, node) {
34293         this.baseParams.category = node.attributes.category;
34294     }, this);
34295 </code></pre><
34296  * This would pass an HTTP parameter called "category" to the server containing
34297  * the value of the Node's "category" attribute.
34298  * @constructor
34299  * Creates a new Treeloader.
34300  * @param {Object} config A config object containing config properties.
34301  */
34302 Roo.tree.TreeLoader = function(config){
34303     this.baseParams = {};
34304     this.requestMethod = "POST";
34305     Roo.apply(this, config);
34306
34307     this.addEvents({
34308     
34309         /**
34310          * @event beforeload
34311          * Fires before a network request is made to retrieve the Json text which specifies a node's children.
34312          * @param {Object} This TreeLoader object.
34313          * @param {Object} node The {@link Roo.tree.TreeNode} object being loaded.
34314          * @param {Object} callback The callback function specified in the {@link #load} call.
34315          */
34316         beforeload : true,
34317         /**
34318          * @event load
34319          * Fires when the node has been successfuly loaded.
34320          * @param {Object} This TreeLoader object.
34321          * @param {Object} node The {@link Roo.tree.TreeNode} object being loaded.
34322          * @param {Object} response The response object containing the data from the server.
34323          */
34324         load : true,
34325         /**
34326          * @event loadexception
34327          * Fires if the network request failed.
34328          * @param {Object} This TreeLoader object.
34329          * @param {Object} node The {@link Roo.tree.TreeNode} object being loaded.
34330          * @param {Object} response The response object containing the data from the server.
34331          */
34332         loadexception : true,
34333         /**
34334          * @event create
34335          * Fires before a node is created, enabling you to return custom Node types 
34336          * @param {Object} This TreeLoader object.
34337          * @param {Object} attr - the data returned from the AJAX call (modify it to suit)
34338          */
34339         create : true
34340     });
34341
34342     Roo.tree.TreeLoader.superclass.constructor.call(this);
34343 };
34344
34345 Roo.extend(Roo.tree.TreeLoader, Roo.util.Observable, {
34346     /**
34347     * @cfg {String} dataUrl The URL from which to request a Json string which
34348     * specifies an array of node definition object representing the child nodes
34349     * to be loaded.
34350     */
34351     /**
34352     * @cfg {String} requestMethod either GET or POST
34353     * defaults to POST (due to BC)
34354     * to be loaded.
34355     */
34356     /**
34357     * @cfg {Object} baseParams (optional) An object containing properties which
34358     * specify HTTP parameters to be passed to each request for child nodes.
34359     */
34360     /**
34361     * @cfg {Object} baseAttrs (optional) An object containing attributes to be added to all nodes
34362     * created by this loader. If the attributes sent by the server have an attribute in this object,
34363     * they take priority.
34364     */
34365     /**
34366     * @cfg {Object} uiProviders (optional) An object containing properties which
34367     * 
34368     * DEPRECATED - use 'create' event handler to modify attributes - which affect creation.
34369     * specify custom {@link Roo.tree.TreeNodeUI} implementations. If the optional
34370     * <i>uiProvider</i> attribute of a returned child node is a string rather
34371     * than a reference to a TreeNodeUI implementation, this that string value
34372     * is used as a property name in the uiProviders object. You can define the provider named
34373     * 'default' , and this will be used for all nodes (if no uiProvider is delivered by the node data)
34374     */
34375     uiProviders : {},
34376
34377     /**
34378     * @cfg {Boolean} clearOnLoad (optional) Default to true. Remove previously existing
34379     * child nodes before loading.
34380     */
34381     clearOnLoad : true,
34382
34383     /**
34384     * @cfg {String} root (optional) Default to false. Use this to read data from an object 
34385     * property on loading, rather than expecting an array. (eg. more compatible to a standard
34386     * Grid query { data : [ .....] }
34387     */
34388     
34389     root : false,
34390      /**
34391     * @cfg {String} queryParam (optional) 
34392     * Name of the query as it will be passed on the querystring (defaults to 'node')
34393     * eg. the request will be ?node=[id]
34394     */
34395     
34396     
34397     queryParam: false,
34398     
34399     /**
34400      * Load an {@link Roo.tree.TreeNode} from the URL specified in the constructor.
34401      * This is called automatically when a node is expanded, but may be used to reload
34402      * a node (or append new children if the {@link #clearOnLoad} option is false.)
34403      * @param {Roo.tree.TreeNode} node
34404      * @param {Function} callback
34405      */
34406     load : function(node, callback){
34407         if(this.clearOnLoad){
34408             while(node.firstChild){
34409                 node.removeChild(node.firstChild);
34410             }
34411         }
34412         if(node.attributes.children){ // preloaded json children
34413             var cs = node.attributes.children;
34414             for(var i = 0, len = cs.length; i < len; i++){
34415                 node.appendChild(this.createNode(cs[i]));
34416             }
34417             if(typeof callback == "function"){
34418                 callback();
34419             }
34420         }else if(this.dataUrl){
34421             this.requestData(node, callback);
34422         }
34423     },
34424
34425     getParams: function(node){
34426         var buf = [], bp = this.baseParams;
34427         for(var key in bp){
34428             if(typeof bp[key] != "function"){
34429                 buf.push(encodeURIComponent(key), "=", encodeURIComponent(bp[key]), "&");
34430             }
34431         }
34432         var n = this.queryParam === false ? 'node' : this.queryParam;
34433         buf.push(n + "=", encodeURIComponent(node.id));
34434         return buf.join("");
34435     },
34436
34437     requestData : function(node, callback){
34438         if(this.fireEvent("beforeload", this, node, callback) !== false){
34439             this.transId = Roo.Ajax.request({
34440                 method:this.requestMethod,
34441                 url: this.dataUrl||this.url,
34442                 success: this.handleResponse,
34443                 failure: this.handleFailure,
34444                 scope: this,
34445                 argument: {callback: callback, node: node},
34446                 params: this.getParams(node)
34447             });
34448         }else{
34449             // if the load is cancelled, make sure we notify
34450             // the node that we are done
34451             if(typeof callback == "function"){
34452                 callback();
34453             }
34454         }
34455     },
34456
34457     isLoading : function(){
34458         return this.transId ? true : false;
34459     },
34460
34461     abort : function(){
34462         if(this.isLoading()){
34463             Roo.Ajax.abort(this.transId);
34464         }
34465     },
34466
34467     // private
34468     createNode : function(attr)
34469     {
34470         // apply baseAttrs, nice idea Corey!
34471         if(this.baseAttrs){
34472             Roo.applyIf(attr, this.baseAttrs);
34473         }
34474         if(this.applyLoader !== false){
34475             attr.loader = this;
34476         }
34477         // uiProvider = depreciated..
34478         
34479         if(typeof(attr.uiProvider) == 'string'){
34480            attr.uiProvider = this.uiProviders[attr.uiProvider] || 
34481                 /**  eval:var:attr */ eval(attr.uiProvider);
34482         }
34483         if(typeof(this.uiProviders['default']) != 'undefined') {
34484             attr.uiProvider = this.uiProviders['default'];
34485         }
34486         
34487         this.fireEvent('create', this, attr);
34488         
34489         attr.leaf  = typeof(attr.leaf) == 'string' ? attr.leaf * 1 : attr.leaf;
34490         return(attr.leaf ?
34491                         new Roo.tree.TreeNode(attr) :
34492                         new Roo.tree.AsyncTreeNode(attr));
34493     },
34494
34495     processResponse : function(response, node, callback)
34496     {
34497         var json = response.responseText;
34498         try {
34499             
34500             var o = Roo.decode(json);
34501             
34502             if (this.root === false && typeof(o.success) != undefined) {
34503                 this.root = 'data'; // the default behaviour for list like data..
34504                 }
34505                 
34506             if (this.root !== false &&  !o.success) {
34507                 // it's a failure condition.
34508                 var a = response.argument;
34509                 this.fireEvent("loadexception", this, a.node, response);
34510                 Roo.log("Load failed - should have a handler really");
34511                 return;
34512             }
34513             
34514             
34515             
34516             if (this.root !== false) {
34517                  o = o[this.root];
34518             }
34519             
34520             for(var i = 0, len = o.length; i < len; i++){
34521                 var n = this.createNode(o[i]);
34522                 if(n){
34523                     node.appendChild(n);
34524                 }
34525             }
34526             if(typeof callback == "function"){
34527                 callback(this, node);
34528             }
34529         }catch(e){
34530             this.handleFailure(response);
34531         }
34532     },
34533
34534     handleResponse : function(response){
34535         this.transId = false;
34536         var a = response.argument;
34537         this.processResponse(response, a.node, a.callback);
34538         this.fireEvent("load", this, a.node, response);
34539     },
34540
34541     handleFailure : function(response)
34542     {
34543         // should handle failure better..
34544         this.transId = false;
34545         var a = response.argument;
34546         this.fireEvent("loadexception", this, a.node, response);
34547         if(typeof a.callback == "function"){
34548             a.callback(this, a.node);
34549         }
34550     }
34551 });/*
34552  * Based on:
34553  * Ext JS Library 1.1.1
34554  * Copyright(c) 2006-2007, Ext JS, LLC.
34555  *
34556  * Originally Released Under LGPL - original licence link has changed is not relivant.
34557  *
34558  * Fork - LGPL
34559  * <script type="text/javascript">
34560  */
34561
34562 /**
34563 * @class Roo.tree.TreeFilter
34564 * Note this class is experimental and doesn't update the indent (lines) or expand collapse icons of the nodes
34565 * @param {TreePanel} tree
34566 * @param {Object} config (optional)
34567  */
34568 Roo.tree.TreeFilter = function(tree, config){
34569     this.tree = tree;
34570     this.filtered = {};
34571     Roo.apply(this, config);
34572 };
34573
34574 Roo.tree.TreeFilter.prototype = {
34575     clearBlank:false,
34576     reverse:false,
34577     autoClear:false,
34578     remove:false,
34579
34580      /**
34581      * Filter the data by a specific attribute.
34582      * @param {String/RegExp} value Either string that the attribute value
34583      * should start with or a RegExp to test against the attribute
34584      * @param {String} attr (optional) The attribute passed in your node's attributes collection. Defaults to "text".
34585      * @param {TreeNode} startNode (optional) The node to start the filter at.
34586      */
34587     filter : function(value, attr, startNode){
34588         attr = attr || "text";
34589         var f;
34590         if(typeof value == "string"){
34591             var vlen = value.length;
34592             // auto clear empty filter
34593             if(vlen == 0 && this.clearBlank){
34594                 this.clear();
34595                 return;
34596             }
34597             value = value.toLowerCase();
34598             f = function(n){
34599                 return n.attributes[attr].substr(0, vlen).toLowerCase() == value;
34600             };
34601         }else if(value.exec){ // regex?
34602             f = function(n){
34603                 return value.test(n.attributes[attr]);
34604             };
34605         }else{
34606             throw 'Illegal filter type, must be string or regex';
34607         }
34608         this.filterBy(f, null, startNode);
34609         },
34610
34611     /**
34612      * Filter by a function. The passed function will be called with each
34613      * node in the tree (or from the startNode). If the function returns true, the node is kept
34614      * otherwise it is filtered. If a node is filtered, its children are also filtered.
34615      * @param {Function} fn The filter function
34616      * @param {Object} scope (optional) The scope of the function (defaults to the current node)
34617      */
34618     filterBy : function(fn, scope, startNode){
34619         startNode = startNode || this.tree.root;
34620         if(this.autoClear){
34621             this.clear();
34622         }
34623         var af = this.filtered, rv = this.reverse;
34624         var f = function(n){
34625             if(n == startNode){
34626                 return true;
34627             }
34628             if(af[n.id]){
34629                 return false;
34630             }
34631             var m = fn.call(scope || n, n);
34632             if(!m || rv){
34633                 af[n.id] = n;
34634                 n.ui.hide();
34635                 return false;
34636             }
34637             return true;
34638         };
34639         startNode.cascade(f);
34640         if(this.remove){
34641            for(var id in af){
34642                if(typeof id != "function"){
34643                    var n = af[id];
34644                    if(n && n.parentNode){
34645                        n.parentNode.removeChild(n);
34646                    }
34647                }
34648            }
34649         }
34650     },
34651
34652     /**
34653      * Clears the current filter. Note: with the "remove" option
34654      * set a filter cannot be cleared.
34655      */
34656     clear : function(){
34657         var t = this.tree;
34658         var af = this.filtered;
34659         for(var id in af){
34660             if(typeof id != "function"){
34661                 var n = af[id];
34662                 if(n){
34663                     n.ui.show();
34664                 }
34665             }
34666         }
34667         this.filtered = {};
34668     }
34669 };
34670 /*
34671  * Based on:
34672  * Ext JS Library 1.1.1
34673  * Copyright(c) 2006-2007, Ext JS, LLC.
34674  *
34675  * Originally Released Under LGPL - original licence link has changed is not relivant.
34676  *
34677  * Fork - LGPL
34678  * <script type="text/javascript">
34679  */
34680  
34681
34682 /**
34683  * @class Roo.tree.TreeSorter
34684  * Provides sorting of nodes in a TreePanel
34685  * 
34686  * @cfg {Boolean} folderSort True to sort leaf nodes under non leaf nodes
34687  * @cfg {String} property The named attribute on the node to sort by (defaults to text)
34688  * @cfg {String} dir The direction to sort (asc or desc) (defaults to asc)
34689  * @cfg {String} leafAttr The attribute used to determine leaf nodes in folder sort (defaults to "leaf")
34690  * @cfg {Boolean} caseSensitive true for case sensitive sort (defaults to false)
34691  * @cfg {Function} sortType A custom "casting" function used to convert node values before sorting
34692  * @constructor
34693  * @param {TreePanel} tree
34694  * @param {Object} config
34695  */
34696 Roo.tree.TreeSorter = function(tree, config){
34697     Roo.apply(this, config);
34698     tree.on("beforechildrenrendered", this.doSort, this);
34699     tree.on("append", this.updateSort, this);
34700     tree.on("insert", this.updateSort, this);
34701     
34702     var dsc = this.dir && this.dir.toLowerCase() == "desc";
34703     var p = this.property || "text";
34704     var sortType = this.sortType;
34705     var fs = this.folderSort;
34706     var cs = this.caseSensitive === true;
34707     var leafAttr = this.leafAttr || 'leaf';
34708
34709     this.sortFn = function(n1, n2){
34710         if(fs){
34711             if(n1.attributes[leafAttr] && !n2.attributes[leafAttr]){
34712                 return 1;
34713             }
34714             if(!n1.attributes[leafAttr] && n2.attributes[leafAttr]){
34715                 return -1;
34716             }
34717         }
34718         var v1 = sortType ? sortType(n1) : (cs ? n1.attributes[p] : n1.attributes[p].toUpperCase());
34719         var v2 = sortType ? sortType(n2) : (cs ? n2.attributes[p] : n2.attributes[p].toUpperCase());
34720         if(v1 < v2){
34721                         return dsc ? +1 : -1;
34722                 }else if(v1 > v2){
34723                         return dsc ? -1 : +1;
34724         }else{
34725                 return 0;
34726         }
34727     };
34728 };
34729
34730 Roo.tree.TreeSorter.prototype = {
34731     doSort : function(node){
34732         node.sort(this.sortFn);
34733     },
34734     
34735     compareNodes : function(n1, n2){
34736         return (n1.text.toUpperCase() > n2.text.toUpperCase() ? 1 : -1);
34737     },
34738     
34739     updateSort : function(tree, node){
34740         if(node.childrenRendered){
34741             this.doSort.defer(1, this, [node]);
34742         }
34743     }
34744 };/*
34745  * Based on:
34746  * Ext JS Library 1.1.1
34747  * Copyright(c) 2006-2007, Ext JS, LLC.
34748  *
34749  * Originally Released Under LGPL - original licence link has changed is not relivant.
34750  *
34751  * Fork - LGPL
34752  * <script type="text/javascript">
34753  */
34754
34755 if(Roo.dd.DropZone){
34756     
34757 Roo.tree.TreeDropZone = function(tree, config){
34758     this.allowParentInsert = false;
34759     this.allowContainerDrop = false;
34760     this.appendOnly = false;
34761     Roo.tree.TreeDropZone.superclass.constructor.call(this, tree.innerCt, config);
34762     this.tree = tree;
34763     this.lastInsertClass = "x-tree-no-status";
34764     this.dragOverData = {};
34765 };
34766
34767 Roo.extend(Roo.tree.TreeDropZone, Roo.dd.DropZone, {
34768     ddGroup : "TreeDD",
34769     scroll:  true,
34770     
34771     expandDelay : 1000,
34772     
34773     expandNode : function(node){
34774         if(node.hasChildNodes() && !node.isExpanded()){
34775             node.expand(false, null, this.triggerCacheRefresh.createDelegate(this));
34776         }
34777     },
34778     
34779     queueExpand : function(node){
34780         this.expandProcId = this.expandNode.defer(this.expandDelay, this, [node]);
34781     },
34782     
34783     cancelExpand : function(){
34784         if(this.expandProcId){
34785             clearTimeout(this.expandProcId);
34786             this.expandProcId = false;
34787         }
34788     },
34789     
34790     isValidDropPoint : function(n, pt, dd, e, data){
34791         if(!n || !data){ return false; }
34792         var targetNode = n.node;
34793         var dropNode = data.node;
34794         // default drop rules
34795         if(!(targetNode && targetNode.isTarget && pt)){
34796             return false;
34797         }
34798         if(pt == "append" && targetNode.allowChildren === false){
34799             return false;
34800         }
34801         if((pt == "above" || pt == "below") && (targetNode.parentNode && targetNode.parentNode.allowChildren === false)){
34802             return false;
34803         }
34804         if(dropNode && (targetNode == dropNode || dropNode.contains(targetNode))){
34805             return false;
34806         }
34807         // reuse the object
34808         var overEvent = this.dragOverData;
34809         overEvent.tree = this.tree;
34810         overEvent.target = targetNode;
34811         overEvent.data = data;
34812         overEvent.point = pt;
34813         overEvent.source = dd;
34814         overEvent.rawEvent = e;
34815         overEvent.dropNode = dropNode;
34816         overEvent.cancel = false;  
34817         var result = this.tree.fireEvent("nodedragover", overEvent);
34818         return overEvent.cancel === false && result !== false;
34819     },
34820     
34821     getDropPoint : function(e, n, dd)
34822     {
34823         var tn = n.node;
34824         if(tn.isRoot){
34825             return tn.allowChildren !== false ? "append" : false; // always append for root
34826         }
34827         var dragEl = n.ddel;
34828         var t = Roo.lib.Dom.getY(dragEl), b = t + dragEl.offsetHeight;
34829         var y = Roo.lib.Event.getPageY(e);
34830         //var noAppend = tn.allowChildren === false || tn.isLeaf();
34831         
34832         // we may drop nodes anywhere, as long as allowChildren has not been set to false..
34833         var noAppend = tn.allowChildren === false;
34834         if(this.appendOnly || tn.parentNode.allowChildren === false){
34835             return noAppend ? false : "append";
34836         }
34837         var noBelow = false;
34838         if(!this.allowParentInsert){
34839             noBelow = tn.hasChildNodes() && tn.isExpanded();
34840         }
34841         var q = (b - t) / (noAppend ? 2 : 3);
34842         if(y >= t && y < (t + q)){
34843             return "above";
34844         }else if(!noBelow && (noAppend || y >= b-q && y <= b)){
34845             return "below";
34846         }else{
34847             return "append";
34848         }
34849     },
34850     
34851     onNodeEnter : function(n, dd, e, data)
34852     {
34853         this.cancelExpand();
34854     },
34855     
34856     onNodeOver : function(n, dd, e, data)
34857     {
34858        
34859         var pt = this.getDropPoint(e, n, dd);
34860         var node = n.node;
34861         
34862         // auto node expand check
34863         if(!this.expandProcId && pt == "append" && node.hasChildNodes() && !n.node.isExpanded()){
34864             this.queueExpand(node);
34865         }else if(pt != "append"){
34866             this.cancelExpand();
34867         }
34868         
34869         // set the insert point style on the target node
34870         var returnCls = this.dropNotAllowed;
34871         if(this.isValidDropPoint(n, pt, dd, e, data)){
34872            if(pt){
34873                var el = n.ddel;
34874                var cls;
34875                if(pt == "above"){
34876                    returnCls = n.node.isFirst() ? "x-tree-drop-ok-above" : "x-tree-drop-ok-between";
34877                    cls = "x-tree-drag-insert-above";
34878                }else if(pt == "below"){
34879                    returnCls = n.node.isLast() ? "x-tree-drop-ok-below" : "x-tree-drop-ok-between";
34880                    cls = "x-tree-drag-insert-below";
34881                }else{
34882                    returnCls = "x-tree-drop-ok-append";
34883                    cls = "x-tree-drag-append";
34884                }
34885                if(this.lastInsertClass != cls){
34886                    Roo.fly(el).replaceClass(this.lastInsertClass, cls);
34887                    this.lastInsertClass = cls;
34888                }
34889            }
34890        }
34891        return returnCls;
34892     },
34893     
34894     onNodeOut : function(n, dd, e, data){
34895         
34896         this.cancelExpand();
34897         this.removeDropIndicators(n);
34898     },
34899     
34900     onNodeDrop : function(n, dd, e, data){
34901         var point = this.getDropPoint(e, n, dd);
34902         var targetNode = n.node;
34903         targetNode.ui.startDrop();
34904         if(!this.isValidDropPoint(n, point, dd, e, data)){
34905             targetNode.ui.endDrop();
34906             return false;
34907         }
34908         // first try to find the drop node
34909         var dropNode = data.node || (dd.getTreeNode ? dd.getTreeNode(data, targetNode, point, e) : null);
34910         var dropEvent = {
34911             tree : this.tree,
34912             target: targetNode,
34913             data: data,
34914             point: point,
34915             source: dd,
34916             rawEvent: e,
34917             dropNode: dropNode,
34918             cancel: !dropNode   
34919         };
34920         var retval = this.tree.fireEvent("beforenodedrop", dropEvent);
34921         if(retval === false || dropEvent.cancel === true || !dropEvent.dropNode){
34922             targetNode.ui.endDrop();
34923             return false;
34924         }
34925         // allow target changing
34926         targetNode = dropEvent.target;
34927         if(point == "append" && !targetNode.isExpanded()){
34928             targetNode.expand(false, null, function(){
34929                 this.completeDrop(dropEvent);
34930             }.createDelegate(this));
34931         }else{
34932             this.completeDrop(dropEvent);
34933         }
34934         return true;
34935     },
34936     
34937     completeDrop : function(de){
34938         var ns = de.dropNode, p = de.point, t = de.target;
34939         if(!(ns instanceof Array)){
34940             ns = [ns];
34941         }
34942         var n;
34943         for(var i = 0, len = ns.length; i < len; i++){
34944             n = ns[i];
34945             if(p == "above"){
34946                 t.parentNode.insertBefore(n, t);
34947             }else if(p == "below"){
34948                 t.parentNode.insertBefore(n, t.nextSibling);
34949             }else{
34950                 t.appendChild(n);
34951             }
34952         }
34953         n.ui.focus();
34954         if(this.tree.hlDrop){
34955             n.ui.highlight();
34956         }
34957         t.ui.endDrop();
34958         this.tree.fireEvent("nodedrop", de);
34959     },
34960     
34961     afterNodeMoved : function(dd, data, e, targetNode, dropNode){
34962         if(this.tree.hlDrop){
34963             dropNode.ui.focus();
34964             dropNode.ui.highlight();
34965         }
34966         this.tree.fireEvent("nodedrop", this.tree, targetNode, data, dd, e);
34967     },
34968     
34969     getTree : function(){
34970         return this.tree;
34971     },
34972     
34973     removeDropIndicators : function(n){
34974         if(n && n.ddel){
34975             var el = n.ddel;
34976             Roo.fly(el).removeClass([
34977                     "x-tree-drag-insert-above",
34978                     "x-tree-drag-insert-below",
34979                     "x-tree-drag-append"]);
34980             this.lastInsertClass = "_noclass";
34981         }
34982     },
34983     
34984     beforeDragDrop : function(target, e, id){
34985         this.cancelExpand();
34986         return true;
34987     },
34988     
34989     afterRepair : function(data){
34990         if(data && Roo.enableFx){
34991             data.node.ui.highlight();
34992         }
34993         this.hideProxy();
34994     } 
34995     
34996 });
34997
34998 }
34999 /*
35000  * Based on:
35001  * Ext JS Library 1.1.1
35002  * Copyright(c) 2006-2007, Ext JS, LLC.
35003  *
35004  * Originally Released Under LGPL - original licence link has changed is not relivant.
35005  *
35006  * Fork - LGPL
35007  * <script type="text/javascript">
35008  */
35009  
35010
35011 if(Roo.dd.DragZone){
35012 Roo.tree.TreeDragZone = function(tree, config){
35013     Roo.tree.TreeDragZone.superclass.constructor.call(this, tree.getTreeEl(), config);
35014     this.tree = tree;
35015 };
35016
35017 Roo.extend(Roo.tree.TreeDragZone, Roo.dd.DragZone, {
35018     ddGroup : "TreeDD",
35019    
35020     onBeforeDrag : function(data, e){
35021         var n = data.node;
35022         return n && n.draggable && !n.disabled;
35023     },
35024      
35025     
35026     onInitDrag : function(e){
35027         var data = this.dragData;
35028         this.tree.getSelectionModel().select(data.node);
35029         this.proxy.update("");
35030         data.node.ui.appendDDGhost(this.proxy.ghost.dom);
35031         this.tree.fireEvent("startdrag", this.tree, data.node, e);
35032     },
35033     
35034     getRepairXY : function(e, data){
35035         return data.node.ui.getDDRepairXY();
35036     },
35037     
35038     onEndDrag : function(data, e){
35039         this.tree.fireEvent("enddrag", this.tree, data.node, e);
35040         
35041         
35042     },
35043     
35044     onValidDrop : function(dd, e, id){
35045         this.tree.fireEvent("dragdrop", this.tree, this.dragData.node, dd, e);
35046         this.hideProxy();
35047     },
35048     
35049     beforeInvalidDrop : function(e, id){
35050         // this scrolls the original position back into view
35051         var sm = this.tree.getSelectionModel();
35052         sm.clearSelections();
35053         sm.select(this.dragData.node);
35054     }
35055 });
35056 }/*
35057  * Based on:
35058  * Ext JS Library 1.1.1
35059  * Copyright(c) 2006-2007, Ext JS, LLC.
35060  *
35061  * Originally Released Under LGPL - original licence link has changed is not relivant.
35062  *
35063  * Fork - LGPL
35064  * <script type="text/javascript">
35065  */
35066 /**
35067  * @class Roo.tree.TreeEditor
35068  * @extends Roo.Editor
35069  * Provides editor functionality for inline tree node editing.  Any valid {@link Roo.form.Field} can be used
35070  * as the editor field.
35071  * @constructor
35072  * @param {Object} config (used to be the tree panel.)
35073  * @param {Object} oldconfig DEPRECIATED Either a prebuilt {@link Roo.form.Field} instance or a Field config object
35074  * 
35075  * @cfg {Roo.tree.TreePanel} tree The tree to bind to.
35076  * @cfg {Roo.form.TextField|Object} field The field configuration
35077  *
35078  * 
35079  */
35080 Roo.tree.TreeEditor = function(config, oldconfig) { // was -- (tree, config){
35081     var tree = config;
35082     var field;
35083     if (oldconfig) { // old style..
35084         field = oldconfig.events ? oldconfig : new Roo.form.TextField(oldconfig);
35085     } else {
35086         // new style..
35087         tree = config.tree;
35088         config.field = config.field  || {};
35089         config.field.xtype = 'TextField';
35090         field = Roo.factory(config.field, Roo.form);
35091     }
35092     config = config || {};
35093     
35094     
35095     this.addEvents({
35096         /**
35097          * @event beforenodeedit
35098          * Fires when editing is initiated, but before the value changes.  Editing can be canceled by returning
35099          * false from the handler of this event.
35100          * @param {Editor} this
35101          * @param {Roo.tree.Node} node 
35102          */
35103         "beforenodeedit" : true
35104     });
35105     
35106     //Roo.log(config);
35107     Roo.tree.TreeEditor.superclass.constructor.call(this, field, config);
35108
35109     this.tree = tree;
35110
35111     tree.on('beforeclick', this.beforeNodeClick, this);
35112     tree.getTreeEl().on('mousedown', this.hide, this);
35113     this.on('complete', this.updateNode, this);
35114     this.on('beforestartedit', this.fitToTree, this);
35115     this.on('startedit', this.bindScroll, this, {delay:10});
35116     this.on('specialkey', this.onSpecialKey, this);
35117 };
35118
35119 Roo.extend(Roo.tree.TreeEditor, Roo.Editor, {
35120     /**
35121      * @cfg {String} alignment
35122      * The position to align to (see {@link Roo.Element#alignTo} for more details, defaults to "l-l").
35123      */
35124     alignment: "l-l",
35125     // inherit
35126     autoSize: false,
35127     /**
35128      * @cfg {Boolean} hideEl
35129      * True to hide the bound element while the editor is displayed (defaults to false)
35130      */
35131     hideEl : false,
35132     /**
35133      * @cfg {String} cls
35134      * CSS class to apply to the editor (defaults to "x-small-editor x-tree-editor")
35135      */
35136     cls: "x-small-editor x-tree-editor",
35137     /**
35138      * @cfg {Boolean} shim
35139      * True to shim the editor if selects/iframes could be displayed beneath it (defaults to false)
35140      */
35141     shim:false,
35142     // inherit
35143     shadow:"frame",
35144     /**
35145      * @cfg {Number} maxWidth
35146      * The maximum width in pixels of the editor field (defaults to 250).  Note that if the maxWidth would exceed
35147      * the containing tree element's size, it will be automatically limited for you to the container width, taking
35148      * scroll and client offsets into account prior to each edit.
35149      */
35150     maxWidth: 250,
35151
35152     editDelay : 350,
35153
35154     // private
35155     fitToTree : function(ed, el){
35156         var td = this.tree.getTreeEl().dom, nd = el.dom;
35157         if(td.scrollLeft >  nd.offsetLeft){ // ensure the node left point is visible
35158             td.scrollLeft = nd.offsetLeft;
35159         }
35160         var w = Math.min(
35161                 this.maxWidth,
35162                 (td.clientWidth > 20 ? td.clientWidth : td.offsetWidth) - Math.max(0, nd.offsetLeft-td.scrollLeft) - /*cushion*/5);
35163         this.setSize(w, '');
35164         
35165         return this.fireEvent('beforenodeedit', this, this.editNode);
35166         
35167     },
35168
35169     // private
35170     triggerEdit : function(node){
35171         this.completeEdit();
35172         this.editNode = node;
35173         this.startEdit(node.ui.textNode, node.text);
35174     },
35175
35176     // private
35177     bindScroll : function(){
35178         this.tree.getTreeEl().on('scroll', this.cancelEdit, this);
35179     },
35180
35181     // private
35182     beforeNodeClick : function(node, e){
35183         var sinceLast = (this.lastClick ? this.lastClick.getElapsed() : 0);
35184         this.lastClick = new Date();
35185         if(sinceLast > this.editDelay && this.tree.getSelectionModel().isSelected(node)){
35186             e.stopEvent();
35187             this.triggerEdit(node);
35188             return false;
35189         }
35190         return true;
35191     },
35192
35193     // private
35194     updateNode : function(ed, value){
35195         this.tree.getTreeEl().un('scroll', this.cancelEdit, this);
35196         this.editNode.setText(value);
35197     },
35198
35199     // private
35200     onHide : function(){
35201         Roo.tree.TreeEditor.superclass.onHide.call(this);
35202         if(this.editNode){
35203             this.editNode.ui.focus();
35204         }
35205     },
35206
35207     // private
35208     onSpecialKey : function(field, e){
35209         var k = e.getKey();
35210         if(k == e.ESC){
35211             e.stopEvent();
35212             this.cancelEdit();
35213         }else if(k == e.ENTER && !e.hasModifier()){
35214             e.stopEvent();
35215             this.completeEdit();
35216         }
35217     }
35218 });//<Script type="text/javascript">
35219 /*
35220  * Based on:
35221  * Ext JS Library 1.1.1
35222  * Copyright(c) 2006-2007, Ext JS, LLC.
35223  *
35224  * Originally Released Under LGPL - original licence link has changed is not relivant.
35225  *
35226  * Fork - LGPL
35227  * <script type="text/javascript">
35228  */
35229  
35230 /**
35231  * Not documented??? - probably should be...
35232  */
35233
35234 Roo.tree.ColumnNodeUI = Roo.extend(Roo.tree.TreeNodeUI, {
35235     //focus: Roo.emptyFn, // prevent odd scrolling behavior
35236     
35237     renderElements : function(n, a, targetNode, bulkRender){
35238         //consel.log("renderElements?");
35239         this.indentMarkup = n.parentNode ? n.parentNode.ui.getChildIndent() : '';
35240
35241         var t = n.getOwnerTree();
35242         var tid = Pman.Tab.Document_TypesTree.tree.el.id;
35243         
35244         var cols = t.columns;
35245         var bw = t.borderWidth;
35246         var c = cols[0];
35247         var href = a.href ? a.href : Roo.isGecko ? "" : "#";
35248          var cb = typeof a.checked == "boolean";
35249         var tx = String.format('{0}',n.text || (c.renderer ? c.renderer(a[c.dataIndex], n, a) : a[c.dataIndex]));
35250         var colcls = 'x-t-' + tid + '-c0';
35251         var buf = [
35252             '<li class="x-tree-node">',
35253             
35254                 
35255                 '<div class="x-tree-node-el ', a.cls,'">',
35256                     // extran...
35257                     '<div class="x-tree-col ', colcls, '" style="width:', c.width-bw, 'px;">',
35258                 
35259                 
35260                         '<span class="x-tree-node-indent">',this.indentMarkup,'</span>',
35261                         '<img src="', this.emptyIcon, '" class="x-tree-ec-icon  " />',
35262                         '<img src="', a.icon || this.emptyIcon, '" class="x-tree-node-icon',
35263                            (a.icon ? ' x-tree-node-inline-icon' : ''),
35264                            (a.iconCls ? ' '+a.iconCls : ''),
35265                            '" unselectable="on" />',
35266                         (cb ? ('<input class="x-tree-node-cb" type="checkbox" ' + 
35267                              (a.checked ? 'checked="checked" />' : ' />')) : ''),
35268                              
35269                         '<a class="x-tree-node-anchor" hidefocus="on" href="',href,'" tabIndex="1" ',
35270                             (a.hrefTarget ? ' target="' +a.hrefTarget + '"' : ''), '>',
35271                             '<span unselectable="on" qtip="' + tx + '">',
35272                              tx,
35273                              '</span></a>' ,
35274                     '</div>',
35275                      '<a class="x-tree-node-anchor" hidefocus="on" href="',href,'" tabIndex="1" ',
35276                             (a.hrefTarget ? ' target="' +a.hrefTarget + '"' : ''), '>'
35277                  ];
35278         for(var i = 1, len = cols.length; i < len; i++){
35279             c = cols[i];
35280             colcls = 'x-t-' + tid + '-c' +i;
35281             tx = String.format('{0}', (c.renderer ? c.renderer(a[c.dataIndex], n, a) : a[c.dataIndex]));
35282             buf.push('<div class="x-tree-col ', colcls, ' ' ,(c.cls?c.cls:''),'" style="width:',c.width-bw,'px;">',
35283                         '<div class="x-tree-col-text" qtip="' + tx +'">',tx,"</div>",
35284                       "</div>");
35285          }
35286          
35287          buf.push(
35288             '</a>',
35289             '<div class="x-clear"></div></div>',
35290             '<ul class="x-tree-node-ct" style="display:none;"></ul>',
35291             "</li>");
35292         
35293         if(bulkRender !== true && n.nextSibling && n.nextSibling.ui.getEl()){
35294             this.wrap = Roo.DomHelper.insertHtml("beforeBegin",
35295                                 n.nextSibling.ui.getEl(), buf.join(""));
35296         }else{
35297             this.wrap = Roo.DomHelper.insertHtml("beforeEnd", targetNode, buf.join(""));
35298         }
35299         var el = this.wrap.firstChild;
35300         this.elRow = el;
35301         this.elNode = el.firstChild;
35302         this.ranchor = el.childNodes[1];
35303         this.ctNode = this.wrap.childNodes[1];
35304         var cs = el.firstChild.childNodes;
35305         this.indentNode = cs[0];
35306         this.ecNode = cs[1];
35307         this.iconNode = cs[2];
35308         var index = 3;
35309         if(cb){
35310             this.checkbox = cs[3];
35311             index++;
35312         }
35313         this.anchor = cs[index];
35314         
35315         this.textNode = cs[index].firstChild;
35316         
35317         //el.on("click", this.onClick, this);
35318         //el.on("dblclick", this.onDblClick, this);
35319         
35320         
35321        // console.log(this);
35322     },
35323     initEvents : function(){
35324         Roo.tree.ColumnNodeUI.superclass.initEvents.call(this);
35325         
35326             
35327         var a = this.ranchor;
35328
35329         var el = Roo.get(a);
35330
35331         if(Roo.isOpera){ // opera render bug ignores the CSS
35332             el.setStyle("text-decoration", "none");
35333         }
35334
35335         el.on("click", this.onClick, this);
35336         el.on("dblclick", this.onDblClick, this);
35337         el.on("contextmenu", this.onContextMenu, this);
35338         
35339     },
35340     
35341     /*onSelectedChange : function(state){
35342         if(state){
35343             this.focus();
35344             this.addClass("x-tree-selected");
35345         }else{
35346             //this.blur();
35347             this.removeClass("x-tree-selected");
35348         }
35349     },*/
35350     addClass : function(cls){
35351         if(this.elRow){
35352             Roo.fly(this.elRow).addClass(cls);
35353         }
35354         
35355     },
35356     
35357     
35358     removeClass : function(cls){
35359         if(this.elRow){
35360             Roo.fly(this.elRow).removeClass(cls);
35361         }
35362     }
35363
35364     
35365     
35366 });//<Script type="text/javascript">
35367
35368 /*
35369  * Based on:
35370  * Ext JS Library 1.1.1
35371  * Copyright(c) 2006-2007, Ext JS, LLC.
35372  *
35373  * Originally Released Under LGPL - original licence link has changed is not relivant.
35374  *
35375  * Fork - LGPL
35376  * <script type="text/javascript">
35377  */
35378  
35379
35380 /**
35381  * @class Roo.tree.ColumnTree
35382  * @extends Roo.data.TreePanel
35383  * @cfg {Object} columns  Including width, header, renderer, cls, dataIndex 
35384  * @cfg {int} borderWidth  compined right/left border allowance
35385  * @constructor
35386  * @param {String/HTMLElement/Element} el The container element
35387  * @param {Object} config
35388  */
35389 Roo.tree.ColumnTree =  function(el, config)
35390 {
35391    Roo.tree.ColumnTree.superclass.constructor.call(this, el , config);
35392    this.addEvents({
35393         /**
35394         * @event resize
35395         * Fire this event on a container when it resizes
35396         * @param {int} w Width
35397         * @param {int} h Height
35398         */
35399        "resize" : true
35400     });
35401     this.on('resize', this.onResize, this);
35402 };
35403
35404 Roo.extend(Roo.tree.ColumnTree, Roo.tree.TreePanel, {
35405     //lines:false,
35406     
35407     
35408     borderWidth: Roo.isBorderBox ? 0 : 2, 
35409     headEls : false,
35410     
35411     render : function(){
35412         // add the header.....
35413        
35414         Roo.tree.ColumnTree.superclass.render.apply(this);
35415         
35416         this.el.addClass('x-column-tree');
35417         
35418         this.headers = this.el.createChild(
35419             {cls:'x-tree-headers'},this.innerCt.dom);
35420    
35421         var cols = this.columns, c;
35422         var totalWidth = 0;
35423         this.headEls = [];
35424         var  len = cols.length;
35425         for(var i = 0; i < len; i++){
35426              c = cols[i];
35427              totalWidth += c.width;
35428             this.headEls.push(this.headers.createChild({
35429                  cls:'x-tree-hd ' + (c.cls?c.cls+'-hd':''),
35430                  cn: {
35431                      cls:'x-tree-hd-text',
35432                      html: c.header
35433                  },
35434                  style:'width:'+(c.width-this.borderWidth)+'px;'
35435              }));
35436         }
35437         this.headers.createChild({cls:'x-clear'});
35438         // prevent floats from wrapping when clipped
35439         this.headers.setWidth(totalWidth);
35440         //this.innerCt.setWidth(totalWidth);
35441         this.innerCt.setStyle({ overflow: 'auto' });
35442         this.onResize(this.width, this.height);
35443              
35444         
35445     },
35446     onResize : function(w,h)
35447     {
35448         this.height = h;
35449         this.width = w;
35450         // resize cols..
35451         this.innerCt.setWidth(this.width);
35452         this.innerCt.setHeight(this.height-20);
35453         
35454         // headers...
35455         var cols = this.columns, c;
35456         var totalWidth = 0;
35457         var expEl = false;
35458         var len = cols.length;
35459         for(var i = 0; i < len; i++){
35460             c = cols[i];
35461             if (this.autoExpandColumn !== false && c.dataIndex == this.autoExpandColumn) {
35462                 // it's the expander..
35463                 expEl  = this.headEls[i];
35464                 continue;
35465             }
35466             totalWidth += c.width;
35467             
35468         }
35469         if (expEl) {
35470             expEl.setWidth(  ((w - totalWidth)-this.borderWidth - 20));
35471         }
35472         this.headers.setWidth(w-20);
35473
35474         
35475         
35476         
35477     }
35478 });
35479 /*
35480  * Based on:
35481  * Ext JS Library 1.1.1
35482  * Copyright(c) 2006-2007, Ext JS, LLC.
35483  *
35484  * Originally Released Under LGPL - original licence link has changed is not relivant.
35485  *
35486  * Fork - LGPL
35487  * <script type="text/javascript">
35488  */
35489  
35490 /**
35491  * @class Roo.menu.Menu
35492  * @extends Roo.util.Observable
35493  * A menu object.  This is the container to which you add all other menu items.  Menu can also serve a as a base class
35494  * when you want a specialzed menu based off of another component (like {@link Roo.menu.DateMenu} for example).
35495  * @constructor
35496  * Creates a new Menu
35497  * @param {Object} config Configuration options
35498  */
35499 Roo.menu.Menu = function(config){
35500     Roo.apply(this, config);
35501     this.id = this.id || Roo.id();
35502     this.addEvents({
35503         /**
35504          * @event beforeshow
35505          * Fires before this menu is displayed
35506          * @param {Roo.menu.Menu} this
35507          */
35508         beforeshow : true,
35509         /**
35510          * @event beforehide
35511          * Fires before this menu is hidden
35512          * @param {Roo.menu.Menu} this
35513          */
35514         beforehide : true,
35515         /**
35516          * @event show
35517          * Fires after this menu is displayed
35518          * @param {Roo.menu.Menu} this
35519          */
35520         show : true,
35521         /**
35522          * @event hide
35523          * Fires after this menu is hidden
35524          * @param {Roo.menu.Menu} this
35525          */
35526         hide : true,
35527         /**
35528          * @event click
35529          * Fires when this menu is clicked (or when the enter key is pressed while it is active)
35530          * @param {Roo.menu.Menu} this
35531          * @param {Roo.menu.Item} menuItem The menu item that was clicked
35532          * @param {Roo.EventObject} e
35533          */
35534         click : true,
35535         /**
35536          * @event mouseover
35537          * Fires when the mouse is hovering over this menu
35538          * @param {Roo.menu.Menu} this
35539          * @param {Roo.EventObject} e
35540          * @param {Roo.menu.Item} menuItem The menu item that was clicked
35541          */
35542         mouseover : true,
35543         /**
35544          * @event mouseout
35545          * Fires when the mouse exits this menu
35546          * @param {Roo.menu.Menu} this
35547          * @param {Roo.EventObject} e
35548          * @param {Roo.menu.Item} menuItem The menu item that was clicked
35549          */
35550         mouseout : true,
35551         /**
35552          * @event itemclick
35553          * Fires when a menu item contained in this menu is clicked
35554          * @param {Roo.menu.BaseItem} baseItem The BaseItem that was clicked
35555          * @param {Roo.EventObject} e
35556          */
35557         itemclick: true
35558     });
35559     if (this.registerMenu) {
35560         Roo.menu.MenuMgr.register(this);
35561     }
35562     
35563     var mis = this.items;
35564     this.items = new Roo.util.MixedCollection();
35565     if(mis){
35566         this.add.apply(this, mis);
35567     }
35568 };
35569
35570 Roo.extend(Roo.menu.Menu, Roo.util.Observable, {
35571     /**
35572      * @cfg {Number} minWidth The minimum width of the menu in pixels (defaults to 120)
35573      */
35574     minWidth : 120,
35575     /**
35576      * @cfg {Boolean/String} shadow True or "sides" for the default effect, "frame" for 4-way shadow, and "drop"
35577      * for bottom-right shadow (defaults to "sides")
35578      */
35579     shadow : "sides",
35580     /**
35581      * @cfg {String} subMenuAlign The {@link Roo.Element#alignTo} anchor position value to use for submenus of
35582      * this menu (defaults to "tl-tr?")
35583      */
35584     subMenuAlign : "tl-tr?",
35585     /**
35586      * @cfg {String} defaultAlign The default {@link Roo.Element#alignTo) anchor position value for this menu
35587      * relative to its element of origin (defaults to "tl-bl?")
35588      */
35589     defaultAlign : "tl-bl?",
35590     /**
35591      * @cfg {Boolean} allowOtherMenus True to allow multiple menus to be displayed at the same time (defaults to false)
35592      */
35593     allowOtherMenus : false,
35594     /**
35595      * @cfg {Boolean} registerMenu True (default) - means that clicking on screen etc. hides it.
35596      */
35597     registerMenu : true,
35598
35599     hidden:true,
35600
35601     // private
35602     render : function(){
35603         if(this.el){
35604             return;
35605         }
35606         var el = this.el = new Roo.Layer({
35607             cls: "x-menu",
35608             shadow:this.shadow,
35609             constrain: false,
35610             parentEl: this.parentEl || document.body,
35611             zindex:15000
35612         });
35613
35614         this.keyNav = new Roo.menu.MenuNav(this);
35615
35616         if(this.plain){
35617             el.addClass("x-menu-plain");
35618         }
35619         if(this.cls){
35620             el.addClass(this.cls);
35621         }
35622         // generic focus element
35623         this.focusEl = el.createChild({
35624             tag: "a", cls: "x-menu-focus", href: "#", onclick: "return false;", tabIndex:"-1"
35625         });
35626         var ul = el.createChild({tag: "ul", cls: "x-menu-list"});
35627         ul.on(Roo.isTouch ? 'touchstart' : 'click'   , this.onClick, this);
35628         
35629         ul.on("mouseover", this.onMouseOver, this);
35630         ul.on("mouseout", this.onMouseOut, this);
35631         this.items.each(function(item){
35632             if (item.hidden) {
35633                 return;
35634             }
35635             
35636             var li = document.createElement("li");
35637             li.className = "x-menu-list-item";
35638             ul.dom.appendChild(li);
35639             item.render(li, this);
35640         }, this);
35641         this.ul = ul;
35642         this.autoWidth();
35643     },
35644
35645     // private
35646     autoWidth : function(){
35647         var el = this.el, ul = this.ul;
35648         if(!el){
35649             return;
35650         }
35651         var w = this.width;
35652         if(w){
35653             el.setWidth(w);
35654         }else if(Roo.isIE){
35655             el.setWidth(this.minWidth);
35656             var t = el.dom.offsetWidth; // force recalc
35657             el.setWidth(ul.getWidth()+el.getFrameWidth("lr"));
35658         }
35659     },
35660
35661     // private
35662     delayAutoWidth : function(){
35663         if(this.rendered){
35664             if(!this.awTask){
35665                 this.awTask = new Roo.util.DelayedTask(this.autoWidth, this);
35666             }
35667             this.awTask.delay(20);
35668         }
35669     },
35670
35671     // private
35672     findTargetItem : function(e){
35673         var t = e.getTarget(".x-menu-list-item", this.ul,  true);
35674         if(t && t.menuItemId){
35675             return this.items.get(t.menuItemId);
35676         }
35677     },
35678
35679     // private
35680     onClick : function(e){
35681         Roo.log("menu.onClick");
35682         var t = this.findTargetItem(e);
35683         if(!t){
35684             return;
35685         }
35686         Roo.log(e);
35687         if (Roo.isTouch && e.type == 'touchstart' && t.menu  && !t.disabled) {
35688             if(t == this.activeItem && t.shouldDeactivate(e)){
35689                 this.activeItem.deactivate();
35690                 delete this.activeItem;
35691                 return;
35692             }
35693             if(t.canActivate){
35694                 this.setActiveItem(t, true);
35695             }
35696             return;
35697             
35698             
35699         }
35700         
35701         t.onClick(e);
35702         this.fireEvent("click", this, t, e);
35703     },
35704
35705     // private
35706     setActiveItem : function(item, autoExpand){
35707         if(item != this.activeItem){
35708             if(this.activeItem){
35709                 this.activeItem.deactivate();
35710             }
35711             this.activeItem = item;
35712             item.activate(autoExpand);
35713         }else if(autoExpand){
35714             item.expandMenu();
35715         }
35716     },
35717
35718     // private
35719     tryActivate : function(start, step){
35720         var items = this.items;
35721         for(var i = start, len = items.length; i >= 0 && i < len; i+= step){
35722             var item = items.get(i);
35723             if(!item.disabled && item.canActivate){
35724                 this.setActiveItem(item, false);
35725                 return item;
35726             }
35727         }
35728         return false;
35729     },
35730
35731     // private
35732     onMouseOver : function(e){
35733         var t;
35734         if(t = this.findTargetItem(e)){
35735             if(t.canActivate && !t.disabled){
35736                 this.setActiveItem(t, true);
35737             }
35738         }
35739         this.fireEvent("mouseover", this, e, t);
35740     },
35741
35742     // private
35743     onMouseOut : function(e){
35744         var t;
35745         if(t = this.findTargetItem(e)){
35746             if(t == this.activeItem && t.shouldDeactivate(e)){
35747                 this.activeItem.deactivate();
35748                 delete this.activeItem;
35749             }
35750         }
35751         this.fireEvent("mouseout", this, e, t);
35752     },
35753
35754     /**
35755      * Read-only.  Returns true if the menu is currently displayed, else false.
35756      * @type Boolean
35757      */
35758     isVisible : function(){
35759         return this.el && !this.hidden;
35760     },
35761
35762     /**
35763      * Displays this menu relative to another element
35764      * @param {String/HTMLElement/Roo.Element} element The element to align to
35765      * @param {String} position (optional) The {@link Roo.Element#alignTo} anchor position to use in aligning to
35766      * the element (defaults to this.defaultAlign)
35767      * @param {Roo.menu.Menu} parentMenu (optional) This menu's parent menu, if applicable (defaults to undefined)
35768      */
35769     show : function(el, pos, parentMenu){
35770         this.parentMenu = parentMenu;
35771         if(!this.el){
35772             this.render();
35773         }
35774         this.fireEvent("beforeshow", this);
35775         this.showAt(this.el.getAlignToXY(el, pos || this.defaultAlign), parentMenu, false);
35776     },
35777
35778     /**
35779      * Displays this menu at a specific xy position
35780      * @param {Array} xyPosition Contains X & Y [x, y] values for the position at which to show the menu (coordinates are page-based)
35781      * @param {Roo.menu.Menu} parentMenu (optional) This menu's parent menu, if applicable (defaults to undefined)
35782      */
35783     showAt : function(xy, parentMenu, /* private: */_e){
35784         this.parentMenu = parentMenu;
35785         if(!this.el){
35786             this.render();
35787         }
35788         if(_e !== false){
35789             this.fireEvent("beforeshow", this);
35790             xy = this.el.adjustForConstraints(xy);
35791         }
35792         this.el.setXY(xy);
35793         this.el.show();
35794         this.hidden = false;
35795         this.focus();
35796         this.fireEvent("show", this);
35797     },
35798
35799     focus : function(){
35800         if(!this.hidden){
35801             this.doFocus.defer(50, this);
35802         }
35803     },
35804
35805     doFocus : function(){
35806         if(!this.hidden){
35807             this.focusEl.focus();
35808         }
35809     },
35810
35811     /**
35812      * Hides this menu and optionally all parent menus
35813      * @param {Boolean} deep (optional) True to hide all parent menus recursively, if any (defaults to false)
35814      */
35815     hide : function(deep){
35816         if(this.el && this.isVisible()){
35817             this.fireEvent("beforehide", this);
35818             if(this.activeItem){
35819                 this.activeItem.deactivate();
35820                 this.activeItem = null;
35821             }
35822             this.el.hide();
35823             this.hidden = true;
35824             this.fireEvent("hide", this);
35825         }
35826         if(deep === true && this.parentMenu){
35827             this.parentMenu.hide(true);
35828         }
35829     },
35830
35831     /**
35832      * Addds one or more items of any type supported by the Menu class, or that can be converted into menu items.
35833      * Any of the following are valid:
35834      * <ul>
35835      * <li>Any menu item object based on {@link Roo.menu.Item}</li>
35836      * <li>An HTMLElement object which will be converted to a menu item</li>
35837      * <li>A menu item config object that will be created as a new menu item</li>
35838      * <li>A string, which can either be '-' or 'separator' to add a menu separator, otherwise
35839      * it will be converted into a {@link Roo.menu.TextItem} and added</li>
35840      * </ul>
35841      * Usage:
35842      * <pre><code>
35843 // Create the menu
35844 var menu = new Roo.menu.Menu();
35845
35846 // Create a menu item to add by reference
35847 var menuItem = new Roo.menu.Item({ text: 'New Item!' });
35848
35849 // Add a bunch of items at once using different methods.
35850 // Only the last item added will be returned.
35851 var item = menu.add(
35852     menuItem,                // add existing item by ref
35853     'Dynamic Item',          // new TextItem
35854     '-',                     // new separator
35855     { text: 'Config Item' }  // new item by config
35856 );
35857 </code></pre>
35858      * @param {Mixed} args One or more menu items, menu item configs or other objects that can be converted to menu items
35859      * @return {Roo.menu.Item} The menu item that was added, or the last one if multiple items were added
35860      */
35861     add : function(){
35862         var a = arguments, l = a.length, item;
35863         for(var i = 0; i < l; i++){
35864             var el = a[i];
35865             if ((typeof(el) == "object") && el.xtype && el.xns) {
35866                 el = Roo.factory(el, Roo.menu);
35867             }
35868             
35869             if(el.render){ // some kind of Item
35870                 item = this.addItem(el);
35871             }else if(typeof el == "string"){ // string
35872                 if(el == "separator" || el == "-"){
35873                     item = this.addSeparator();
35874                 }else{
35875                     item = this.addText(el);
35876                 }
35877             }else if(el.tagName || el.el){ // element
35878                 item = this.addElement(el);
35879             }else if(typeof el == "object"){ // must be menu item config?
35880                 item = this.addMenuItem(el);
35881             }
35882         }
35883         return item;
35884     },
35885
35886     /**
35887      * Returns this menu's underlying {@link Roo.Element} object
35888      * @return {Roo.Element} The element
35889      */
35890     getEl : function(){
35891         if(!this.el){
35892             this.render();
35893         }
35894         return this.el;
35895     },
35896
35897     /**
35898      * Adds a separator bar to the menu
35899      * @return {Roo.menu.Item} The menu item that was added
35900      */
35901     addSeparator : function(){
35902         return this.addItem(new Roo.menu.Separator());
35903     },
35904
35905     /**
35906      * Adds an {@link Roo.Element} object to the menu
35907      * @param {String/HTMLElement/Roo.Element} el The element or DOM node to add, or its id
35908      * @return {Roo.menu.Item} The menu item that was added
35909      */
35910     addElement : function(el){
35911         return this.addItem(new Roo.menu.BaseItem(el));
35912     },
35913
35914     /**
35915      * Adds an existing object based on {@link Roo.menu.Item} to the menu
35916      * @param {Roo.menu.Item} item The menu item to add
35917      * @return {Roo.menu.Item} The menu item that was added
35918      */
35919     addItem : function(item){
35920         this.items.add(item);
35921         if(this.ul){
35922             var li = document.createElement("li");
35923             li.className = "x-menu-list-item";
35924             this.ul.dom.appendChild(li);
35925             item.render(li, this);
35926             this.delayAutoWidth();
35927         }
35928         return item;
35929     },
35930
35931     /**
35932      * Creates a new {@link Roo.menu.Item} based an the supplied config object and adds it to the menu
35933      * @param {Object} config A MenuItem config object
35934      * @return {Roo.menu.Item} The menu item that was added
35935      */
35936     addMenuItem : function(config){
35937         if(!(config instanceof Roo.menu.Item)){
35938             if(typeof config.checked == "boolean"){ // must be check menu item config?
35939                 config = new Roo.menu.CheckItem(config);
35940             }else{
35941                 config = new Roo.menu.Item(config);
35942             }
35943         }
35944         return this.addItem(config);
35945     },
35946
35947     /**
35948      * Creates a new {@link Roo.menu.TextItem} with the supplied text and adds it to the menu
35949      * @param {String} text The text to display in the menu item
35950      * @return {Roo.menu.Item} The menu item that was added
35951      */
35952     addText : function(text){
35953         return this.addItem(new Roo.menu.TextItem({ text : text }));
35954     },
35955
35956     /**
35957      * Inserts an existing object based on {@link Roo.menu.Item} to the menu at a specified index
35958      * @param {Number} index The index in the menu's list of current items where the new item should be inserted
35959      * @param {Roo.menu.Item} item The menu item to add
35960      * @return {Roo.menu.Item} The menu item that was added
35961      */
35962     insert : function(index, item){
35963         this.items.insert(index, item);
35964         if(this.ul){
35965             var li = document.createElement("li");
35966             li.className = "x-menu-list-item";
35967             this.ul.dom.insertBefore(li, this.ul.dom.childNodes[index]);
35968             item.render(li, this);
35969             this.delayAutoWidth();
35970         }
35971         return item;
35972     },
35973
35974     /**
35975      * Removes an {@link Roo.menu.Item} from the menu and destroys the object
35976      * @param {Roo.menu.Item} item The menu item to remove
35977      */
35978     remove : function(item){
35979         this.items.removeKey(item.id);
35980         item.destroy();
35981     },
35982
35983     /**
35984      * Removes and destroys all items in the menu
35985      */
35986     removeAll : function(){
35987         var f;
35988         while(f = this.items.first()){
35989             this.remove(f);
35990         }
35991     }
35992 });
35993
35994 // MenuNav is a private utility class used internally by the Menu
35995 Roo.menu.MenuNav = function(menu){
35996     Roo.menu.MenuNav.superclass.constructor.call(this, menu.el);
35997     this.scope = this.menu = menu;
35998 };
35999
36000 Roo.extend(Roo.menu.MenuNav, Roo.KeyNav, {
36001     doRelay : function(e, h){
36002         var k = e.getKey();
36003         if(!this.menu.activeItem && e.isNavKeyPress() && k != e.SPACE && k != e.RETURN){
36004             this.menu.tryActivate(0, 1);
36005             return false;
36006         }
36007         return h.call(this.scope || this, e, this.menu);
36008     },
36009
36010     up : function(e, m){
36011         if(!m.tryActivate(m.items.indexOf(m.activeItem)-1, -1)){
36012             m.tryActivate(m.items.length-1, -1);
36013         }
36014     },
36015
36016     down : function(e, m){
36017         if(!m.tryActivate(m.items.indexOf(m.activeItem)+1, 1)){
36018             m.tryActivate(0, 1);
36019         }
36020     },
36021
36022     right : function(e, m){
36023         if(m.activeItem){
36024             m.activeItem.expandMenu(true);
36025         }
36026     },
36027
36028     left : function(e, m){
36029         m.hide();
36030         if(m.parentMenu && m.parentMenu.activeItem){
36031             m.parentMenu.activeItem.activate();
36032         }
36033     },
36034
36035     enter : function(e, m){
36036         if(m.activeItem){
36037             e.stopPropagation();
36038             m.activeItem.onClick(e);
36039             m.fireEvent("click", this, m.activeItem);
36040             return true;
36041         }
36042     }
36043 });/*
36044  * Based on:
36045  * Ext JS Library 1.1.1
36046  * Copyright(c) 2006-2007, Ext JS, LLC.
36047  *
36048  * Originally Released Under LGPL - original licence link has changed is not relivant.
36049  *
36050  * Fork - LGPL
36051  * <script type="text/javascript">
36052  */
36053  
36054 /**
36055  * @class Roo.menu.MenuMgr
36056  * Provides a common registry of all menu items on a page so that they can be easily accessed by id.
36057  * @singleton
36058  */
36059 Roo.menu.MenuMgr = function(){
36060    var menus, active, groups = {}, attached = false, lastShow = new Date();
36061
36062    // private - called when first menu is created
36063    function init(){
36064        menus = {};
36065        active = new Roo.util.MixedCollection();
36066        Roo.get(document).addKeyListener(27, function(){
36067            if(active.length > 0){
36068                hideAll();
36069            }
36070        });
36071    }
36072
36073    // private
36074    function hideAll(){
36075        if(active && active.length > 0){
36076            var c = active.clone();
36077            c.each(function(m){
36078                m.hide();
36079            });
36080        }
36081    }
36082
36083    // private
36084    function onHide(m){
36085        active.remove(m);
36086        if(active.length < 1){
36087            Roo.get(document).un("mousedown", onMouseDown);
36088            attached = false;
36089        }
36090    }
36091
36092    // private
36093    function onShow(m){
36094        var last = active.last();
36095        lastShow = new Date();
36096        active.add(m);
36097        if(!attached){
36098            Roo.get(document).on("mousedown", onMouseDown);
36099            attached = true;
36100        }
36101        if(m.parentMenu){
36102           m.getEl().setZIndex(parseInt(m.parentMenu.getEl().getStyle("z-index"), 10) + 3);
36103           m.parentMenu.activeChild = m;
36104        }else if(last && last.isVisible()){
36105           m.getEl().setZIndex(parseInt(last.getEl().getStyle("z-index"), 10) + 3);
36106        }
36107    }
36108
36109    // private
36110    function onBeforeHide(m){
36111        if(m.activeChild){
36112            m.activeChild.hide();
36113        }
36114        if(m.autoHideTimer){
36115            clearTimeout(m.autoHideTimer);
36116            delete m.autoHideTimer;
36117        }
36118    }
36119
36120    // private
36121    function onBeforeShow(m){
36122        var pm = m.parentMenu;
36123        if(!pm && !m.allowOtherMenus){
36124            hideAll();
36125        }else if(pm && pm.activeChild && active != m){
36126            pm.activeChild.hide();
36127        }
36128    }
36129
36130    // private
36131    function onMouseDown(e){
36132        if(lastShow.getElapsed() > 50 && active.length > 0 && !e.getTarget(".x-menu")){
36133            hideAll();
36134        }
36135    }
36136
36137    // private
36138    function onBeforeCheck(mi, state){
36139        if(state){
36140            var g = groups[mi.group];
36141            for(var i = 0, l = g.length; i < l; i++){
36142                if(g[i] != mi){
36143                    g[i].setChecked(false);
36144                }
36145            }
36146        }
36147    }
36148
36149    return {
36150
36151        /**
36152         * Hides all menus that are currently visible
36153         */
36154        hideAll : function(){
36155             hideAll();  
36156        },
36157
36158        // private
36159        register : function(menu){
36160            if(!menus){
36161                init();
36162            }
36163            menus[menu.id] = menu;
36164            menu.on("beforehide", onBeforeHide);
36165            menu.on("hide", onHide);
36166            menu.on("beforeshow", onBeforeShow);
36167            menu.on("show", onShow);
36168            var g = menu.group;
36169            if(g && menu.events["checkchange"]){
36170                if(!groups[g]){
36171                    groups[g] = [];
36172                }
36173                groups[g].push(menu);
36174                menu.on("checkchange", onCheck);
36175            }
36176        },
36177
36178         /**
36179          * Returns a {@link Roo.menu.Menu} object
36180          * @param {String/Object} menu The string menu id, an existing menu object reference, or a Menu config that will
36181          * be used to generate and return a new Menu instance.
36182          */
36183        get : function(menu){
36184            if(typeof menu == "string"){ // menu id
36185                return menus[menu];
36186            }else if(menu.events){  // menu instance
36187                return menu;
36188            }else if(typeof menu.length == 'number'){ // array of menu items?
36189                return new Roo.menu.Menu({items:menu});
36190            }else{ // otherwise, must be a config
36191                return new Roo.menu.Menu(menu);
36192            }
36193        },
36194
36195        // private
36196        unregister : function(menu){
36197            delete menus[menu.id];
36198            menu.un("beforehide", onBeforeHide);
36199            menu.un("hide", onHide);
36200            menu.un("beforeshow", onBeforeShow);
36201            menu.un("show", onShow);
36202            var g = menu.group;
36203            if(g && menu.events["checkchange"]){
36204                groups[g].remove(menu);
36205                menu.un("checkchange", onCheck);
36206            }
36207        },
36208
36209        // private
36210        registerCheckable : function(menuItem){
36211            var g = menuItem.group;
36212            if(g){
36213                if(!groups[g]){
36214                    groups[g] = [];
36215                }
36216                groups[g].push(menuItem);
36217                menuItem.on("beforecheckchange", onBeforeCheck);
36218            }
36219        },
36220
36221        // private
36222        unregisterCheckable : function(menuItem){
36223            var g = menuItem.group;
36224            if(g){
36225                groups[g].remove(menuItem);
36226                menuItem.un("beforecheckchange", onBeforeCheck);
36227            }
36228        }
36229    };
36230 }();/*
36231  * Based on:
36232  * Ext JS Library 1.1.1
36233  * Copyright(c) 2006-2007, Ext JS, LLC.
36234  *
36235  * Originally Released Under LGPL - original licence link has changed is not relivant.
36236  *
36237  * Fork - LGPL
36238  * <script type="text/javascript">
36239  */
36240  
36241
36242 /**
36243  * @class Roo.menu.BaseItem
36244  * @extends Roo.Component
36245  * The base class for all items that render into menus.  BaseItem provides default rendering, activated state
36246  * management and base configuration options shared by all menu components.
36247  * @constructor
36248  * Creates a new BaseItem
36249  * @param {Object} config Configuration options
36250  */
36251 Roo.menu.BaseItem = function(config){
36252     Roo.menu.BaseItem.superclass.constructor.call(this, config);
36253
36254     this.addEvents({
36255         /**
36256          * @event click
36257          * Fires when this item is clicked
36258          * @param {Roo.menu.BaseItem} this
36259          * @param {Roo.EventObject} e
36260          */
36261         click: true,
36262         /**
36263          * @event activate
36264          * Fires when this item is activated
36265          * @param {Roo.menu.BaseItem} this
36266          */
36267         activate : true,
36268         /**
36269          * @event deactivate
36270          * Fires when this item is deactivated
36271          * @param {Roo.menu.BaseItem} this
36272          */
36273         deactivate : true
36274     });
36275
36276     if(this.handler){
36277         this.on("click", this.handler, this.scope, true);
36278     }
36279 };
36280
36281 Roo.extend(Roo.menu.BaseItem, Roo.Component, {
36282     /**
36283      * @cfg {Function} handler
36284      * A function that will handle the click event of this menu item (defaults to undefined)
36285      */
36286     /**
36287      * @cfg {Boolean} canActivate True if this item can be visually activated (defaults to false)
36288      */
36289     canActivate : false,
36290     
36291      /**
36292      * @cfg {Boolean} hidden True to prevent creation of this menu item (defaults to false)
36293      */
36294     hidden: false,
36295     
36296     /**
36297      * @cfg {String} activeClass The CSS class to use when the item becomes activated (defaults to "x-menu-item-active")
36298      */
36299     activeClass : "x-menu-item-active",
36300     /**
36301      * @cfg {Boolean} hideOnClick True to hide the containing menu after this item is clicked (defaults to true)
36302      */
36303     hideOnClick : true,
36304     /**
36305      * @cfg {Number} hideDelay Length of time in milliseconds to wait before hiding after a click (defaults to 100)
36306      */
36307     hideDelay : 100,
36308
36309     // private
36310     ctype: "Roo.menu.BaseItem",
36311
36312     // private
36313     actionMode : "container",
36314
36315     // private
36316     render : function(container, parentMenu){
36317         this.parentMenu = parentMenu;
36318         Roo.menu.BaseItem.superclass.render.call(this, container);
36319         this.container.menuItemId = this.id;
36320     },
36321
36322     // private
36323     onRender : function(container, position){
36324         this.el = Roo.get(this.el);
36325         container.dom.appendChild(this.el.dom);
36326     },
36327
36328     // private
36329     onClick : function(e){
36330         if(!this.disabled && this.fireEvent("click", this, e) !== false
36331                 && this.parentMenu.fireEvent("itemclick", this, e) !== false){
36332             this.handleClick(e);
36333         }else{
36334             e.stopEvent();
36335         }
36336     },
36337
36338     // private
36339     activate : function(){
36340         if(this.disabled){
36341             return false;
36342         }
36343         var li = this.container;
36344         li.addClass(this.activeClass);
36345         this.region = li.getRegion().adjust(2, 2, -2, -2);
36346         this.fireEvent("activate", this);
36347         return true;
36348     },
36349
36350     // private
36351     deactivate : function(){
36352         this.container.removeClass(this.activeClass);
36353         this.fireEvent("deactivate", this);
36354     },
36355
36356     // private
36357     shouldDeactivate : function(e){
36358         return !this.region || !this.region.contains(e.getPoint());
36359     },
36360
36361     // private
36362     handleClick : function(e){
36363         if(this.hideOnClick){
36364             this.parentMenu.hide.defer(this.hideDelay, this.parentMenu, [true]);
36365         }
36366     },
36367
36368     // private
36369     expandMenu : function(autoActivate){
36370         // do nothing
36371     },
36372
36373     // private
36374     hideMenu : function(){
36375         // do nothing
36376     }
36377 });/*
36378  * Based on:
36379  * Ext JS Library 1.1.1
36380  * Copyright(c) 2006-2007, Ext JS, LLC.
36381  *
36382  * Originally Released Under LGPL - original licence link has changed is not relivant.
36383  *
36384  * Fork - LGPL
36385  * <script type="text/javascript">
36386  */
36387  
36388 /**
36389  * @class Roo.menu.Adapter
36390  * @extends Roo.menu.BaseItem
36391  * 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.
36392  * It provides basic rendering, activation management and enable/disable logic required to work in menus.
36393  * @constructor
36394  * Creates a new Adapter
36395  * @param {Object} config Configuration options
36396  */
36397 Roo.menu.Adapter = function(component, config){
36398     Roo.menu.Adapter.superclass.constructor.call(this, config);
36399     this.component = component;
36400 };
36401 Roo.extend(Roo.menu.Adapter, Roo.menu.BaseItem, {
36402     // private
36403     canActivate : true,
36404
36405     // private
36406     onRender : function(container, position){
36407         this.component.render(container);
36408         this.el = this.component.getEl();
36409     },
36410
36411     // private
36412     activate : function(){
36413         if(this.disabled){
36414             return false;
36415         }
36416         this.component.focus();
36417         this.fireEvent("activate", this);
36418         return true;
36419     },
36420
36421     // private
36422     deactivate : function(){
36423         this.fireEvent("deactivate", this);
36424     },
36425
36426     // private
36427     disable : function(){
36428         this.component.disable();
36429         Roo.menu.Adapter.superclass.disable.call(this);
36430     },
36431
36432     // private
36433     enable : function(){
36434         this.component.enable();
36435         Roo.menu.Adapter.superclass.enable.call(this);
36436     }
36437 });/*
36438  * Based on:
36439  * Ext JS Library 1.1.1
36440  * Copyright(c) 2006-2007, Ext JS, LLC.
36441  *
36442  * Originally Released Under LGPL - original licence link has changed is not relivant.
36443  *
36444  * Fork - LGPL
36445  * <script type="text/javascript">
36446  */
36447
36448 /**
36449  * @class Roo.menu.TextItem
36450  * @extends Roo.menu.BaseItem
36451  * Adds a static text string to a menu, usually used as either a heading or group separator.
36452  * Note: old style constructor with text is still supported.
36453  * 
36454  * @constructor
36455  * Creates a new TextItem
36456  * @param {Object} cfg Configuration
36457  */
36458 Roo.menu.TextItem = function(cfg){
36459     if (typeof(cfg) == 'string') {
36460         this.text = cfg;
36461     } else {
36462         Roo.apply(this,cfg);
36463     }
36464     
36465     Roo.menu.TextItem.superclass.constructor.call(this);
36466 };
36467
36468 Roo.extend(Roo.menu.TextItem, Roo.menu.BaseItem, {
36469     /**
36470      * @cfg {Boolean} text Text to show on item.
36471      */
36472     text : '',
36473     
36474     /**
36475      * @cfg {Boolean} hideOnClick True to hide the containing menu after this item is clicked (defaults to false)
36476      */
36477     hideOnClick : false,
36478     /**
36479      * @cfg {String} itemCls The default CSS class to use for text items (defaults to "x-menu-text")
36480      */
36481     itemCls : "x-menu-text",
36482
36483     // private
36484     onRender : function(){
36485         var s = document.createElement("span");
36486         s.className = this.itemCls;
36487         s.innerHTML = this.text;
36488         this.el = s;
36489         Roo.menu.TextItem.superclass.onRender.apply(this, arguments);
36490     }
36491 });/*
36492  * Based on:
36493  * Ext JS Library 1.1.1
36494  * Copyright(c) 2006-2007, Ext JS, LLC.
36495  *
36496  * Originally Released Under LGPL - original licence link has changed is not relivant.
36497  *
36498  * Fork - LGPL
36499  * <script type="text/javascript">
36500  */
36501
36502 /**
36503  * @class Roo.menu.Separator
36504  * @extends Roo.menu.BaseItem
36505  * Adds a separator bar to a menu, used to divide logical groups of menu items. Generally you will
36506  * add one of these by using "-" in you call to add() or in your items config rather than creating one directly.
36507  * @constructor
36508  * @param {Object} config Configuration options
36509  */
36510 Roo.menu.Separator = function(config){
36511     Roo.menu.Separator.superclass.constructor.call(this, config);
36512 };
36513
36514 Roo.extend(Roo.menu.Separator, Roo.menu.BaseItem, {
36515     /**
36516      * @cfg {String} itemCls The default CSS class to use for separators (defaults to "x-menu-sep")
36517      */
36518     itemCls : "x-menu-sep",
36519     /**
36520      * @cfg {Boolean} hideOnClick True to hide the containing menu after this item is clicked (defaults to false)
36521      */
36522     hideOnClick : false,
36523
36524     // private
36525     onRender : function(li){
36526         var s = document.createElement("span");
36527         s.className = this.itemCls;
36528         s.innerHTML = "&#160;";
36529         this.el = s;
36530         li.addClass("x-menu-sep-li");
36531         Roo.menu.Separator.superclass.onRender.apply(this, arguments);
36532     }
36533 });/*
36534  * Based on:
36535  * Ext JS Library 1.1.1
36536  * Copyright(c) 2006-2007, Ext JS, LLC.
36537  *
36538  * Originally Released Under LGPL - original licence link has changed is not relivant.
36539  *
36540  * Fork - LGPL
36541  * <script type="text/javascript">
36542  */
36543 /**
36544  * @class Roo.menu.Item
36545  * @extends Roo.menu.BaseItem
36546  * A base class for all menu items that require menu-related functionality (like sub-menus) and are not static
36547  * display items.  Item extends the base functionality of {@link Roo.menu.BaseItem} by adding menu-specific
36548  * activation and click handling.
36549  * @constructor
36550  * Creates a new Item
36551  * @param {Object} config Configuration options
36552  */
36553 Roo.menu.Item = function(config){
36554     Roo.menu.Item.superclass.constructor.call(this, config);
36555     if(this.menu){
36556         this.menu = Roo.menu.MenuMgr.get(this.menu);
36557     }
36558 };
36559 Roo.extend(Roo.menu.Item, Roo.menu.BaseItem, {
36560     
36561     /**
36562      * @cfg {String} text
36563      * The text to show on the menu item.
36564      */
36565     text: '',
36566      /**
36567      * @cfg {String} HTML to render in menu
36568      * The text to show on the menu item (HTML version).
36569      */
36570     html: '',
36571     /**
36572      * @cfg {String} icon
36573      * The path to an icon to display in this menu item (defaults to Roo.BLANK_IMAGE_URL)
36574      */
36575     icon: undefined,
36576     /**
36577      * @cfg {String} itemCls The default CSS class to use for menu items (defaults to "x-menu-item")
36578      */
36579     itemCls : "x-menu-item",
36580     /**
36581      * @cfg {Boolean} canActivate True if this item can be visually activated (defaults to true)
36582      */
36583     canActivate : true,
36584     /**
36585      * @cfg {Number} showDelay Length of time in milliseconds to wait before showing this item (defaults to 200)
36586      */
36587     showDelay: 200,
36588     // doc'd in BaseItem
36589     hideDelay: 200,
36590
36591     // private
36592     ctype: "Roo.menu.Item",
36593     
36594     // private
36595     onRender : function(container, position){
36596         var el = document.createElement("a");
36597         el.hideFocus = true;
36598         el.unselectable = "on";
36599         el.href = this.href || "#";
36600         if(this.hrefTarget){
36601             el.target = this.hrefTarget;
36602         }
36603         el.className = this.itemCls + (this.menu ?  " x-menu-item-arrow" : "") + (this.cls ?  " " + this.cls : "");
36604         
36605         var html = this.html.length ? this.html  : String.format('{0}',this.text);
36606         
36607         el.innerHTML = String.format(
36608                 '<img src="{0}" class="x-menu-item-icon {1}" />' + html,
36609                 this.icon || Roo.BLANK_IMAGE_URL, this.iconCls || '');
36610         this.el = el;
36611         Roo.menu.Item.superclass.onRender.call(this, container, position);
36612     },
36613
36614     /**
36615      * Sets the text to display in this menu item
36616      * @param {String} text The text to display
36617      * @param {Boolean} isHTML true to indicate text is pure html.
36618      */
36619     setText : function(text, isHTML){
36620         if (isHTML) {
36621             this.html = text;
36622         } else {
36623             this.text = text;
36624             this.html = '';
36625         }
36626         if(this.rendered){
36627             var html = this.html.length ? this.html  : String.format('{0}',this.text);
36628      
36629             this.el.update(String.format(
36630                 '<img src="{0}" class="x-menu-item-icon {2}">' + html,
36631                 this.icon || Roo.BLANK_IMAGE_URL, this.text, this.iconCls || ''));
36632             this.parentMenu.autoWidth();
36633         }
36634     },
36635
36636     // private
36637     handleClick : function(e){
36638         if(!this.href){ // if no link defined, stop the event automatically
36639             e.stopEvent();
36640         }
36641         Roo.menu.Item.superclass.handleClick.apply(this, arguments);
36642     },
36643
36644     // private
36645     activate : function(autoExpand){
36646         if(Roo.menu.Item.superclass.activate.apply(this, arguments)){
36647             this.focus();
36648             if(autoExpand){
36649                 this.expandMenu();
36650             }
36651         }
36652         return true;
36653     },
36654
36655     // private
36656     shouldDeactivate : function(e){
36657         if(Roo.menu.Item.superclass.shouldDeactivate.call(this, e)){
36658             if(this.menu && this.menu.isVisible()){
36659                 return !this.menu.getEl().getRegion().contains(e.getPoint());
36660             }
36661             return true;
36662         }
36663         return false;
36664     },
36665
36666     // private
36667     deactivate : function(){
36668         Roo.menu.Item.superclass.deactivate.apply(this, arguments);
36669         this.hideMenu();
36670     },
36671
36672     // private
36673     expandMenu : function(autoActivate){
36674         if(!this.disabled && this.menu){
36675             clearTimeout(this.hideTimer);
36676             delete this.hideTimer;
36677             if(!this.menu.isVisible() && !this.showTimer){
36678                 this.showTimer = this.deferExpand.defer(this.showDelay, this, [autoActivate]);
36679             }else if (this.menu.isVisible() && autoActivate){
36680                 this.menu.tryActivate(0, 1);
36681             }
36682         }
36683     },
36684
36685     // private
36686     deferExpand : function(autoActivate){
36687         delete this.showTimer;
36688         this.menu.show(this.container, this.parentMenu.subMenuAlign || "tl-tr?", this.parentMenu);
36689         if(autoActivate){
36690             this.menu.tryActivate(0, 1);
36691         }
36692     },
36693
36694     // private
36695     hideMenu : function(){
36696         clearTimeout(this.showTimer);
36697         delete this.showTimer;
36698         if(!this.hideTimer && this.menu && this.menu.isVisible()){
36699             this.hideTimer = this.deferHide.defer(this.hideDelay, this);
36700         }
36701     },
36702
36703     // private
36704     deferHide : function(){
36705         delete this.hideTimer;
36706         this.menu.hide();
36707     }
36708 });/*
36709  * Based on:
36710  * Ext JS Library 1.1.1
36711  * Copyright(c) 2006-2007, Ext JS, LLC.
36712  *
36713  * Originally Released Under LGPL - original licence link has changed is not relivant.
36714  *
36715  * Fork - LGPL
36716  * <script type="text/javascript">
36717  */
36718  
36719 /**
36720  * @class Roo.menu.CheckItem
36721  * @extends Roo.menu.Item
36722  * Adds a menu item that contains a checkbox by default, but can also be part of a radio group.
36723  * @constructor
36724  * Creates a new CheckItem
36725  * @param {Object} config Configuration options
36726  */
36727 Roo.menu.CheckItem = function(config){
36728     Roo.menu.CheckItem.superclass.constructor.call(this, config);
36729     this.addEvents({
36730         /**
36731          * @event beforecheckchange
36732          * Fires before the checked value is set, providing an opportunity to cancel if needed
36733          * @param {Roo.menu.CheckItem} this
36734          * @param {Boolean} checked The new checked value that will be set
36735          */
36736         "beforecheckchange" : true,
36737         /**
36738          * @event checkchange
36739          * Fires after the checked value has been set
36740          * @param {Roo.menu.CheckItem} this
36741          * @param {Boolean} checked The checked value that was set
36742          */
36743         "checkchange" : true
36744     });
36745     if(this.checkHandler){
36746         this.on('checkchange', this.checkHandler, this.scope);
36747     }
36748 };
36749 Roo.extend(Roo.menu.CheckItem, Roo.menu.Item, {
36750     /**
36751      * @cfg {String} group
36752      * All check items with the same group name will automatically be grouped into a single-select
36753      * radio button group (defaults to '')
36754      */
36755     /**
36756      * @cfg {String} itemCls The default CSS class to use for check items (defaults to "x-menu-item x-menu-check-item")
36757      */
36758     itemCls : "x-menu-item x-menu-check-item",
36759     /**
36760      * @cfg {String} groupClass The default CSS class to use for radio group check items (defaults to "x-menu-group-item")
36761      */
36762     groupClass : "x-menu-group-item",
36763
36764     /**
36765      * @cfg {Boolean} checked True to initialize this checkbox as checked (defaults to false).  Note that
36766      * if this checkbox is part of a radio group (group = true) only the last item in the group that is
36767      * initialized with checked = true will be rendered as checked.
36768      */
36769     checked: false,
36770
36771     // private
36772     ctype: "Roo.menu.CheckItem",
36773
36774     // private
36775     onRender : function(c){
36776         Roo.menu.CheckItem.superclass.onRender.apply(this, arguments);
36777         if(this.group){
36778             this.el.addClass(this.groupClass);
36779         }
36780         Roo.menu.MenuMgr.registerCheckable(this);
36781         if(this.checked){
36782             this.checked = false;
36783             this.setChecked(true, true);
36784         }
36785     },
36786
36787     // private
36788     destroy : function(){
36789         if(this.rendered){
36790             Roo.menu.MenuMgr.unregisterCheckable(this);
36791         }
36792         Roo.menu.CheckItem.superclass.destroy.apply(this, arguments);
36793     },
36794
36795     /**
36796      * Set the checked state of this item
36797      * @param {Boolean} checked The new checked value
36798      * @param {Boolean} suppressEvent (optional) True to prevent the checkchange event from firing (defaults to false)
36799      */
36800     setChecked : function(state, suppressEvent){
36801         if(this.checked != state && this.fireEvent("beforecheckchange", this, state) !== false){
36802             if(this.container){
36803                 this.container[state ? "addClass" : "removeClass"]("x-menu-item-checked");
36804             }
36805             this.checked = state;
36806             if(suppressEvent !== true){
36807                 this.fireEvent("checkchange", this, state);
36808             }
36809         }
36810     },
36811
36812     // private
36813     handleClick : function(e){
36814        if(!this.disabled && !(this.checked && this.group)){// disable unselect on radio item
36815            this.setChecked(!this.checked);
36816        }
36817        Roo.menu.CheckItem.superclass.handleClick.apply(this, arguments);
36818     }
36819 });/*
36820  * Based on:
36821  * Ext JS Library 1.1.1
36822  * Copyright(c) 2006-2007, Ext JS, LLC.
36823  *
36824  * Originally Released Under LGPL - original licence link has changed is not relivant.
36825  *
36826  * Fork - LGPL
36827  * <script type="text/javascript">
36828  */
36829  
36830 /**
36831  * @class Roo.menu.DateItem
36832  * @extends Roo.menu.Adapter
36833  * A menu item that wraps the {@link Roo.DatPicker} component.
36834  * @constructor
36835  * Creates a new DateItem
36836  * @param {Object} config Configuration options
36837  */
36838 Roo.menu.DateItem = function(config){
36839     Roo.menu.DateItem.superclass.constructor.call(this, new Roo.DatePicker(config), config);
36840     /** The Roo.DatePicker object @type Roo.DatePicker */
36841     this.picker = this.component;
36842     this.addEvents({select: true});
36843     
36844     this.picker.on("render", function(picker){
36845         picker.getEl().swallowEvent("click");
36846         picker.container.addClass("x-menu-date-item");
36847     });
36848
36849     this.picker.on("select", this.onSelect, this);
36850 };
36851
36852 Roo.extend(Roo.menu.DateItem, Roo.menu.Adapter, {
36853     // private
36854     onSelect : function(picker, date){
36855         this.fireEvent("select", this, date, picker);
36856         Roo.menu.DateItem.superclass.handleClick.call(this);
36857     }
36858 });/*
36859  * Based on:
36860  * Ext JS Library 1.1.1
36861  * Copyright(c) 2006-2007, Ext JS, LLC.
36862  *
36863  * Originally Released Under LGPL - original licence link has changed is not relivant.
36864  *
36865  * Fork - LGPL
36866  * <script type="text/javascript">
36867  */
36868  
36869 /**
36870  * @class Roo.menu.ColorItem
36871  * @extends Roo.menu.Adapter
36872  * A menu item that wraps the {@link Roo.ColorPalette} component.
36873  * @constructor
36874  * Creates a new ColorItem
36875  * @param {Object} config Configuration options
36876  */
36877 Roo.menu.ColorItem = function(config){
36878     Roo.menu.ColorItem.superclass.constructor.call(this, new Roo.ColorPalette(config), config);
36879     /** The Roo.ColorPalette object @type Roo.ColorPalette */
36880     this.palette = this.component;
36881     this.relayEvents(this.palette, ["select"]);
36882     if(this.selectHandler){
36883         this.on('select', this.selectHandler, this.scope);
36884     }
36885 };
36886 Roo.extend(Roo.menu.ColorItem, Roo.menu.Adapter);/*
36887  * Based on:
36888  * Ext JS Library 1.1.1
36889  * Copyright(c) 2006-2007, Ext JS, LLC.
36890  *
36891  * Originally Released Under LGPL - original licence link has changed is not relivant.
36892  *
36893  * Fork - LGPL
36894  * <script type="text/javascript">
36895  */
36896  
36897
36898 /**
36899  * @class Roo.menu.DateMenu
36900  * @extends Roo.menu.Menu
36901  * A menu containing a {@link Roo.menu.DateItem} component (which provides a date picker).
36902  * @constructor
36903  * Creates a new DateMenu
36904  * @param {Object} config Configuration options
36905  */
36906 Roo.menu.DateMenu = function(config){
36907     Roo.menu.DateMenu.superclass.constructor.call(this, config);
36908     this.plain = true;
36909     var di = new Roo.menu.DateItem(config);
36910     this.add(di);
36911     /**
36912      * The {@link Roo.DatePicker} instance for this DateMenu
36913      * @type DatePicker
36914      */
36915     this.picker = di.picker;
36916     /**
36917      * @event select
36918      * @param {DatePicker} picker
36919      * @param {Date} date
36920      */
36921     this.relayEvents(di, ["select"]);
36922     this.on('beforeshow', function(){
36923         if(this.picker){
36924             this.picker.hideMonthPicker(false);
36925         }
36926     }, this);
36927 };
36928 Roo.extend(Roo.menu.DateMenu, Roo.menu.Menu, {
36929     cls:'x-date-menu'
36930 });/*
36931  * Based on:
36932  * Ext JS Library 1.1.1
36933  * Copyright(c) 2006-2007, Ext JS, LLC.
36934  *
36935  * Originally Released Under LGPL - original licence link has changed is not relivant.
36936  *
36937  * Fork - LGPL
36938  * <script type="text/javascript">
36939  */
36940  
36941
36942 /**
36943  * @class Roo.menu.ColorMenu
36944  * @extends Roo.menu.Menu
36945  * A menu containing a {@link Roo.menu.ColorItem} component (which provides a basic color picker).
36946  * @constructor
36947  * Creates a new ColorMenu
36948  * @param {Object} config Configuration options
36949  */
36950 Roo.menu.ColorMenu = function(config){
36951     Roo.menu.ColorMenu.superclass.constructor.call(this, config);
36952     this.plain = true;
36953     var ci = new Roo.menu.ColorItem(config);
36954     this.add(ci);
36955     /**
36956      * The {@link Roo.ColorPalette} instance for this ColorMenu
36957      * @type ColorPalette
36958      */
36959     this.palette = ci.palette;
36960     /**
36961      * @event select
36962      * @param {ColorPalette} palette
36963      * @param {String} color
36964      */
36965     this.relayEvents(ci, ["select"]);
36966 };
36967 Roo.extend(Roo.menu.ColorMenu, Roo.menu.Menu);/*
36968  * Based on:
36969  * Ext JS Library 1.1.1
36970  * Copyright(c) 2006-2007, Ext JS, LLC.
36971  *
36972  * Originally Released Under LGPL - original licence link has changed is not relivant.
36973  *
36974  * Fork - LGPL
36975  * <script type="text/javascript">
36976  */
36977  
36978 /**
36979  * @class Roo.form.Field
36980  * @extends Roo.BoxComponent
36981  * Base class for form fields that provides default event handling, sizing, value handling and other functionality.
36982  * @constructor
36983  * Creates a new Field
36984  * @param {Object} config Configuration options
36985  */
36986 Roo.form.Field = function(config){
36987     Roo.form.Field.superclass.constructor.call(this, config);
36988 };
36989
36990 Roo.extend(Roo.form.Field, Roo.BoxComponent,  {
36991     /**
36992      * @cfg {String} fieldLabel Label to use when rendering a form.
36993      */
36994        /**
36995      * @cfg {String} qtip Mouse over tip
36996      */
36997      
36998     /**
36999      * @cfg {String} invalidClass The CSS class to use when marking a field invalid (defaults to "x-form-invalid")
37000      */
37001     invalidClass : "x-form-invalid",
37002     /**
37003      * @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")
37004      */
37005     invalidText : "The value in this field is invalid",
37006     /**
37007      * @cfg {String} focusClass The CSS class to use when the field receives focus (defaults to "x-form-focus")
37008      */
37009     focusClass : "x-form-focus",
37010     /**
37011      * @cfg {String/Boolean} validationEvent The event that should initiate field validation. Set to false to disable
37012       automatic validation (defaults to "keyup").
37013      */
37014     validationEvent : "keyup",
37015     /**
37016      * @cfg {Boolean} validateOnBlur Whether the field should validate when it loses focus (defaults to true).
37017      */
37018     validateOnBlur : true,
37019     /**
37020      * @cfg {Number} validationDelay The length of time in milliseconds after user input begins until validation is initiated (defaults to 250)
37021      */
37022     validationDelay : 250,
37023     /**
37024      * @cfg {String/Object} autoCreate A DomHelper element spec, or true for a default element spec (defaults to
37025      * {tag: "input", type: "text", size: "20", autocomplete: "off"})
37026      */
37027     defaultAutoCreate : {tag: "input", type: "text", size: "20", autocomplete: "off"},
37028     /**
37029      * @cfg {String} fieldClass The default CSS class for the field (defaults to "x-form-field")
37030      */
37031     fieldClass : "x-form-field",
37032     /**
37033      * @cfg {String} msgTarget The location where error text should display.  Should be one of the following values (defaults to 'qtip'):
37034      *<pre>
37035 Value         Description
37036 -----------   ----------------------------------------------------------------------
37037 qtip          Display a quick tip when the user hovers over the field
37038 title         Display a default browser title attribute popup
37039 under         Add a block div beneath the field containing the error text
37040 side          Add an error icon to the right of the field with a popup on hover
37041 [element id]  Add the error text directly to the innerHTML of the specified element
37042 </pre>
37043      */
37044     msgTarget : 'qtip',
37045     /**
37046      * @cfg {String} msgFx <b>Experimental</b> The effect used when displaying a validation message under the field (defaults to 'normal').
37047      */
37048     msgFx : 'normal',
37049
37050     /**
37051      * @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.
37052      */
37053     readOnly : false,
37054
37055     /**
37056      * @cfg {Boolean} disabled True to disable the field (defaults to false).
37057      */
37058     disabled : false,
37059
37060     /**
37061      * @cfg {String} inputType The type attribute for input fields -- e.g. radio, text, password (defaults to "text").
37062      */
37063     inputType : undefined,
37064     
37065     /**
37066      * @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).
37067          */
37068         tabIndex : undefined,
37069         
37070     // private
37071     isFormField : true,
37072
37073     // private
37074     hasFocus : false,
37075     /**
37076      * @property {Roo.Element} fieldEl
37077      * Element Containing the rendered Field (with label etc.)
37078      */
37079     /**
37080      * @cfg {Mixed} value A value to initialize this field with.
37081      */
37082     value : undefined,
37083
37084     /**
37085      * @cfg {String} name The field's HTML name attribute.
37086      */
37087     /**
37088      * @cfg {String} cls A CSS class to apply to the field's underlying element.
37089      */
37090
37091         // private ??
37092         initComponent : function(){
37093         Roo.form.Field.superclass.initComponent.call(this);
37094         this.addEvents({
37095             /**
37096              * @event focus
37097              * Fires when this field receives input focus.
37098              * @param {Roo.form.Field} this
37099              */
37100             focus : true,
37101             /**
37102              * @event blur
37103              * Fires when this field loses input focus.
37104              * @param {Roo.form.Field} this
37105              */
37106             blur : true,
37107             /**
37108              * @event specialkey
37109              * Fires when any key related to navigation (arrows, tab, enter, esc, etc.) is pressed.  You can check
37110              * {@link Roo.EventObject#getKey} to determine which key was pressed.
37111              * @param {Roo.form.Field} this
37112              * @param {Roo.EventObject} e The event object
37113              */
37114             specialkey : true,
37115             /**
37116              * @event change
37117              * Fires just before the field blurs if the field value has changed.
37118              * @param {Roo.form.Field} this
37119              * @param {Mixed} newValue The new value
37120              * @param {Mixed} oldValue The original value
37121              */
37122             change : true,
37123             /**
37124              * @event invalid
37125              * Fires after the field has been marked as invalid.
37126              * @param {Roo.form.Field} this
37127              * @param {String} msg The validation message
37128              */
37129             invalid : true,
37130             /**
37131              * @event valid
37132              * Fires after the field has been validated with no errors.
37133              * @param {Roo.form.Field} this
37134              */
37135             valid : true,
37136              /**
37137              * @event keyup
37138              * Fires after the key up
37139              * @param {Roo.form.Field} this
37140              * @param {Roo.EventObject}  e The event Object
37141              */
37142             keyup : true
37143         });
37144     },
37145
37146     /**
37147      * Returns the name attribute of the field if available
37148      * @return {String} name The field name
37149      */
37150     getName: function(){
37151          return this.rendered && this.el.dom.name ? this.el.dom.name : (this.hiddenName || '');
37152     },
37153
37154     // private
37155     onRender : function(ct, position){
37156         Roo.form.Field.superclass.onRender.call(this, ct, position);
37157         if(!this.el){
37158             var cfg = this.getAutoCreate();
37159             if(!cfg.name){
37160                 cfg.name = typeof(this.name) == 'undefined' ? this.id : this.name;
37161             }
37162             if (!cfg.name.length) {
37163                 delete cfg.name;
37164             }
37165             if(this.inputType){
37166                 cfg.type = this.inputType;
37167             }
37168             this.el = ct.createChild(cfg, position);
37169         }
37170         var type = this.el.dom.type;
37171         if(type){
37172             if(type == 'password'){
37173                 type = 'text';
37174             }
37175             this.el.addClass('x-form-'+type);
37176         }
37177         if(this.readOnly){
37178             this.el.dom.readOnly = true;
37179         }
37180         if(this.tabIndex !== undefined){
37181             this.el.dom.setAttribute('tabIndex', this.tabIndex);
37182         }
37183
37184         this.el.addClass([this.fieldClass, this.cls]);
37185         this.initValue();
37186     },
37187
37188     /**
37189      * Apply the behaviors of this component to an existing element. <b>This is used instead of render().</b>
37190      * @param {String/HTMLElement/Element} el The id of the node, a DOM node or an existing Element
37191      * @return {Roo.form.Field} this
37192      */
37193     applyTo : function(target){
37194         this.allowDomMove = false;
37195         this.el = Roo.get(target);
37196         this.render(this.el.dom.parentNode);
37197         return this;
37198     },
37199
37200     // private
37201     initValue : function(){
37202         if(this.value !== undefined){
37203             this.setValue(this.value);
37204         }else if(this.el.dom.value.length > 0){
37205             this.setValue(this.el.dom.value);
37206         }
37207     },
37208
37209     /**
37210      * Returns true if this field has been changed since it was originally loaded and is not disabled.
37211      */
37212     isDirty : function() {
37213         if(this.disabled) {
37214             return false;
37215         }
37216         return String(this.getValue()) !== String(this.originalValue);
37217     },
37218
37219     // private
37220     afterRender : function(){
37221         Roo.form.Field.superclass.afterRender.call(this);
37222         this.initEvents();
37223     },
37224
37225     // private
37226     fireKey : function(e){
37227         //Roo.log('field ' + e.getKey());
37228         if(e.isNavKeyPress()){
37229             this.fireEvent("specialkey", this, e);
37230         }
37231     },
37232
37233     /**
37234      * Resets the current field value to the originally loaded value and clears any validation messages
37235      */
37236     reset : function(){
37237         this.setValue(this.resetValue);
37238         this.clearInvalid();
37239     },
37240
37241     // private
37242     initEvents : function(){
37243         // safari killled keypress - so keydown is now used..
37244         this.el.on("keydown" , this.fireKey,  this);
37245         this.el.on("focus", this.onFocus,  this);
37246         this.el.on("blur", this.onBlur,  this);
37247         this.el.relayEvent('keyup', this);
37248
37249         // reference to original value for reset
37250         this.originalValue = this.getValue();
37251         this.resetValue =  this.getValue();
37252     },
37253
37254     // private
37255     onFocus : function(){
37256         if(!Roo.isOpera && this.focusClass){ // don't touch in Opera
37257             this.el.addClass(this.focusClass);
37258         }
37259         if(!this.hasFocus){
37260             this.hasFocus = true;
37261             this.startValue = this.getValue();
37262             this.fireEvent("focus", this);
37263         }
37264     },
37265
37266     beforeBlur : Roo.emptyFn,
37267
37268     // private
37269     onBlur : function(){
37270         this.beforeBlur();
37271         if(!Roo.isOpera && this.focusClass){ // don't touch in Opera
37272             this.el.removeClass(this.focusClass);
37273         }
37274         this.hasFocus = false;
37275         if(this.validationEvent !== false && this.validateOnBlur && this.validationEvent != "blur"){
37276             this.validate();
37277         }
37278         var v = this.getValue();
37279         if(String(v) !== String(this.startValue)){
37280             this.fireEvent('change', this, v, this.startValue);
37281         }
37282         this.fireEvent("blur", this);
37283     },
37284
37285     /**
37286      * Returns whether or not the field value is currently valid
37287      * @param {Boolean} preventMark True to disable marking the field invalid
37288      * @return {Boolean} True if the value is valid, else false
37289      */
37290     isValid : function(preventMark){
37291         if(this.disabled){
37292             return true;
37293         }
37294         var restore = this.preventMark;
37295         this.preventMark = preventMark === true;
37296         var v = this.validateValue(this.processValue(this.getRawValue()));
37297         this.preventMark = restore;
37298         return v;
37299     },
37300
37301     /**
37302      * Validates the field value
37303      * @return {Boolean} True if the value is valid, else false
37304      */
37305     validate : function(){
37306         if(this.disabled || this.validateValue(this.processValue(this.getRawValue()))){
37307             this.clearInvalid();
37308             return true;
37309         }
37310         return false;
37311     },
37312
37313     processValue : function(value){
37314         return value;
37315     },
37316
37317     // private
37318     // Subclasses should provide the validation implementation by overriding this
37319     validateValue : function(value){
37320         return true;
37321     },
37322
37323     /**
37324      * Mark this field as invalid
37325      * @param {String} msg The validation message
37326      */
37327     markInvalid : function(msg){
37328         if(!this.rendered || this.preventMark){ // not rendered
37329             return;
37330         }
37331         
37332         var obj = (typeof(this.combo) != 'undefined') ? this.combo : this; // fix the combox array!!
37333         
37334         obj.el.addClass(this.invalidClass);
37335         msg = msg || this.invalidText;
37336         switch(this.msgTarget){
37337             case 'qtip':
37338                 obj.el.dom.qtip = msg;
37339                 obj.el.dom.qclass = 'x-form-invalid-tip';
37340                 if(Roo.QuickTips){ // fix for floating editors interacting with DND
37341                     Roo.QuickTips.enable();
37342                 }
37343                 break;
37344             case 'title':
37345                 this.el.dom.title = msg;
37346                 break;
37347             case 'under':
37348                 if(!this.errorEl){
37349                     var elp = this.el.findParent('.x-form-element', 5, true);
37350                     this.errorEl = elp.createChild({cls:'x-form-invalid-msg'});
37351                     this.errorEl.setWidth(elp.getWidth(true)-20);
37352                 }
37353                 this.errorEl.update(msg);
37354                 Roo.form.Field.msgFx[this.msgFx].show(this.errorEl, this);
37355                 break;
37356             case 'side':
37357                 if(!this.errorIcon){
37358                     var elp = this.el.findParent('.x-form-element', 5, true);
37359                     this.errorIcon = elp.createChild({cls:'x-form-invalid-icon'});
37360                 }
37361                 this.alignErrorIcon();
37362                 this.errorIcon.dom.qtip = msg;
37363                 this.errorIcon.dom.qclass = 'x-form-invalid-tip';
37364                 this.errorIcon.show();
37365                 this.on('resize', this.alignErrorIcon, this);
37366                 break;
37367             default:
37368                 var t = Roo.getDom(this.msgTarget);
37369                 t.innerHTML = msg;
37370                 t.style.display = this.msgDisplay;
37371                 break;
37372         }
37373         this.fireEvent('invalid', this, msg);
37374     },
37375
37376     // private
37377     alignErrorIcon : function(){
37378         this.errorIcon.alignTo(this.el, 'tl-tr', [2, 0]);
37379     },
37380
37381     /**
37382      * Clear any invalid styles/messages for this field
37383      */
37384     clearInvalid : function(){
37385         if(!this.rendered || this.preventMark){ // not rendered
37386             return;
37387         }
37388         var obj = (typeof(this.combo) != 'undefined') ? this.combo : this; // fix the combox array!!
37389         
37390         obj.el.removeClass(this.invalidClass);
37391         switch(this.msgTarget){
37392             case 'qtip':
37393                 obj.el.dom.qtip = '';
37394                 break;
37395             case 'title':
37396                 this.el.dom.title = '';
37397                 break;
37398             case 'under':
37399                 if(this.errorEl){
37400                     Roo.form.Field.msgFx[this.msgFx].hide(this.errorEl, this);
37401                 }
37402                 break;
37403             case 'side':
37404                 if(this.errorIcon){
37405                     this.errorIcon.dom.qtip = '';
37406                     this.errorIcon.hide();
37407                     this.un('resize', this.alignErrorIcon, this);
37408                 }
37409                 break;
37410             default:
37411                 var t = Roo.getDom(this.msgTarget);
37412                 t.innerHTML = '';
37413                 t.style.display = 'none';
37414                 break;
37415         }
37416         this.fireEvent('valid', this);
37417     },
37418
37419     /**
37420      * Returns the raw data value which may or may not be a valid, defined value.  To return a normalized value see {@link #getValue}.
37421      * @return {Mixed} value The field value
37422      */
37423     getRawValue : function(){
37424         var v = this.el.getValue();
37425         
37426         return v;
37427     },
37428
37429     /**
37430      * Returns the normalized data value (undefined or emptyText will be returned as '').  To return the raw value see {@link #getRawValue}.
37431      * @return {Mixed} value The field value
37432      */
37433     getValue : function(){
37434         var v = this.el.getValue();
37435          
37436         return v;
37437     },
37438
37439     /**
37440      * Sets the underlying DOM field's value directly, bypassing validation.  To set the value with validation see {@link #setValue}.
37441      * @param {Mixed} value The value to set
37442      */
37443     setRawValue : function(v){
37444         return this.el.dom.value = (v === null || v === undefined ? '' : v);
37445     },
37446
37447     /**
37448      * Sets a data value into the field and validates it.  To set the value directly without validation see {@link #setRawValue}.
37449      * @param {Mixed} value The value to set
37450      */
37451     setValue : function(v){
37452         this.value = v;
37453         if(this.rendered){
37454             this.el.dom.value = (v === null || v === undefined ? '' : v);
37455              this.validate();
37456         }
37457     },
37458
37459     adjustSize : function(w, h){
37460         var s = Roo.form.Field.superclass.adjustSize.call(this, w, h);
37461         s.width = this.adjustWidth(this.el.dom.tagName, s.width);
37462         return s;
37463     },
37464
37465     adjustWidth : function(tag, w){
37466         tag = tag.toLowerCase();
37467         if(typeof w == 'number' && Roo.isStrict && !Roo.isSafari){
37468             if(Roo.isIE && (tag == 'input' || tag == 'textarea')){
37469                 if(tag == 'input'){
37470                     return w + 2;
37471                 }
37472                 if(tag == 'textarea'){
37473                     return w-2;
37474                 }
37475             }else if(Roo.isOpera){
37476                 if(tag == 'input'){
37477                     return w + 2;
37478                 }
37479                 if(tag == 'textarea'){
37480                     return w-2;
37481                 }
37482             }
37483         }
37484         return w;
37485     }
37486 });
37487
37488
37489 // anything other than normal should be considered experimental
37490 Roo.form.Field.msgFx = {
37491     normal : {
37492         show: function(msgEl, f){
37493             msgEl.setDisplayed('block');
37494         },
37495
37496         hide : function(msgEl, f){
37497             msgEl.setDisplayed(false).update('');
37498         }
37499     },
37500
37501     slide : {
37502         show: function(msgEl, f){
37503             msgEl.slideIn('t', {stopFx:true});
37504         },
37505
37506         hide : function(msgEl, f){
37507             msgEl.slideOut('t', {stopFx:true,useDisplay:true});
37508         }
37509     },
37510
37511     slideRight : {
37512         show: function(msgEl, f){
37513             msgEl.fixDisplay();
37514             msgEl.alignTo(f.el, 'tl-tr');
37515             msgEl.slideIn('l', {stopFx:true});
37516         },
37517
37518         hide : function(msgEl, f){
37519             msgEl.slideOut('l', {stopFx:true,useDisplay:true});
37520         }
37521     }
37522 };/*
37523  * Based on:
37524  * Ext JS Library 1.1.1
37525  * Copyright(c) 2006-2007, Ext JS, LLC.
37526  *
37527  * Originally Released Under LGPL - original licence link has changed is not relivant.
37528  *
37529  * Fork - LGPL
37530  * <script type="text/javascript">
37531  */
37532  
37533
37534 /**
37535  * @class Roo.form.TextField
37536  * @extends Roo.form.Field
37537  * Basic text field.  Can be used as a direct replacement for traditional text inputs, or as the base
37538  * class for more sophisticated input controls (like {@link Roo.form.TextArea} and {@link Roo.form.ComboBox}).
37539  * @constructor
37540  * Creates a new TextField
37541  * @param {Object} config Configuration options
37542  */
37543 Roo.form.TextField = function(config){
37544     Roo.form.TextField.superclass.constructor.call(this, config);
37545     this.addEvents({
37546         /**
37547          * @event autosize
37548          * Fires when the autosize function is triggered.  The field may or may not have actually changed size
37549          * according to the default logic, but this event provides a hook for the developer to apply additional
37550          * logic at runtime to resize the field if needed.
37551              * @param {Roo.form.Field} this This text field
37552              * @param {Number} width The new field width
37553              */
37554         autosize : true
37555     });
37556 };
37557
37558 Roo.extend(Roo.form.TextField, Roo.form.Field,  {
37559     /**
37560      * @cfg {Boolean} grow True if this field should automatically grow and shrink to its content
37561      */
37562     grow : false,
37563     /**
37564      * @cfg {Number} growMin The minimum width to allow when grow = true (defaults to 30)
37565      */
37566     growMin : 30,
37567     /**
37568      * @cfg {Number} growMax The maximum width to allow when grow = true (defaults to 800)
37569      */
37570     growMax : 800,
37571     /**
37572      * @cfg {String} vtype A validation type name as defined in {@link Roo.form.VTypes} (defaults to null)
37573      */
37574     vtype : null,
37575     /**
37576      * @cfg {String} maskRe An input mask regular expression that will be used to filter keystrokes that don't match (defaults to null)
37577      */
37578     maskRe : null,
37579     /**
37580      * @cfg {Boolean} disableKeyFilter True to disable input keystroke filtering (defaults to false)
37581      */
37582     disableKeyFilter : false,
37583     /**
37584      * @cfg {Boolean} allowBlank False to validate that the value length > 0 (defaults to true)
37585      */
37586     allowBlank : true,
37587     /**
37588      * @cfg {Number} minLength Minimum input field length required (defaults to 0)
37589      */
37590     minLength : 0,
37591     /**
37592      * @cfg {Number} maxLength Maximum input field length allowed (defaults to Number.MAX_VALUE)
37593      */
37594     maxLength : Number.MAX_VALUE,
37595     /**
37596      * @cfg {String} minLengthText Error text to display if the minimum length validation fails (defaults to "The minimum length for this field is {minLength}")
37597      */
37598     minLengthText : "The minimum length for this field is {0}",
37599     /**
37600      * @cfg {String} maxLengthText Error text to display if the maximum length validation fails (defaults to "The maximum length for this field is {maxLength}")
37601      */
37602     maxLengthText : "The maximum length for this field is {0}",
37603     /**
37604      * @cfg {Boolean} selectOnFocus True to automatically select any existing field text when the field receives input focus (defaults to false)
37605      */
37606     selectOnFocus : false,
37607     /**
37608      * @cfg {String} blankText Error text to display if the allow blank validation fails (defaults to "This field is required")
37609      */
37610     blankText : "This field is required",
37611     /**
37612      * @cfg {Function} validator A custom validation function to be called during field validation (defaults to null).
37613      * If available, this function will be called only after the basic validators all return true, and will be passed the
37614      * current field value and expected to return boolean true if the value is valid or a string error message if invalid.
37615      */
37616     validator : null,
37617     /**
37618      * @cfg {RegExp} regex A JavaScript RegExp object to be tested against the field value during validation (defaults to null).
37619      * If available, this regex will be evaluated only after the basic validators all return true, and will be passed the
37620      * current field value.  If the test fails, the field will be marked invalid using {@link #regexText}.
37621      */
37622     regex : null,
37623     /**
37624      * @cfg {String} regexText The error text to display if {@link #regex} is used and the test fails during validation (defaults to "")
37625      */
37626     regexText : "",
37627     /**
37628      * @cfg {String} emptyText The default text to display in an empty field - placeholder... (defaults to null).
37629      */
37630     emptyText : null,
37631    
37632
37633     // private
37634     initEvents : function()
37635     {
37636         if (this.emptyText) {
37637             this.el.attr('placeholder', this.emptyText);
37638         }
37639         
37640         Roo.form.TextField.superclass.initEvents.call(this);
37641         if(this.validationEvent == 'keyup'){
37642             this.validationTask = new Roo.util.DelayedTask(this.validate, this);
37643             this.el.on('keyup', this.filterValidation, this);
37644         }
37645         else if(this.validationEvent !== false){
37646             this.el.on(this.validationEvent, this.validate, this, {buffer: this.validationDelay});
37647         }
37648         
37649         if(this.selectOnFocus){
37650             this.on("focus", this.preFocus, this);
37651             
37652         }
37653         if(this.maskRe || (this.vtype && this.disableKeyFilter !== true && (this.maskRe = Roo.form.VTypes[this.vtype+'Mask']))){
37654             this.el.on("keypress", this.filterKeys, this);
37655         }
37656         if(this.grow){
37657             this.el.on("keyup", this.onKeyUp,  this, {buffer:50});
37658             this.el.on("click", this.autoSize,  this);
37659         }
37660         if(this.el.is('input[type=password]') && Roo.isSafari){
37661             this.el.on('keydown', this.SafariOnKeyDown, this);
37662         }
37663     },
37664
37665     processValue : function(value){
37666         if(this.stripCharsRe){
37667             var newValue = value.replace(this.stripCharsRe, '');
37668             if(newValue !== value){
37669                 this.setRawValue(newValue);
37670                 return newValue;
37671             }
37672         }
37673         return value;
37674     },
37675
37676     filterValidation : function(e){
37677         if(!e.isNavKeyPress()){
37678             this.validationTask.delay(this.validationDelay);
37679         }
37680     },
37681
37682     // private
37683     onKeyUp : function(e){
37684         if(!e.isNavKeyPress()){
37685             this.autoSize();
37686         }
37687     },
37688
37689     /**
37690      * Resets the current field value to the originally-loaded value and clears any validation messages.
37691      *  
37692      */
37693     reset : function(){
37694         Roo.form.TextField.superclass.reset.call(this);
37695        
37696     },
37697
37698     
37699     // private
37700     preFocus : function(){
37701         
37702         if(this.selectOnFocus){
37703             this.el.dom.select();
37704         }
37705     },
37706
37707     
37708     // private
37709     filterKeys : function(e){
37710         var k = e.getKey();
37711         if(!Roo.isIE && (e.isNavKeyPress() || k == e.BACKSPACE || (k == e.DELETE && e.button == -1))){
37712             return;
37713         }
37714         var c = e.getCharCode(), cc = String.fromCharCode(c);
37715         if(Roo.isIE && (e.isSpecialKey() || !cc)){
37716             return;
37717         }
37718         if(!this.maskRe.test(cc)){
37719             e.stopEvent();
37720         }
37721     },
37722
37723     setValue : function(v){
37724         
37725         Roo.form.TextField.superclass.setValue.apply(this, arguments);
37726         
37727         this.autoSize();
37728     },
37729
37730     /**
37731      * Validates a value according to the field's validation rules and marks the field as invalid
37732      * if the validation fails
37733      * @param {Mixed} value The value to validate
37734      * @return {Boolean} True if the value is valid, else false
37735      */
37736     validateValue : function(value){
37737         if(value.length < 1)  { // if it's blank
37738              if(this.allowBlank){
37739                 this.clearInvalid();
37740                 return true;
37741              }else{
37742                 this.markInvalid(this.blankText);
37743                 return false;
37744              }
37745         }
37746         if(value.length < this.minLength){
37747             this.markInvalid(String.format(this.minLengthText, this.minLength));
37748             return false;
37749         }
37750         if(value.length > this.maxLength){
37751             this.markInvalid(String.format(this.maxLengthText, this.maxLength));
37752             return false;
37753         }
37754         if(this.vtype){
37755             var vt = Roo.form.VTypes;
37756             if(!vt[this.vtype](value, this)){
37757                 this.markInvalid(this.vtypeText || vt[this.vtype +'Text']);
37758                 return false;
37759             }
37760         }
37761         if(typeof this.validator == "function"){
37762             var msg = this.validator(value);
37763             if(msg !== true){
37764                 this.markInvalid(msg);
37765                 return false;
37766             }
37767         }
37768         if(this.regex && !this.regex.test(value)){
37769             this.markInvalid(this.regexText);
37770             return false;
37771         }
37772         return true;
37773     },
37774
37775     /**
37776      * Selects text in this field
37777      * @param {Number} start (optional) The index where the selection should start (defaults to 0)
37778      * @param {Number} end (optional) The index where the selection should end (defaults to the text length)
37779      */
37780     selectText : function(start, end){
37781         var v = this.getRawValue();
37782         if(v.length > 0){
37783             start = start === undefined ? 0 : start;
37784             end = end === undefined ? v.length : end;
37785             var d = this.el.dom;
37786             if(d.setSelectionRange){
37787                 d.setSelectionRange(start, end);
37788             }else if(d.createTextRange){
37789                 var range = d.createTextRange();
37790                 range.moveStart("character", start);
37791                 range.moveEnd("character", v.length-end);
37792                 range.select();
37793             }
37794         }
37795     },
37796
37797     /**
37798      * Automatically grows the field to accomodate the width of the text up to the maximum field width allowed.
37799      * This only takes effect if grow = true, and fires the autosize event.
37800      */
37801     autoSize : function(){
37802         if(!this.grow || !this.rendered){
37803             return;
37804         }
37805         if(!this.metrics){
37806             this.metrics = Roo.util.TextMetrics.createInstance(this.el);
37807         }
37808         var el = this.el;
37809         var v = el.dom.value;
37810         var d = document.createElement('div');
37811         d.appendChild(document.createTextNode(v));
37812         v = d.innerHTML;
37813         d = null;
37814         v += "&#160;";
37815         var w = Math.min(this.growMax, Math.max(this.metrics.getWidth(v) + /* add extra padding */ 10, this.growMin));
37816         this.el.setWidth(w);
37817         this.fireEvent("autosize", this, w);
37818     },
37819     
37820     // private
37821     SafariOnKeyDown : function(event)
37822     {
37823         // this is a workaround for a password hang bug on chrome/ webkit.
37824         
37825         var isSelectAll = false;
37826         
37827         if(this.el.dom.selectionEnd > 0){
37828             isSelectAll = (this.el.dom.selectionEnd - this.el.dom.selectionStart - this.getValue().length == 0) ? true : false;
37829         }
37830         if(((event.getKey() == 8 || event.getKey() == 46) && this.getValue().length ==1)){ // backspace and delete key
37831             event.preventDefault();
37832             this.setValue('');
37833             return;
37834         }
37835         
37836         if(isSelectAll){ // backspace and delete key
37837             
37838             event.preventDefault();
37839             // this is very hacky as keydown always get's upper case.
37840             //
37841             var cc = String.fromCharCode(event.getCharCode());
37842             this.setValue( event.shiftKey ?  cc : cc.toLowerCase());
37843             
37844         }
37845         
37846         
37847     }
37848 });/*
37849  * Based on:
37850  * Ext JS Library 1.1.1
37851  * Copyright(c) 2006-2007, Ext JS, LLC.
37852  *
37853  * Originally Released Under LGPL - original licence link has changed is not relivant.
37854  *
37855  * Fork - LGPL
37856  * <script type="text/javascript">
37857  */
37858  
37859 /**
37860  * @class Roo.form.Hidden
37861  * @extends Roo.form.TextField
37862  * Simple Hidden element used on forms 
37863  * 
37864  * usage: form.add(new Roo.form.HiddenField({ 'name' : 'test1' }));
37865  * 
37866  * @constructor
37867  * Creates a new Hidden form element.
37868  * @param {Object} config Configuration options
37869  */
37870
37871
37872
37873 // easy hidden field...
37874 Roo.form.Hidden = function(config){
37875     Roo.form.Hidden.superclass.constructor.call(this, config);
37876 };
37877   
37878 Roo.extend(Roo.form.Hidden, Roo.form.TextField, {
37879     fieldLabel:      '',
37880     inputType:      'hidden',
37881     width:          50,
37882     allowBlank:     true,
37883     labelSeparator: '',
37884     hidden:         true,
37885     itemCls :       'x-form-item-display-none'
37886
37887
37888 });
37889
37890
37891 /*
37892  * Based on:
37893  * Ext JS Library 1.1.1
37894  * Copyright(c) 2006-2007, Ext JS, LLC.
37895  *
37896  * Originally Released Under LGPL - original licence link has changed is not relivant.
37897  *
37898  * Fork - LGPL
37899  * <script type="text/javascript">
37900  */
37901  
37902 /**
37903  * @class Roo.form.TriggerField
37904  * @extends Roo.form.TextField
37905  * Provides a convenient wrapper for TextFields that adds a clickable trigger button (looks like a combobox by default).
37906  * The trigger has no default action, so you must assign a function to implement the trigger click handler by
37907  * overriding {@link #onTriggerClick}. You can create a TriggerField directly, as it renders exactly like a combobox
37908  * for which you can provide a custom implementation.  For example:
37909  * <pre><code>
37910 var trigger = new Roo.form.TriggerField();
37911 trigger.onTriggerClick = myTriggerFn;
37912 trigger.applyTo('my-field');
37913 </code></pre>
37914  *
37915  * However, in general you will most likely want to use TriggerField as the base class for a reusable component.
37916  * {@link Roo.form.DateField} and {@link Roo.form.ComboBox} are perfect examples of this.
37917  * @cfg {String} triggerClass An additional CSS class used to style the trigger button.  The trigger will always get the
37918  * class 'x-form-trigger' by default and triggerClass will be <b>appended</b> if specified.
37919  * @constructor
37920  * Create a new TriggerField.
37921  * @param {Object} config Configuration options (valid {@Roo.form.TextField} config options will also be applied
37922  * to the base TextField)
37923  */
37924 Roo.form.TriggerField = function(config){
37925     this.mimicing = false;
37926     Roo.form.TriggerField.superclass.constructor.call(this, config);
37927 };
37928
37929 Roo.extend(Roo.form.TriggerField, Roo.form.TextField,  {
37930     /**
37931      * @cfg {String} triggerClass A CSS class to apply to the trigger
37932      */
37933     /**
37934      * @cfg {String/Object} autoCreate A DomHelper element spec, or true for a default element spec (defaults to
37935      * {tag: "input", type: "text", size: "16", autocomplete: "off"})
37936      */
37937     defaultAutoCreate : {tag: "input", type: "text", size: "16", autocomplete: "off"},
37938     /**
37939      * @cfg {Boolean} hideTrigger True to hide the trigger element and display only the base text field (defaults to false)
37940      */
37941     hideTrigger:false,
37942
37943     /** @cfg {Boolean} grow @hide */
37944     /** @cfg {Number} growMin @hide */
37945     /** @cfg {Number} growMax @hide */
37946
37947     /**
37948      * @hide 
37949      * @method
37950      */
37951     autoSize: Roo.emptyFn,
37952     // private
37953     monitorTab : true,
37954     // private
37955     deferHeight : true,
37956
37957     
37958     actionMode : 'wrap',
37959     // private
37960     onResize : function(w, h){
37961         Roo.form.TriggerField.superclass.onResize.apply(this, arguments);
37962         if(typeof w == 'number'){
37963             var x = w - this.trigger.getWidth();
37964             this.el.setWidth(this.adjustWidth('input', x));
37965             this.trigger.setStyle('left', x+'px');
37966         }
37967     },
37968
37969     // private
37970     adjustSize : Roo.BoxComponent.prototype.adjustSize,
37971
37972     // private
37973     getResizeEl : function(){
37974         return this.wrap;
37975     },
37976
37977     // private
37978     getPositionEl : function(){
37979         return this.wrap;
37980     },
37981
37982     // private
37983     alignErrorIcon : function(){
37984         this.errorIcon.alignTo(this.wrap, 'tl-tr', [2, 0]);
37985     },
37986
37987     // private
37988     onRender : function(ct, position){
37989         Roo.form.TriggerField.superclass.onRender.call(this, ct, position);
37990         this.wrap = this.el.wrap({cls: "x-form-field-wrap"});
37991         this.trigger = this.wrap.createChild(this.triggerConfig ||
37992                 {tag: "img", src: Roo.BLANK_IMAGE_URL, cls: "x-form-trigger " + this.triggerClass});
37993         if(this.hideTrigger){
37994             this.trigger.setDisplayed(false);
37995         }
37996         this.initTrigger();
37997         if(!this.width){
37998             this.wrap.setWidth(this.el.getWidth()+this.trigger.getWidth());
37999         }
38000     },
38001
38002     // private
38003     initTrigger : function(){
38004         this.trigger.on("click", this.onTriggerClick, this, {preventDefault:true});
38005         this.trigger.addClassOnOver('x-form-trigger-over');
38006         this.trigger.addClassOnClick('x-form-trigger-click');
38007     },
38008
38009     // private
38010     onDestroy : function(){
38011         if(this.trigger){
38012             this.trigger.removeAllListeners();
38013             this.trigger.remove();
38014         }
38015         if(this.wrap){
38016             this.wrap.remove();
38017         }
38018         Roo.form.TriggerField.superclass.onDestroy.call(this);
38019     },
38020
38021     // private
38022     onFocus : function(){
38023         Roo.form.TriggerField.superclass.onFocus.call(this);
38024         if(!this.mimicing){
38025             this.wrap.addClass('x-trigger-wrap-focus');
38026             this.mimicing = true;
38027             Roo.get(Roo.isIE ? document.body : document).on("mousedown", this.mimicBlur, this);
38028             if(this.monitorTab){
38029                 this.el.on("keydown", this.checkTab, this);
38030             }
38031         }
38032     },
38033
38034     // private
38035     checkTab : function(e){
38036         if(e.getKey() == e.TAB){
38037             this.triggerBlur();
38038         }
38039     },
38040
38041     // private
38042     onBlur : function(){
38043         // do nothing
38044     },
38045
38046     // private
38047     mimicBlur : function(e, t){
38048         if(!this.wrap.contains(t) && this.validateBlur()){
38049             this.triggerBlur();
38050         }
38051     },
38052
38053     // private
38054     triggerBlur : function(){
38055         this.mimicing = false;
38056         Roo.get(Roo.isIE ? document.body : document).un("mousedown", this.mimicBlur);
38057         if(this.monitorTab){
38058             this.el.un("keydown", this.checkTab, this);
38059         }
38060         this.wrap.removeClass('x-trigger-wrap-focus');
38061         Roo.form.TriggerField.superclass.onBlur.call(this);
38062     },
38063
38064     // private
38065     // This should be overriden by any subclass that needs to check whether or not the field can be blurred.
38066     validateBlur : function(e, t){
38067         return true;
38068     },
38069
38070     // private
38071     onDisable : function(){
38072         Roo.form.TriggerField.superclass.onDisable.call(this);
38073         if(this.wrap){
38074             this.wrap.addClass('x-item-disabled');
38075         }
38076     },
38077
38078     // private
38079     onEnable : function(){
38080         Roo.form.TriggerField.superclass.onEnable.call(this);
38081         if(this.wrap){
38082             this.wrap.removeClass('x-item-disabled');
38083         }
38084     },
38085
38086     // private
38087     onShow : function(){
38088         var ae = this.getActionEl();
38089         
38090         if(ae){
38091             ae.dom.style.display = '';
38092             ae.dom.style.visibility = 'visible';
38093         }
38094     },
38095
38096     // private
38097     
38098     onHide : function(){
38099         var ae = this.getActionEl();
38100         ae.dom.style.display = 'none';
38101     },
38102
38103     /**
38104      * The function that should handle the trigger's click event.  This method does nothing by default until overridden
38105      * by an implementing function.
38106      * @method
38107      * @param {EventObject} e
38108      */
38109     onTriggerClick : Roo.emptyFn
38110 });
38111
38112 // TwinTriggerField is not a public class to be used directly.  It is meant as an abstract base class
38113 // to be extended by an implementing class.  For an example of implementing this class, see the custom
38114 // SearchField implementation here: http://extjs.com/deploy/ext/examples/form/custom.html
38115 Roo.form.TwinTriggerField = Roo.extend(Roo.form.TriggerField, {
38116     initComponent : function(){
38117         Roo.form.TwinTriggerField.superclass.initComponent.call(this);
38118
38119         this.triggerConfig = {
38120             tag:'span', cls:'x-form-twin-triggers', cn:[
38121             {tag: "img", src: Roo.BLANK_IMAGE_URL, cls: "x-form-trigger " + this.trigger1Class},
38122             {tag: "img", src: Roo.BLANK_IMAGE_URL, cls: "x-form-trigger " + this.trigger2Class}
38123         ]};
38124     },
38125
38126     getTrigger : function(index){
38127         return this.triggers[index];
38128     },
38129
38130     initTrigger : function(){
38131         var ts = this.trigger.select('.x-form-trigger', true);
38132         this.wrap.setStyle('overflow', 'hidden');
38133         var triggerField = this;
38134         ts.each(function(t, all, index){
38135             t.hide = function(){
38136                 var w = triggerField.wrap.getWidth();
38137                 this.dom.style.display = 'none';
38138                 triggerField.el.setWidth(w-triggerField.trigger.getWidth());
38139             };
38140             t.show = function(){
38141                 var w = triggerField.wrap.getWidth();
38142                 this.dom.style.display = '';
38143                 triggerField.el.setWidth(w-triggerField.trigger.getWidth());
38144             };
38145             var triggerIndex = 'Trigger'+(index+1);
38146
38147             if(this['hide'+triggerIndex]){
38148                 t.dom.style.display = 'none';
38149             }
38150             t.on("click", this['on'+triggerIndex+'Click'], this, {preventDefault:true});
38151             t.addClassOnOver('x-form-trigger-over');
38152             t.addClassOnClick('x-form-trigger-click');
38153         }, this);
38154         this.triggers = ts.elements;
38155     },
38156
38157     onTrigger1Click : Roo.emptyFn,
38158     onTrigger2Click : Roo.emptyFn
38159 });/*
38160  * Based on:
38161  * Ext JS Library 1.1.1
38162  * Copyright(c) 2006-2007, Ext JS, LLC.
38163  *
38164  * Originally Released Under LGPL - original licence link has changed is not relivant.
38165  *
38166  * Fork - LGPL
38167  * <script type="text/javascript">
38168  */
38169  
38170 /**
38171  * @class Roo.form.TextArea
38172  * @extends Roo.form.TextField
38173  * Multiline text field.  Can be used as a direct replacement for traditional textarea fields, plus adds
38174  * support for auto-sizing.
38175  * @constructor
38176  * Creates a new TextArea
38177  * @param {Object} config Configuration options
38178  */
38179 Roo.form.TextArea = function(config){
38180     Roo.form.TextArea.superclass.constructor.call(this, config);
38181     // these are provided exchanges for backwards compat
38182     // minHeight/maxHeight were replaced by growMin/growMax to be
38183     // compatible with TextField growing config values
38184     if(this.minHeight !== undefined){
38185         this.growMin = this.minHeight;
38186     }
38187     if(this.maxHeight !== undefined){
38188         this.growMax = this.maxHeight;
38189     }
38190 };
38191
38192 Roo.extend(Roo.form.TextArea, Roo.form.TextField,  {
38193     /**
38194      * @cfg {Number} growMin The minimum height to allow when grow = true (defaults to 60)
38195      */
38196     growMin : 60,
38197     /**
38198      * @cfg {Number} growMax The maximum height to allow when grow = true (defaults to 1000)
38199      */
38200     growMax: 1000,
38201     /**
38202      * @cfg {Boolean} preventScrollbars True to prevent scrollbars from appearing regardless of how much text is
38203      * in the field (equivalent to setting overflow: hidden, defaults to false)
38204      */
38205     preventScrollbars: false,
38206     /**
38207      * @cfg {String/Object} autoCreate A DomHelper element spec, or true for a default element spec (defaults to
38208      * {tag: "textarea", style: "width:300px;height:60px;", autocomplete: "off"})
38209      */
38210
38211     // private
38212     onRender : function(ct, position){
38213         if(!this.el){
38214             this.defaultAutoCreate = {
38215                 tag: "textarea",
38216                 style:"width:300px;height:60px;",
38217                 autocomplete: "off"
38218             };
38219         }
38220         Roo.form.TextArea.superclass.onRender.call(this, ct, position);
38221         if(this.grow){
38222             this.textSizeEl = Roo.DomHelper.append(document.body, {
38223                 tag: "pre", cls: "x-form-grow-sizer"
38224             });
38225             if(this.preventScrollbars){
38226                 this.el.setStyle("overflow", "hidden");
38227             }
38228             this.el.setHeight(this.growMin);
38229         }
38230     },
38231
38232     onDestroy : function(){
38233         if(this.textSizeEl){
38234             this.textSizeEl.parentNode.removeChild(this.textSizeEl);
38235         }
38236         Roo.form.TextArea.superclass.onDestroy.call(this);
38237     },
38238
38239     // private
38240     onKeyUp : function(e){
38241         if(!e.isNavKeyPress() || e.getKey() == e.ENTER){
38242             this.autoSize();
38243         }
38244     },
38245
38246     /**
38247      * Automatically grows the field to accomodate the height of the text up to the maximum field height allowed.
38248      * This only takes effect if grow = true, and fires the autosize event if the height changes.
38249      */
38250     autoSize : function(){
38251         if(!this.grow || !this.textSizeEl){
38252             return;
38253         }
38254         var el = this.el;
38255         var v = el.dom.value;
38256         var ts = this.textSizeEl;
38257
38258         ts.innerHTML = '';
38259         ts.appendChild(document.createTextNode(v));
38260         v = ts.innerHTML;
38261
38262         Roo.fly(ts).setWidth(this.el.getWidth());
38263         if(v.length < 1){
38264             v = "&#160;&#160;";
38265         }else{
38266             if(Roo.isIE){
38267                 v = v.replace(/\n/g, '<p>&#160;</p>');
38268             }
38269             v += "&#160;\n&#160;";
38270         }
38271         ts.innerHTML = v;
38272         var h = Math.min(this.growMax, Math.max(ts.offsetHeight, this.growMin));
38273         if(h != this.lastHeight){
38274             this.lastHeight = h;
38275             this.el.setHeight(h);
38276             this.fireEvent("autosize", this, h);
38277         }
38278     }
38279 });/*
38280  * Based on:
38281  * Ext JS Library 1.1.1
38282  * Copyright(c) 2006-2007, Ext JS, LLC.
38283  *
38284  * Originally Released Under LGPL - original licence link has changed is not relivant.
38285  *
38286  * Fork - LGPL
38287  * <script type="text/javascript">
38288  */
38289  
38290
38291 /**
38292  * @class Roo.form.NumberField
38293  * @extends Roo.form.TextField
38294  * Numeric text field that provides automatic keystroke filtering and numeric validation.
38295  * @constructor
38296  * Creates a new NumberField
38297  * @param {Object} config Configuration options
38298  */
38299 Roo.form.NumberField = function(config){
38300     Roo.form.NumberField.superclass.constructor.call(this, config);
38301 };
38302
38303 Roo.extend(Roo.form.NumberField, Roo.form.TextField,  {
38304     /**
38305      * @cfg {String} fieldClass The default CSS class for the field (defaults to "x-form-field x-form-num-field")
38306      */
38307     fieldClass: "x-form-field x-form-num-field",
38308     /**
38309      * @cfg {Boolean} allowDecimals False to disallow decimal values (defaults to true)
38310      */
38311     allowDecimals : true,
38312     /**
38313      * @cfg {String} decimalSeparator Character(s) to allow as the decimal separator (defaults to '.')
38314      */
38315     decimalSeparator : ".",
38316     /**
38317      * @cfg {Number} decimalPrecision The maximum precision to display after the decimal separator (defaults to 2)
38318      */
38319     decimalPrecision : 2,
38320     /**
38321      * @cfg {Boolean} allowNegative False to prevent entering a negative sign (defaults to true)
38322      */
38323     allowNegative : true,
38324     /**
38325      * @cfg {Number} minValue The minimum allowed value (defaults to Number.NEGATIVE_INFINITY)
38326      */
38327     minValue : Number.NEGATIVE_INFINITY,
38328     /**
38329      * @cfg {Number} maxValue The maximum allowed value (defaults to Number.MAX_VALUE)
38330      */
38331     maxValue : Number.MAX_VALUE,
38332     /**
38333      * @cfg {String} minText Error text to display if the minimum value validation fails (defaults to "The minimum value for this field is {minValue}")
38334      */
38335     minText : "The minimum value for this field is {0}",
38336     /**
38337      * @cfg {String} maxText Error text to display if the maximum value validation fails (defaults to "The maximum value for this field is {maxValue}")
38338      */
38339     maxText : "The maximum value for this field is {0}",
38340     /**
38341      * @cfg {String} nanText Error text to display if the value is not a valid number.  For example, this can happen
38342      * if a valid character like '.' or '-' is left in the field with no number (defaults to "{value} is not a valid number")
38343      */
38344     nanText : "{0} is not a valid number",
38345
38346     // private
38347     initEvents : function(){
38348         Roo.form.NumberField.superclass.initEvents.call(this);
38349         var allowed = "0123456789";
38350         if(this.allowDecimals){
38351             allowed += this.decimalSeparator;
38352         }
38353         if(this.allowNegative){
38354             allowed += "-";
38355         }
38356         this.stripCharsRe = new RegExp('[^'+allowed+']', 'gi');
38357         var keyPress = function(e){
38358             var k = e.getKey();
38359             if(!Roo.isIE && (e.isSpecialKey() || k == e.BACKSPACE || k == e.DELETE)){
38360                 return;
38361             }
38362             var c = e.getCharCode();
38363             if(allowed.indexOf(String.fromCharCode(c)) === -1){
38364                 e.stopEvent();
38365             }
38366         };
38367         this.el.on("keypress", keyPress, this);
38368     },
38369
38370     // private
38371     validateValue : function(value){
38372         if(!Roo.form.NumberField.superclass.validateValue.call(this, value)){
38373             return false;
38374         }
38375         if(value.length < 1){ // if it's blank and textfield didn't flag it then it's valid
38376              return true;
38377         }
38378         var num = this.parseValue(value);
38379         if(isNaN(num)){
38380             this.markInvalid(String.format(this.nanText, value));
38381             return false;
38382         }
38383         if(num < this.minValue){
38384             this.markInvalid(String.format(this.minText, this.minValue));
38385             return false;
38386         }
38387         if(num > this.maxValue){
38388             this.markInvalid(String.format(this.maxText, this.maxValue));
38389             return false;
38390         }
38391         return true;
38392     },
38393
38394     getValue : function(){
38395         return this.fixPrecision(this.parseValue(Roo.form.NumberField.superclass.getValue.call(this)));
38396     },
38397
38398     // private
38399     parseValue : function(value){
38400         value = parseFloat(String(value).replace(this.decimalSeparator, "."));
38401         return isNaN(value) ? '' : value;
38402     },
38403
38404     // private
38405     fixPrecision : function(value){
38406         var nan = isNaN(value);
38407         if(!this.allowDecimals || this.decimalPrecision == -1 || nan || !value){
38408             return nan ? '' : value;
38409         }
38410         return parseFloat(value).toFixed(this.decimalPrecision);
38411     },
38412
38413     setValue : function(v){
38414         v = this.fixPrecision(v);
38415         Roo.form.NumberField.superclass.setValue.call(this, String(v).replace(".", this.decimalSeparator));
38416     },
38417
38418     // private
38419     decimalPrecisionFcn : function(v){
38420         return Math.floor(v);
38421     },
38422
38423     beforeBlur : function(){
38424         var v = this.parseValue(this.getRawValue());
38425         if(v){
38426             this.setValue(v);
38427         }
38428     }
38429 });/*
38430  * Based on:
38431  * Ext JS Library 1.1.1
38432  * Copyright(c) 2006-2007, Ext JS, LLC.
38433  *
38434  * Originally Released Under LGPL - original licence link has changed is not relivant.
38435  *
38436  * Fork - LGPL
38437  * <script type="text/javascript">
38438  */
38439  
38440 /**
38441  * @class Roo.form.DateField
38442  * @extends Roo.form.TriggerField
38443  * Provides a date input field with a {@link Roo.DatePicker} dropdown and automatic date validation.
38444 * @constructor
38445 * Create a new DateField
38446 * @param {Object} config
38447  */
38448 Roo.form.DateField = function(config){
38449     Roo.form.DateField.superclass.constructor.call(this, config);
38450     
38451       this.addEvents({
38452          
38453         /**
38454          * @event select
38455          * Fires when a date is selected
38456              * @param {Roo.form.DateField} combo This combo box
38457              * @param {Date} date The date selected
38458              */
38459         'select' : true
38460          
38461     });
38462     
38463     
38464     if(typeof this.minValue == "string") this.minValue = this.parseDate(this.minValue);
38465     if(typeof this.maxValue == "string") this.maxValue = this.parseDate(this.maxValue);
38466     this.ddMatch = null;
38467     if(this.disabledDates){
38468         var dd = this.disabledDates;
38469         var re = "(?:";
38470         for(var i = 0; i < dd.length; i++){
38471             re += dd[i];
38472             if(i != dd.length-1) re += "|";
38473         }
38474         this.ddMatch = new RegExp(re + ")");
38475     }
38476 };
38477
38478 Roo.extend(Roo.form.DateField, Roo.form.TriggerField,  {
38479     /**
38480      * @cfg {String} format
38481      * The default date format string which can be overriden for localization support.  The format must be
38482      * valid according to {@link Date#parseDate} (defaults to 'm/d/y').
38483      */
38484     format : "m/d/y",
38485     /**
38486      * @cfg {String} altFormats
38487      * Multiple date formats separated by "|" to try when parsing a user input value and it doesn't match the defined
38488      * format (defaults to 'm/d/Y|m-d-y|m-d-Y|m/d|m-d|d').
38489      */
38490     altFormats : "m/d/Y|m-d-y|m-d-Y|m/d|m-d|md|mdy|mdY|d",
38491     /**
38492      * @cfg {Array} disabledDays
38493      * An array of days to disable, 0 based. For example, [0, 6] disables Sunday and Saturday (defaults to null).
38494      */
38495     disabledDays : null,
38496     /**
38497      * @cfg {String} disabledDaysText
38498      * The tooltip to display when the date falls on a disabled day (defaults to 'Disabled')
38499      */
38500     disabledDaysText : "Disabled",
38501     /**
38502      * @cfg {Array} disabledDates
38503      * An array of "dates" to disable, as strings. These strings will be used to build a dynamic regular
38504      * expression so they are very powerful. Some examples:
38505      * <ul>
38506      * <li>["03/08/2003", "09/16/2003"] would disable those exact dates</li>
38507      * <li>["03/08", "09/16"] would disable those days for every year</li>
38508      * <li>["^03/08"] would only match the beginning (useful if you are using short years)</li>
38509      * <li>["03/../2006"] would disable every day in March 2006</li>
38510      * <li>["^03"] would disable every day in every March</li>
38511      * </ul>
38512      * In order to support regular expressions, if you are using a date format that has "." in it, you will have to
38513      * escape the dot when restricting dates. For example: ["03\\.08\\.03"].
38514      */
38515     disabledDates : null,
38516     /**
38517      * @cfg {String} disabledDatesText
38518      * The tooltip text to display when the date falls on a disabled date (defaults to 'Disabled')
38519      */
38520     disabledDatesText : "Disabled",
38521     /**
38522      * @cfg {Date/String} minValue
38523      * The minimum allowed date. Can be either a Javascript date object or a string date in a
38524      * valid format (defaults to null).
38525      */
38526     minValue : null,
38527     /**
38528      * @cfg {Date/String} maxValue
38529      * The maximum allowed date. Can be either a Javascript date object or a string date in a
38530      * valid format (defaults to null).
38531      */
38532     maxValue : null,
38533     /**
38534      * @cfg {String} minText
38535      * The error text to display when the date in the cell is before minValue (defaults to
38536      * 'The date in this field must be after {minValue}').
38537      */
38538     minText : "The date in this field must be equal to or after {0}",
38539     /**
38540      * @cfg {String} maxText
38541      * The error text to display when the date in the cell is after maxValue (defaults to
38542      * 'The date in this field must be before {maxValue}').
38543      */
38544     maxText : "The date in this field must be equal to or before {0}",
38545     /**
38546      * @cfg {String} invalidText
38547      * The error text to display when the date in the field is invalid (defaults to
38548      * '{value} is not a valid date - it must be in the format {format}').
38549      */
38550     invalidText : "{0} is not a valid date - it must be in the format {1}",
38551     /**
38552      * @cfg {String} triggerClass
38553      * An additional CSS class used to style the trigger button.  The trigger will always get the
38554      * class 'x-form-trigger' and triggerClass will be <b>appended</b> if specified (defaults to 'x-form-date-trigger'
38555      * which displays a calendar icon).
38556      */
38557     triggerClass : 'x-form-date-trigger',
38558     
38559
38560     /**
38561      * @cfg {Boolean} useIso
38562      * if enabled, then the date field will use a hidden field to store the 
38563      * real value as iso formated date. default (false)
38564      */ 
38565     useIso : false,
38566     /**
38567      * @cfg {String/Object} autoCreate
38568      * A DomHelper element spec, or true for a default element spec (defaults to
38569      * {tag: "input", type: "text", size: "10", autocomplete: "off"})
38570      */ 
38571     // private
38572     defaultAutoCreate : {tag: "input", type: "text", size: "10", autocomplete: "off"},
38573     
38574     // private
38575     hiddenField: false,
38576     
38577     onRender : function(ct, position)
38578     {
38579         Roo.form.DateField.superclass.onRender.call(this, ct, position);
38580         if (this.useIso) {
38581             //this.el.dom.removeAttribute('name'); 
38582             Roo.log("Changing name?");
38583             this.el.dom.setAttribute('name', this.name + '____hidden___' ); 
38584             this.hiddenField = this.el.insertSibling({ tag:'input', type:'hidden', name: this.name },
38585                     'before', true);
38586             this.hiddenField.value = this.value ? this.formatDate(this.value, 'Y-m-d') : '';
38587             // prevent input submission
38588             this.hiddenName = this.name;
38589         }
38590             
38591             
38592     },
38593     
38594     // private
38595     validateValue : function(value)
38596     {
38597         value = this.formatDate(value);
38598         if(!Roo.form.DateField.superclass.validateValue.call(this, value)){
38599             Roo.log('super failed');
38600             return false;
38601         }
38602         if(value.length < 1){ // if it's blank and textfield didn't flag it then it's valid
38603              return true;
38604         }
38605         var svalue = value;
38606         value = this.parseDate(value);
38607         if(!value){
38608             Roo.log('parse date failed' + svalue);
38609             this.markInvalid(String.format(this.invalidText, svalue, this.format));
38610             return false;
38611         }
38612         var time = value.getTime();
38613         if(this.minValue && time < this.minValue.getTime()){
38614             this.markInvalid(String.format(this.minText, this.formatDate(this.minValue)));
38615             return false;
38616         }
38617         if(this.maxValue && time > this.maxValue.getTime()){
38618             this.markInvalid(String.format(this.maxText, this.formatDate(this.maxValue)));
38619             return false;
38620         }
38621         if(this.disabledDays){
38622             var day = value.getDay();
38623             for(var i = 0; i < this.disabledDays.length; i++) {
38624                 if(day === this.disabledDays[i]){
38625                     this.markInvalid(this.disabledDaysText);
38626                     return false;
38627                 }
38628             }
38629         }
38630         var fvalue = this.formatDate(value);
38631         if(this.ddMatch && this.ddMatch.test(fvalue)){
38632             this.markInvalid(String.format(this.disabledDatesText, fvalue));
38633             return false;
38634         }
38635         return true;
38636     },
38637
38638     // private
38639     // Provides logic to override the default TriggerField.validateBlur which just returns true
38640     validateBlur : function(){
38641         return !this.menu || !this.menu.isVisible();
38642     },
38643     
38644     getName: function()
38645     {
38646         // returns hidden if it's set..
38647         if (!this.rendered) {return ''};
38648         return !this.hiddenName && this.el.dom.name  ? this.el.dom.name : (this.hiddenName || '');
38649         
38650     },
38651
38652     /**
38653      * Returns the current date value of the date field.
38654      * @return {Date} The date value
38655      */
38656     getValue : function(){
38657         
38658         return  this.hiddenField ?
38659                 this.hiddenField.value :
38660                 this.parseDate(Roo.form.DateField.superclass.getValue.call(this)) || "";
38661     },
38662
38663     /**
38664      * Sets the value of the date field.  You can pass a date object or any string that can be parsed into a valid
38665      * date, using DateField.format as the date format, according to the same rules as {@link Date#parseDate}
38666      * (the default format used is "m/d/y").
38667      * <br />Usage:
38668      * <pre><code>
38669 //All of these calls set the same date value (May 4, 2006)
38670
38671 //Pass a date object:
38672 var dt = new Date('5/4/06');
38673 dateField.setValue(dt);
38674
38675 //Pass a date string (default format):
38676 dateField.setValue('5/4/06');
38677
38678 //Pass a date string (custom format):
38679 dateField.format = 'Y-m-d';
38680 dateField.setValue('2006-5-4');
38681 </code></pre>
38682      * @param {String/Date} date The date or valid date string
38683      */
38684     setValue : function(date){
38685         if (this.hiddenField) {
38686             this.hiddenField.value = this.formatDate(this.parseDate(date), 'Y-m-d');
38687         }
38688         Roo.form.DateField.superclass.setValue.call(this, this.formatDate(this.parseDate(date)));
38689         // make sure the value field is always stored as a date..
38690         this.value = this.parseDate(date);
38691         
38692         
38693     },
38694
38695     // private
38696     parseDate : function(value){
38697         if(!value || value instanceof Date){
38698             return value;
38699         }
38700         var v = Date.parseDate(value, this.format);
38701          if (!v && this.useIso) {
38702             v = Date.parseDate(value, 'Y-m-d');
38703         }
38704         if(!v && this.altFormats){
38705             if(!this.altFormatsArray){
38706                 this.altFormatsArray = this.altFormats.split("|");
38707             }
38708             for(var i = 0, len = this.altFormatsArray.length; i < len && !v; i++){
38709                 v = Date.parseDate(value, this.altFormatsArray[i]);
38710             }
38711         }
38712         return v;
38713     },
38714
38715     // private
38716     formatDate : function(date, fmt){
38717         return (!date || !(date instanceof Date)) ?
38718                date : date.dateFormat(fmt || this.format);
38719     },
38720
38721     // private
38722     menuListeners : {
38723         select: function(m, d){
38724             
38725             this.setValue(d);
38726             this.fireEvent('select', this, d);
38727         },
38728         show : function(){ // retain focus styling
38729             this.onFocus();
38730         },
38731         hide : function(){
38732             this.focus.defer(10, this);
38733             var ml = this.menuListeners;
38734             this.menu.un("select", ml.select,  this);
38735             this.menu.un("show", ml.show,  this);
38736             this.menu.un("hide", ml.hide,  this);
38737         }
38738     },
38739
38740     // private
38741     // Implements the default empty TriggerField.onTriggerClick function to display the DatePicker
38742     onTriggerClick : function(){
38743         if(this.disabled){
38744             return;
38745         }
38746         if(this.menu == null){
38747             this.menu = new Roo.menu.DateMenu();
38748         }
38749         Roo.apply(this.menu.picker,  {
38750             showClear: this.allowBlank,
38751             minDate : this.minValue,
38752             maxDate : this.maxValue,
38753             disabledDatesRE : this.ddMatch,
38754             disabledDatesText : this.disabledDatesText,
38755             disabledDays : this.disabledDays,
38756             disabledDaysText : this.disabledDaysText,
38757             format : this.useIso ? 'Y-m-d' : this.format,
38758             minText : String.format(this.minText, this.formatDate(this.minValue)),
38759             maxText : String.format(this.maxText, this.formatDate(this.maxValue))
38760         });
38761         this.menu.on(Roo.apply({}, this.menuListeners, {
38762             scope:this
38763         }));
38764         this.menu.picker.setValue(this.getValue() || new Date());
38765         this.menu.show(this.el, "tl-bl?");
38766     },
38767
38768     beforeBlur : function(){
38769         var v = this.parseDate(this.getRawValue());
38770         if(v){
38771             this.setValue(v);
38772         }
38773     },
38774
38775     /*@
38776      * overide
38777      * 
38778      */
38779     isDirty : function() {
38780         if(this.disabled) {
38781             return false;
38782         }
38783         
38784         if(typeof(this.startValue) === 'undefined'){
38785             return false;
38786         }
38787         
38788         return String(this.getValue()) !== String(this.startValue);
38789         
38790     }
38791 });/*
38792  * Based on:
38793  * Ext JS Library 1.1.1
38794  * Copyright(c) 2006-2007, Ext JS, LLC.
38795  *
38796  * Originally Released Under LGPL - original licence link has changed is not relivant.
38797  *
38798  * Fork - LGPL
38799  * <script type="text/javascript">
38800  */
38801  
38802 /**
38803  * @class Roo.form.MonthField
38804  * @extends Roo.form.TriggerField
38805  * Provides a date input field with a {@link Roo.DatePicker} dropdown and automatic date validation.
38806 * @constructor
38807 * Create a new MonthField
38808 * @param {Object} config
38809  */
38810 Roo.form.MonthField = function(config){
38811     
38812     Roo.form.MonthField.superclass.constructor.call(this, config);
38813     
38814       this.addEvents({
38815          
38816         /**
38817          * @event select
38818          * Fires when a date is selected
38819              * @param {Roo.form.MonthFieeld} combo This combo box
38820              * @param {Date} date The date selected
38821              */
38822         'select' : true
38823          
38824     });
38825     
38826     
38827     if(typeof this.minValue == "string") this.minValue = this.parseDate(this.minValue);
38828     if(typeof this.maxValue == "string") this.maxValue = this.parseDate(this.maxValue);
38829     this.ddMatch = null;
38830     if(this.disabledDates){
38831         var dd = this.disabledDates;
38832         var re = "(?:";
38833         for(var i = 0; i < dd.length; i++){
38834             re += dd[i];
38835             if(i != dd.length-1) re += "|";
38836         }
38837         this.ddMatch = new RegExp(re + ")");
38838     }
38839 };
38840
38841 Roo.extend(Roo.form.MonthField, Roo.form.TriggerField,  {
38842     /**
38843      * @cfg {String} format
38844      * The default date format string which can be overriden for localization support.  The format must be
38845      * valid according to {@link Date#parseDate} (defaults to 'm/d/y').
38846      */
38847     format : "M Y",
38848     /**
38849      * @cfg {String} altFormats
38850      * Multiple date formats separated by "|" to try when parsing a user input value and it doesn't match the defined
38851      * format (defaults to 'm/d/Y|m-d-y|m-d-Y|m/d|m-d|d').
38852      */
38853     altFormats : "M Y|m/Y|m-y|m-Y|my|mY",
38854     /**
38855      * @cfg {Array} disabledDays
38856      * An array of days to disable, 0 based. For example, [0, 6] disables Sunday and Saturday (defaults to null).
38857      */
38858     disabledDays : [0,1,2,3,4,5,6],
38859     /**
38860      * @cfg {String} disabledDaysText
38861      * The tooltip to display when the date falls on a disabled day (defaults to 'Disabled')
38862      */
38863     disabledDaysText : "Disabled",
38864     /**
38865      * @cfg {Array} disabledDates
38866      * An array of "dates" to disable, as strings. These strings will be used to build a dynamic regular
38867      * expression so they are very powerful. Some examples:
38868      * <ul>
38869      * <li>["03/08/2003", "09/16/2003"] would disable those exact dates</li>
38870      * <li>["03/08", "09/16"] would disable those days for every year</li>
38871      * <li>["^03/08"] would only match the beginning (useful if you are using short years)</li>
38872      * <li>["03/../2006"] would disable every day in March 2006</li>
38873      * <li>["^03"] would disable every day in every March</li>
38874      * </ul>
38875      * In order to support regular expressions, if you are using a date format that has "." in it, you will have to
38876      * escape the dot when restricting dates. For example: ["03\\.08\\.03"].
38877      */
38878     disabledDates : null,
38879     /**
38880      * @cfg {String} disabledDatesText
38881      * The tooltip text to display when the date falls on a disabled date (defaults to 'Disabled')
38882      */
38883     disabledDatesText : "Disabled",
38884     /**
38885      * @cfg {Date/String} minValue
38886      * The minimum allowed date. Can be either a Javascript date object or a string date in a
38887      * valid format (defaults to null).
38888      */
38889     minValue : null,
38890     /**
38891      * @cfg {Date/String} maxValue
38892      * The maximum allowed date. Can be either a Javascript date object or a string date in a
38893      * valid format (defaults to null).
38894      */
38895     maxValue : null,
38896     /**
38897      * @cfg {String} minText
38898      * The error text to display when the date in the cell is before minValue (defaults to
38899      * 'The date in this field must be after {minValue}').
38900      */
38901     minText : "The date in this field must be equal to or after {0}",
38902     /**
38903      * @cfg {String} maxTextf
38904      * The error text to display when the date in the cell is after maxValue (defaults to
38905      * 'The date in this field must be before {maxValue}').
38906      */
38907     maxText : "The date in this field must be equal to or before {0}",
38908     /**
38909      * @cfg {String} invalidText
38910      * The error text to display when the date in the field is invalid (defaults to
38911      * '{value} is not a valid date - it must be in the format {format}').
38912      */
38913     invalidText : "{0} is not a valid date - it must be in the format {1}",
38914     /**
38915      * @cfg {String} triggerClass
38916      * An additional CSS class used to style the trigger button.  The trigger will always get the
38917      * class 'x-form-trigger' and triggerClass will be <b>appended</b> if specified (defaults to 'x-form-date-trigger'
38918      * which displays a calendar icon).
38919      */
38920     triggerClass : 'x-form-date-trigger',
38921     
38922
38923     /**
38924      * @cfg {Boolean} useIso
38925      * if enabled, then the date field will use a hidden field to store the 
38926      * real value as iso formated date. default (true)
38927      */ 
38928     useIso : true,
38929     /**
38930      * @cfg {String/Object} autoCreate
38931      * A DomHelper element spec, or true for a default element spec (defaults to
38932      * {tag: "input", type: "text", size: "10", autocomplete: "off"})
38933      */ 
38934     // private
38935     defaultAutoCreate : {tag: "input", type: "text", size: "10", autocomplete: "off"},
38936     
38937     // private
38938     hiddenField: false,
38939     
38940     hideMonthPicker : false,
38941     
38942     onRender : function(ct, position)
38943     {
38944         Roo.form.MonthField.superclass.onRender.call(this, ct, position);
38945         if (this.useIso) {
38946             this.el.dom.removeAttribute('name'); 
38947             this.hiddenField = this.el.insertSibling({ tag:'input', type:'hidden', name: this.name },
38948                     'before', true);
38949             this.hiddenField.value = this.value ? this.formatDate(this.value, 'Y-m-d') : '';
38950             // prevent input submission
38951             this.hiddenName = this.name;
38952         }
38953             
38954             
38955     },
38956     
38957     // private
38958     validateValue : function(value)
38959     {
38960         value = this.formatDate(value);
38961         if(!Roo.form.MonthField.superclass.validateValue.call(this, value)){
38962             return false;
38963         }
38964         if(value.length < 1){ // if it's blank and textfield didn't flag it then it's valid
38965              return true;
38966         }
38967         var svalue = value;
38968         value = this.parseDate(value);
38969         if(!value){
38970             this.markInvalid(String.format(this.invalidText, svalue, this.format));
38971             return false;
38972         }
38973         var time = value.getTime();
38974         if(this.minValue && time < this.minValue.getTime()){
38975             this.markInvalid(String.format(this.minText, this.formatDate(this.minValue)));
38976             return false;
38977         }
38978         if(this.maxValue && time > this.maxValue.getTime()){
38979             this.markInvalid(String.format(this.maxText, this.formatDate(this.maxValue)));
38980             return false;
38981         }
38982         /*if(this.disabledDays){
38983             var day = value.getDay();
38984             for(var i = 0; i < this.disabledDays.length; i++) {
38985                 if(day === this.disabledDays[i]){
38986                     this.markInvalid(this.disabledDaysText);
38987                     return false;
38988                 }
38989             }
38990         }
38991         */
38992         var fvalue = this.formatDate(value);
38993         /*if(this.ddMatch && this.ddMatch.test(fvalue)){
38994             this.markInvalid(String.format(this.disabledDatesText, fvalue));
38995             return false;
38996         }
38997         */
38998         return true;
38999     },
39000
39001     // private
39002     // Provides logic to override the default TriggerField.validateBlur which just returns true
39003     validateBlur : function(){
39004         return !this.menu || !this.menu.isVisible();
39005     },
39006
39007     /**
39008      * Returns the current date value of the date field.
39009      * @return {Date} The date value
39010      */
39011     getValue : function(){
39012         
39013         
39014         
39015         return  this.hiddenField ?
39016                 this.hiddenField.value :
39017                 this.parseDate(Roo.form.MonthField.superclass.getValue.call(this)) || "";
39018     },
39019
39020     /**
39021      * Sets the value of the date field.  You can pass a date object or any string that can be parsed into a valid
39022      * date, using MonthField.format as the date format, according to the same rules as {@link Date#parseDate}
39023      * (the default format used is "m/d/y").
39024      * <br />Usage:
39025      * <pre><code>
39026 //All of these calls set the same date value (May 4, 2006)
39027
39028 //Pass a date object:
39029 var dt = new Date('5/4/06');
39030 monthField.setValue(dt);
39031
39032 //Pass a date string (default format):
39033 monthField.setValue('5/4/06');
39034
39035 //Pass a date string (custom format):
39036 monthField.format = 'Y-m-d';
39037 monthField.setValue('2006-5-4');
39038 </code></pre>
39039      * @param {String/Date} date The date or valid date string
39040      */
39041     setValue : function(date){
39042         Roo.log('month setValue' + date);
39043         // can only be first of month..
39044         
39045         var val = this.parseDate(date);
39046         
39047         if (this.hiddenField) {
39048             this.hiddenField.value = this.formatDate(this.parseDate(date), 'Y-m-d');
39049         }
39050         Roo.form.MonthField.superclass.setValue.call(this, this.formatDate(this.parseDate(date)));
39051         this.value = this.parseDate(date);
39052     },
39053
39054     // private
39055     parseDate : function(value){
39056         if(!value || value instanceof Date){
39057             value = value ? Date.parseDate(value.format('Y-m') + '-01', 'Y-m-d') : null;
39058             return value;
39059         }
39060         var v = Date.parseDate(value, this.format);
39061         if (!v && this.useIso) {
39062             v = Date.parseDate(value, 'Y-m-d');
39063         }
39064         if (v) {
39065             // 
39066             v = Date.parseDate(v.format('Y-m') +'-01', 'Y-m-d');
39067         }
39068         
39069         
39070         if(!v && this.altFormats){
39071             if(!this.altFormatsArray){
39072                 this.altFormatsArray = this.altFormats.split("|");
39073             }
39074             for(var i = 0, len = this.altFormatsArray.length; i < len && !v; i++){
39075                 v = Date.parseDate(value, this.altFormatsArray[i]);
39076             }
39077         }
39078         return v;
39079     },
39080
39081     // private
39082     formatDate : function(date, fmt){
39083         return (!date || !(date instanceof Date)) ?
39084                date : date.dateFormat(fmt || this.format);
39085     },
39086
39087     // private
39088     menuListeners : {
39089         select: function(m, d){
39090             this.setValue(d);
39091             this.fireEvent('select', this, d);
39092         },
39093         show : function(){ // retain focus styling
39094             this.onFocus();
39095         },
39096         hide : function(){
39097             this.focus.defer(10, this);
39098             var ml = this.menuListeners;
39099             this.menu.un("select", ml.select,  this);
39100             this.menu.un("show", ml.show,  this);
39101             this.menu.un("hide", ml.hide,  this);
39102         }
39103     },
39104     // private
39105     // Implements the default empty TriggerField.onTriggerClick function to display the DatePicker
39106     onTriggerClick : function(){
39107         if(this.disabled){
39108             return;
39109         }
39110         if(this.menu == null){
39111             this.menu = new Roo.menu.DateMenu();
39112            
39113         }
39114         
39115         Roo.apply(this.menu.picker,  {
39116             
39117             showClear: this.allowBlank,
39118             minDate : this.minValue,
39119             maxDate : this.maxValue,
39120             disabledDatesRE : this.ddMatch,
39121             disabledDatesText : this.disabledDatesText,
39122             
39123             format : this.useIso ? 'Y-m-d' : this.format,
39124             minText : String.format(this.minText, this.formatDate(this.minValue)),
39125             maxText : String.format(this.maxText, this.formatDate(this.maxValue))
39126             
39127         });
39128          this.menu.on(Roo.apply({}, this.menuListeners, {
39129             scope:this
39130         }));
39131        
39132         
39133         var m = this.menu;
39134         var p = m.picker;
39135         
39136         // hide month picker get's called when we called by 'before hide';
39137         
39138         var ignorehide = true;
39139         p.hideMonthPicker  = function(disableAnim){
39140             if (ignorehide) {
39141                 return;
39142             }
39143              if(this.monthPicker){
39144                 Roo.log("hideMonthPicker called");
39145                 if(disableAnim === true){
39146                     this.monthPicker.hide();
39147                 }else{
39148                     this.monthPicker.slideOut('t', {duration:.2});
39149                     p.setValue(new Date(m.picker.mpSelYear, m.picker.mpSelMonth, 1));
39150                     p.fireEvent("select", this, this.value);
39151                     m.hide();
39152                 }
39153             }
39154         }
39155         
39156         Roo.log('picker set value');
39157         Roo.log(this.getValue());
39158         p.setValue(this.getValue() ? this.parseDate(this.getValue()) : new Date());
39159         m.show(this.el, 'tl-bl?');
39160         ignorehide  = false;
39161         // this will trigger hideMonthPicker..
39162         
39163         
39164         // hidden the day picker
39165         Roo.select('.x-date-picker table', true).first().dom.style.visibility = "hidden";
39166         
39167         
39168         
39169       
39170         
39171         p.showMonthPicker.defer(100, p);
39172     
39173         
39174        
39175     },
39176
39177     beforeBlur : function(){
39178         var v = this.parseDate(this.getRawValue());
39179         if(v){
39180             this.setValue(v);
39181         }
39182     }
39183
39184     /** @cfg {Boolean} grow @hide */
39185     /** @cfg {Number} growMin @hide */
39186     /** @cfg {Number} growMax @hide */
39187     /**
39188      * @hide
39189      * @method autoSize
39190      */
39191 });/*
39192  * Based on:
39193  * Ext JS Library 1.1.1
39194  * Copyright(c) 2006-2007, Ext JS, LLC.
39195  *
39196  * Originally Released Under LGPL - original licence link has changed is not relivant.
39197  *
39198  * Fork - LGPL
39199  * <script type="text/javascript">
39200  */
39201  
39202
39203 /**
39204  * @class Roo.form.ComboBox
39205  * @extends Roo.form.TriggerField
39206  * A combobox control with support for autocomplete, remote-loading, paging and many other features.
39207  * @constructor
39208  * Create a new ComboBox.
39209  * @param {Object} config Configuration options
39210  */
39211 Roo.form.ComboBox = function(config){
39212     Roo.form.ComboBox.superclass.constructor.call(this, config);
39213     this.addEvents({
39214         /**
39215          * @event expand
39216          * Fires when the dropdown list is expanded
39217              * @param {Roo.form.ComboBox} combo This combo box
39218              */
39219         'expand' : true,
39220         /**
39221          * @event collapse
39222          * Fires when the dropdown list is collapsed
39223              * @param {Roo.form.ComboBox} combo This combo box
39224              */
39225         'collapse' : true,
39226         /**
39227          * @event beforeselect
39228          * Fires before a list item is selected. Return false to cancel the selection.
39229              * @param {Roo.form.ComboBox} combo This combo box
39230              * @param {Roo.data.Record} record The data record returned from the underlying store
39231              * @param {Number} index The index of the selected item in the dropdown list
39232              */
39233         'beforeselect' : true,
39234         /**
39235          * @event select
39236          * Fires when a list item is selected
39237              * @param {Roo.form.ComboBox} combo This combo box
39238              * @param {Roo.data.Record} record The data record returned from the underlying store (or false on clear)
39239              * @param {Number} index The index of the selected item in the dropdown list
39240              */
39241         'select' : true,
39242         /**
39243          * @event beforequery
39244          * Fires before all queries are processed. Return false to cancel the query or set cancel to true.
39245          * The event object passed has these properties:
39246              * @param {Roo.form.ComboBox} combo This combo box
39247              * @param {String} query The query
39248              * @param {Boolean} forceAll true to force "all" query
39249              * @param {Boolean} cancel true to cancel the query
39250              * @param {Object} e The query event object
39251              */
39252         'beforequery': true,
39253          /**
39254          * @event add
39255          * Fires when the 'add' icon is pressed (add a listener to enable add button)
39256              * @param {Roo.form.ComboBox} combo This combo box
39257              */
39258         'add' : true,
39259         /**
39260          * @event edit
39261          * Fires when the 'edit' icon is pressed (add a listener to enable add button)
39262              * @param {Roo.form.ComboBox} combo This combo box
39263              * @param {Roo.data.Record|false} record The data record returned from the underlying store (or false on nothing selected)
39264              */
39265         'edit' : true
39266         
39267         
39268     });
39269     if(this.transform){
39270         this.allowDomMove = false;
39271         var s = Roo.getDom(this.transform);
39272         if(!this.hiddenName){
39273             this.hiddenName = s.name;
39274         }
39275         if(!this.store){
39276             this.mode = 'local';
39277             var d = [], opts = s.options;
39278             for(var i = 0, len = opts.length;i < len; i++){
39279                 var o = opts[i];
39280                 var value = (Roo.isIE ? o.getAttributeNode('value').specified : o.hasAttribute('value')) ? o.value : o.text;
39281                 if(o.selected) {
39282                     this.value = value;
39283                 }
39284                 d.push([value, o.text]);
39285             }
39286             this.store = new Roo.data.SimpleStore({
39287                 'id': 0,
39288                 fields: ['value', 'text'],
39289                 data : d
39290             });
39291             this.valueField = 'value';
39292             this.displayField = 'text';
39293         }
39294         s.name = Roo.id(); // wipe out the name in case somewhere else they have a reference
39295         if(!this.lazyRender){
39296             this.target = true;
39297             this.el = Roo.DomHelper.insertBefore(s, this.autoCreate || this.defaultAutoCreate);
39298             s.parentNode.removeChild(s); // remove it
39299             this.render(this.el.parentNode);
39300         }else{
39301             s.parentNode.removeChild(s); // remove it
39302         }
39303
39304     }
39305     if (this.store) {
39306         this.store = Roo.factory(this.store, Roo.data);
39307     }
39308     
39309     this.selectedIndex = -1;
39310     if(this.mode == 'local'){
39311         if(config.queryDelay === undefined){
39312             this.queryDelay = 10;
39313         }
39314         if(config.minChars === undefined){
39315             this.minChars = 0;
39316         }
39317     }
39318 };
39319
39320 Roo.extend(Roo.form.ComboBox, Roo.form.TriggerField, {
39321     /**
39322      * @cfg {String/HTMLElement/Element} transform The id, DOM node or element of an existing select to convert to a ComboBox
39323      */
39324     /**
39325      * @cfg {Boolean} lazyRender True to prevent the ComboBox from rendering until requested (should always be used when
39326      * rendering into an Roo.Editor, defaults to false)
39327      */
39328     /**
39329      * @cfg {Boolean/Object} autoCreate A DomHelper element spec, or true for a default element spec (defaults to:
39330      * {tag: "input", type: "text", size: "24", autocomplete: "off"})
39331      */
39332     /**
39333      * @cfg {Roo.data.Store} store The data store to which this combo is bound (defaults to undefined)
39334      */
39335     /**
39336      * @cfg {String} title If supplied, a header element is created containing this text and added into the top of
39337      * the dropdown list (defaults to undefined, with no header element)
39338      */
39339
39340      /**
39341      * @cfg {String/Roo.Template} tpl The template to use to render the output
39342      */
39343      
39344     // private
39345     defaultAutoCreate : {tag: "input", type: "text", size: "24", autocomplete: "off"},
39346     /**
39347      * @cfg {Number} listWidth The width in pixels of the dropdown list (defaults to the width of the ComboBox field)
39348      */
39349     listWidth: undefined,
39350     /**
39351      * @cfg {String} displayField The underlying data field name to bind to this CombBox (defaults to undefined if
39352      * mode = 'remote' or 'text' if mode = 'local')
39353      */
39354     displayField: undefined,
39355     /**
39356      * @cfg {String} valueField The underlying data value name to bind to this CombBox (defaults to undefined if
39357      * mode = 'remote' or 'value' if mode = 'local'). 
39358      * Note: use of a valueField requires the user make a selection
39359      * in order for a value to be mapped.
39360      */
39361     valueField: undefined,
39362     
39363     
39364     /**
39365      * @cfg {String} hiddenName If specified, a hidden form field with this name is dynamically generated to store the
39366      * field's data value (defaults to the underlying DOM element's name)
39367      */
39368     hiddenName: undefined,
39369     /**
39370      * @cfg {String} listClass CSS class to apply to the dropdown list element (defaults to '')
39371      */
39372     listClass: '',
39373     /**
39374      * @cfg {String} selectedClass CSS class to apply to the selected item in the dropdown list (defaults to 'x-combo-selected')
39375      */
39376     selectedClass: 'x-combo-selected',
39377     /**
39378      * @cfg {String} triggerClass An additional CSS class used to style the trigger button.  The trigger will always get the
39379      * class 'x-form-trigger' and triggerClass will be <b>appended</b> if specified (defaults to 'x-form-arrow-trigger'
39380      * which displays a downward arrow icon).
39381      */
39382     triggerClass : 'x-form-arrow-trigger',
39383     /**
39384      * @cfg {Boolean/String} shadow True or "sides" for the default effect, "frame" for 4-way shadow, and "drop" for bottom-right
39385      */
39386     shadow:'sides',
39387     /**
39388      * @cfg {String} listAlign A valid anchor position value. See {@link Roo.Element#alignTo} for details on supported
39389      * anchor positions (defaults to 'tl-bl')
39390      */
39391     listAlign: 'tl-bl?',
39392     /**
39393      * @cfg {Number} maxHeight The maximum height in pixels of the dropdown list before scrollbars are shown (defaults to 300)
39394      */
39395     maxHeight: 300,
39396     /**
39397      * @cfg {String} triggerAction The action to execute when the trigger field is activated.  Use 'all' to run the
39398      * query specified by the allQuery config option (defaults to 'query')
39399      */
39400     triggerAction: 'query',
39401     /**
39402      * @cfg {Number} minChars The minimum number of characters the user must type before autocomplete and typeahead activate
39403      * (defaults to 4, does not apply if editable = false)
39404      */
39405     minChars : 4,
39406     /**
39407      * @cfg {Boolean} typeAhead True to populate and autoselect the remainder of the text being typed after a configurable
39408      * delay (typeAheadDelay) if it matches a known value (defaults to false)
39409      */
39410     typeAhead: false,
39411     /**
39412      * @cfg {Number} queryDelay The length of time in milliseconds to delay between the start of typing and sending the
39413      * query to filter the dropdown list (defaults to 500 if mode = 'remote' or 10 if mode = 'local')
39414      */
39415     queryDelay: 500,
39416     /**
39417      * @cfg {Number} pageSize If greater than 0, a paging toolbar is displayed in the footer of the dropdown list and the
39418      * filter queries will execute with page start and limit parameters.  Only applies when mode = 'remote' (defaults to 0)
39419      */
39420     pageSize: 0,
39421     /**
39422      * @cfg {Boolean} selectOnFocus True to select any existing text in the field immediately on focus.  Only applies
39423      * when editable = true (defaults to false)
39424      */
39425     selectOnFocus:false,
39426     /**
39427      * @cfg {String} queryParam Name of the query as it will be passed on the querystring (defaults to 'query')
39428      */
39429     queryParam: 'query',
39430     /**
39431      * @cfg {String} loadingText The text to display in the dropdown list while data is loading.  Only applies
39432      * when mode = 'remote' (defaults to 'Loading...')
39433      */
39434     loadingText: 'Loading...',
39435     /**
39436      * @cfg {Boolean} resizable True to add a resize handle to the bottom of the dropdown list (defaults to false)
39437      */
39438     resizable: false,
39439     /**
39440      * @cfg {Number} handleHeight The height in pixels of the dropdown list resize handle if resizable = true (defaults to 8)
39441      */
39442     handleHeight : 8,
39443     /**
39444      * @cfg {Boolean} editable False to prevent the user from typing text directly into the field, just like a
39445      * traditional select (defaults to true)
39446      */
39447     editable: true,
39448     /**
39449      * @cfg {String} allQuery The text query to send to the server to return all records for the list with no filtering (defaults to '')
39450      */
39451     allQuery: '',
39452     /**
39453      * @cfg {String} mode Set to 'local' if the ComboBox loads local data (defaults to 'remote' which loads from the server)
39454      */
39455     mode: 'remote',
39456     /**
39457      * @cfg {Number} minListWidth The minimum width of the dropdown list in pixels (defaults to 70, will be ignored if
39458      * listWidth has a higher value)
39459      */
39460     minListWidth : 70,
39461     /**
39462      * @cfg {Boolean} forceSelection True to restrict the selected value to one of the values in the list, false to
39463      * allow the user to set arbitrary text into the field (defaults to false)
39464      */
39465     forceSelection:false,
39466     /**
39467      * @cfg {Number} typeAheadDelay The length of time in milliseconds to wait until the typeahead text is displayed
39468      * if typeAhead = true (defaults to 250)
39469      */
39470     typeAheadDelay : 250,
39471     /**
39472      * @cfg {String} valueNotFoundText When using a name/value combo, if the value passed to setValue is not found in
39473      * the store, valueNotFoundText will be displayed as the field text if defined (defaults to undefined)
39474      */
39475     valueNotFoundText : undefined,
39476     /**
39477      * @cfg {Boolean} blockFocus Prevents all focus calls, so it can work with things like HTML edtor bar
39478      */
39479     blockFocus : false,
39480     
39481     /**
39482      * @cfg {Boolean} disableClear Disable showing of clear button.
39483      */
39484     disableClear : false,
39485     /**
39486      * @cfg {Boolean} alwaysQuery  Disable caching of results, and always send query
39487      */
39488     alwaysQuery : false,
39489     
39490     //private
39491     addicon : false,
39492     editicon: false,
39493     
39494     // element that contains real text value.. (when hidden is used..)
39495      
39496     // private
39497     onRender : function(ct, position){
39498         Roo.form.ComboBox.superclass.onRender.call(this, ct, position);
39499         if(this.hiddenName){
39500             this.hiddenField = this.el.insertSibling({tag:'input', type:'hidden', name: this.hiddenName, id:  (this.hiddenId||this.hiddenName)},
39501                     'before', true);
39502             this.hiddenField.value =
39503                 this.hiddenValue !== undefined ? this.hiddenValue :
39504                 this.value !== undefined ? this.value : '';
39505
39506             // prevent input submission
39507             this.el.dom.removeAttribute('name');
39508              
39509              
39510         }
39511         if(Roo.isGecko){
39512             this.el.dom.setAttribute('autocomplete', 'off');
39513         }
39514
39515         var cls = 'x-combo-list';
39516
39517         this.list = new Roo.Layer({
39518             shadow: this.shadow, cls: [cls, this.listClass].join(' '), constrain:false
39519         });
39520
39521         var lw = this.listWidth || Math.max(this.wrap.getWidth(), this.minListWidth);
39522         this.list.setWidth(lw);
39523         this.list.swallowEvent('mousewheel');
39524         this.assetHeight = 0;
39525
39526         if(this.title){
39527             this.header = this.list.createChild({cls:cls+'-hd', html: this.title});
39528             this.assetHeight += this.header.getHeight();
39529         }
39530
39531         this.innerList = this.list.createChild({cls:cls+'-inner'});
39532         this.innerList.on('mouseover', this.onViewOver, this);
39533         this.innerList.on('mousemove', this.onViewMove, this);
39534         this.innerList.setWidth(lw - this.list.getFrameWidth('lr'));
39535         
39536         if(this.allowBlank && !this.pageSize && !this.disableClear){
39537             this.footer = this.list.createChild({cls:cls+'-ft'});
39538             this.pageTb = new Roo.Toolbar(this.footer);
39539            
39540         }
39541         if(this.pageSize){
39542             this.footer = this.list.createChild({cls:cls+'-ft'});
39543             this.pageTb = new Roo.PagingToolbar(this.footer, this.store,
39544                     {pageSize: this.pageSize});
39545             
39546         }
39547         
39548         if (this.pageTb && this.allowBlank && !this.disableClear) {
39549             var _this = this;
39550             this.pageTb.add(new Roo.Toolbar.Fill(), {
39551                 cls: 'x-btn-icon x-btn-clear',
39552                 text: '&#160;',
39553                 handler: function()
39554                 {
39555                     _this.collapse();
39556                     _this.clearValue();
39557                     _this.onSelect(false, -1);
39558                 }
39559             });
39560         }
39561         if (this.footer) {
39562             this.assetHeight += this.footer.getHeight();
39563         }
39564         
39565
39566         if(!this.tpl){
39567             this.tpl = '<div class="'+cls+'-item">{' + this.displayField + '}</div>';
39568         }
39569
39570         this.view = new Roo.View(this.innerList, this.tpl, {
39571             singleSelect:true, store: this.store, selectedClass: this.selectedClass
39572         });
39573
39574         this.view.on('click', this.onViewClick, this);
39575
39576         this.store.on('beforeload', this.onBeforeLoad, this);
39577         this.store.on('load', this.onLoad, this);
39578         this.store.on('loadexception', this.onLoadException, this);
39579
39580         if(this.resizable){
39581             this.resizer = new Roo.Resizable(this.list,  {
39582                pinned:true, handles:'se'
39583             });
39584             this.resizer.on('resize', function(r, w, h){
39585                 this.maxHeight = h-this.handleHeight-this.list.getFrameWidth('tb')-this.assetHeight;
39586                 this.listWidth = w;
39587                 this.innerList.setWidth(w - this.list.getFrameWidth('lr'));
39588                 this.restrictHeight();
39589             }, this);
39590             this[this.pageSize?'footer':'innerList'].setStyle('margin-bottom', this.handleHeight+'px');
39591         }
39592         if(!this.editable){
39593             this.editable = true;
39594             this.setEditable(false);
39595         }  
39596         
39597         
39598         if (typeof(this.events.add.listeners) != 'undefined') {
39599             
39600             this.addicon = this.wrap.createChild(
39601                 {tag: 'img', src: Roo.BLANK_IMAGE_URL, cls: 'x-form-combo-add' });  
39602        
39603             this.addicon.on('click', function(e) {
39604                 this.fireEvent('add', this);
39605             }, this);
39606         }
39607         if (typeof(this.events.edit.listeners) != 'undefined') {
39608             
39609             this.editicon = this.wrap.createChild(
39610                 {tag: 'img', src: Roo.BLANK_IMAGE_URL, cls: 'x-form-combo-edit' });  
39611             if (this.addicon) {
39612                 this.editicon.setStyle('margin-left', '40px');
39613             }
39614             this.editicon.on('click', function(e) {
39615                 
39616                 // we fire even  if inothing is selected..
39617                 this.fireEvent('edit', this, this.lastData );
39618                 
39619             }, this);
39620         }
39621         
39622         
39623         
39624     },
39625
39626     // private
39627     initEvents : function(){
39628         Roo.form.ComboBox.superclass.initEvents.call(this);
39629
39630         this.keyNav = new Roo.KeyNav(this.el, {
39631             "up" : function(e){
39632                 this.inKeyMode = true;
39633                 this.selectPrev();
39634             },
39635
39636             "down" : function(e){
39637                 if(!this.isExpanded()){
39638                     this.onTriggerClick();
39639                 }else{
39640                     this.inKeyMode = true;
39641                     this.selectNext();
39642                 }
39643             },
39644
39645             "enter" : function(e){
39646                 this.onViewClick();
39647                 //return true;
39648             },
39649
39650             "esc" : function(e){
39651                 this.collapse();
39652             },
39653
39654             "tab" : function(e){
39655                 this.onViewClick(false);
39656                 this.fireEvent("specialkey", this, e);
39657                 return true;
39658             },
39659
39660             scope : this,
39661
39662             doRelay : function(foo, bar, hname){
39663                 if(hname == 'down' || this.scope.isExpanded()){
39664                    return Roo.KeyNav.prototype.doRelay.apply(this, arguments);
39665                 }
39666                 return true;
39667             },
39668
39669             forceKeyDown: true
39670         });
39671         this.queryDelay = Math.max(this.queryDelay || 10,
39672                 this.mode == 'local' ? 10 : 250);
39673         this.dqTask = new Roo.util.DelayedTask(this.initQuery, this);
39674         if(this.typeAhead){
39675             this.taTask = new Roo.util.DelayedTask(this.onTypeAhead, this);
39676         }
39677         if(this.editable !== false){
39678             this.el.on("keyup", this.onKeyUp, this);
39679         }
39680         if(this.forceSelection){
39681             this.on('blur', this.doForce, this);
39682         }
39683     },
39684
39685     onDestroy : function(){
39686         if(this.view){
39687             this.view.setStore(null);
39688             this.view.el.removeAllListeners();
39689             this.view.el.remove();
39690             this.view.purgeListeners();
39691         }
39692         if(this.list){
39693             this.list.destroy();
39694         }
39695         if(this.store){
39696             this.store.un('beforeload', this.onBeforeLoad, this);
39697             this.store.un('load', this.onLoad, this);
39698             this.store.un('loadexception', this.onLoadException, this);
39699         }
39700         Roo.form.ComboBox.superclass.onDestroy.call(this);
39701     },
39702
39703     // private
39704     fireKey : function(e){
39705         if(e.isNavKeyPress() && !this.list.isVisible()){
39706             this.fireEvent("specialkey", this, e);
39707         }
39708     },
39709
39710     // private
39711     onResize: function(w, h){
39712         Roo.form.ComboBox.superclass.onResize.apply(this, arguments);
39713         
39714         if(typeof w != 'number'){
39715             // we do not handle it!?!?
39716             return;
39717         }
39718         var tw = this.trigger.getWidth();
39719         tw += this.addicon ? this.addicon.getWidth() : 0;
39720         tw += this.editicon ? this.editicon.getWidth() : 0;
39721         var x = w - tw;
39722         this.el.setWidth( this.adjustWidth('input', x));
39723             
39724         this.trigger.setStyle('left', x+'px');
39725         
39726         if(this.list && this.listWidth === undefined){
39727             var lw = Math.max(x + this.trigger.getWidth(), this.minListWidth);
39728             this.list.setWidth(lw);
39729             this.innerList.setWidth(lw - this.list.getFrameWidth('lr'));
39730         }
39731         
39732     
39733         
39734     },
39735
39736     /**
39737      * Allow or prevent the user from directly editing the field text.  If false is passed,
39738      * the user will only be able to select from the items defined in the dropdown list.  This method
39739      * is the runtime equivalent of setting the 'editable' config option at config time.
39740      * @param {Boolean} value True to allow the user to directly edit the field text
39741      */
39742     setEditable : function(value){
39743         if(value == this.editable){
39744             return;
39745         }
39746         this.editable = value;
39747         if(!value){
39748             this.el.dom.setAttribute('readOnly', true);
39749             this.el.on('mousedown', this.onTriggerClick,  this);
39750             this.el.addClass('x-combo-noedit');
39751         }else{
39752             this.el.dom.setAttribute('readOnly', false);
39753             this.el.un('mousedown', this.onTriggerClick,  this);
39754             this.el.removeClass('x-combo-noedit');
39755         }
39756     },
39757
39758     // private
39759     onBeforeLoad : function(){
39760         if(!this.hasFocus){
39761             return;
39762         }
39763         this.innerList.update(this.loadingText ?
39764                '<div class="loading-indicator">'+this.loadingText+'</div>' : '');
39765         this.restrictHeight();
39766         this.selectedIndex = -1;
39767     },
39768
39769     // private
39770     onLoad : function(){
39771         if(!this.hasFocus){
39772             return;
39773         }
39774         if(this.store.getCount() > 0){
39775             this.expand();
39776             this.restrictHeight();
39777             if(this.lastQuery == this.allQuery){
39778                 if(this.editable){
39779                     this.el.dom.select();
39780                 }
39781                 if(!this.selectByValue(this.value, true)){
39782                     this.select(0, true);
39783                 }
39784             }else{
39785                 this.selectNext();
39786                 if(this.typeAhead && this.lastKey != Roo.EventObject.BACKSPACE && this.lastKey != Roo.EventObject.DELETE){
39787                     this.taTask.delay(this.typeAheadDelay);
39788                 }
39789             }
39790         }else{
39791             this.onEmptyResults();
39792         }
39793         //this.el.focus();
39794     },
39795     // private
39796     onLoadException : function()
39797     {
39798         this.collapse();
39799         Roo.log(this.store.reader.jsonData);
39800         if (this.store && typeof(this.store.reader.jsonData.errorMsg) != 'undefined') {
39801             Roo.MessageBox.alert("Error loading",this.store.reader.jsonData.errorMsg);
39802         }
39803         
39804         
39805     },
39806     // private
39807     onTypeAhead : function(){
39808         if(this.store.getCount() > 0){
39809             var r = this.store.getAt(0);
39810             var newValue = r.data[this.displayField];
39811             var len = newValue.length;
39812             var selStart = this.getRawValue().length;
39813             if(selStart != len){
39814                 this.setRawValue(newValue);
39815                 this.selectText(selStart, newValue.length);
39816             }
39817         }
39818     },
39819
39820     // private
39821     onSelect : function(record, index){
39822         if(this.fireEvent('beforeselect', this, record, index) !== false){
39823             this.setFromData(index > -1 ? record.data : false);
39824             this.collapse();
39825             this.fireEvent('select', this, record, index);
39826         }
39827     },
39828
39829     /**
39830      * Returns the currently selected field value or empty string if no value is set.
39831      * @return {String} value The selected value
39832      */
39833     getValue : function(){
39834         if(this.valueField){
39835             return typeof this.value != 'undefined' ? this.value : '';
39836         }
39837         return Roo.form.ComboBox.superclass.getValue.call(this);
39838     },
39839
39840     /**
39841      * Clears any text/value currently set in the field
39842      */
39843     clearValue : function(){
39844         if(this.hiddenField){
39845             this.hiddenField.value = '';
39846         }
39847         this.value = '';
39848         this.setRawValue('');
39849         this.lastSelectionText = '';
39850         
39851     },
39852
39853     /**
39854      * Sets the specified value into the field.  If the value finds a match, the corresponding record text
39855      * will be displayed in the field.  If the value does not match the data value of an existing item,
39856      * and the valueNotFoundText config option is defined, it will be displayed as the default field text.
39857      * Otherwise the field will be blank (although the value will still be set).
39858      * @param {String} value The value to match
39859      */
39860     setValue : function(v){
39861         var text = v;
39862         if(this.valueField){
39863             var r = this.findRecord(this.valueField, v);
39864             if(r){
39865                 text = r.data[this.displayField];
39866             }else if(this.valueNotFoundText !== undefined){
39867                 text = this.valueNotFoundText;
39868             }
39869         }
39870         this.lastSelectionText = text;
39871         if(this.hiddenField){
39872             this.hiddenField.value = v;
39873         }
39874         Roo.form.ComboBox.superclass.setValue.call(this, text);
39875         this.value = v;
39876     },
39877     /**
39878      * @property {Object} the last set data for the element
39879      */
39880     
39881     lastData : false,
39882     /**
39883      * Sets the value of the field based on a object which is related to the record format for the store.
39884      * @param {Object} value the value to set as. or false on reset?
39885      */
39886     setFromData : function(o){
39887         var dv = ''; // display value
39888         var vv = ''; // value value..
39889         this.lastData = o;
39890         if (this.displayField) {
39891             dv = !o || typeof(o[this.displayField]) == 'undefined' ? '' : o[this.displayField];
39892         } else {
39893             // this is an error condition!!!
39894             Roo.log('no  displayField value set for '+ (this.name ? this.name : this.id));
39895         }
39896         
39897         if(this.valueField){
39898             vv = !o || typeof(o[this.valueField]) == 'undefined' ? dv : o[this.valueField];
39899         }
39900         if(this.hiddenField){
39901             this.hiddenField.value = vv;
39902             
39903             this.lastSelectionText = dv;
39904             Roo.form.ComboBox.superclass.setValue.call(this, dv);
39905             this.value = vv;
39906             return;
39907         }
39908         // no hidden field.. - we store the value in 'value', but still display
39909         // display field!!!!
39910         this.lastSelectionText = dv;
39911         Roo.form.ComboBox.superclass.setValue.call(this, dv);
39912         this.value = vv;
39913         
39914         
39915     },
39916     // private
39917     reset : function(){
39918         // overridden so that last data is reset..
39919         this.setValue(this.resetValue);
39920         this.clearInvalid();
39921         this.lastData = false;
39922         if (this.view) {
39923             this.view.clearSelections();
39924         }
39925     },
39926     // private
39927     findRecord : function(prop, value){
39928         var record;
39929         if(this.store.getCount() > 0){
39930             this.store.each(function(r){
39931                 if(r.data[prop] == value){
39932                     record = r;
39933                     return false;
39934                 }
39935                 return true;
39936             });
39937         }
39938         return record;
39939     },
39940     
39941     getName: function()
39942     {
39943         // returns hidden if it's set..
39944         if (!this.rendered) {return ''};
39945         return !this.hiddenName && this.el.dom.name  ? this.el.dom.name : (this.hiddenName || '');
39946         
39947     },
39948     // private
39949     onViewMove : function(e, t){
39950         this.inKeyMode = false;
39951     },
39952
39953     // private
39954     onViewOver : function(e, t){
39955         if(this.inKeyMode){ // prevent key nav and mouse over conflicts
39956             return;
39957         }
39958         var item = this.view.findItemFromChild(t);
39959         if(item){
39960             var index = this.view.indexOf(item);
39961             this.select(index, false);
39962         }
39963     },
39964
39965     // private
39966     onViewClick : function(doFocus)
39967     {
39968         var index = this.view.getSelectedIndexes()[0];
39969         var r = this.store.getAt(index);
39970         if(r){
39971             this.onSelect(r, index);
39972         }
39973         if(doFocus !== false && !this.blockFocus){
39974             this.el.focus();
39975         }
39976     },
39977
39978     // private
39979     restrictHeight : function(){
39980         this.innerList.dom.style.height = '';
39981         var inner = this.innerList.dom;
39982         var h = Math.max(inner.clientHeight, inner.offsetHeight, inner.scrollHeight);
39983         this.innerList.setHeight(h < this.maxHeight ? 'auto' : this.maxHeight);
39984         this.list.beginUpdate();
39985         this.list.setHeight(this.innerList.getHeight()+this.list.getFrameWidth('tb')+(this.resizable?this.handleHeight:0)+this.assetHeight);
39986         this.list.alignTo(this.el, this.listAlign);
39987         this.list.endUpdate();
39988     },
39989
39990     // private
39991     onEmptyResults : function(){
39992         this.collapse();
39993     },
39994
39995     /**
39996      * Returns true if the dropdown list is expanded, else false.
39997      */
39998     isExpanded : function(){
39999         return this.list.isVisible();
40000     },
40001
40002     /**
40003      * Select an item in the dropdown list by its data value. This function does NOT cause the select event to fire.
40004      * The store must be loaded and the list expanded for this function to work, otherwise use setValue.
40005      * @param {String} value The data value of the item to select
40006      * @param {Boolean} scrollIntoView False to prevent the dropdown list from autoscrolling to display the
40007      * selected item if it is not currently in view (defaults to true)
40008      * @return {Boolean} True if the value matched an item in the list, else false
40009      */
40010     selectByValue : function(v, scrollIntoView){
40011         if(v !== undefined && v !== null){
40012             var r = this.findRecord(this.valueField || this.displayField, v);
40013             if(r){
40014                 this.select(this.store.indexOf(r), scrollIntoView);
40015                 return true;
40016             }
40017         }
40018         return false;
40019     },
40020
40021     /**
40022      * Select an item in the dropdown list by its numeric index in the list. This function does NOT cause the select event to fire.
40023      * The store must be loaded and the list expanded for this function to work, otherwise use setValue.
40024      * @param {Number} index The zero-based index of the list item to select
40025      * @param {Boolean} scrollIntoView False to prevent the dropdown list from autoscrolling to display the
40026      * selected item if it is not currently in view (defaults to true)
40027      */
40028     select : function(index, scrollIntoView){
40029         this.selectedIndex = index;
40030         this.view.select(index);
40031         if(scrollIntoView !== false){
40032             var el = this.view.getNode(index);
40033             if(el){
40034                 this.innerList.scrollChildIntoView(el, false);
40035             }
40036         }
40037     },
40038
40039     // private
40040     selectNext : function(){
40041         var ct = this.store.getCount();
40042         if(ct > 0){
40043             if(this.selectedIndex == -1){
40044                 this.select(0);
40045             }else if(this.selectedIndex < ct-1){
40046                 this.select(this.selectedIndex+1);
40047             }
40048         }
40049     },
40050
40051     // private
40052     selectPrev : function(){
40053         var ct = this.store.getCount();
40054         if(ct > 0){
40055             if(this.selectedIndex == -1){
40056                 this.select(0);
40057             }else if(this.selectedIndex != 0){
40058                 this.select(this.selectedIndex-1);
40059             }
40060         }
40061     },
40062
40063     // private
40064     onKeyUp : function(e){
40065         if(this.editable !== false && !e.isSpecialKey()){
40066             this.lastKey = e.getKey();
40067             this.dqTask.delay(this.queryDelay);
40068         }
40069     },
40070
40071     // private
40072     validateBlur : function(){
40073         return !this.list || !this.list.isVisible();   
40074     },
40075
40076     // private
40077     initQuery : function(){
40078         this.doQuery(this.getRawValue());
40079     },
40080
40081     // private
40082     doForce : function(){
40083         if(this.el.dom.value.length > 0){
40084             this.el.dom.value =
40085                 this.lastSelectionText === undefined ? '' : this.lastSelectionText;
40086              
40087         }
40088     },
40089
40090     /**
40091      * Execute a query to filter the dropdown list.  Fires the beforequery event prior to performing the
40092      * query allowing the query action to be canceled if needed.
40093      * @param {String} query The SQL query to execute
40094      * @param {Boolean} forceAll True to force the query to execute even if there are currently fewer characters
40095      * in the field than the minimum specified by the minChars config option.  It also clears any filter previously
40096      * saved in the current store (defaults to false)
40097      */
40098     doQuery : function(q, forceAll){
40099         if(q === undefined || q === null){
40100             q = '';
40101         }
40102         var qe = {
40103             query: q,
40104             forceAll: forceAll,
40105             combo: this,
40106             cancel:false
40107         };
40108         if(this.fireEvent('beforequery', qe)===false || qe.cancel){
40109             return false;
40110         }
40111         q = qe.query;
40112         forceAll = qe.forceAll;
40113         if(forceAll === true || (q.length >= this.minChars)){
40114             if(this.lastQuery != q || this.alwaysQuery){
40115                 this.lastQuery = q;
40116                 if(this.mode == 'local'){
40117                     this.selectedIndex = -1;
40118                     if(forceAll){
40119                         this.store.clearFilter();
40120                     }else{
40121                         this.store.filter(this.displayField, q);
40122                     }
40123                     this.onLoad();
40124                 }else{
40125                     this.store.baseParams[this.queryParam] = q;
40126                     this.store.load({
40127                         params: this.getParams(q)
40128                     });
40129                     this.expand();
40130                 }
40131             }else{
40132                 this.selectedIndex = -1;
40133                 this.onLoad();   
40134             }
40135         }
40136     },
40137
40138     // private
40139     getParams : function(q){
40140         var p = {};
40141         //p[this.queryParam] = q;
40142         if(this.pageSize){
40143             p.start = 0;
40144             p.limit = this.pageSize;
40145         }
40146         return p;
40147     },
40148
40149     /**
40150      * Hides the dropdown list if it is currently expanded. Fires the 'collapse' event on completion.
40151      */
40152     collapse : function(){
40153         if(!this.isExpanded()){
40154             return;
40155         }
40156         this.list.hide();
40157         Roo.get(document).un('mousedown', this.collapseIf, this);
40158         Roo.get(document).un('mousewheel', this.collapseIf, this);
40159         if (!this.editable) {
40160             Roo.get(document).un('keydown', this.listKeyPress, this);
40161         }
40162         this.fireEvent('collapse', this);
40163     },
40164
40165     // private
40166     collapseIf : function(e){
40167         if(!e.within(this.wrap) && !e.within(this.list)){
40168             this.collapse();
40169         }
40170     },
40171
40172     /**
40173      * Expands the dropdown list if it is currently hidden. Fires the 'expand' event on completion.
40174      */
40175     expand : function(){
40176         if(this.isExpanded() || !this.hasFocus){
40177             return;
40178         }
40179         this.list.alignTo(this.el, this.listAlign);
40180         this.list.show();
40181         Roo.get(document).on('mousedown', this.collapseIf, this);
40182         Roo.get(document).on('mousewheel', this.collapseIf, this);
40183         if (!this.editable) {
40184             Roo.get(document).on('keydown', this.listKeyPress, this);
40185         }
40186         
40187         this.fireEvent('expand', this);
40188     },
40189
40190     // private
40191     // Implements the default empty TriggerField.onTriggerClick function
40192     onTriggerClick : function(){
40193         if(this.disabled){
40194             return;
40195         }
40196         if(this.isExpanded()){
40197             this.collapse();
40198             if (!this.blockFocus) {
40199                 this.el.focus();
40200             }
40201             
40202         }else {
40203             this.hasFocus = true;
40204             if(this.triggerAction == 'all') {
40205                 this.doQuery(this.allQuery, true);
40206             } else {
40207                 this.doQuery(this.getRawValue());
40208             }
40209             if (!this.blockFocus) {
40210                 this.el.focus();
40211             }
40212         }
40213     },
40214     listKeyPress : function(e)
40215     {
40216         //Roo.log('listkeypress');
40217         // scroll to first matching element based on key pres..
40218         if (e.isSpecialKey()) {
40219             return false;
40220         }
40221         var k = String.fromCharCode(e.getKey()).toUpperCase();
40222         //Roo.log(k);
40223         var match  = false;
40224         var csel = this.view.getSelectedNodes();
40225         var cselitem = false;
40226         if (csel.length) {
40227             var ix = this.view.indexOf(csel[0]);
40228             cselitem  = this.store.getAt(ix);
40229             if (!cselitem.get(this.displayField) || cselitem.get(this.displayField).substring(0,1).toUpperCase() != k) {
40230                 cselitem = false;
40231             }
40232             
40233         }
40234         
40235         this.store.each(function(v) { 
40236             if (cselitem) {
40237                 // start at existing selection.
40238                 if (cselitem.id == v.id) {
40239                     cselitem = false;
40240                 }
40241                 return;
40242             }
40243                 
40244             if (v.get(this.displayField) && v.get(this.displayField).substring(0,1).toUpperCase() == k) {
40245                 match = this.store.indexOf(v);
40246                 return false;
40247             }
40248         }, this);
40249         
40250         if (match === false) {
40251             return true; // no more action?
40252         }
40253         // scroll to?
40254         this.view.select(match);
40255         var sn = Roo.get(this.view.getSelectedNodes()[0])
40256         sn.scrollIntoView(sn.dom.parentNode, false);
40257     }
40258
40259     /** 
40260     * @cfg {Boolean} grow 
40261     * @hide 
40262     */
40263     /** 
40264     * @cfg {Number} growMin 
40265     * @hide 
40266     */
40267     /** 
40268     * @cfg {Number} growMax 
40269     * @hide 
40270     */
40271     /**
40272      * @hide
40273      * @method autoSize
40274      */
40275 });/*
40276  * Copyright(c) 2010-2012, Roo J Solutions Limited
40277  *
40278  * Licence LGPL
40279  *
40280  */
40281
40282 /**
40283  * @class Roo.form.ComboBoxArray
40284  * @extends Roo.form.TextField
40285  * A facebook style adder... for lists of email / people / countries  etc...
40286  * pick multiple items from a combo box, and shows each one.
40287  *
40288  *  Fred [x]  Brian [x]  [Pick another |v]
40289  *
40290  *
40291  *  For this to work: it needs various extra information
40292  *    - normal combo problay has
40293  *      name, hiddenName
40294  *    + displayField, valueField
40295  *
40296  *    For our purpose...
40297  *
40298  *
40299  *   If we change from 'extends' to wrapping...
40300  *   
40301  *  
40302  *
40303  
40304  
40305  * @constructor
40306  * Create a new ComboBoxArray.
40307  * @param {Object} config Configuration options
40308  */
40309  
40310
40311 Roo.form.ComboBoxArray = function(config)
40312 {
40313     this.addEvents({
40314         /**
40315          * @event remove
40316          * Fires when remove the value from the list
40317              * @param {Roo.form.ComboBoxArray} _self This combo box array
40318              * @param {Roo.form.ComboBoxArray.Item} item removed item
40319              */
40320         'remove' : true
40321         
40322         
40323     });
40324     
40325     Roo.form.ComboBoxArray.superclass.constructor.call(this, config);
40326     
40327     this.items = new Roo.util.MixedCollection(false);
40328     
40329     // construct the child combo...
40330     
40331     
40332     
40333     
40334    
40335     
40336 }
40337
40338  
40339 Roo.extend(Roo.form.ComboBoxArray, Roo.form.TextField,
40340
40341     /**
40342      * @cfg {Roo.form.Combo} combo The combo box that is wrapped
40343      */
40344     
40345     lastData : false,
40346     
40347     // behavies liek a hiddne field
40348     inputType:      'hidden',
40349     /**
40350      * @cfg {Number} width The width of the box that displays the selected element
40351      */ 
40352     width:          300,
40353
40354     
40355     
40356     /**
40357      * @cfg {String} name    The name of the visable items on this form (eg. titles not ids)
40358      */
40359     name : false,
40360     /**
40361      * @cfg {String} hiddenName    The hidden name of the field, often contains an comma seperated list of names
40362      */
40363     hiddenName : false,
40364     
40365     
40366     // private the array of items that are displayed..
40367     items  : false,
40368     // private - the hidden field el.
40369     hiddenEl : false,
40370     // private - the filed el..
40371     el : false,
40372     
40373     //validateValue : function() { return true; }, // all values are ok!
40374     //onAddClick: function() { },
40375     
40376     onRender : function(ct, position) 
40377     {
40378         
40379         // create the standard hidden element
40380         //Roo.form.ComboBoxArray.superclass.onRender.call(this, ct, position);
40381         
40382         
40383         // give fake names to child combo;
40384         this.combo.hiddenName = this.hiddenName ? (this.hiddenName+'-subcombo') : this.hiddenName;
40385         this.combo.name = this.name? (this.name+'-subcombo') : this.name;
40386         
40387         this.combo = Roo.factory(this.combo, Roo.form);
40388         this.combo.onRender(ct, position);
40389         if (typeof(this.combo.width) != 'undefined') {
40390             this.combo.onResize(this.combo.width,0);
40391         }
40392         
40393         this.combo.initEvents();
40394         
40395         // assigned so form know we need to do this..
40396         this.store          = this.combo.store;
40397         this.valueField     = this.combo.valueField;
40398         this.displayField   = this.combo.displayField ;
40399         
40400         
40401         this.combo.wrap.addClass('x-cbarray-grp');
40402         
40403         var cbwrap = this.combo.wrap.createChild(
40404             {tag: 'div', cls: 'x-cbarray-cb'},
40405             this.combo.el.dom
40406         );
40407         
40408              
40409         this.hiddenEl = this.combo.wrap.createChild({
40410             tag: 'input',  type:'hidden' , name: this.hiddenName, value : ''
40411         });
40412         this.el = this.combo.wrap.createChild({
40413             tag: 'input',  type:'hidden' , name: this.name, value : ''
40414         });
40415          //   this.el.dom.removeAttribute("name");
40416         
40417         
40418         this.outerWrap = this.combo.wrap;
40419         this.wrap = cbwrap;
40420         
40421         this.outerWrap.setWidth(this.width);
40422         this.outerWrap.dom.removeChild(this.el.dom);
40423         
40424         this.wrap.dom.appendChild(this.el.dom);
40425         this.outerWrap.dom.removeChild(this.combo.trigger.dom);
40426         this.combo.wrap.dom.appendChild(this.combo.trigger.dom);
40427         
40428         this.combo.trigger.setStyle('position','relative');
40429         this.combo.trigger.setStyle('left', '0px');
40430         this.combo.trigger.setStyle('top', '2px');
40431         
40432         this.combo.el.setStyle('vertical-align', 'text-bottom');
40433         
40434         //this.trigger.setStyle('vertical-align', 'top');
40435         
40436         // this should use the code from combo really... on('add' ....)
40437         if (this.adder) {
40438             
40439         
40440             this.adder = this.outerWrap.createChild(
40441                 {tag: 'img', src: Roo.BLANK_IMAGE_URL, cls: 'x-form-adder', style: 'margin-left:2px'});  
40442             var _t = this;
40443             this.adder.on('click', function(e) {
40444                 _t.fireEvent('adderclick', this, e);
40445             }, _t);
40446         }
40447         //var _t = this;
40448         //this.adder.on('click', this.onAddClick, _t);
40449         
40450         
40451         this.combo.on('select', function(cb, rec, ix) {
40452             this.addItem(rec.data);
40453             
40454             cb.setValue('');
40455             cb.el.dom.value = '';
40456             //cb.lastData = rec.data;
40457             // add to list
40458             
40459         }, this);
40460         
40461         
40462     },
40463     
40464     
40465     getName: function()
40466     {
40467         // returns hidden if it's set..
40468         if (!this.rendered) {return ''};
40469         return  this.hiddenName ? this.hiddenName : this.name;
40470         
40471     },
40472     
40473     
40474     onResize: function(w, h){
40475         
40476         return;
40477         // not sure if this is needed..
40478         //this.combo.onResize(w,h);
40479         
40480         if(typeof w != 'number'){
40481             // we do not handle it!?!?
40482             return;
40483         }
40484         var tw = this.combo.trigger.getWidth();
40485         tw += this.addicon ? this.addicon.getWidth() : 0;
40486         tw += this.editicon ? this.editicon.getWidth() : 0;
40487         var x = w - tw;
40488         this.combo.el.setWidth( this.combo.adjustWidth('input', x));
40489             
40490         this.combo.trigger.setStyle('left', '0px');
40491         
40492         if(this.list && this.listWidth === undefined){
40493             var lw = Math.max(x + this.combo.trigger.getWidth(), this.combo.minListWidth);
40494             this.list.setWidth(lw);
40495             this.innerList.setWidth(lw - this.list.getFrameWidth('lr'));
40496         }
40497         
40498     
40499         
40500     },
40501     
40502     addItem: function(rec)
40503     {
40504         var valueField = this.combo.valueField;
40505         var displayField = this.combo.displayField;
40506         if (this.items.indexOfKey(rec[valueField]) > -1) {
40507             //console.log("GOT " + rec.data.id);
40508             return;
40509         }
40510         
40511         var x = new Roo.form.ComboBoxArray.Item({
40512             //id : rec[this.idField],
40513             data : rec,
40514             displayField : displayField ,
40515             tipField : displayField ,
40516             cb : this
40517         });
40518         // use the 
40519         this.items.add(rec[valueField],x);
40520         // add it before the element..
40521         this.updateHiddenEl();
40522         x.render(this.outerWrap, this.wrap.dom);
40523         // add the image handler..
40524     },
40525     
40526     updateHiddenEl : function()
40527     {
40528         this.validate();
40529         if (!this.hiddenEl) {
40530             return;
40531         }
40532         var ar = [];
40533         var idField = this.combo.valueField;
40534         
40535         this.items.each(function(f) {
40536             ar.push(f.data[idField]);
40537            
40538         });
40539         this.hiddenEl.dom.value = ar.join(',');
40540         this.validate();
40541     },
40542     
40543     reset : function()
40544     {
40545         //Roo.form.ComboBoxArray.superclass.reset.call(this); 
40546         this.items.each(function(f) {
40547            f.remove(); 
40548         });
40549         this.el.dom.value = '';
40550         if (this.hiddenEl) {
40551             this.hiddenEl.dom.value = '';
40552         }
40553         
40554     },
40555     getValue: function()
40556     {
40557         return this.hiddenEl ? this.hiddenEl.dom.value : '';
40558     },
40559     setValue: function(v) // not a valid action - must use addItems..
40560     {
40561          
40562         this.reset();
40563         
40564         
40565         
40566         if (this.store.isLocal && (typeof(v) == 'string')) {
40567             // then we can use the store to find the values..
40568             // comma seperated at present.. this needs to allow JSON based encoding..
40569             this.hiddenEl.value  = v;
40570             var v_ar = [];
40571             Roo.each(v.split(','), function(k) {
40572                 Roo.log("CHECK " + this.valueField + ',' + k);
40573                 var li = this.store.query(this.valueField, k);
40574                 if (!li.length) {
40575                     return;
40576                 }
40577                 var add = {};
40578                 add[this.valueField] = k;
40579                 add[this.displayField] = li.item(0).data[this.displayField];
40580                 
40581                 this.addItem(add);
40582             }, this) 
40583              
40584         }
40585         if (typeof(v) == 'object' ) {
40586             // then let's assume it's an array of objects..
40587             Roo.each(v, function(l) {
40588                 this.addItem(l);
40589             }, this);
40590              
40591         }
40592         
40593         
40594     },
40595     setFromData: function(v)
40596     {
40597         // this recieves an object, if setValues is called.
40598         this.reset();
40599         this.el.dom.value = v[this.displayField];
40600         this.hiddenEl.dom.value = v[this.valueField];
40601         if (typeof(v[this.valueField]) != 'string' || !v[this.valueField].length) {
40602             return;
40603         }
40604         var kv = v[this.valueField];
40605         var dv = v[this.displayField];
40606         kv = typeof(kv) != 'string' ? '' : kv;
40607         dv = typeof(dv) != 'string' ? '' : dv;
40608         
40609         
40610         var keys = kv.split(',');
40611         var display = dv.split(',');
40612         for (var i = 0 ; i < keys.length; i++) {
40613             
40614             add = {};
40615             add[this.valueField] = keys[i];
40616             add[this.displayField] = display[i];
40617             this.addItem(add);
40618         }
40619       
40620         
40621     },
40622     
40623     /**
40624      * Validates the combox array value
40625      * @return {Boolean} True if the value is valid, else false
40626      */
40627     validate : function(){
40628         if(this.disabled || this.validateValue(this.processValue(this.getValue()))){
40629             this.clearInvalid();
40630             return true;
40631         }
40632         return false;
40633     },
40634     
40635     validateValue : function(value){
40636         return Roo.form.ComboBoxArray.superclass.validateValue.call(this, this.getValue());
40637         
40638     },
40639     
40640     /*@
40641      * overide
40642      * 
40643      */
40644     isDirty : function() {
40645         if(this.disabled) {
40646             return false;
40647         }
40648         
40649         try {
40650             var d = Roo.decode(String(this.originalValue));
40651         } catch (e) {
40652             return String(this.getValue()) !== String(this.originalValue);
40653         }
40654         
40655         var originalValue = [];
40656         
40657         for (var i = 0; i < d.length; i++){
40658             originalValue.push(d[i][this.valueField]);
40659         }
40660         
40661         return String(this.getValue()) !== String(originalValue.join(','));
40662         
40663     }
40664     
40665 });
40666
40667
40668
40669 /**
40670  * @class Roo.form.ComboBoxArray.Item
40671  * @extends Roo.BoxComponent
40672  * A selected item in the list
40673  *  Fred [x]  Brian [x]  [Pick another |v]
40674  * 
40675  * @constructor
40676  * Create a new item.
40677  * @param {Object} config Configuration options
40678  */
40679  
40680 Roo.form.ComboBoxArray.Item = function(config) {
40681     config.id = Roo.id();
40682     Roo.form.ComboBoxArray.Item.superclass.constructor.call(this, config);
40683 }
40684
40685 Roo.extend(Roo.form.ComboBoxArray.Item, Roo.BoxComponent, {
40686     data : {},
40687     cb: false,
40688     displayField : false,
40689     tipField : false,
40690     
40691     
40692     defaultAutoCreate : {
40693         tag: 'div',
40694         cls: 'x-cbarray-item',
40695         cn : [ 
40696             { tag: 'div' },
40697             {
40698                 tag: 'img',
40699                 width:16,
40700                 height : 16,
40701                 src : Roo.BLANK_IMAGE_URL ,
40702                 align: 'center'
40703             }
40704         ]
40705         
40706     },
40707     
40708  
40709     onRender : function(ct, position)
40710     {
40711         Roo.form.Field.superclass.onRender.call(this, ct, position);
40712         
40713         if(!this.el){
40714             var cfg = this.getAutoCreate();
40715             this.el = ct.createChild(cfg, position);
40716         }
40717         
40718         this.el.child('img').dom.setAttribute('src', Roo.BLANK_IMAGE_URL);
40719         
40720         this.el.child('div').dom.innerHTML = this.cb.renderer ? 
40721             this.cb.renderer(this.data) :
40722             String.format('{0}',this.data[this.displayField]);
40723         
40724             
40725         this.el.child('div').dom.setAttribute('qtip',
40726                         String.format('{0}',this.data[this.tipField])
40727         );
40728         
40729         this.el.child('img').on('click', this.remove, this);
40730         
40731     },
40732    
40733     remove : function()
40734     {
40735         this.cb.items.remove(this);
40736         this.el.child('img').un('click', this.remove, this);
40737         this.el.remove();
40738         this.cb.updateHiddenEl();
40739         
40740         this.cb.fireEvent('remove', this.cb, this);
40741     }
40742 });/*
40743  * Based on:
40744  * Ext JS Library 1.1.1
40745  * Copyright(c) 2006-2007, Ext JS, LLC.
40746  *
40747  * Originally Released Under LGPL - original licence link has changed is not relivant.
40748  *
40749  * Fork - LGPL
40750  * <script type="text/javascript">
40751  */
40752 /**
40753  * @class Roo.form.Checkbox
40754  * @extends Roo.form.Field
40755  * Single checkbox field.  Can be used as a direct replacement for traditional checkbox fields.
40756  * @constructor
40757  * Creates a new Checkbox
40758  * @param {Object} config Configuration options
40759  */
40760 Roo.form.Checkbox = function(config){
40761     Roo.form.Checkbox.superclass.constructor.call(this, config);
40762     this.addEvents({
40763         /**
40764          * @event check
40765          * Fires when the checkbox is checked or unchecked.
40766              * @param {Roo.form.Checkbox} this This checkbox
40767              * @param {Boolean} checked The new checked value
40768              */
40769         check : true
40770     });
40771 };
40772
40773 Roo.extend(Roo.form.Checkbox, Roo.form.Field,  {
40774     /**
40775      * @cfg {String} focusClass The CSS class to use when the checkbox receives focus (defaults to undefined)
40776      */
40777     focusClass : undefined,
40778     /**
40779      * @cfg {String} fieldClass The default CSS class for the checkbox (defaults to "x-form-field")
40780      */
40781     fieldClass: "x-form-field",
40782     /**
40783      * @cfg {Boolean} checked True if the the checkbox should render already checked (defaults to false)
40784      */
40785     checked: false,
40786     /**
40787      * @cfg {String/Object} autoCreate A DomHelper element spec, or true for a default element spec (defaults to
40788      * {tag: "input", type: "checkbox", autocomplete: "off"})
40789      */
40790     defaultAutoCreate : { tag: "input", type: 'hidden', autocomplete: "off"},
40791     /**
40792      * @cfg {String} boxLabel The text that appears beside the checkbox
40793      */
40794     boxLabel : "",
40795     /**
40796      * @cfg {String} inputValue The value that should go into the generated input element's value attribute
40797      */  
40798     inputValue : '1',
40799     /**
40800      * @cfg {String} valueOff The value that should go into the generated input element's value when unchecked.
40801      */
40802      valueOff: '0', // value when not checked..
40803
40804     actionMode : 'viewEl', 
40805     //
40806     // private
40807     itemCls : 'x-menu-check-item x-form-item',
40808     groupClass : 'x-menu-group-item',
40809     inputType : 'hidden',
40810     
40811     
40812     inSetChecked: false, // check that we are not calling self...
40813     
40814     inputElement: false, // real input element?
40815     basedOn: false, // ????
40816     
40817     isFormField: true, // not sure where this is needed!!!!
40818
40819     onResize : function(){
40820         Roo.form.Checkbox.superclass.onResize.apply(this, arguments);
40821         if(!this.boxLabel){
40822             this.el.alignTo(this.wrap, 'c-c');
40823         }
40824     },
40825
40826     initEvents : function(){
40827         Roo.form.Checkbox.superclass.initEvents.call(this);
40828         this.el.on("click", this.onClick,  this);
40829         this.el.on("change", this.onClick,  this);
40830     },
40831
40832
40833     getResizeEl : function(){
40834         return this.wrap;
40835     },
40836
40837     getPositionEl : function(){
40838         return this.wrap;
40839     },
40840
40841     // private
40842     onRender : function(ct, position){
40843         Roo.form.Checkbox.superclass.onRender.call(this, ct, position);
40844         /*
40845         if(this.inputValue !== undefined){
40846             this.el.dom.value = this.inputValue;
40847         }
40848         */
40849         //this.wrap = this.el.wrap({cls: "x-form-check-wrap"});
40850         this.wrap = this.el.wrap({cls: 'x-menu-check-item '});
40851         var viewEl = this.wrap.createChild({ 
40852             tag: 'img', cls: 'x-menu-item-icon', style: 'margin: 0px;' ,src : Roo.BLANK_IMAGE_URL });
40853         this.viewEl = viewEl;   
40854         this.wrap.on('click', this.onClick,  this); 
40855         
40856         this.el.on('DOMAttrModified', this.setFromHidden,  this); //ff
40857         this.el.on('propertychange', this.setFromHidden,  this);  //ie
40858         
40859         
40860         
40861         if(this.boxLabel){
40862             this.wrap.createChild({tag: 'label', htmlFor: this.el.id, cls: 'x-form-cb-label', html: this.boxLabel});
40863         //    viewEl.on('click', this.onClick,  this); 
40864         }
40865         //if(this.checked){
40866             this.setChecked(this.checked);
40867         //}else{
40868             //this.checked = this.el.dom;
40869         //}
40870
40871     },
40872
40873     // private
40874     initValue : Roo.emptyFn,
40875
40876     /**
40877      * Returns the checked state of the checkbox.
40878      * @return {Boolean} True if checked, else false
40879      */
40880     getValue : function(){
40881         if(this.el){
40882             return String(this.el.dom.value) == String(this.inputValue ) ? this.inputValue : this.valueOff;
40883         }
40884         return this.valueOff;
40885         
40886     },
40887
40888         // private
40889     onClick : function(){ 
40890         this.setChecked(!this.checked);
40891
40892         //if(this.el.dom.checked != this.checked){
40893         //    this.setValue(this.el.dom.checked);
40894        // }
40895     },
40896
40897     /**
40898      * Sets the checked state of the checkbox.
40899      * On is always based on a string comparison between inputValue and the param.
40900      * @param {Boolean/String} value - the value to set 
40901      * @param {Boolean/String} suppressEvent - whether to suppress the checkchange event.
40902      */
40903     setValue : function(v,suppressEvent){
40904         
40905         
40906         //this.checked = (v === true || v === 'true' || v == '1' || String(v).toLowerCase() == 'on');
40907         //if(this.el && this.el.dom){
40908         //    this.el.dom.checked = this.checked;
40909         //    this.el.dom.defaultChecked = this.checked;
40910         //}
40911         this.setChecked(String(v) === String(this.inputValue), suppressEvent);
40912         //this.fireEvent("check", this, this.checked);
40913     },
40914     // private..
40915     setChecked : function(state,suppressEvent)
40916     {
40917         if (this.inSetChecked) {
40918             this.checked = state;
40919             return;
40920         }
40921         
40922     
40923         if(this.wrap){
40924             this.wrap[state ? 'addClass' : 'removeClass']('x-menu-item-checked');
40925         }
40926         this.checked = state;
40927         if(suppressEvent !== true){
40928             this.fireEvent('check', this, state);
40929         }
40930         this.inSetChecked = true;
40931         this.el.dom.value = state ? this.inputValue : this.valueOff;
40932         this.inSetChecked = false;
40933         
40934     },
40935     // handle setting of hidden value by some other method!!?!?
40936     setFromHidden: function()
40937     {
40938         if(!this.el){
40939             return;
40940         }
40941         //console.log("SET FROM HIDDEN");
40942         //alert('setFrom hidden');
40943         this.setValue(this.el.dom.value);
40944     },
40945     
40946     onDestroy : function()
40947     {
40948         if(this.viewEl){
40949             Roo.get(this.viewEl).remove();
40950         }
40951          
40952         Roo.form.Checkbox.superclass.onDestroy.call(this);
40953     }
40954
40955 });/*
40956  * Based on:
40957  * Ext JS Library 1.1.1
40958  * Copyright(c) 2006-2007, Ext JS, LLC.
40959  *
40960  * Originally Released Under LGPL - original licence link has changed is not relivant.
40961  *
40962  * Fork - LGPL
40963  * <script type="text/javascript">
40964  */
40965  
40966 /**
40967  * @class Roo.form.Radio
40968  * @extends Roo.form.Checkbox
40969  * Single radio field.  Same as Checkbox, but provided as a convenience for automatically setting the input type.
40970  * Radio grouping is handled automatically by the browser if you give each radio in a group the same name.
40971  * @constructor
40972  * Creates a new Radio
40973  * @param {Object} config Configuration options
40974  */
40975 Roo.form.Radio = function(){
40976     Roo.form.Radio.superclass.constructor.apply(this, arguments);
40977 };
40978 Roo.extend(Roo.form.Radio, Roo.form.Checkbox, {
40979     inputType: 'radio',
40980
40981     /**
40982      * If this radio is part of a group, it will return the selected value
40983      * @return {String}
40984      */
40985     getGroupValue : function(){
40986         return this.el.up('form').child('input[name='+this.el.dom.name+']:checked', true).value;
40987     },
40988     
40989     
40990     onRender : function(ct, position){
40991         Roo.form.Checkbox.superclass.onRender.call(this, ct, position);
40992         
40993         if(this.inputValue !== undefined){
40994             this.el.dom.value = this.inputValue;
40995         }
40996          
40997         this.wrap = this.el.wrap({cls: "x-form-check-wrap"});
40998         //this.wrap = this.el.wrap({cls: 'x-menu-check-item '});
40999         //var viewEl = this.wrap.createChild({ 
41000         //    tag: 'img', cls: 'x-menu-item-icon', style: 'margin: 0px;' ,src : Roo.BLANK_IMAGE_URL });
41001         //this.viewEl = viewEl;   
41002         //this.wrap.on('click', this.onClick,  this); 
41003         
41004         //this.el.on('DOMAttrModified', this.setFromHidden,  this); //ff
41005         //this.el.on('propertychange', this.setFromHidden,  this);  //ie
41006         
41007         
41008         
41009         if(this.boxLabel){
41010             this.wrap.createChild({tag: 'label', htmlFor: this.el.id, cls: 'x-form-cb-label', html: this.boxLabel});
41011         //    viewEl.on('click', this.onClick,  this); 
41012         }
41013          if(this.checked){
41014             this.el.dom.checked =   'checked' ;
41015         }
41016          
41017     } 
41018     
41019     
41020 });//<script type="text/javascript">
41021
41022 /*
41023  * Based  Ext JS Library 1.1.1
41024  * Copyright(c) 2006-2007, Ext JS, LLC.
41025  * LGPL
41026  *
41027  */
41028  
41029 /**
41030  * @class Roo.HtmlEditorCore
41031  * @extends Roo.Component
41032  * Provides a the editing component for the HTML editors in Roo. (bootstrap and Roo.form)
41033  *
41034  * any element that has display set to 'none' can cause problems in Safari and Firefox.<br/><br/>
41035  */
41036
41037 Roo.HtmlEditorCore = function(config){
41038     
41039     
41040     Roo.HtmlEditorCore.superclass.constructor.call(this, config);
41041     
41042     
41043     this.addEvents({
41044         /**
41045          * @event initialize
41046          * Fires when the editor is fully initialized (including the iframe)
41047          * @param {Roo.HtmlEditorCore} this
41048          */
41049         initialize: true,
41050         /**
41051          * @event activate
41052          * Fires when the editor is first receives the focus. Any insertion must wait
41053          * until after this event.
41054          * @param {Roo.HtmlEditorCore} this
41055          */
41056         activate: true,
41057          /**
41058          * @event beforesync
41059          * Fires before the textarea is updated with content from the editor iframe. Return false
41060          * to cancel the sync.
41061          * @param {Roo.HtmlEditorCore} this
41062          * @param {String} html
41063          */
41064         beforesync: true,
41065          /**
41066          * @event beforepush
41067          * Fires before the iframe editor is updated with content from the textarea. Return false
41068          * to cancel the push.
41069          * @param {Roo.HtmlEditorCore} this
41070          * @param {String} html
41071          */
41072         beforepush: true,
41073          /**
41074          * @event sync
41075          * Fires when the textarea is updated with content from the editor iframe.
41076          * @param {Roo.HtmlEditorCore} this
41077          * @param {String} html
41078          */
41079         sync: true,
41080          /**
41081          * @event push
41082          * Fires when the iframe editor is updated with content from the textarea.
41083          * @param {Roo.HtmlEditorCore} this
41084          * @param {String} html
41085          */
41086         push: true,
41087         
41088         /**
41089          * @event editorevent
41090          * Fires when on any editor (mouse up/down cursor movement etc.) - used for toolbar hooks.
41091          * @param {Roo.HtmlEditorCore} this
41092          */
41093         editorevent: true
41094     });
41095     
41096     // at this point this.owner is set, so we can start working out the whitelisted / blacklisted elements
41097     
41098     // defaults : white / black...
41099     this.applyBlacklists();
41100     
41101     
41102     
41103 };
41104
41105
41106 Roo.extend(Roo.HtmlEditorCore, Roo.Component,  {
41107
41108
41109      /**
41110      * @cfg {Roo.form.HtmlEditor|Roo.bootstrap.HtmlEditor} the owner field 
41111      */
41112     
41113     owner : false,
41114     
41115      /**
41116      * @cfg {String} resizable  's' or 'se' or 'e' - wrapps the element in a
41117      *                        Roo.resizable.
41118      */
41119     resizable : false,
41120      /**
41121      * @cfg {Number} height (in pixels)
41122      */   
41123     height: 300,
41124    /**
41125      * @cfg {Number} width (in pixels)
41126      */   
41127     width: 500,
41128     
41129     /**
41130      * @cfg {Array} stylesheets url of stylesheets. set to [] to disable stylesheets.
41131      * 
41132      */
41133     stylesheets: false,
41134     
41135     // id of frame..
41136     frameId: false,
41137     
41138     // private properties
41139     validationEvent : false,
41140     deferHeight: true,
41141     initialized : false,
41142     activated : false,
41143     sourceEditMode : false,
41144     onFocus : Roo.emptyFn,
41145     iframePad:3,
41146     hideMode:'offsets',
41147     
41148     clearUp: true,
41149     
41150     // blacklist + whitelisted elements..
41151     black: false,
41152     white: false,
41153      
41154     
41155
41156     /**
41157      * Protected method that will not generally be called directly. It
41158      * is called when the editor initializes the iframe with HTML contents. Override this method if you
41159      * want to change the initialization markup of the iframe (e.g. to add stylesheets).
41160      */
41161     getDocMarkup : function(){
41162         // body styles..
41163         var st = '';
41164         Roo.log(this.stylesheets);
41165         
41166         // inherit styels from page...?? 
41167         if (this.stylesheets === false) {
41168             
41169             Roo.get(document.head).select('style').each(function(node) {
41170                 st += node.dom.outerHTML || new XMLSerializer().serializeToString(node.dom);
41171             });
41172             
41173             Roo.get(document.head).select('link').each(function(node) { 
41174                 st += node.dom.outerHTML || new XMLSerializer().serializeToString(node.dom);
41175             });
41176             
41177         } else if (!this.stylesheets.length) {
41178                 // simple..
41179                 st = '<style type="text/css">' +
41180                     'body{border:0;margin:0;padding:3px;height:98%;cursor:text;}' +
41181                    '</style>';
41182         } else {
41183             Roo.each(this.stylesheets, function(s) {
41184                 st += '<link rel="stylesheet" type="text/css" href="' + s +'" />'
41185             });
41186             
41187         }
41188         
41189         st +=  '<style type="text/css">' +
41190             'IMG { cursor: pointer } ' +
41191         '</style>';
41192
41193         
41194         return '<html><head>' + st  +
41195             //<style type="text/css">' +
41196             //'body{border:0;margin:0;padding:3px;height:98%;cursor:text;}' +
41197             //'</style>' +
41198             ' </head><body class="roo-htmleditor-body"></body></html>';
41199     },
41200
41201     // private
41202     onRender : function(ct, position)
41203     {
41204         var _t = this;
41205         //Roo.HtmlEditorCore.superclass.onRender.call(this, ct, position);
41206         this.el = this.owner.inputEl ? this.owner.inputEl() : this.owner.el;
41207         
41208         
41209         this.el.dom.style.border = '0 none';
41210         this.el.dom.setAttribute('tabIndex', -1);
41211         this.el.addClass('x-hidden hide');
41212         
41213         
41214         
41215         if(Roo.isIE){ // fix IE 1px bogus margin
41216             this.el.applyStyles('margin-top:-1px;margin-bottom:-1px;')
41217         }
41218        
41219         
41220         this.frameId = Roo.id();
41221         
41222          
41223         
41224         var iframe = this.owner.wrap.createChild({
41225             tag: 'iframe',
41226             cls: 'form-control', // bootstrap..
41227             id: this.frameId,
41228             name: this.frameId,
41229             frameBorder : 'no',
41230             'src' : Roo.SSL_SECURE_URL ? Roo.SSL_SECURE_URL  :  "javascript:false"
41231         }, this.el
41232         );
41233         
41234         
41235         this.iframe = iframe.dom;
41236
41237          this.assignDocWin();
41238         
41239         this.doc.designMode = 'on';
41240        
41241         this.doc.open();
41242         this.doc.write(this.getDocMarkup());
41243         this.doc.close();
41244
41245         
41246         var task = { // must defer to wait for browser to be ready
41247             run : function(){
41248                 //console.log("run task?" + this.doc.readyState);
41249                 this.assignDocWin();
41250                 if(this.doc.body || this.doc.readyState == 'complete'){
41251                     try {
41252                         this.doc.designMode="on";
41253                     } catch (e) {
41254                         return;
41255                     }
41256                     Roo.TaskMgr.stop(task);
41257                     this.initEditor.defer(10, this);
41258                 }
41259             },
41260             interval : 10,
41261             duration: 10000,
41262             scope: this
41263         };
41264         Roo.TaskMgr.start(task);
41265
41266         
41267          
41268     },
41269
41270     // private
41271     onResize : function(w, h)
41272     {
41273          Roo.log('resize: ' +w + ',' + h );
41274         //Roo.HtmlEditorCore.superclass.onResize.apply(this, arguments);
41275         if(!this.iframe){
41276             return;
41277         }
41278         if(typeof w == 'number'){
41279             
41280             this.iframe.style.width = w + 'px';
41281         }
41282         if(typeof h == 'number'){
41283             
41284             this.iframe.style.height = h + 'px';
41285             if(this.doc){
41286                 (this.doc.body || this.doc.documentElement).style.height = (h - (this.iframePad*2)) + 'px';
41287             }
41288         }
41289         
41290     },
41291
41292     /**
41293      * Toggles the editor between standard and source edit mode.
41294      * @param {Boolean} sourceEdit (optional) True for source edit, false for standard
41295      */
41296     toggleSourceEdit : function(sourceEditMode){
41297         
41298         this.sourceEditMode = sourceEditMode === true;
41299         
41300         if(this.sourceEditMode){
41301  
41302             Roo.get(this.iframe).addClass(['x-hidden','hide']);     //FIXME - what's the BS styles for these
41303             
41304         }else{
41305             Roo.get(this.iframe).removeClass(['x-hidden','hide']);
41306             //this.iframe.className = '';
41307             this.deferFocus();
41308         }
41309         //this.setSize(this.owner.wrap.getSize());
41310         //this.fireEvent('editmodechange', this, this.sourceEditMode);
41311     },
41312
41313     
41314   
41315
41316     /**
41317      * Protected method that will not generally be called directly. If you need/want
41318      * custom HTML cleanup, this is the method you should override.
41319      * @param {String} html The HTML to be cleaned
41320      * return {String} The cleaned HTML
41321      */
41322     cleanHtml : function(html){
41323         html = String(html);
41324         if(html.length > 5){
41325             if(Roo.isSafari){ // strip safari nonsense
41326                 html = html.replace(/\sclass="(?:Apple-style-span|khtml-block-placeholder)"/gi, '');
41327             }
41328         }
41329         if(html == '&nbsp;'){
41330             html = '';
41331         }
41332         return html;
41333     },
41334
41335     /**
41336      * HTML Editor -> Textarea
41337      * Protected method that will not generally be called directly. Syncs the contents
41338      * of the editor iframe with the textarea.
41339      */
41340     syncValue : function(){
41341         if(this.initialized){
41342             var bd = (this.doc.body || this.doc.documentElement);
41343             //this.cleanUpPaste(); -- this is done else where and causes havoc..
41344             var html = bd.innerHTML;
41345             if(Roo.isSafari){
41346                 var bs = bd.getAttribute('style'); // Safari puts text-align styles on the body element!
41347                 var m = bs ? bs.match(/text-align:(.*?);/i) : false;
41348                 if(m && m[1]){
41349                     html = '<div style="'+m[0]+'">' + html + '</div>';
41350                 }
41351             }
41352             html = this.cleanHtml(html);
41353             // fix up the special chars.. normaly like back quotes in word...
41354             // however we do not want to do this with chinese..
41355             html = html.replace(/([\x80-\uffff])/g, function (a, b) {
41356                 var cc = b.charCodeAt();
41357                 if (
41358                     (cc >= 0x4E00 && cc < 0xA000 ) ||
41359                     (cc >= 0x3400 && cc < 0x4E00 ) ||
41360                     (cc >= 0xf900 && cc < 0xfb00 )
41361                 ) {
41362                         return b;
41363                 }
41364                 return "&#"+cc+";" 
41365             });
41366             if(this.owner.fireEvent('beforesync', this, html) !== false){
41367                 this.el.dom.value = html;
41368                 this.owner.fireEvent('sync', this, html);
41369             }
41370         }
41371     },
41372
41373     /**
41374      * Protected method that will not generally be called directly. Pushes the value of the textarea
41375      * into the iframe editor.
41376      */
41377     pushValue : function(){
41378         if(this.initialized){
41379             var v = this.el.dom.value.trim();
41380             
41381 //            if(v.length < 1){
41382 //                v = '&#160;';
41383 //            }
41384             
41385             if(this.owner.fireEvent('beforepush', this, v) !== false){
41386                 var d = (this.doc.body || this.doc.documentElement);
41387                 d.innerHTML = v;
41388                 this.cleanUpPaste();
41389                 this.el.dom.value = d.innerHTML;
41390                 this.owner.fireEvent('push', this, v);
41391             }
41392         }
41393     },
41394
41395     // private
41396     deferFocus : function(){
41397         this.focus.defer(10, this);
41398     },
41399
41400     // doc'ed in Field
41401     focus : function(){
41402         if(this.win && !this.sourceEditMode){
41403             this.win.focus();
41404         }else{
41405             this.el.focus();
41406         }
41407     },
41408     
41409     assignDocWin: function()
41410     {
41411         var iframe = this.iframe;
41412         
41413          if(Roo.isIE){
41414             this.doc = iframe.contentWindow.document;
41415             this.win = iframe.contentWindow;
41416         } else {
41417 //            if (!Roo.get(this.frameId)) {
41418 //                return;
41419 //            }
41420 //            this.doc = (iframe.contentDocument || Roo.get(this.frameId).dom.document);
41421 //            this.win = Roo.get(this.frameId).dom.contentWindow;
41422             
41423             if (!Roo.get(this.frameId) && !iframe.contentDocument) {
41424                 return;
41425             }
41426             
41427             this.doc = (iframe.contentDocument || Roo.get(this.frameId).dom.document);
41428             this.win = (iframe.contentWindow || Roo.get(this.frameId).dom.contentWindow);
41429         }
41430     },
41431     
41432     // private
41433     initEditor : function(){
41434         //console.log("INIT EDITOR");
41435         this.assignDocWin();
41436         
41437         
41438         
41439         this.doc.designMode="on";
41440         this.doc.open();
41441         this.doc.write(this.getDocMarkup());
41442         this.doc.close();
41443         
41444         var dbody = (this.doc.body || this.doc.documentElement);
41445         //var ss = this.el.getStyles('font-size', 'font-family', 'background-image', 'background-repeat');
41446         // this copies styles from the containing element into thsi one..
41447         // not sure why we need all of this..
41448         //var ss = this.el.getStyles('font-size', 'background-image', 'background-repeat');
41449         
41450         //var ss = this.el.getStyles( 'background-image', 'background-repeat');
41451         //ss['background-attachment'] = 'fixed'; // w3c
41452         dbody.bgProperties = 'fixed'; // ie
41453         //Roo.DomHelper.applyStyles(dbody, ss);
41454         Roo.EventManager.on(this.doc, {
41455             //'mousedown': this.onEditorEvent,
41456             'mouseup': this.onEditorEvent,
41457             'dblclick': this.onEditorEvent,
41458             'click': this.onEditorEvent,
41459             'keyup': this.onEditorEvent,
41460             buffer:100,
41461             scope: this
41462         });
41463         if(Roo.isGecko){
41464             Roo.EventManager.on(this.doc, 'keypress', this.mozKeyPress, this);
41465         }
41466         if(Roo.isIE || Roo.isSafari || Roo.isOpera){
41467             Roo.EventManager.on(this.doc, 'keydown', this.fixKeys, this);
41468         }
41469         this.initialized = true;
41470
41471         this.owner.fireEvent('initialize', this);
41472         this.pushValue();
41473     },
41474
41475     // private
41476     onDestroy : function(){
41477         
41478         
41479         
41480         if(this.rendered){
41481             
41482             //for (var i =0; i < this.toolbars.length;i++) {
41483             //    // fixme - ask toolbars for heights?
41484             //    this.toolbars[i].onDestroy();
41485            // }
41486             
41487             //this.wrap.dom.innerHTML = '';
41488             //this.wrap.remove();
41489         }
41490     },
41491
41492     // private
41493     onFirstFocus : function(){
41494         
41495         this.assignDocWin();
41496         
41497         
41498         this.activated = true;
41499          
41500     
41501         if(Roo.isGecko){ // prevent silly gecko errors
41502             this.win.focus();
41503             var s = this.win.getSelection();
41504             if(!s.focusNode || s.focusNode.nodeType != 3){
41505                 var r = s.getRangeAt(0);
41506                 r.selectNodeContents((this.doc.body || this.doc.documentElement));
41507                 r.collapse(true);
41508                 this.deferFocus();
41509             }
41510             try{
41511                 this.execCmd('useCSS', true);
41512                 this.execCmd('styleWithCSS', false);
41513             }catch(e){}
41514         }
41515         this.owner.fireEvent('activate', this);
41516     },
41517
41518     // private
41519     adjustFont: function(btn){
41520         var adjust = btn.cmd == 'increasefontsize' ? 1 : -1;
41521         //if(Roo.isSafari){ // safari
41522         //    adjust *= 2;
41523        // }
41524         var v = parseInt(this.doc.queryCommandValue('FontSize')|| 3, 10);
41525         if(Roo.isSafari){ // safari
41526             var sm = { 10 : 1, 13: 2, 16:3, 18:4, 24: 5, 32:6, 48: 7 };
41527             v =  (v < 10) ? 10 : v;
41528             v =  (v > 48) ? 48 : v;
41529             v = typeof(sm[v]) == 'undefined' ? 1 : sm[v];
41530             
41531         }
41532         
41533         
41534         v = Math.max(1, v+adjust);
41535         
41536         this.execCmd('FontSize', v  );
41537     },
41538
41539     onEditorEvent : function(e){
41540         this.owner.fireEvent('editorevent', this, e);
41541       //  this.updateToolbar();
41542         this.syncValue(); //we can not sync so often.. sync cleans, so this breaks stuff
41543     },
41544
41545     insertTag : function(tg)
41546     {
41547         // could be a bit smarter... -> wrap the current selected tRoo..
41548         if (tg.toLowerCase() == 'span' || tg.toLowerCase() == 'code') {
41549             
41550             range = this.createRange(this.getSelection());
41551             var wrappingNode = this.doc.createElement(tg.toLowerCase());
41552             wrappingNode.appendChild(range.extractContents());
41553             range.insertNode(wrappingNode);
41554
41555             return;
41556             
41557             
41558             
41559         }
41560         this.execCmd("formatblock",   tg);
41561         
41562     },
41563     
41564     insertText : function(txt)
41565     {
41566         
41567         
41568         var range = this.createRange();
41569         range.deleteContents();
41570                //alert(Sender.getAttribute('label'));
41571                
41572         range.insertNode(this.doc.createTextNode(txt));
41573     } ,
41574     
41575      
41576
41577     /**
41578      * Executes a Midas editor command on the editor document and performs necessary focus and
41579      * toolbar updates. <b>This should only be called after the editor is initialized.</b>
41580      * @param {String} cmd The Midas command
41581      * @param {String/Boolean} value (optional) The value to pass to the command (defaults to null)
41582      */
41583     relayCmd : function(cmd, value){
41584         this.win.focus();
41585         this.execCmd(cmd, value);
41586         this.owner.fireEvent('editorevent', this);
41587         //this.updateToolbar();
41588         this.owner.deferFocus();
41589     },
41590
41591     /**
41592      * Executes a Midas editor command directly on the editor document.
41593      * For visual commands, you should use {@link #relayCmd} instead.
41594      * <b>This should only be called after the editor is initialized.</b>
41595      * @param {String} cmd The Midas command
41596      * @param {String/Boolean} value (optional) The value to pass to the command (defaults to null)
41597      */
41598     execCmd : function(cmd, value){
41599         this.doc.execCommand(cmd, false, value === undefined ? null : value);
41600         this.syncValue();
41601     },
41602  
41603  
41604    
41605     /**
41606      * Inserts the passed text at the current cursor position. Note: the editor must be initialized and activated
41607      * to insert tRoo.
41608      * @param {String} text | dom node.. 
41609      */
41610     insertAtCursor : function(text)
41611     {
41612         
41613         
41614         
41615         if(!this.activated){
41616             return;
41617         }
41618         /*
41619         if(Roo.isIE){
41620             this.win.focus();
41621             var r = this.doc.selection.createRange();
41622             if(r){
41623                 r.collapse(true);
41624                 r.pasteHTML(text);
41625                 this.syncValue();
41626                 this.deferFocus();
41627             
41628             }
41629             return;
41630         }
41631         */
41632         if(Roo.isGecko || Roo.isOpera || Roo.isSafari){
41633             this.win.focus();
41634             
41635             
41636             // from jquery ui (MIT licenced)
41637             var range, node;
41638             var win = this.win;
41639             
41640             if (win.getSelection && win.getSelection().getRangeAt) {
41641                 range = win.getSelection().getRangeAt(0);
41642                 node = typeof(text) == 'string' ? range.createContextualFragment(text) : text;
41643                 range.insertNode(node);
41644             } else if (win.document.selection && win.document.selection.createRange) {
41645                 // no firefox support
41646                 var txt = typeof(text) == 'string' ? text : text.outerHTML;
41647                 win.document.selection.createRange().pasteHTML(txt);
41648             } else {
41649                 // no firefox support
41650                 var txt = typeof(text) == 'string' ? text : text.outerHTML;
41651                 this.execCmd('InsertHTML', txt);
41652             } 
41653             
41654             this.syncValue();
41655             
41656             this.deferFocus();
41657         }
41658     },
41659  // private
41660     mozKeyPress : function(e){
41661         if(e.ctrlKey){
41662             var c = e.getCharCode(), cmd;
41663           
41664             if(c > 0){
41665                 c = String.fromCharCode(c).toLowerCase();
41666                 switch(c){
41667                     case 'b':
41668                         cmd = 'bold';
41669                         break;
41670                     case 'i':
41671                         cmd = 'italic';
41672                         break;
41673                     
41674                     case 'u':
41675                         cmd = 'underline';
41676                         break;
41677                     
41678                     case 'v':
41679                         this.cleanUpPaste.defer(100, this);
41680                         return;
41681                         
41682                 }
41683                 if(cmd){
41684                     this.win.focus();
41685                     this.execCmd(cmd);
41686                     this.deferFocus();
41687                     e.preventDefault();
41688                 }
41689                 
41690             }
41691         }
41692     },
41693
41694     // private
41695     fixKeys : function(){ // load time branching for fastest keydown performance
41696         if(Roo.isIE){
41697             return function(e){
41698                 var k = e.getKey(), r;
41699                 if(k == e.TAB){
41700                     e.stopEvent();
41701                     r = this.doc.selection.createRange();
41702                     if(r){
41703                         r.collapse(true);
41704                         r.pasteHTML('&#160;&#160;&#160;&#160;');
41705                         this.deferFocus();
41706                     }
41707                     return;
41708                 }
41709                 
41710                 if(k == e.ENTER){
41711                     r = this.doc.selection.createRange();
41712                     if(r){
41713                         var target = r.parentElement();
41714                         if(!target || target.tagName.toLowerCase() != 'li'){
41715                             e.stopEvent();
41716                             r.pasteHTML('<br />');
41717                             r.collapse(false);
41718                             r.select();
41719                         }
41720                     }
41721                 }
41722                 if (String.fromCharCode(k).toLowerCase() == 'v') { // paste
41723                     this.cleanUpPaste.defer(100, this);
41724                     return;
41725                 }
41726                 
41727                 
41728             };
41729         }else if(Roo.isOpera){
41730             return function(e){
41731                 var k = e.getKey();
41732                 if(k == e.TAB){
41733                     e.stopEvent();
41734                     this.win.focus();
41735                     this.execCmd('InsertHTML','&#160;&#160;&#160;&#160;');
41736                     this.deferFocus();
41737                 }
41738                 if (String.fromCharCode(k).toLowerCase() == 'v') { // paste
41739                     this.cleanUpPaste.defer(100, this);
41740                     return;
41741                 }
41742                 
41743             };
41744         }else if(Roo.isSafari){
41745             return function(e){
41746                 var k = e.getKey();
41747                 
41748                 if(k == e.TAB){
41749                     e.stopEvent();
41750                     this.execCmd('InsertText','\t');
41751                     this.deferFocus();
41752                     return;
41753                 }
41754                if (String.fromCharCode(k).toLowerCase() == 'v') { // paste
41755                     this.cleanUpPaste.defer(100, this);
41756                     return;
41757                 }
41758                 
41759              };
41760         }
41761     }(),
41762     
41763     getAllAncestors: function()
41764     {
41765         var p = this.getSelectedNode();
41766         var a = [];
41767         if (!p) {
41768             a.push(p); // push blank onto stack..
41769             p = this.getParentElement();
41770         }
41771         
41772         
41773         while (p && (p.nodeType == 1) && (p.tagName.toLowerCase() != 'body')) {
41774             a.push(p);
41775             p = p.parentNode;
41776         }
41777         a.push(this.doc.body);
41778         return a;
41779     },
41780     lastSel : false,
41781     lastSelNode : false,
41782     
41783     
41784     getSelection : function() 
41785     {
41786         this.assignDocWin();
41787         return Roo.isIE ? this.doc.selection : this.win.getSelection();
41788     },
41789     
41790     getSelectedNode: function() 
41791     {
41792         // this may only work on Gecko!!!
41793         
41794         // should we cache this!!!!
41795         
41796         
41797         
41798          
41799         var range = this.createRange(this.getSelection()).cloneRange();
41800         
41801         if (Roo.isIE) {
41802             var parent = range.parentElement();
41803             while (true) {
41804                 var testRange = range.duplicate();
41805                 testRange.moveToElementText(parent);
41806                 if (testRange.inRange(range)) {
41807                     break;
41808                 }
41809                 if ((parent.nodeType != 1) || (parent.tagName.toLowerCase() == 'body')) {
41810                     break;
41811                 }
41812                 parent = parent.parentElement;
41813             }
41814             return parent;
41815         }
41816         
41817         // is ancestor a text element.
41818         var ac =  range.commonAncestorContainer;
41819         if (ac.nodeType == 3) {
41820             ac = ac.parentNode;
41821         }
41822         
41823         var ar = ac.childNodes;
41824          
41825         var nodes = [];
41826         var other_nodes = [];
41827         var has_other_nodes = false;
41828         for (var i=0;i<ar.length;i++) {
41829             if ((ar[i].nodeType == 3) && (!ar[i].data.length)) { // empty text ? 
41830                 continue;
41831             }
41832             // fullly contained node.
41833             
41834             if (this.rangeIntersectsNode(range,ar[i]) && this.rangeCompareNode(range,ar[i]) == 3) {
41835                 nodes.push(ar[i]);
41836                 continue;
41837             }
41838             
41839             // probably selected..
41840             if ((ar[i].nodeType == 1) && this.rangeIntersectsNode(range,ar[i]) && (this.rangeCompareNode(range,ar[i]) > 0)) {
41841                 other_nodes.push(ar[i]);
41842                 continue;
41843             }
41844             // outer..
41845             if (!this.rangeIntersectsNode(range,ar[i])|| (this.rangeCompareNode(range,ar[i]) == 0))  {
41846                 continue;
41847             }
41848             
41849             
41850             has_other_nodes = true;
41851         }
41852         if (!nodes.length && other_nodes.length) {
41853             nodes= other_nodes;
41854         }
41855         if (has_other_nodes || !nodes.length || (nodes.length > 1)) {
41856             return false;
41857         }
41858         
41859         return nodes[0];
41860     },
41861     createRange: function(sel)
41862     {
41863         // this has strange effects when using with 
41864         // top toolbar - not sure if it's a great idea.
41865         //this.editor.contentWindow.focus();
41866         if (typeof sel != "undefined") {
41867             try {
41868                 return sel.getRangeAt ? sel.getRangeAt(0) : sel.createRange();
41869             } catch(e) {
41870                 return this.doc.createRange();
41871             }
41872         } else {
41873             return this.doc.createRange();
41874         }
41875     },
41876     getParentElement: function()
41877     {
41878         
41879         this.assignDocWin();
41880         var sel = Roo.isIE ? this.doc.selection : this.win.getSelection();
41881         
41882         var range = this.createRange(sel);
41883          
41884         try {
41885             var p = range.commonAncestorContainer;
41886             while (p.nodeType == 3) { // text node
41887                 p = p.parentNode;
41888             }
41889             return p;
41890         } catch (e) {
41891             return null;
41892         }
41893     
41894     },
41895     /***
41896      *
41897      * Range intersection.. the hard stuff...
41898      *  '-1' = before
41899      *  '0' = hits..
41900      *  '1' = after.
41901      *         [ -- selected range --- ]
41902      *   [fail]                        [fail]
41903      *
41904      *    basically..
41905      *      if end is before start or  hits it. fail.
41906      *      if start is after end or hits it fail.
41907      *
41908      *   if either hits (but other is outside. - then it's not 
41909      *   
41910      *    
41911      **/
41912     
41913     
41914     // @see http://www.thismuchiknow.co.uk/?p=64.
41915     rangeIntersectsNode : function(range, node)
41916     {
41917         var nodeRange = node.ownerDocument.createRange();
41918         try {
41919             nodeRange.selectNode(node);
41920         } catch (e) {
41921             nodeRange.selectNodeContents(node);
41922         }
41923     
41924         var rangeStartRange = range.cloneRange();
41925         rangeStartRange.collapse(true);
41926     
41927         var rangeEndRange = range.cloneRange();
41928         rangeEndRange.collapse(false);
41929     
41930         var nodeStartRange = nodeRange.cloneRange();
41931         nodeStartRange.collapse(true);
41932     
41933         var nodeEndRange = nodeRange.cloneRange();
41934         nodeEndRange.collapse(false);
41935     
41936         return rangeStartRange.compareBoundaryPoints(
41937                  Range.START_TO_START, nodeEndRange) == -1 &&
41938                rangeEndRange.compareBoundaryPoints(
41939                  Range.START_TO_START, nodeStartRange) == 1;
41940         
41941          
41942     },
41943     rangeCompareNode : function(range, node)
41944     {
41945         var nodeRange = node.ownerDocument.createRange();
41946         try {
41947             nodeRange.selectNode(node);
41948         } catch (e) {
41949             nodeRange.selectNodeContents(node);
41950         }
41951         
41952         
41953         range.collapse(true);
41954     
41955         nodeRange.collapse(true);
41956      
41957         var ss = range.compareBoundaryPoints( Range.START_TO_START, nodeRange);
41958         var ee = range.compareBoundaryPoints(  Range.END_TO_END, nodeRange);
41959          
41960         //Roo.log(node.tagName + ': ss='+ss +', ee='+ee)
41961         
41962         var nodeIsBefore   =  ss == 1;
41963         var nodeIsAfter    = ee == -1;
41964         
41965         if (nodeIsBefore && nodeIsAfter)
41966             return 0; // outer
41967         if (!nodeIsBefore && nodeIsAfter)
41968             return 1; //right trailed.
41969         
41970         if (nodeIsBefore && !nodeIsAfter)
41971             return 2;  // left trailed.
41972         // fully contined.
41973         return 3;
41974     },
41975
41976     // private? - in a new class?
41977     cleanUpPaste :  function()
41978     {
41979         // cleans up the whole document..
41980         Roo.log('cleanuppaste');
41981         
41982         this.cleanUpChildren(this.doc.body);
41983         var clean = this.cleanWordChars(this.doc.body.innerHTML);
41984         if (clean != this.doc.body.innerHTML) {
41985             this.doc.body.innerHTML = clean;
41986         }
41987         
41988     },
41989     
41990     cleanWordChars : function(input) {// change the chars to hex code
41991         var he = Roo.HtmlEditorCore;
41992         
41993         var output = input;
41994         Roo.each(he.swapCodes, function(sw) { 
41995             var swapper = new RegExp("\\u" + sw[0].toString(16), "g"); // hex codes
41996             
41997             output = output.replace(swapper, sw[1]);
41998         });
41999         
42000         return output;
42001     },
42002     
42003     
42004     cleanUpChildren : function (n)
42005     {
42006         if (!n.childNodes.length) {
42007             return;
42008         }
42009         for (var i = n.childNodes.length-1; i > -1 ; i--) {
42010            this.cleanUpChild(n.childNodes[i]);
42011         }
42012     },
42013     
42014     
42015         
42016     
42017     cleanUpChild : function (node)
42018     {
42019         var ed = this;
42020         //console.log(node);
42021         if (node.nodeName == "#text") {
42022             // clean up silly Windows -- stuff?
42023             return; 
42024         }
42025         if (node.nodeName == "#comment") {
42026             node.parentNode.removeChild(node);
42027             // clean up silly Windows -- stuff?
42028             return; 
42029         }
42030         var lcname = node.tagName.toLowerCase();
42031         // we ignore whitelists... ?? = not really the way to go, but we probably have not got a full
42032         // whitelist of tags..
42033         
42034         if (this.black.indexOf(lcname) > -1 && this.clearUp ) {
42035             // remove node.
42036             node.parentNode.removeChild(node);
42037             return;
42038             
42039         }
42040         
42041         var remove_keep_children= Roo.HtmlEditorCore.remove.indexOf(node.tagName.toLowerCase()) > -1;
42042         
42043         // remove <a name=....> as rendering on yahoo mailer is borked with this.
42044         // this will have to be flaged elsewhere - perhaps ablack=name... on the mailer..
42045         
42046         //if (node.tagName.toLowerCase() == 'a' && !node.hasAttribute('href')) {
42047         //    remove_keep_children = true;
42048         //}
42049         
42050         if (remove_keep_children) {
42051             this.cleanUpChildren(node);
42052             // inserts everything just before this node...
42053             while (node.childNodes.length) {
42054                 var cn = node.childNodes[0];
42055                 node.removeChild(cn);
42056                 node.parentNode.insertBefore(cn, node);
42057             }
42058             node.parentNode.removeChild(node);
42059             return;
42060         }
42061         
42062         if (!node.attributes || !node.attributes.length) {
42063             this.cleanUpChildren(node);
42064             return;
42065         }
42066         
42067         function cleanAttr(n,v)
42068         {
42069             
42070             if (v.match(/^\./) || v.match(/^\//)) {
42071                 return;
42072             }
42073             if (v.match(/^(http|https):\/\//) || v.match(/^mailto:/)) {
42074                 return;
42075             }
42076             if (v.match(/^#/)) {
42077                 return;
42078             }
42079 //            Roo.log("(REMOVE TAG)"+ node.tagName +'.' + n + '=' + v);
42080             node.removeAttribute(n);
42081             
42082         }
42083         
42084         var cwhite = this.cwhite;
42085         var cblack = this.cblack;
42086             
42087         function cleanStyle(n,v)
42088         {
42089             if (v.match(/expression/)) { //XSS?? should we even bother..
42090                 node.removeAttribute(n);
42091                 return;
42092             }
42093             
42094             var parts = v.split(/;/);
42095             var clean = [];
42096             
42097             Roo.each(parts, function(p) {
42098                 p = p.replace(/^\s+/g,'').replace(/\s+$/g,'');
42099                 if (!p.length) {
42100                     return true;
42101                 }
42102                 var l = p.split(':').shift().replace(/\s+/g,'');
42103                 l = l.replace(/^\s+/g,'').replace(/\s+$/g,'');
42104                 
42105                 if ( cwhite.length && cblack.indexOf(l) > -1) {
42106 //                    Roo.log('(REMOVE CSS)' + node.tagName +'.' + n + ':'+l + '=' + v);
42107                     //node.removeAttribute(n);
42108                     return true;
42109                 }
42110                 //Roo.log()
42111                 // only allow 'c whitelisted system attributes'
42112                 if ( cwhite.length &&  cwhite.indexOf(l) < 0) {
42113 //                    Roo.log('(REMOVE CSS)' + node.tagName +'.' + n + ':'+l + '=' + v);
42114                     //node.removeAttribute(n);
42115                     return true;
42116                 }
42117                 
42118                 
42119                  
42120                 
42121                 clean.push(p);
42122                 return true;
42123             });
42124             if (clean.length) { 
42125                 node.setAttribute(n, clean.join(';'));
42126             } else {
42127                 node.removeAttribute(n);
42128             }
42129             
42130         }
42131         
42132         
42133         for (var i = node.attributes.length-1; i > -1 ; i--) {
42134             var a = node.attributes[i];
42135             //console.log(a);
42136             
42137             if (a.name.toLowerCase().substr(0,2)=='on')  {
42138                 node.removeAttribute(a.name);
42139                 continue;
42140             }
42141             if (Roo.HtmlEditorCore.ablack.indexOf(a.name.toLowerCase()) > -1) {
42142                 node.removeAttribute(a.name);
42143                 continue;
42144             }
42145             if (Roo.HtmlEditorCore.aclean.indexOf(a.name.toLowerCase()) > -1) {
42146                 cleanAttr(a.name,a.value); // fixme..
42147                 continue;
42148             }
42149             if (a.name == 'style') {
42150                 cleanStyle(a.name,a.value);
42151                 continue;
42152             }
42153             /// clean up MS crap..
42154             // tecnically this should be a list of valid class'es..
42155             
42156             
42157             if (a.name == 'class') {
42158                 if (a.value.match(/^Mso/)) {
42159                     node.className = '';
42160                 }
42161                 
42162                 if (a.value.match(/body/)) {
42163                     node.className = '';
42164                 }
42165                 continue;
42166             }
42167             
42168             // style cleanup!?
42169             // class cleanup?
42170             
42171         }
42172         
42173         
42174         this.cleanUpChildren(node);
42175         
42176         
42177     },
42178     /**
42179      * Clean up MS wordisms...
42180      */
42181     cleanWord : function(node)
42182     {
42183         var _t = this;
42184         var cleanWordChildren = function()
42185         {
42186             if (!node.childNodes.length) {
42187                 return;
42188             }
42189             for (var i = node.childNodes.length-1; i > -1 ; i--) {
42190                _t.cleanWord(node.childNodes[i]);
42191             }
42192         }
42193         
42194         
42195         if (!node) {
42196             this.cleanWord(this.doc.body);
42197             return;
42198         }
42199         if (node.nodeName == "#text") {
42200             // clean up silly Windows -- stuff?
42201             return; 
42202         }
42203         if (node.nodeName == "#comment") {
42204             node.parentNode.removeChild(node);
42205             // clean up silly Windows -- stuff?
42206             return; 
42207         }
42208         
42209         if (node.tagName.toLowerCase().match(/^(style|script|applet|embed|noframes|noscript)$/)) {
42210             node.parentNode.removeChild(node);
42211             return;
42212         }
42213         
42214         // remove - but keep children..
42215         if (node.tagName.toLowerCase().match(/^(meta|link|\\?xml:|st1:|o:|font)/)) {
42216             while (node.childNodes.length) {
42217                 var cn = node.childNodes[0];
42218                 node.removeChild(cn);
42219                 node.parentNode.insertBefore(cn, node);
42220             }
42221             node.parentNode.removeChild(node);
42222             cleanWordChildren();
42223             return;
42224         }
42225         // clean styles
42226         if (node.className.length) {
42227             
42228             var cn = node.className.split(/\W+/);
42229             var cna = [];
42230             Roo.each(cn, function(cls) {
42231                 if (cls.match(/Mso[a-zA-Z]+/)) {
42232                     return;
42233                 }
42234                 cna.push(cls);
42235             });
42236             node.className = cna.length ? cna.join(' ') : '';
42237             if (!cna.length) {
42238                 node.removeAttribute("class");
42239             }
42240         }
42241         
42242         if (node.hasAttribute("lang")) {
42243             node.removeAttribute("lang");
42244         }
42245         
42246         if (node.hasAttribute("style")) {
42247             
42248             var styles = node.getAttribute("style").split(";");
42249             var nstyle = [];
42250             Roo.each(styles, function(s) {
42251                 if (!s.match(/:/)) {
42252                     return;
42253                 }
42254                 var kv = s.split(":");
42255                 if (kv[0].match(/^(mso-|line|font|background|margin|padding|color)/)) {
42256                     return;
42257                 }
42258                 // what ever is left... we allow.
42259                 nstyle.push(s);
42260             });
42261             node.setAttribute("style", nstyle.length ? nstyle.join(';') : '');
42262             if (!nstyle.length) {
42263                 node.removeAttribute('style');
42264             }
42265         }
42266         
42267         cleanWordChildren();
42268         
42269         
42270     },
42271     domToHTML : function(currentElement, depth, nopadtext) {
42272         
42273         depth = depth || 0;
42274         nopadtext = nopadtext || false;
42275     
42276         if (!currentElement) {
42277             return this.domToHTML(this.doc.body);
42278         }
42279         
42280         //Roo.log(currentElement);
42281         var j;
42282         var allText = false;
42283         var nodeName = currentElement.nodeName;
42284         var tagName = Roo.util.Format.htmlEncode(currentElement.tagName);
42285         
42286         if  (nodeName == '#text') {
42287             return currentElement.nodeValue;
42288         }
42289         
42290         
42291         var ret = '';
42292         if (nodeName != 'BODY') {
42293              
42294             var i = 0;
42295             // Prints the node tagName, such as <A>, <IMG>, etc
42296             if (tagName) {
42297                 var attr = [];
42298                 for(i = 0; i < currentElement.attributes.length;i++) {
42299                     // quoting?
42300                     var aname = currentElement.attributes.item(i).name;
42301                     if (!currentElement.attributes.item(i).value.length) {
42302                         continue;
42303                     }
42304                     attr.push(aname + '="' + Roo.util.Format.htmlEncode(currentElement.attributes.item(i).value) + '"' );
42305                 }
42306                 
42307                 ret = "<"+currentElement.tagName+ ( attr.length ? (' ' + attr.join(' ') ) : '') + ">";
42308             } 
42309             else {
42310                 
42311                 // eack
42312             }
42313         } else {
42314             tagName = false;
42315         }
42316         if (['IMG', 'BR', 'HR', 'INPUT'].indexOf(tagName) > -1) {
42317             return ret;
42318         }
42319         if (['PRE', 'TEXTAREA', 'TD', 'A', 'SPAN'].indexOf(tagName) > -1) { // or code?
42320             nopadtext = true;
42321         }
42322         
42323         
42324         // Traverse the tree
42325         i = 0;
42326         var currentElementChild = currentElement.childNodes.item(i);
42327         var allText = true;
42328         var innerHTML  = '';
42329         lastnode = '';
42330         while (currentElementChild) {
42331             // Formatting code (indent the tree so it looks nice on the screen)
42332             var nopad = nopadtext;
42333             if (lastnode == 'SPAN') {
42334                 nopad  = true;
42335             }
42336             // text
42337             if  (currentElementChild.nodeName == '#text') {
42338                 var toadd = Roo.util.Format.htmlEncode(currentElementChild.nodeValue);
42339                 if (!nopad && toadd.length > 80) {
42340                     innerHTML  += "\n" + (new Array( depth + 1 )).join( "  "  );
42341                 }
42342                 innerHTML  += toadd;
42343                 
42344                 i++;
42345                 currentElementChild = currentElement.childNodes.item(i);
42346                 lastNode = '';
42347                 continue;
42348             }
42349             allText = false;
42350             
42351             innerHTML  += nopad ? '' : "\n" + (new Array( depth + 1 )).join( "  "  );
42352                 
42353             // Recursively traverse the tree structure of the child node
42354             innerHTML   += this.domToHTML(currentElementChild, depth+1, nopadtext);
42355             lastnode = currentElementChild.nodeName;
42356             i++;
42357             currentElementChild=currentElement.childNodes.item(i);
42358         }
42359         
42360         ret += innerHTML;
42361         
42362         if (!allText) {
42363                 // The remaining code is mostly for formatting the tree
42364             ret+= nopadtext ? '' : "\n" + (new Array( depth  )).join( "  "  );
42365         }
42366         
42367         
42368         if (tagName) {
42369             ret+= "</"+tagName+">";
42370         }
42371         return ret;
42372         
42373     },
42374         
42375     applyBlacklists : function()
42376     {
42377         var w = typeof(this.owner.white) != 'undefined' && this.owner.white ? this.owner.white  : [];
42378         var b = typeof(this.owner.black) != 'undefined' && this.owner.black ? this.owner.black :  [];
42379         
42380         this.white = [];
42381         this.black = [];
42382         Roo.each(Roo.HtmlEditorCore.white, function(tag) {
42383             if (b.indexOf(tag) > -1) {
42384                 return;
42385             }
42386             this.white.push(tag);
42387             
42388         }, this);
42389         
42390         Roo.each(w, function(tag) {
42391             if (b.indexOf(tag) > -1) {
42392                 return;
42393             }
42394             if (this.white.indexOf(tag) > -1) {
42395                 return;
42396             }
42397             this.white.push(tag);
42398             
42399         }, this);
42400         
42401         
42402         Roo.each(Roo.HtmlEditorCore.black, function(tag) {
42403             if (w.indexOf(tag) > -1) {
42404                 return;
42405             }
42406             this.black.push(tag);
42407             
42408         }, this);
42409         
42410         Roo.each(b, function(tag) {
42411             if (w.indexOf(tag) > -1) {
42412                 return;
42413             }
42414             if (this.black.indexOf(tag) > -1) {
42415                 return;
42416             }
42417             this.black.push(tag);
42418             
42419         }, this);
42420         
42421         
42422         w = typeof(this.owner.cwhite) != 'undefined' && this.owner.cwhite ? this.owner.cwhite  : [];
42423         b = typeof(this.owner.cblack) != 'undefined' && this.owner.cblack ? this.owner.cblack :  [];
42424         
42425         this.cwhite = [];
42426         this.cblack = [];
42427         Roo.each(Roo.HtmlEditorCore.cwhite, function(tag) {
42428             if (b.indexOf(tag) > -1) {
42429                 return;
42430             }
42431             this.cwhite.push(tag);
42432             
42433         }, this);
42434         
42435         Roo.each(w, function(tag) {
42436             if (b.indexOf(tag) > -1) {
42437                 return;
42438             }
42439             if (this.cwhite.indexOf(tag) > -1) {
42440                 return;
42441             }
42442             this.cwhite.push(tag);
42443             
42444         }, this);
42445         
42446         
42447         Roo.each(Roo.HtmlEditorCore.cblack, function(tag) {
42448             if (w.indexOf(tag) > -1) {
42449                 return;
42450             }
42451             this.cblack.push(tag);
42452             
42453         }, this);
42454         
42455         Roo.each(b, function(tag) {
42456             if (w.indexOf(tag) > -1) {
42457                 return;
42458             }
42459             if (this.cblack.indexOf(tag) > -1) {
42460                 return;
42461             }
42462             this.cblack.push(tag);
42463             
42464         }, this);
42465     }
42466     
42467     // hide stuff that is not compatible
42468     /**
42469      * @event blur
42470      * @hide
42471      */
42472     /**
42473      * @event change
42474      * @hide
42475      */
42476     /**
42477      * @event focus
42478      * @hide
42479      */
42480     /**
42481      * @event specialkey
42482      * @hide
42483      */
42484     /**
42485      * @cfg {String} fieldClass @hide
42486      */
42487     /**
42488      * @cfg {String} focusClass @hide
42489      */
42490     /**
42491      * @cfg {String} autoCreate @hide
42492      */
42493     /**
42494      * @cfg {String} inputType @hide
42495      */
42496     /**
42497      * @cfg {String} invalidClass @hide
42498      */
42499     /**
42500      * @cfg {String} invalidText @hide
42501      */
42502     /**
42503      * @cfg {String} msgFx @hide
42504      */
42505     /**
42506      * @cfg {String} validateOnBlur @hide
42507      */
42508 });
42509
42510 Roo.HtmlEditorCore.white = [
42511         'area', 'br', 'img', 'input', 'hr', 'wbr',
42512         
42513        'address', 'blockquote', 'center', 'dd',      'dir',       'div', 
42514        'dl',      'dt',         'h1',     'h2',      'h3',        'h4', 
42515        'h5',      'h6',         'hr',     'isindex', 'listing',   'marquee', 
42516        'menu',    'multicol',   'ol',     'p',       'plaintext', 'pre', 
42517        'table',   'ul',         'xmp', 
42518        
42519        'caption', 'col', 'colgroup', 'tbody', 'td', 'tfoot', 'th', 
42520       'thead',   'tr', 
42521      
42522       'dir', 'menu', 'ol', 'ul', 'dl',
42523        
42524       'embed',  'object'
42525 ];
42526
42527
42528 Roo.HtmlEditorCore.black = [
42529     //    'embed',  'object', // enable - backend responsiblity to clean thiese
42530         'applet', // 
42531         'base',   'basefont', 'bgsound', 'blink',  'body', 
42532         'frame',  'frameset', 'head',    'html',   'ilayer', 
42533         'iframe', 'layer',  'link',     'meta',    'object',   
42534         'script', 'style' ,'title',  'xml' // clean later..
42535 ];
42536 Roo.HtmlEditorCore.clean = [
42537     'script', 'style', 'title', 'xml'
42538 ];
42539 Roo.HtmlEditorCore.remove = [
42540     'font'
42541 ];
42542 // attributes..
42543
42544 Roo.HtmlEditorCore.ablack = [
42545     'on'
42546 ];
42547     
42548 Roo.HtmlEditorCore.aclean = [ 
42549     'action', 'background', 'codebase', 'dynsrc', 'href', 'lowsrc' 
42550 ];
42551
42552 // protocols..
42553 Roo.HtmlEditorCore.pwhite= [
42554         'http',  'https',  'mailto'
42555 ];
42556
42557 // white listed style attributes.
42558 Roo.HtmlEditorCore.cwhite= [
42559       //  'text-align', /// default is to allow most things..
42560       
42561          
42562 //        'font-size'//??
42563 ];
42564
42565 // black listed style attributes.
42566 Roo.HtmlEditorCore.cblack= [
42567       //  'font-size' -- this can be set by the project 
42568 ];
42569
42570
42571 Roo.HtmlEditorCore.swapCodes   =[ 
42572     [    8211, "--" ], 
42573     [    8212, "--" ], 
42574     [    8216,  "'" ],  
42575     [    8217, "'" ],  
42576     [    8220, '"' ],  
42577     [    8221, '"' ],  
42578     [    8226, "*" ],  
42579     [    8230, "..." ]
42580 ]; 
42581
42582     //<script type="text/javascript">
42583
42584 /*
42585  * Ext JS Library 1.1.1
42586  * Copyright(c) 2006-2007, Ext JS, LLC.
42587  * Licence LGPL
42588  * 
42589  */
42590  
42591  
42592 Roo.form.HtmlEditor = function(config){
42593     
42594     
42595     
42596     Roo.form.HtmlEditor.superclass.constructor.call(this, config);
42597     
42598     if (!this.toolbars) {
42599         this.toolbars = [];
42600     }
42601     this.editorcore = new Roo.HtmlEditorCore(Roo.apply({ owner : this} , config));
42602     
42603     
42604 };
42605
42606 /**
42607  * @class Roo.form.HtmlEditor
42608  * @extends Roo.form.Field
42609  * Provides a lightweight HTML Editor component.
42610  *
42611  * This has been tested on Fireforx / Chrome.. IE may not be so great..
42612  * 
42613  * <br><br><b>Note: The focus/blur and validation marking functionality inherited from Ext.form.Field is NOT
42614  * supported by this editor.</b><br/><br/>
42615  * An Editor is a sensitive component that can't be used in all spots standard fields can be used. Putting an Editor within
42616  * any element that has display set to 'none' can cause problems in Safari and Firefox.<br/><br/>
42617  */
42618 Roo.extend(Roo.form.HtmlEditor, Roo.form.Field, {
42619     /**
42620      * @cfg {Boolean} clearUp
42621      */
42622     clearUp : true,
42623       /**
42624      * @cfg {Array} toolbars Array of toolbars. - defaults to just the Standard one
42625      */
42626     toolbars : false,
42627    
42628      /**
42629      * @cfg {String} resizable  's' or 'se' or 'e' - wrapps the element in a
42630      *                        Roo.resizable.
42631      */
42632     resizable : false,
42633      /**
42634      * @cfg {Number} height (in pixels)
42635      */   
42636     height: 300,
42637    /**
42638      * @cfg {Number} width (in pixels)
42639      */   
42640     width: 500,
42641     
42642     /**
42643      * @cfg {Array} stylesheets url of stylesheets. set to [] to disable stylesheets.
42644      * 
42645      */
42646     stylesheets: false,
42647     
42648     
42649      /**
42650      * @cfg {Array} blacklist of css styles style attributes (blacklist overrides whitelist)
42651      * 
42652      */
42653     cblack: false,
42654     /**
42655      * @cfg {Array} whitelist of css styles style attributes (blacklist overrides whitelist)
42656      * 
42657      */
42658     cwhite: false,
42659     
42660      /**
42661      * @cfg {Array} blacklist of html tags - in addition to standard blacklist.
42662      * 
42663      */
42664     black: false,
42665     /**
42666      * @cfg {Array} whitelist of html tags - in addition to statndard whitelist
42667      * 
42668      */
42669     white: false,
42670     
42671     // id of frame..
42672     frameId: false,
42673     
42674     // private properties
42675     validationEvent : false,
42676     deferHeight: true,
42677     initialized : false,
42678     activated : false,
42679     
42680     onFocus : Roo.emptyFn,
42681     iframePad:3,
42682     hideMode:'offsets',
42683     
42684     defaultAutoCreate : { // modified by initCompnoent..
42685         tag: "textarea",
42686         style:"width:500px;height:300px;",
42687         autocomplete: "off"
42688     },
42689
42690     // private
42691     initComponent : function(){
42692         this.addEvents({
42693             /**
42694              * @event initialize
42695              * Fires when the editor is fully initialized (including the iframe)
42696              * @param {HtmlEditor} this
42697              */
42698             initialize: true,
42699             /**
42700              * @event activate
42701              * Fires when the editor is first receives the focus. Any insertion must wait
42702              * until after this event.
42703              * @param {HtmlEditor} this
42704              */
42705             activate: true,
42706              /**
42707              * @event beforesync
42708              * Fires before the textarea is updated with content from the editor iframe. Return false
42709              * to cancel the sync.
42710              * @param {HtmlEditor} this
42711              * @param {String} html
42712              */
42713             beforesync: true,
42714              /**
42715              * @event beforepush
42716              * Fires before the iframe editor is updated with content from the textarea. Return false
42717              * to cancel the push.
42718              * @param {HtmlEditor} this
42719              * @param {String} html
42720              */
42721             beforepush: true,
42722              /**
42723              * @event sync
42724              * Fires when the textarea is updated with content from the editor iframe.
42725              * @param {HtmlEditor} this
42726              * @param {String} html
42727              */
42728             sync: true,
42729              /**
42730              * @event push
42731              * Fires when the iframe editor is updated with content from the textarea.
42732              * @param {HtmlEditor} this
42733              * @param {String} html
42734              */
42735             push: true,
42736              /**
42737              * @event editmodechange
42738              * Fires when the editor switches edit modes
42739              * @param {HtmlEditor} this
42740              * @param {Boolean} sourceEdit True if source edit, false if standard editing.
42741              */
42742             editmodechange: true,
42743             /**
42744              * @event editorevent
42745              * Fires when on any editor (mouse up/down cursor movement etc.) - used for toolbar hooks.
42746              * @param {HtmlEditor} this
42747              */
42748             editorevent: true,
42749             /**
42750              * @event firstfocus
42751              * Fires when on first focus - needed by toolbars..
42752              * @param {HtmlEditor} this
42753              */
42754             firstfocus: true,
42755             /**
42756              * @event autosave
42757              * Auto save the htmlEditor value as a file into Events
42758              * @param {HtmlEditor} this
42759              */
42760             autosave: true,
42761             /**
42762              * @event savedpreview
42763              * preview the saved version of htmlEditor
42764              * @param {HtmlEditor} this
42765              */
42766             savedpreview: true
42767         });
42768         this.defaultAutoCreate =  {
42769             tag: "textarea",
42770             style:'width: ' + this.width + 'px;height: ' + this.height + 'px;',
42771             autocomplete: "off"
42772         };
42773     },
42774
42775     /**
42776      * Protected method that will not generally be called directly. It
42777      * is called when the editor creates its toolbar. Override this method if you need to
42778      * add custom toolbar buttons.
42779      * @param {HtmlEditor} editor
42780      */
42781     createToolbar : function(editor){
42782         Roo.log("create toolbars");
42783         if (!editor.toolbars || !editor.toolbars.length) {
42784             editor.toolbars = [ new Roo.form.HtmlEditor.ToolbarStandard() ]; // can be empty?
42785         }
42786         
42787         for (var i =0 ; i < editor.toolbars.length;i++) {
42788             editor.toolbars[i] = Roo.factory(
42789                     typeof(editor.toolbars[i]) == 'string' ?
42790                         { xtype: editor.toolbars[i]} : editor.toolbars[i],
42791                 Roo.form.HtmlEditor);
42792             editor.toolbars[i].init(editor);
42793         }
42794          
42795         
42796     },
42797
42798      
42799     // private
42800     onRender : function(ct, position)
42801     {
42802         var _t = this;
42803         Roo.form.HtmlEditor.superclass.onRender.call(this, ct, position);
42804         
42805         this.wrap = this.el.wrap({
42806             cls:'x-html-editor-wrap', cn:{cls:'x-html-editor-tb'}
42807         });
42808         
42809         this.editorcore.onRender(ct, position);
42810          
42811         if (this.resizable) {
42812             this.resizeEl = new Roo.Resizable(this.wrap, {
42813                 pinned : true,
42814                 wrap: true,
42815                 dynamic : true,
42816                 minHeight : this.height,
42817                 height: this.height,
42818                 handles : this.resizable,
42819                 width: this.width,
42820                 listeners : {
42821                     resize : function(r, w, h) {
42822                         _t.onResize(w,h); // -something
42823                     }
42824                 }
42825             });
42826             
42827         }
42828         this.createToolbar(this);
42829        
42830         
42831         if(!this.width){
42832             this.setSize(this.wrap.getSize());
42833         }
42834         if (this.resizeEl) {
42835             this.resizeEl.resizeTo.defer(100, this.resizeEl,[ this.width,this.height ] );
42836             // should trigger onReize..
42837         }
42838         
42839 //        if(this.autosave && this.w){
42840 //            this.autoSaveFn = setInterval(this.autosave, 1000);
42841 //        }
42842     },
42843
42844     // private
42845     onResize : function(w, h)
42846     {
42847         //Roo.log('resize: ' +w + ',' + h );
42848         Roo.form.HtmlEditor.superclass.onResize.apply(this, arguments);
42849         var ew = false;
42850         var eh = false;
42851         
42852         if(this.el ){
42853             if(typeof w == 'number'){
42854                 var aw = w - this.wrap.getFrameWidth('lr');
42855                 this.el.setWidth(this.adjustWidth('textarea', aw));
42856                 ew = aw;
42857             }
42858             if(typeof h == 'number'){
42859                 var tbh = 0;
42860                 for (var i =0; i < this.toolbars.length;i++) {
42861                     // fixme - ask toolbars for heights?
42862                     tbh += this.toolbars[i].tb.el.getHeight();
42863                     if (this.toolbars[i].footer) {
42864                         tbh += this.toolbars[i].footer.el.getHeight();
42865                     }
42866                 }
42867                 
42868                 
42869                 
42870                 
42871                 var ah = h - this.wrap.getFrameWidth('tb') - tbh;// this.tb.el.getHeight();
42872                 ah -= 5; // knock a few pixes off for look..
42873                 this.el.setHeight(this.adjustWidth('textarea', ah));
42874                 var eh = ah;
42875             }
42876         }
42877         Roo.log('onResize:' + [w,h,ew,eh].join(',') );
42878         this.editorcore.onResize(ew,eh);
42879         
42880     },
42881
42882     /**
42883      * Toggles the editor between standard and source edit mode.
42884      * @param {Boolean} sourceEdit (optional) True for source edit, false for standard
42885      */
42886     toggleSourceEdit : function(sourceEditMode)
42887     {
42888         this.editorcore.toggleSourceEdit(sourceEditMode);
42889         
42890         if(this.editorcore.sourceEditMode){
42891             Roo.log('editor - showing textarea');
42892             
42893 //            Roo.log('in');
42894 //            Roo.log(this.syncValue());
42895             this.editorcore.syncValue();
42896             this.el.removeClass('x-hidden');
42897             this.el.dom.removeAttribute('tabIndex');
42898             this.el.focus();
42899         }else{
42900             Roo.log('editor - hiding textarea');
42901 //            Roo.log('out')
42902 //            Roo.log(this.pushValue()); 
42903             this.editorcore.pushValue();
42904             
42905             this.el.addClass('x-hidden');
42906             this.el.dom.setAttribute('tabIndex', -1);
42907             //this.deferFocus();
42908         }
42909          
42910         this.setSize(this.wrap.getSize());
42911         this.fireEvent('editmodechange', this, this.editorcore.sourceEditMode);
42912     },
42913  
42914     // private (for BoxComponent)
42915     adjustSize : Roo.BoxComponent.prototype.adjustSize,
42916
42917     // private (for BoxComponent)
42918     getResizeEl : function(){
42919         return this.wrap;
42920     },
42921
42922     // private (for BoxComponent)
42923     getPositionEl : function(){
42924         return this.wrap;
42925     },
42926
42927     // private
42928     initEvents : function(){
42929         this.originalValue = this.getValue();
42930     },
42931
42932     /**
42933      * Overridden and disabled. The editor element does not support standard valid/invalid marking. @hide
42934      * @method
42935      */
42936     markInvalid : Roo.emptyFn,
42937     /**
42938      * Overridden and disabled. The editor element does not support standard valid/invalid marking. @hide
42939      * @method
42940      */
42941     clearInvalid : Roo.emptyFn,
42942
42943     setValue : function(v){
42944         Roo.form.HtmlEditor.superclass.setValue.call(this, v);
42945         this.editorcore.pushValue();
42946     },
42947
42948      
42949     // private
42950     deferFocus : function(){
42951         this.focus.defer(10, this);
42952     },
42953
42954     // doc'ed in Field
42955     focus : function(){
42956         this.editorcore.focus();
42957         
42958     },
42959       
42960
42961     // private
42962     onDestroy : function(){
42963         
42964         
42965         
42966         if(this.rendered){
42967             
42968             for (var i =0; i < this.toolbars.length;i++) {
42969                 // fixme - ask toolbars for heights?
42970                 this.toolbars[i].onDestroy();
42971             }
42972             
42973             this.wrap.dom.innerHTML = '';
42974             this.wrap.remove();
42975         }
42976     },
42977
42978     // private
42979     onFirstFocus : function(){
42980         //Roo.log("onFirstFocus");
42981         this.editorcore.onFirstFocus();
42982          for (var i =0; i < this.toolbars.length;i++) {
42983             this.toolbars[i].onFirstFocus();
42984         }
42985         
42986     },
42987     
42988     // private
42989     syncValue : function()
42990     {
42991         this.editorcore.syncValue();
42992     },
42993     
42994     pushValue : function()
42995     {
42996         this.editorcore.pushValue();
42997     }
42998      
42999     
43000     // hide stuff that is not compatible
43001     /**
43002      * @event blur
43003      * @hide
43004      */
43005     /**
43006      * @event change
43007      * @hide
43008      */
43009     /**
43010      * @event focus
43011      * @hide
43012      */
43013     /**
43014      * @event specialkey
43015      * @hide
43016      */
43017     /**
43018      * @cfg {String} fieldClass @hide
43019      */
43020     /**
43021      * @cfg {String} focusClass @hide
43022      */
43023     /**
43024      * @cfg {String} autoCreate @hide
43025      */
43026     /**
43027      * @cfg {String} inputType @hide
43028      */
43029     /**
43030      * @cfg {String} invalidClass @hide
43031      */
43032     /**
43033      * @cfg {String} invalidText @hide
43034      */
43035     /**
43036      * @cfg {String} msgFx @hide
43037      */
43038     /**
43039      * @cfg {String} validateOnBlur @hide
43040      */
43041 });
43042  
43043     // <script type="text/javascript">
43044 /*
43045  * Based on
43046  * Ext JS Library 1.1.1
43047  * Copyright(c) 2006-2007, Ext JS, LLC.
43048  *  
43049  
43050  */
43051
43052 /**
43053  * @class Roo.form.HtmlEditorToolbar1
43054  * Basic Toolbar
43055  * 
43056  * Usage:
43057  *
43058  new Roo.form.HtmlEditor({
43059     ....
43060     toolbars : [
43061         new Roo.form.HtmlEditorToolbar1({
43062             disable : { fonts: 1 , format: 1, ..., ... , ...],
43063             btns : [ .... ]
43064         })
43065     }
43066      
43067  * 
43068  * @cfg {Object} disable List of elements to disable..
43069  * @cfg {Array} btns List of additional buttons.
43070  * 
43071  * 
43072  * NEEDS Extra CSS? 
43073  * .x-html-editor-tb .x-edit-none .x-btn-text { background: none; }
43074  */
43075  
43076 Roo.form.HtmlEditor.ToolbarStandard = function(config)
43077 {
43078     
43079     Roo.apply(this, config);
43080     
43081     // default disabled, based on 'good practice'..
43082     this.disable = this.disable || {};
43083     Roo.applyIf(this.disable, {
43084         fontSize : true,
43085         colors : true,
43086         specialElements : true
43087     });
43088     
43089     
43090     //Roo.form.HtmlEditorToolbar1.superclass.constructor.call(this, editor.wrap.dom.firstChild, [], config);
43091     // dont call parent... till later.
43092 }
43093
43094 Roo.apply(Roo.form.HtmlEditor.ToolbarStandard.prototype,  {
43095     
43096     tb: false,
43097     
43098     rendered: false,
43099     
43100     editor : false,
43101     editorcore : false,
43102     /**
43103      * @cfg {Object} disable  List of toolbar elements to disable
43104          
43105      */
43106     disable : false,
43107     
43108     
43109      /**
43110      * @cfg {String} createLinkText The default text for the create link prompt
43111      */
43112     createLinkText : 'Please enter the URL for the link:',
43113     /**
43114      * @cfg {String} defaultLinkValue The default value for the create link prompt (defaults to http:/ /)
43115      */
43116     defaultLinkValue : 'http:/'+'/',
43117    
43118     
43119       /**
43120      * @cfg {Array} fontFamilies An array of available font families
43121      */
43122     fontFamilies : [
43123         'Arial',
43124         'Courier New',
43125         'Tahoma',
43126         'Times New Roman',
43127         'Verdana'
43128     ],
43129     
43130     specialChars : [
43131            "&#169;",
43132           "&#174;",     
43133           "&#8482;",    
43134           "&#163;" ,    
43135          // "&#8212;",    
43136           "&#8230;",    
43137           "&#247;" ,    
43138         //  "&#225;" ,     ?? a acute?
43139            "&#8364;"    , //Euro
43140        //   "&#8220;"    ,
43141         //  "&#8221;"    ,
43142         //  "&#8226;"    ,
43143           "&#176;"  //   , // degrees
43144
43145          // "&#233;"     , // e ecute
43146          // "&#250;"     , // u ecute?
43147     ],
43148     
43149     specialElements : [
43150         {
43151             text: "Insert Table",
43152             xtype: 'MenuItem',
43153             xns : Roo.Menu,
43154             ihtml :  '<table><tr><td>Cell</td></tr></table>' 
43155                 
43156         },
43157         {    
43158             text: "Insert Image",
43159             xtype: 'MenuItem',
43160             xns : Roo.Menu,
43161             ihtml : '<img src="about:blank"/>'
43162             
43163         }
43164         
43165          
43166     ],
43167     
43168     
43169     inputElements : [ 
43170             "form", "input:text", "input:hidden", "input:checkbox", "input:radio", "input:password", 
43171             "input:submit", "input:button", "select", "textarea", "label" ],
43172     formats : [
43173         ["p"] ,  
43174         ["h1"],["h2"],["h3"],["h4"],["h5"],["h6"], 
43175         ["pre"],[ "code"], 
43176         ["abbr"],[ "acronym"],[ "address"],[ "cite"],[ "samp"],[ "var"],
43177         ['div'],['span']
43178     ],
43179     
43180     cleanStyles : [
43181         "font-size"
43182     ],
43183      /**
43184      * @cfg {String} defaultFont default font to use.
43185      */
43186     defaultFont: 'tahoma',
43187    
43188     fontSelect : false,
43189     
43190     
43191     formatCombo : false,
43192     
43193     init : function(editor)
43194     {
43195         this.editor = editor;
43196         this.editorcore = editor.editorcore ? editor.editorcore : editor;
43197         var editorcore = this.editorcore;
43198         
43199         var _t = this;
43200         
43201         var fid = editorcore.frameId;
43202         var etb = this;
43203         function btn(id, toggle, handler){
43204             var xid = fid + '-'+ id ;
43205             return {
43206                 id : xid,
43207                 cmd : id,
43208                 cls : 'x-btn-icon x-edit-'+id,
43209                 enableToggle:toggle !== false,
43210                 scope: _t, // was editor...
43211                 handler:handler||_t.relayBtnCmd,
43212                 clickEvent:'mousedown',
43213                 tooltip: etb.buttonTips[id] || undefined, ///tips ???
43214                 tabIndex:-1
43215             };
43216         }
43217         
43218         
43219         
43220         var tb = new Roo.Toolbar(editor.wrap.dom.firstChild);
43221         this.tb = tb;
43222          // stop form submits
43223         tb.el.on('click', function(e){
43224             e.preventDefault(); // what does this do?
43225         });
43226
43227         if(!this.disable.font) { // && !Roo.isSafari){
43228             /* why no safari for fonts 
43229             editor.fontSelect = tb.el.createChild({
43230                 tag:'select',
43231                 tabIndex: -1,
43232                 cls:'x-font-select',
43233                 html: this.createFontOptions()
43234             });
43235             
43236             editor.fontSelect.on('change', function(){
43237                 var font = editor.fontSelect.dom.value;
43238                 editor.relayCmd('fontname', font);
43239                 editor.deferFocus();
43240             }, editor);
43241             
43242             tb.add(
43243                 editor.fontSelect.dom,
43244                 '-'
43245             );
43246             */
43247             
43248         };
43249         if(!this.disable.formats){
43250             this.formatCombo = new Roo.form.ComboBox({
43251                 store: new Roo.data.SimpleStore({
43252                     id : 'tag',
43253                     fields: ['tag'],
43254                     data : this.formats // from states.js
43255                 }),
43256                 blockFocus : true,
43257                 name : '',
43258                 //autoCreate : {tag: "div",  size: "20"},
43259                 displayField:'tag',
43260                 typeAhead: false,
43261                 mode: 'local',
43262                 editable : false,
43263                 triggerAction: 'all',
43264                 emptyText:'Add tag',
43265                 selectOnFocus:true,
43266                 width:135,
43267                 listeners : {
43268                     'select': function(c, r, i) {
43269                         editorcore.insertTag(r.get('tag'));
43270                         editor.focus();
43271                     }
43272                 }
43273
43274             });
43275             tb.addField(this.formatCombo);
43276             
43277         }
43278         
43279         if(!this.disable.format){
43280             tb.add(
43281                 btn('bold'),
43282                 btn('italic'),
43283                 btn('underline')
43284             );
43285         };
43286         if(!this.disable.fontSize){
43287             tb.add(
43288                 '-',
43289                 
43290                 
43291                 btn('increasefontsize', false, editorcore.adjustFont),
43292                 btn('decreasefontsize', false, editorcore.adjustFont)
43293             );
43294         };
43295         
43296         
43297         if(!this.disable.colors){
43298             tb.add(
43299                 '-', {
43300                     id:editorcore.frameId +'-forecolor',
43301                     cls:'x-btn-icon x-edit-forecolor',
43302                     clickEvent:'mousedown',
43303                     tooltip: this.buttonTips['forecolor'] || undefined,
43304                     tabIndex:-1,
43305                     menu : new Roo.menu.ColorMenu({
43306                         allowReselect: true,
43307                         focus: Roo.emptyFn,
43308                         value:'000000',
43309                         plain:true,
43310                         selectHandler: function(cp, color){
43311                             editorcore.execCmd('forecolor', Roo.isSafari || Roo.isIE ? '#'+color : color);
43312                             editor.deferFocus();
43313                         },
43314                         scope: editorcore,
43315                         clickEvent:'mousedown'
43316                     })
43317                 }, {
43318                     id:editorcore.frameId +'backcolor',
43319                     cls:'x-btn-icon x-edit-backcolor',
43320                     clickEvent:'mousedown',
43321                     tooltip: this.buttonTips['backcolor'] || undefined,
43322                     tabIndex:-1,
43323                     menu : new Roo.menu.ColorMenu({
43324                         focus: Roo.emptyFn,
43325                         value:'FFFFFF',
43326                         plain:true,
43327                         allowReselect: true,
43328                         selectHandler: function(cp, color){
43329                             if(Roo.isGecko){
43330                                 editorcore.execCmd('useCSS', false);
43331                                 editorcore.execCmd('hilitecolor', color);
43332                                 editorcore.execCmd('useCSS', true);
43333                                 editor.deferFocus();
43334                             }else{
43335                                 editorcore.execCmd(Roo.isOpera ? 'hilitecolor' : 'backcolor', 
43336                                     Roo.isSafari || Roo.isIE ? '#'+color : color);
43337                                 editor.deferFocus();
43338                             }
43339                         },
43340                         scope:editorcore,
43341                         clickEvent:'mousedown'
43342                     })
43343                 }
43344             );
43345         };
43346         // now add all the items...
43347         
43348
43349         if(!this.disable.alignments){
43350             tb.add(
43351                 '-',
43352                 btn('justifyleft'),
43353                 btn('justifycenter'),
43354                 btn('justifyright')
43355             );
43356         };
43357
43358         //if(!Roo.isSafari){
43359             if(!this.disable.links){
43360                 tb.add(
43361                     '-',
43362                     btn('createlink', false, this.createLink)    /// MOVE TO HERE?!!?!?!?!
43363                 );
43364             };
43365
43366             if(!this.disable.lists){
43367                 tb.add(
43368                     '-',
43369                     btn('insertorderedlist'),
43370                     btn('insertunorderedlist')
43371                 );
43372             }
43373             if(!this.disable.sourceEdit){
43374                 tb.add(
43375                     '-',
43376                     btn('sourceedit', true, function(btn){
43377                         Roo.log(this);
43378                         this.toggleSourceEdit(btn.pressed);
43379                     })
43380                 );
43381             }
43382         //}
43383         
43384         var smenu = { };
43385         // special menu.. - needs to be tidied up..
43386         if (!this.disable.special) {
43387             smenu = {
43388                 text: "&#169;",
43389                 cls: 'x-edit-none',
43390                 
43391                 menu : {
43392                     items : []
43393                 }
43394             };
43395             for (var i =0; i < this.specialChars.length; i++) {
43396                 smenu.menu.items.push({
43397                     
43398                     html: this.specialChars[i],
43399                     handler: function(a,b) {
43400                         editorcore.insertAtCursor(String.fromCharCode(a.html.replace('&#','').replace(';', '')));
43401                         //editor.insertAtCursor(a.html);
43402                         
43403                     },
43404                     tabIndex:-1
43405                 });
43406             }
43407             
43408             
43409             tb.add(smenu);
43410             
43411             
43412         }
43413         
43414         var cmenu = { };
43415         if (!this.disable.cleanStyles) {
43416             cmenu = {
43417                 cls: 'x-btn-icon x-btn-clear',
43418                 
43419                 menu : {
43420                     items : []
43421                 }
43422             };
43423             for (var i =0; i < this.cleanStyles.length; i++) {
43424                 cmenu.menu.items.push({
43425                     actiontype : this.cleanStyles[i],
43426                     html: 'Remove ' + this.cleanStyles[i],
43427                     handler: function(a,b) {
43428                         Roo.log(a);
43429                         Roo.log(b);
43430                         var c = Roo.get(editorcore.doc.body);
43431                         c.select('[style]').each(function(s) {
43432                             s.dom.style.removeProperty(a.actiontype);
43433                         });
43434                         editorcore.syncValue();
43435                     },
43436                     tabIndex:-1
43437                 });
43438             }
43439             cmenu.menu.items.push({
43440                 actiontype : 'word',
43441                 html: 'Remove MS Word Formating',
43442                 handler: function(a,b) {
43443                     editorcore.cleanWord();
43444                     editorcore.syncValue();
43445                 },
43446                 tabIndex:-1
43447             });
43448             
43449             cmenu.menu.items.push({
43450                 actiontype : 'all',
43451                 html: 'Remove All Styles',
43452                 handler: function(a,b) {
43453                     
43454                     var c = Roo.get(editorcore.doc.body);
43455                     c.select('[style]').each(function(s) {
43456                         s.dom.removeAttribute('style');
43457                     });
43458                     editorcore.syncValue();
43459                 },
43460                 tabIndex:-1
43461             });
43462              cmenu.menu.items.push({
43463                 actiontype : 'word',
43464                 html: 'Tidy HTML Source',
43465                 handler: function(a,b) {
43466                     editorcore.doc.body.innerHTML = editorcore.domToHTML();
43467                     editorcore.syncValue();
43468                 },
43469                 tabIndex:-1
43470             });
43471             
43472             
43473             tb.add(cmenu);
43474         }
43475          
43476         if (!this.disable.specialElements) {
43477             var semenu = {
43478                 text: "Other;",
43479                 cls: 'x-edit-none',
43480                 menu : {
43481                     items : []
43482                 }
43483             };
43484             for (var i =0; i < this.specialElements.length; i++) {
43485                 semenu.menu.items.push(
43486                     Roo.apply({ 
43487                         handler: function(a,b) {
43488                             editor.insertAtCursor(this.ihtml);
43489                         }
43490                     }, this.specialElements[i])
43491                 );
43492                     
43493             }
43494             
43495             tb.add(semenu);
43496             
43497             
43498         }
43499          
43500         
43501         if (this.btns) {
43502             for(var i =0; i< this.btns.length;i++) {
43503                 var b = Roo.factory(this.btns[i],Roo.form);
43504                 b.cls =  'x-edit-none';
43505                 b.scope = editorcore;
43506                 tb.add(b);
43507             }
43508         
43509         }
43510         
43511         
43512         
43513         // disable everything...
43514         
43515         this.tb.items.each(function(item){
43516            if(item.id != editorcore.frameId+ '-sourceedit'){
43517                 item.disable();
43518             }
43519         });
43520         this.rendered = true;
43521         
43522         // the all the btns;
43523         editor.on('editorevent', this.updateToolbar, this);
43524         // other toolbars need to implement this..
43525         //editor.on('editmodechange', this.updateToolbar, this);
43526     },
43527     
43528     
43529     relayBtnCmd : function(btn) {
43530         this.editorcore.relayCmd(btn.cmd);
43531     },
43532     // private used internally
43533     createLink : function(){
43534         Roo.log("create link?");
43535         var url = prompt(this.createLinkText, this.defaultLinkValue);
43536         if(url && url != 'http:/'+'/'){
43537             this.editorcore.relayCmd('createlink', url);
43538         }
43539     },
43540
43541     
43542     /**
43543      * Protected method that will not generally be called directly. It triggers
43544      * a toolbar update by reading the markup state of the current selection in the editor.
43545      */
43546     updateToolbar: function(){
43547
43548         if(!this.editorcore.activated){
43549             this.editor.onFirstFocus();
43550             return;
43551         }
43552
43553         var btns = this.tb.items.map, 
43554             doc = this.editorcore.doc,
43555             frameId = this.editorcore.frameId;
43556
43557         if(!this.disable.font && !Roo.isSafari){
43558             /*
43559             var name = (doc.queryCommandValue('FontName')||this.editor.defaultFont).toLowerCase();
43560             if(name != this.fontSelect.dom.value){
43561                 this.fontSelect.dom.value = name;
43562             }
43563             */
43564         }
43565         if(!this.disable.format){
43566             btns[frameId + '-bold'].toggle(doc.queryCommandState('bold'));
43567             btns[frameId + '-italic'].toggle(doc.queryCommandState('italic'));
43568             btns[frameId + '-underline'].toggle(doc.queryCommandState('underline'));
43569         }
43570         if(!this.disable.alignments){
43571             btns[frameId + '-justifyleft'].toggle(doc.queryCommandState('justifyleft'));
43572             btns[frameId + '-justifycenter'].toggle(doc.queryCommandState('justifycenter'));
43573             btns[frameId + '-justifyright'].toggle(doc.queryCommandState('justifyright'));
43574         }
43575         if(!Roo.isSafari && !this.disable.lists){
43576             btns[frameId + '-insertorderedlist'].toggle(doc.queryCommandState('insertorderedlist'));
43577             btns[frameId + '-insertunorderedlist'].toggle(doc.queryCommandState('insertunorderedlist'));
43578         }
43579         
43580         var ans = this.editorcore.getAllAncestors();
43581         if (this.formatCombo) {
43582             
43583             
43584             var store = this.formatCombo.store;
43585             this.formatCombo.setValue("");
43586             for (var i =0; i < ans.length;i++) {
43587                 if (ans[i] && store.query('tag',ans[i].tagName.toLowerCase(), false).length) {
43588                     // select it..
43589                     this.formatCombo.setValue(ans[i].tagName.toLowerCase());
43590                     break;
43591                 }
43592             }
43593         }
43594         
43595         
43596         
43597         // hides menus... - so this cant be on a menu...
43598         Roo.menu.MenuMgr.hideAll();
43599
43600         //this.editorsyncValue();
43601     },
43602    
43603     
43604     createFontOptions : function(){
43605         var buf = [], fs = this.fontFamilies, ff, lc;
43606         
43607         
43608         
43609         for(var i = 0, len = fs.length; i< len; i++){
43610             ff = fs[i];
43611             lc = ff.toLowerCase();
43612             buf.push(
43613                 '<option value="',lc,'" style="font-family:',ff,';"',
43614                     (this.defaultFont == lc ? ' selected="true">' : '>'),
43615                     ff,
43616                 '</option>'
43617             );
43618         }
43619         return buf.join('');
43620     },
43621     
43622     toggleSourceEdit : function(sourceEditMode){
43623         
43624         Roo.log("toolbar toogle");
43625         if(sourceEditMode === undefined){
43626             sourceEditMode = !this.sourceEditMode;
43627         }
43628         this.sourceEditMode = sourceEditMode === true;
43629         var btn = this.tb.items.get(this.editorcore.frameId +'-sourceedit');
43630         // just toggle the button?
43631         if(btn.pressed !== this.sourceEditMode){
43632             btn.toggle(this.sourceEditMode);
43633             return;
43634         }
43635         
43636         if(sourceEditMode){
43637             Roo.log("disabling buttons");
43638             this.tb.items.each(function(item){
43639                 if(item.cmd != 'sourceedit'){
43640                     item.disable();
43641                 }
43642             });
43643           
43644         }else{
43645             Roo.log("enabling buttons");
43646             if(this.editorcore.initialized){
43647                 this.tb.items.each(function(item){
43648                     item.enable();
43649                 });
43650             }
43651             
43652         }
43653         Roo.log("calling toggole on editor");
43654         // tell the editor that it's been pressed..
43655         this.editor.toggleSourceEdit(sourceEditMode);
43656        
43657     },
43658      /**
43659      * Object collection of toolbar tooltips for the buttons in the editor. The key
43660      * is the command id associated with that button and the value is a valid QuickTips object.
43661      * For example:
43662 <pre><code>
43663 {
43664     bold : {
43665         title: 'Bold (Ctrl+B)',
43666         text: 'Make the selected text bold.',
43667         cls: 'x-html-editor-tip'
43668     },
43669     italic : {
43670         title: 'Italic (Ctrl+I)',
43671         text: 'Make the selected text italic.',
43672         cls: 'x-html-editor-tip'
43673     },
43674     ...
43675 </code></pre>
43676     * @type Object
43677      */
43678     buttonTips : {
43679         bold : {
43680             title: 'Bold (Ctrl+B)',
43681             text: 'Make the selected text bold.',
43682             cls: 'x-html-editor-tip'
43683         },
43684         italic : {
43685             title: 'Italic (Ctrl+I)',
43686             text: 'Make the selected text italic.',
43687             cls: 'x-html-editor-tip'
43688         },
43689         underline : {
43690             title: 'Underline (Ctrl+U)',
43691             text: 'Underline the selected text.',
43692             cls: 'x-html-editor-tip'
43693         },
43694         increasefontsize : {
43695             title: 'Grow Text',
43696             text: 'Increase the font size.',
43697             cls: 'x-html-editor-tip'
43698         },
43699         decreasefontsize : {
43700             title: 'Shrink Text',
43701             text: 'Decrease the font size.',
43702             cls: 'x-html-editor-tip'
43703         },
43704         backcolor : {
43705             title: 'Text Highlight Color',
43706             text: 'Change the background color of the selected text.',
43707             cls: 'x-html-editor-tip'
43708         },
43709         forecolor : {
43710             title: 'Font Color',
43711             text: 'Change the color of the selected text.',
43712             cls: 'x-html-editor-tip'
43713         },
43714         justifyleft : {
43715             title: 'Align Text Left',
43716             text: 'Align text to the left.',
43717             cls: 'x-html-editor-tip'
43718         },
43719         justifycenter : {
43720             title: 'Center Text',
43721             text: 'Center text in the editor.',
43722             cls: 'x-html-editor-tip'
43723         },
43724         justifyright : {
43725             title: 'Align Text Right',
43726             text: 'Align text to the right.',
43727             cls: 'x-html-editor-tip'
43728         },
43729         insertunorderedlist : {
43730             title: 'Bullet List',
43731             text: 'Start a bulleted list.',
43732             cls: 'x-html-editor-tip'
43733         },
43734         insertorderedlist : {
43735             title: 'Numbered List',
43736             text: 'Start a numbered list.',
43737             cls: 'x-html-editor-tip'
43738         },
43739         createlink : {
43740             title: 'Hyperlink',
43741             text: 'Make the selected text a hyperlink.',
43742             cls: 'x-html-editor-tip'
43743         },
43744         sourceedit : {
43745             title: 'Source Edit',
43746             text: 'Switch to source editing mode.',
43747             cls: 'x-html-editor-tip'
43748         }
43749     },
43750     // private
43751     onDestroy : function(){
43752         if(this.rendered){
43753             
43754             this.tb.items.each(function(item){
43755                 if(item.menu){
43756                     item.menu.removeAll();
43757                     if(item.menu.el){
43758                         item.menu.el.destroy();
43759                     }
43760                 }
43761                 item.destroy();
43762             });
43763              
43764         }
43765     },
43766     onFirstFocus: function() {
43767         this.tb.items.each(function(item){
43768            item.enable();
43769         });
43770     }
43771 });
43772
43773
43774
43775
43776 // <script type="text/javascript">
43777 /*
43778  * Based on
43779  * Ext JS Library 1.1.1
43780  * Copyright(c) 2006-2007, Ext JS, LLC.
43781  *  
43782  
43783  */
43784
43785  
43786 /**
43787  * @class Roo.form.HtmlEditor.ToolbarContext
43788  * Context Toolbar
43789  * 
43790  * Usage:
43791  *
43792  new Roo.form.HtmlEditor({
43793     ....
43794     toolbars : [
43795         { xtype: 'ToolbarStandard', styles : {} }
43796         { xtype: 'ToolbarContext', disable : {} }
43797     ]
43798 })
43799
43800      
43801  * 
43802  * @config : {Object} disable List of elements to disable.. (not done yet.)
43803  * @config : {Object} styles  Map of styles available.
43804  * 
43805  */
43806
43807 Roo.form.HtmlEditor.ToolbarContext = function(config)
43808 {
43809     
43810     Roo.apply(this, config);
43811     //Roo.form.HtmlEditorToolbar1.superclass.constructor.call(this, editor.wrap.dom.firstChild, [], config);
43812     // dont call parent... till later.
43813     this.styles = this.styles || {};
43814 }
43815
43816  
43817
43818 Roo.form.HtmlEditor.ToolbarContext.types = {
43819     'IMG' : {
43820         width : {
43821             title: "Width",
43822             width: 40
43823         },
43824         height:  {
43825             title: "Height",
43826             width: 40
43827         },
43828         align: {
43829             title: "Align",
43830             opts : [ [""],[ "left"],[ "right"],[ "center"],[ "top"]],
43831             width : 80
43832             
43833         },
43834         border: {
43835             title: "Border",
43836             width: 40
43837         },
43838         alt: {
43839             title: "Alt",
43840             width: 120
43841         },
43842         src : {
43843             title: "Src",
43844             width: 220
43845         }
43846         
43847     },
43848     'A' : {
43849         name : {
43850             title: "Name",
43851             width: 50
43852         },
43853         target:  {
43854             title: "Target",
43855             width: 120
43856         },
43857         href:  {
43858             title: "Href",
43859             width: 220
43860         } // border?
43861         
43862     },
43863     'TABLE' : {
43864         rows : {
43865             title: "Rows",
43866             width: 20
43867         },
43868         cols : {
43869             title: "Cols",
43870             width: 20
43871         },
43872         width : {
43873             title: "Width",
43874             width: 40
43875         },
43876         height : {
43877             title: "Height",
43878             width: 40
43879         },
43880         border : {
43881             title: "Border",
43882             width: 20
43883         }
43884     },
43885     'TD' : {
43886         width : {
43887             title: "Width",
43888             width: 40
43889         },
43890         height : {
43891             title: "Height",
43892             width: 40
43893         },   
43894         align: {
43895             title: "Align",
43896             opts : [[""],[ "left"],[ "center"],[ "right"],[ "justify"],[ "char"]],
43897             width: 80
43898         },
43899         valign: {
43900             title: "Valign",
43901             opts : [[""],[ "top"],[ "middle"],[ "bottom"],[ "baseline"]],
43902             width: 80
43903         },
43904         colspan: {
43905             title: "Colspan",
43906             width: 20
43907             
43908         },
43909          'font-family'  : {
43910             title : "Font",
43911             style : 'fontFamily',
43912             displayField: 'display',
43913             optname : 'font-family',
43914             width: 140
43915         }
43916     },
43917     'INPUT' : {
43918         name : {
43919             title: "name",
43920             width: 120
43921         },
43922         value : {
43923             title: "Value",
43924             width: 120
43925         },
43926         width : {
43927             title: "Width",
43928             width: 40
43929         }
43930     },
43931     'LABEL' : {
43932         'for' : {
43933             title: "For",
43934             width: 120
43935         }
43936     },
43937     'TEXTAREA' : {
43938           name : {
43939             title: "name",
43940             width: 120
43941         },
43942         rows : {
43943             title: "Rows",
43944             width: 20
43945         },
43946         cols : {
43947             title: "Cols",
43948             width: 20
43949         }
43950     },
43951     'SELECT' : {
43952         name : {
43953             title: "name",
43954             width: 120
43955         },
43956         selectoptions : {
43957             title: "Options",
43958             width: 200
43959         }
43960     },
43961     
43962     // should we really allow this??
43963     // should this just be 
43964     'BODY' : {
43965         title : {
43966             title: "Title",
43967             width: 200,
43968             disabled : true
43969         }
43970     },
43971     'SPAN' : {
43972         'font-family'  : {
43973             title : "Font",
43974             style : 'fontFamily',
43975             displayField: 'display',
43976             optname : 'font-family',
43977             width: 140
43978         }
43979     },
43980     'DIV' : {
43981         'font-family'  : {
43982             title : "Font",
43983             style : 'fontFamily',
43984             displayField: 'display',
43985             optname : 'font-family',
43986             width: 140
43987         }
43988     },
43989      'P' : {
43990         'font-family'  : {
43991             title : "Font",
43992             style : 'fontFamily',
43993             displayField: 'display',
43994             optname : 'font-family',
43995             width: 140
43996         }
43997     },
43998     
43999     '*' : {
44000         // empty..
44001     }
44002
44003 };
44004
44005 // this should be configurable.. - you can either set it up using stores, or modify options somehwere..
44006 Roo.form.HtmlEditor.ToolbarContext.stores = false;
44007
44008 Roo.form.HtmlEditor.ToolbarContext.options = {
44009         'font-family'  : [ 
44010                 [ 'Helvetica,Arial,sans-serif', 'Helvetica'],
44011                 [ 'Courier New', 'Courier New'],
44012                 [ 'Tahoma', 'Tahoma'],
44013                 [ 'Times New Roman,serif', 'Times'],
44014                 [ 'Verdana','Verdana' ]
44015         ]
44016 };
44017
44018 // fixme - these need to be configurable..
44019  
44020
44021 Roo.form.HtmlEditor.ToolbarContext.types
44022
44023
44024 Roo.apply(Roo.form.HtmlEditor.ToolbarContext.prototype,  {
44025     
44026     tb: false,
44027     
44028     rendered: false,
44029     
44030     editor : false,
44031     editorcore : false,
44032     /**
44033      * @cfg {Object} disable  List of toolbar elements to disable
44034          
44035      */
44036     disable : false,
44037     /**
44038      * @cfg {Object} styles List of styles 
44039      *    eg. { '*' : [ 'headline' ] , 'TD' : [ 'underline', 'double-underline' ] } 
44040      *
44041      * These must be defined in the page, so they get rendered correctly..
44042      * .headline { }
44043      * TD.underline { }
44044      * 
44045      */
44046     styles : false,
44047     
44048     options: false,
44049     
44050     toolbars : false,
44051     
44052     init : function(editor)
44053     {
44054         this.editor = editor;
44055         this.editorcore = editor.editorcore ? editor.editorcore : editor;
44056         var editorcore = this.editorcore;
44057         
44058         var fid = editorcore.frameId;
44059         var etb = this;
44060         function btn(id, toggle, handler){
44061             var xid = fid + '-'+ id ;
44062             return {
44063                 id : xid,
44064                 cmd : id,
44065                 cls : 'x-btn-icon x-edit-'+id,
44066                 enableToggle:toggle !== false,
44067                 scope: editorcore, // was editor...
44068                 handler:handler||editorcore.relayBtnCmd,
44069                 clickEvent:'mousedown',
44070                 tooltip: etb.buttonTips[id] || undefined, ///tips ???
44071                 tabIndex:-1
44072             };
44073         }
44074         // create a new element.
44075         var wdiv = editor.wrap.createChild({
44076                 tag: 'div'
44077             }, editor.wrap.dom.firstChild.nextSibling, true);
44078         
44079         // can we do this more than once??
44080         
44081          // stop form submits
44082       
44083  
44084         // disable everything...
44085         var ty= Roo.form.HtmlEditor.ToolbarContext.types;
44086         this.toolbars = {};
44087            
44088         for (var i in  ty) {
44089           
44090             this.toolbars[i] = this.buildToolbar(ty[i],i);
44091         }
44092         this.tb = this.toolbars.BODY;
44093         this.tb.el.show();
44094         this.buildFooter();
44095         this.footer.show();
44096         editor.on('hide', function( ) { this.footer.hide() }, this);
44097         editor.on('show', function( ) { this.footer.show() }, this);
44098         
44099          
44100         this.rendered = true;
44101         
44102         // the all the btns;
44103         editor.on('editorevent', this.updateToolbar, this);
44104         // other toolbars need to implement this..
44105         //editor.on('editmodechange', this.updateToolbar, this);
44106     },
44107     
44108     
44109     
44110     /**
44111      * Protected method that will not generally be called directly. It triggers
44112      * a toolbar update by reading the markup state of the current selection in the editor.
44113      */
44114     updateToolbar: function(editor,ev,sel){
44115
44116         //Roo.log(ev);
44117         // capture mouse up - this is handy for selecting images..
44118         // perhaps should go somewhere else...
44119         if(!this.editorcore.activated){
44120              this.editor.onFirstFocus();
44121             return;
44122         }
44123         
44124         // http://developer.yahoo.com/yui/docs/simple-editor.js.html
44125         // selectNode - might want to handle IE?
44126         if (ev &&
44127             (ev.type == 'mouseup' || ev.type == 'click' ) &&
44128             ev.target && ev.target.tagName == 'IMG') {
44129             // they have click on an image...
44130             // let's see if we can change the selection...
44131             sel = ev.target;
44132          
44133               var nodeRange = sel.ownerDocument.createRange();
44134             try {
44135                 nodeRange.selectNode(sel);
44136             } catch (e) {
44137                 nodeRange.selectNodeContents(sel);
44138             }
44139             //nodeRange.collapse(true);
44140             var s = this.editorcore.win.getSelection();
44141             s.removeAllRanges();
44142             s.addRange(nodeRange);
44143         }  
44144         
44145       
44146         var updateFooter = sel ? false : true;
44147         
44148         
44149         var ans = this.editorcore.getAllAncestors();
44150         
44151         // pick
44152         var ty= Roo.form.HtmlEditor.ToolbarContext.types;
44153         
44154         if (!sel) { 
44155             sel = ans.length ? (ans[0] ?  ans[0]  : ans[1]) : this.editorcore.doc.body;
44156             sel = sel ? sel : this.editorcore.doc.body;
44157             sel = sel.tagName.length ? sel : this.editorcore.doc.body;
44158             
44159         }
44160         // pick a menu that exists..
44161         var tn = sel.tagName.toUpperCase();
44162         //sel = typeof(ty[tn]) != 'undefined' ? sel : this.editor.doc.body;
44163         
44164         tn = sel.tagName.toUpperCase();
44165         
44166         var lastSel = this.tb.selectedNode
44167         
44168         this.tb.selectedNode = sel;
44169         
44170         // if current menu does not match..
44171         if ((this.tb.name != tn) || (lastSel != this.tb.selectedNode)) {
44172                 
44173             this.tb.el.hide();
44174             ///console.log("show: " + tn);
44175             this.tb =  typeof(ty[tn]) != 'undefined' ? this.toolbars[tn] : this.toolbars['*'];
44176             this.tb.el.show();
44177             // update name
44178             this.tb.items.first().el.innerHTML = tn + ':&nbsp;';
44179             
44180             
44181             // update attributes
44182             if (this.tb.fields) {
44183                 this.tb.fields.each(function(e) {
44184                     if (e.stylename) {
44185                         e.setValue(sel.style[e.stylename]);
44186                         return;
44187                     } 
44188                    e.setValue(sel.getAttribute(e.attrname));
44189                 });
44190             }
44191             
44192             var hasStyles = false;
44193             for(var i in this.styles) {
44194                 hasStyles = true;
44195                 break;
44196             }
44197             
44198             // update styles
44199             if (hasStyles) { 
44200                 var st = this.tb.fields.item(0);
44201                 
44202                 st.store.removeAll();
44203                
44204                 
44205                 var cn = sel.className.split(/\s+/);
44206                 
44207                 var avs = [];
44208                 if (this.styles['*']) {
44209                     
44210                     Roo.each(this.styles['*'], function(v) {
44211                         avs.push( [ v , cn.indexOf(v) > -1 ? 1 : 0 ] );         
44212                     });
44213                 }
44214                 if (this.styles[tn]) { 
44215                     Roo.each(this.styles[tn], function(v) {
44216                         avs.push( [ v , cn.indexOf(v) > -1 ? 1 : 0 ] );         
44217                     });
44218                 }
44219                 
44220                 st.store.loadData(avs);
44221                 st.collapse();
44222                 st.setValue(cn);
44223             }
44224             // flag our selected Node.
44225             this.tb.selectedNode = sel;
44226            
44227            
44228             Roo.menu.MenuMgr.hideAll();
44229
44230         }
44231         
44232         if (!updateFooter) {
44233             //this.footDisp.dom.innerHTML = ''; 
44234             return;
44235         }
44236         // update the footer
44237         //
44238         var html = '';
44239         
44240         this.footerEls = ans.reverse();
44241         Roo.each(this.footerEls, function(a,i) {
44242             if (!a) { return; }
44243             html += html.length ? ' &gt; '  :  '';
44244             
44245             html += '<span class="x-ed-loc-' + i + '">' + a.tagName + '</span>';
44246             
44247         });
44248        
44249         // 
44250         var sz = this.footDisp.up('td').getSize();
44251         this.footDisp.dom.style.width = (sz.width -10) + 'px';
44252         this.footDisp.dom.style.marginLeft = '5px';
44253         
44254         this.footDisp.dom.style.overflow = 'hidden';
44255         
44256         this.footDisp.dom.innerHTML = html;
44257             
44258         //this.editorsyncValue();
44259     },
44260      
44261     
44262    
44263        
44264     // private
44265     onDestroy : function(){
44266         if(this.rendered){
44267             
44268             this.tb.items.each(function(item){
44269                 if(item.menu){
44270                     item.menu.removeAll();
44271                     if(item.menu.el){
44272                         item.menu.el.destroy();
44273                     }
44274                 }
44275                 item.destroy();
44276             });
44277              
44278         }
44279     },
44280     onFirstFocus: function() {
44281         // need to do this for all the toolbars..
44282         this.tb.items.each(function(item){
44283            item.enable();
44284         });
44285     },
44286     buildToolbar: function(tlist, nm)
44287     {
44288         var editor = this.editor;
44289         var editorcore = this.editorcore;
44290          // create a new element.
44291         var wdiv = editor.wrap.createChild({
44292                 tag: 'div'
44293             }, editor.wrap.dom.firstChild.nextSibling, true);
44294         
44295        
44296         var tb = new Roo.Toolbar(wdiv);
44297         // add the name..
44298         
44299         tb.add(nm+ ":&nbsp;");
44300         
44301         var styles = [];
44302         for(var i in this.styles) {
44303             styles.push(i);
44304         }
44305         
44306         // styles...
44307         if (styles && styles.length) {
44308             
44309             // this needs a multi-select checkbox...
44310             tb.addField( new Roo.form.ComboBox({
44311                 store: new Roo.data.SimpleStore({
44312                     id : 'val',
44313                     fields: ['val', 'selected'],
44314                     data : [] 
44315                 }),
44316                 name : '-roo-edit-className',
44317                 attrname : 'className',
44318                 displayField: 'val',
44319                 typeAhead: false,
44320                 mode: 'local',
44321                 editable : false,
44322                 triggerAction: 'all',
44323                 emptyText:'Select Style',
44324                 selectOnFocus:true,
44325                 width: 130,
44326                 listeners : {
44327                     'select': function(c, r, i) {
44328                         // initial support only for on class per el..
44329                         tb.selectedNode.className =  r ? r.get('val') : '';
44330                         editorcore.syncValue();
44331                     }
44332                 }
44333     
44334             }));
44335         }
44336         
44337         var tbc = Roo.form.HtmlEditor.ToolbarContext;
44338         var tbops = tbc.options;
44339         
44340         for (var i in tlist) {
44341             
44342             var item = tlist[i];
44343             tb.add(item.title + ":&nbsp;");
44344             
44345             
44346             //optname == used so you can configure the options available..
44347             var opts = item.opts ? item.opts : false;
44348             if (item.optname) {
44349                 opts = tbops[item.optname];
44350            
44351             }
44352             
44353             if (opts) {
44354                 // opts == pulldown..
44355                 tb.addField( new Roo.form.ComboBox({
44356                     store:   typeof(tbc.stores[i]) != 'undefined' ?  Roo.factory(tbc.stores[i],Roo.data) : new Roo.data.SimpleStore({
44357                         id : 'val',
44358                         fields: ['val', 'display'],
44359                         data : opts  
44360                     }),
44361                     name : '-roo-edit-' + i,
44362                     attrname : i,
44363                     stylename : item.style ? item.style : false,
44364                     displayField: item.displayField ? item.displayField : 'val',
44365                     valueField :  'val',
44366                     typeAhead: false,
44367                     mode: typeof(tbc.stores[i]) != 'undefined'  ? 'remote' : 'local',
44368                     editable : false,
44369                     triggerAction: 'all',
44370                     emptyText:'Select',
44371                     selectOnFocus:true,
44372                     width: item.width ? item.width  : 130,
44373                     listeners : {
44374                         'select': function(c, r, i) {
44375                             if (c.stylename) {
44376                                 tb.selectedNode.style[c.stylename] =  r.get('val');
44377                                 return;
44378                             }
44379                             tb.selectedNode.setAttribute(c.attrname, r.get('val'));
44380                         }
44381                     }
44382
44383                 }));
44384                 continue;
44385                     
44386                  
44387                 
44388                 tb.addField( new Roo.form.TextField({
44389                     name: i,
44390                     width: 100,
44391                     //allowBlank:false,
44392                     value: ''
44393                 }));
44394                 continue;
44395             }
44396             tb.addField( new Roo.form.TextField({
44397                 name: '-roo-edit-' + i,
44398                 attrname : i,
44399                 
44400                 width: item.width,
44401                 //allowBlank:true,
44402                 value: '',
44403                 listeners: {
44404                     'change' : function(f, nv, ov) {
44405                         tb.selectedNode.setAttribute(f.attrname, nv);
44406                     }
44407                 }
44408             }));
44409              
44410         }
44411         tb.addFill();
44412         var _this = this;
44413         tb.addButton( {
44414             text: 'Remove Tag',
44415     
44416             listeners : {
44417                 click : function ()
44418                 {
44419                     // remove
44420                     // undo does not work.
44421                      
44422                     var sn = tb.selectedNode;
44423                     
44424                     var pn = sn.parentNode;
44425                     
44426                     var stn =  sn.childNodes[0];
44427                     var en = sn.childNodes[sn.childNodes.length - 1 ];
44428                     while (sn.childNodes.length) {
44429                         var node = sn.childNodes[0];
44430                         sn.removeChild(node);
44431                         //Roo.log(node);
44432                         pn.insertBefore(node, sn);
44433                         
44434                     }
44435                     pn.removeChild(sn);
44436                     var range = editorcore.createRange();
44437         
44438                     range.setStart(stn,0);
44439                     range.setEnd(en,0); //????
44440                     //range.selectNode(sel);
44441                     
44442                     
44443                     var selection = editorcore.getSelection();
44444                     selection.removeAllRanges();
44445                     selection.addRange(range);
44446                     
44447                     
44448                     
44449                     //_this.updateToolbar(null, null, pn);
44450                     _this.updateToolbar(null, null, null);
44451                     _this.footDisp.dom.innerHTML = ''; 
44452                 }
44453             }
44454             
44455                     
44456                 
44457             
44458         });
44459         
44460         
44461         tb.el.on('click', function(e){
44462             e.preventDefault(); // what does this do?
44463         });
44464         tb.el.setVisibilityMode( Roo.Element.DISPLAY);
44465         tb.el.hide();
44466         tb.name = nm;
44467         // dont need to disable them... as they will get hidden
44468         return tb;
44469          
44470         
44471     },
44472     buildFooter : function()
44473     {
44474         
44475         var fel = this.editor.wrap.createChild();
44476         this.footer = new Roo.Toolbar(fel);
44477         // toolbar has scrolly on left / right?
44478         var footDisp= new Roo.Toolbar.Fill();
44479         var _t = this;
44480         this.footer.add(
44481             {
44482                 text : '&lt;',
44483                 xtype: 'Button',
44484                 handler : function() {
44485                     _t.footDisp.scrollTo('left',0,true)
44486                 }
44487             }
44488         );
44489         this.footer.add( footDisp );
44490         this.footer.add( 
44491             {
44492                 text : '&gt;',
44493                 xtype: 'Button',
44494                 handler : function() {
44495                     // no animation..
44496                     _t.footDisp.select('span').last().scrollIntoView(_t.footDisp,true);
44497                 }
44498             }
44499         );
44500         var fel = Roo.get(footDisp.el);
44501         fel.addClass('x-editor-context');
44502         this.footDispWrap = fel; 
44503         this.footDispWrap.overflow  = 'hidden';
44504         
44505         this.footDisp = fel.createChild();
44506         this.footDispWrap.on('click', this.onContextClick, this)
44507         
44508         
44509     },
44510     onContextClick : function (ev,dom)
44511     {
44512         ev.preventDefault();
44513         var  cn = dom.className;
44514         //Roo.log(cn);
44515         if (!cn.match(/x-ed-loc-/)) {
44516             return;
44517         }
44518         var n = cn.split('-').pop();
44519         var ans = this.footerEls;
44520         var sel = ans[n];
44521         
44522          // pick
44523         var range = this.editorcore.createRange();
44524         
44525         range.selectNodeContents(sel);
44526         //range.selectNode(sel);
44527         
44528         
44529         var selection = this.editorcore.getSelection();
44530         selection.removeAllRanges();
44531         selection.addRange(range);
44532         
44533         
44534         
44535         this.updateToolbar(null, null, sel);
44536         
44537         
44538     }
44539     
44540     
44541     
44542     
44543     
44544 });
44545
44546
44547
44548
44549
44550 /*
44551  * Based on:
44552  * Ext JS Library 1.1.1
44553  * Copyright(c) 2006-2007, Ext JS, LLC.
44554  *
44555  * Originally Released Under LGPL - original licence link has changed is not relivant.
44556  *
44557  * Fork - LGPL
44558  * <script type="text/javascript">
44559  */
44560  
44561 /**
44562  * @class Roo.form.BasicForm
44563  * @extends Roo.util.Observable
44564  * Supplies the functionality to do "actions" on forms and initialize Roo.form.Field types on existing markup.
44565  * @constructor
44566  * @param {String/HTMLElement/Roo.Element} el The form element or its id
44567  * @param {Object} config Configuration options
44568  */
44569 Roo.form.BasicForm = function(el, config){
44570     this.allItems = [];
44571     this.childForms = [];
44572     Roo.apply(this, config);
44573     /*
44574      * The Roo.form.Field items in this form.
44575      * @type MixedCollection
44576      */
44577      
44578      
44579     this.items = new Roo.util.MixedCollection(false, function(o){
44580         return o.id || (o.id = Roo.id());
44581     });
44582     this.addEvents({
44583         /**
44584          * @event beforeaction
44585          * Fires before any action is performed. Return false to cancel the action.
44586          * @param {Form} this
44587          * @param {Action} action The action to be performed
44588          */
44589         beforeaction: true,
44590         /**
44591          * @event actionfailed
44592          * Fires when an action fails.
44593          * @param {Form} this
44594          * @param {Action} action The action that failed
44595          */
44596         actionfailed : true,
44597         /**
44598          * @event actioncomplete
44599          * Fires when an action is completed.
44600          * @param {Form} this
44601          * @param {Action} action The action that completed
44602          */
44603         actioncomplete : true
44604     });
44605     if(el){
44606         this.initEl(el);
44607     }
44608     Roo.form.BasicForm.superclass.constructor.call(this);
44609 };
44610
44611 Roo.extend(Roo.form.BasicForm, Roo.util.Observable, {
44612     /**
44613      * @cfg {String} method
44614      * The request method to use (GET or POST) for form actions if one isn't supplied in the action options.
44615      */
44616     /**
44617      * @cfg {DataReader} reader
44618      * An Roo.data.DataReader (e.g. {@link Roo.data.XmlReader}) to be used to read data when executing "load" actions.
44619      * This is optional as there is built-in support for processing JSON.
44620      */
44621     /**
44622      * @cfg {DataReader} errorReader
44623      * An Roo.data.DataReader (e.g. {@link Roo.data.XmlReader}) to be used to read data when reading validation errors on "submit" actions.
44624      * This is completely optional as there is built-in support for processing JSON.
44625      */
44626     /**
44627      * @cfg {String} url
44628      * The URL to use for form actions if one isn't supplied in the action options.
44629      */
44630     /**
44631      * @cfg {Boolean} fileUpload
44632      * Set to true if this form is a file upload.
44633      */
44634      
44635     /**
44636      * @cfg {Object} baseParams
44637      * Parameters to pass with all requests. e.g. baseParams: {id: '123', foo: 'bar'}.
44638      */
44639      /**
44640      
44641     /**
44642      * @cfg {Number} timeout Timeout for form actions in seconds (default is 30 seconds).
44643      */
44644     timeout: 30,
44645
44646     // private
44647     activeAction : null,
44648
44649     /**
44650      * @cfg {Boolean} trackResetOnLoad If set to true, form.reset() resets to the last loaded
44651      * or setValues() data instead of when the form was first created.
44652      */
44653     trackResetOnLoad : false,
44654     
44655     
44656     /**
44657      * childForms - used for multi-tab forms
44658      * @type {Array}
44659      */
44660     childForms : false,
44661     
44662     /**
44663      * allItems - full list of fields.
44664      * @type {Array}
44665      */
44666     allItems : false,
44667     
44668     /**
44669      * By default wait messages are displayed with Roo.MessageBox.wait. You can target a specific
44670      * element by passing it or its id or mask the form itself by passing in true.
44671      * @type Mixed
44672      */
44673     waitMsgTarget : false,
44674
44675     // private
44676     initEl : function(el){
44677         this.el = Roo.get(el);
44678         this.id = this.el.id || Roo.id();
44679         this.el.on('submit', this.onSubmit, this);
44680         this.el.addClass('x-form');
44681     },
44682
44683     // private
44684     onSubmit : function(e){
44685         e.stopEvent();
44686     },
44687
44688     /**
44689      * Returns true if client-side validation on the form is successful.
44690      * @return Boolean
44691      */
44692     isValid : function(){
44693         var valid = true;
44694         this.items.each(function(f){
44695            if(!f.validate()){
44696                valid = false;
44697            }
44698         });
44699         return valid;
44700     },
44701
44702     /**
44703      * Returns true if any fields in this form have changed since their original load.
44704      * @return Boolean
44705      */
44706     isDirty : function(){
44707         var dirty = false;
44708         this.items.each(function(f){
44709            if(f.isDirty()){
44710                dirty = true;
44711                return false;
44712            }
44713         });
44714         return dirty;
44715     },
44716
44717     /**
44718      * Performs a predefined action (submit or load) or custom actions you define on this form.
44719      * @param {String} actionName The name of the action type
44720      * @param {Object} options (optional) The options to pass to the action.  All of the config options listed
44721      * below are supported by both the submit and load actions unless otherwise noted (custom actions could also
44722      * accept other config options):
44723      * <pre>
44724 Property          Type             Description
44725 ----------------  ---------------  ----------------------------------------------------------------------------------
44726 url               String           The url for the action (defaults to the form's url)
44727 method            String           The form method to use (defaults to the form's method, or POST if not defined)
44728 params            String/Object    The params to pass (defaults to the form's baseParams, or none if not defined)
44729 clientValidation  Boolean          Applies to submit only.  Pass true to call form.isValid() prior to posting to
44730                                    validate the form on the client (defaults to false)
44731      * </pre>
44732      * @return {BasicForm} this
44733      */
44734     doAction : function(action, options){
44735         if(typeof action == 'string'){
44736             action = new Roo.form.Action.ACTION_TYPES[action](this, options);
44737         }
44738         if(this.fireEvent('beforeaction', this, action) !== false){
44739             this.beforeAction(action);
44740             action.run.defer(100, action);
44741         }
44742         return this;
44743     },
44744
44745     /**
44746      * Shortcut to do a submit action.
44747      * @param {Object} options The options to pass to the action (see {@link #doAction} for details)
44748      * @return {BasicForm} this
44749      */
44750     submit : function(options){
44751         this.doAction('submit', options);
44752         return this;
44753     },
44754
44755     /**
44756      * Shortcut to do a load action.
44757      * @param {Object} options The options to pass to the action (see {@link #doAction} for details)
44758      * @return {BasicForm} this
44759      */
44760     load : function(options){
44761         this.doAction('load', options);
44762         return this;
44763     },
44764
44765     /**
44766      * Persists the values in this form into the passed Roo.data.Record object in a beginEdit/endEdit block.
44767      * @param {Record} record The record to edit
44768      * @return {BasicForm} this
44769      */
44770     updateRecord : function(record){
44771         record.beginEdit();
44772         var fs = record.fields;
44773         fs.each(function(f){
44774             var field = this.findField(f.name);
44775             if(field){
44776                 record.set(f.name, field.getValue());
44777             }
44778         }, this);
44779         record.endEdit();
44780         return this;
44781     },
44782
44783     /**
44784      * Loads an Roo.data.Record into this form.
44785      * @param {Record} record The record to load
44786      * @return {BasicForm} this
44787      */
44788     loadRecord : function(record){
44789         this.setValues(record.data);
44790         return this;
44791     },
44792
44793     // private
44794     beforeAction : function(action){
44795         var o = action.options;
44796         
44797        
44798         if(this.waitMsgTarget === true){
44799             this.el.mask(o.waitMsg || "Sending", 'x-mask-loading');
44800         }else if(this.waitMsgTarget){
44801             this.waitMsgTarget = Roo.get(this.waitMsgTarget);
44802             this.waitMsgTarget.mask(o.waitMsg || "Sending", 'x-mask-loading');
44803         }else {
44804             Roo.MessageBox.wait(o.waitMsg || "Sending", o.waitTitle || this.waitTitle || 'Please Wait...');
44805         }
44806          
44807     },
44808
44809     // private
44810     afterAction : function(action, success){
44811         this.activeAction = null;
44812         var o = action.options;
44813         
44814         if(this.waitMsgTarget === true){
44815             this.el.unmask();
44816         }else if(this.waitMsgTarget){
44817             this.waitMsgTarget.unmask();
44818         }else{
44819             Roo.MessageBox.updateProgress(1);
44820             Roo.MessageBox.hide();
44821         }
44822          
44823         if(success){
44824             if(o.reset){
44825                 this.reset();
44826             }
44827             Roo.callback(o.success, o.scope, [this, action]);
44828             this.fireEvent('actioncomplete', this, action);
44829             
44830         }else{
44831             
44832             // failure condition..
44833             // we have a scenario where updates need confirming.
44834             // eg. if a locking scenario exists..
44835             // we look for { errors : { needs_confirm : true }} in the response.
44836             if (
44837                 (typeof(action.result) != 'undefined')  &&
44838                 (typeof(action.result.errors) != 'undefined')  &&
44839                 (typeof(action.result.errors.needs_confirm) != 'undefined')
44840            ){
44841                 var _t = this;
44842                 Roo.MessageBox.confirm(
44843                     "Change requires confirmation",
44844                     action.result.errorMsg,
44845                     function(r) {
44846                         if (r != 'yes') {
44847                             return;
44848                         }
44849                         _t.doAction('submit', { params :  { _submit_confirmed : 1 } }  );
44850                     }
44851                     
44852                 );
44853                 
44854                 
44855                 
44856                 return;
44857             }
44858             
44859             Roo.callback(o.failure, o.scope, [this, action]);
44860             // show an error message if no failed handler is set..
44861             if (!this.hasListener('actionfailed')) {
44862                 Roo.MessageBox.alert("Error",
44863                     (typeof(action.result) != 'undefined' && typeof(action.result.errorMsg) != 'undefined') ?
44864                         action.result.errorMsg :
44865                         "Saving Failed, please check your entries or try again"
44866                 );
44867             }
44868             
44869             this.fireEvent('actionfailed', this, action);
44870         }
44871         
44872     },
44873
44874     /**
44875      * Find a Roo.form.Field in this form by id, dataIndex, name or hiddenName
44876      * @param {String} id The value to search for
44877      * @return Field
44878      */
44879     findField : function(id){
44880         var field = this.items.get(id);
44881         if(!field){
44882             this.items.each(function(f){
44883                 if(f.isFormField && (f.dataIndex == id || f.id == id || f.getName() == id)){
44884                     field = f;
44885                     return false;
44886                 }
44887             });
44888         }
44889         return field || null;
44890     },
44891
44892     /**
44893      * Add a secondary form to this one, 
44894      * Used to provide tabbed forms. One form is primary, with hidden values 
44895      * which mirror the elements from the other forms.
44896      * 
44897      * @param {Roo.form.Form} form to add.
44898      * 
44899      */
44900     addForm : function(form)
44901     {
44902        
44903         if (this.childForms.indexOf(form) > -1) {
44904             // already added..
44905             return;
44906         }
44907         this.childForms.push(form);
44908         var n = '';
44909         Roo.each(form.allItems, function (fe) {
44910             
44911             n = typeof(fe.getName) == 'undefined' ? fe.name : fe.getName();
44912             if (this.findField(n)) { // already added..
44913                 return;
44914             }
44915             var add = new Roo.form.Hidden({
44916                 name : n
44917             });
44918             add.render(this.el);
44919             
44920             this.add( add );
44921         }, this);
44922         
44923     },
44924     /**
44925      * Mark fields in this form invalid in bulk.
44926      * @param {Array/Object} errors Either an array in the form [{id:'fieldId', msg:'The message'},...] or an object hash of {id: msg, id2: msg2}
44927      * @return {BasicForm} this
44928      */
44929     markInvalid : function(errors){
44930         if(errors instanceof Array){
44931             for(var i = 0, len = errors.length; i < len; i++){
44932                 var fieldError = errors[i];
44933                 var f = this.findField(fieldError.id);
44934                 if(f){
44935                     f.markInvalid(fieldError.msg);
44936                 }
44937             }
44938         }else{
44939             var field, id;
44940             for(id in errors){
44941                 if(typeof errors[id] != 'function' && (field = this.findField(id))){
44942                     field.markInvalid(errors[id]);
44943                 }
44944             }
44945         }
44946         Roo.each(this.childForms || [], function (f) {
44947             f.markInvalid(errors);
44948         });
44949         
44950         return this;
44951     },
44952
44953     /**
44954      * Set values for fields in this form in bulk.
44955      * @param {Array/Object} values Either an array in the form [{id:'fieldId', value:'foo'},...] or an object hash of {id: value, id2: value2}
44956      * @return {BasicForm} this
44957      */
44958     setValues : function(values){
44959         if(values instanceof Array){ // array of objects
44960             for(var i = 0, len = values.length; i < len; i++){
44961                 var v = values[i];
44962                 var f = this.findField(v.id);
44963                 if(f){
44964                     f.setValue(v.value);
44965                     if(this.trackResetOnLoad){
44966                         f.originalValue = f.getValue();
44967                     }
44968                 }
44969             }
44970         }else{ // object hash
44971             var field, id;
44972             for(id in values){
44973                 if(typeof values[id] != 'function' && (field = this.findField(id))){
44974                     
44975                     if (field.setFromData && 
44976                         field.valueField && 
44977                         field.displayField &&
44978                         // combos' with local stores can 
44979                         // be queried via setValue()
44980                         // to set their value..
44981                         (field.store && !field.store.isLocal)
44982                         ) {
44983                         // it's a combo
44984                         var sd = { };
44985                         sd[field.valueField] = typeof(values[field.hiddenName]) == 'undefined' ? '' : values[field.hiddenName];
44986                         sd[field.displayField] = typeof(values[field.name]) == 'undefined' ? '' : values[field.name];
44987                         field.setFromData(sd);
44988                         
44989                     } else {
44990                         field.setValue(values[id]);
44991                     }
44992                     
44993                     
44994                     if(this.trackResetOnLoad){
44995                         field.originalValue = field.getValue();
44996                     }
44997                 }
44998             }
44999         }
45000          
45001         Roo.each(this.childForms || [], function (f) {
45002             f.setValues(values);
45003         });
45004                 
45005         return this;
45006     },
45007
45008     /**
45009      * Returns the fields in this form as an object with key/value pairs. If multiple fields exist with the same name
45010      * they are returned as an array.
45011      * @param {Boolean} asString
45012      * @return {Object}
45013      */
45014     getValues : function(asString){
45015         if (this.childForms) {
45016             // copy values from the child forms
45017             Roo.each(this.childForms, function (f) {
45018                 this.setValues(f.getValues());
45019             }, this);
45020         }
45021         
45022         
45023         
45024         var fs = Roo.lib.Ajax.serializeForm(this.el.dom);
45025         if(asString === true){
45026             return fs;
45027         }
45028         return Roo.urlDecode(fs);
45029     },
45030     
45031     /**
45032      * Returns the fields in this form as an object with key/value pairs. 
45033      * This differs from getValues as it calls getValue on each child item, rather than using dom data.
45034      * @return {Object}
45035      */
45036     getFieldValues : function(with_hidden)
45037     {
45038         if (this.childForms) {
45039             // copy values from the child forms
45040             // should this call getFieldValues - probably not as we do not currently copy
45041             // hidden fields when we generate..
45042             Roo.each(this.childForms, function (f) {
45043                 this.setValues(f.getValues());
45044             }, this);
45045         }
45046         
45047         var ret = {};
45048         this.items.each(function(f){
45049             if (!f.getName()) {
45050                 return;
45051             }
45052             var v = f.getValue();
45053             if (f.inputType =='radio') {
45054                 if (typeof(ret[f.getName()]) == 'undefined') {
45055                     ret[f.getName()] = ''; // empty..
45056                 }
45057                 
45058                 if (!f.el.dom.checked) {
45059                     return;
45060                     
45061                 }
45062                 v = f.el.dom.value;
45063                 
45064             }
45065             
45066             // not sure if this supported any more..
45067             if ((typeof(v) == 'object') && f.getRawValue) {
45068                 v = f.getRawValue() ; // dates..
45069             }
45070             // combo boxes where name != hiddenName...
45071             if (f.name != f.getName()) {
45072                 ret[f.name] = f.getRawValue();
45073             }
45074             ret[f.getName()] = v;
45075         });
45076         
45077         return ret;
45078     },
45079
45080     /**
45081      * Clears all invalid messages in this form.
45082      * @return {BasicForm} this
45083      */
45084     clearInvalid : function(){
45085         this.items.each(function(f){
45086            f.clearInvalid();
45087         });
45088         
45089         Roo.each(this.childForms || [], function (f) {
45090             f.clearInvalid();
45091         });
45092         
45093         
45094         return this;
45095     },
45096
45097     /**
45098      * Resets this form.
45099      * @return {BasicForm} this
45100      */
45101     reset : function(){
45102         this.items.each(function(f){
45103             f.reset();
45104         });
45105         
45106         Roo.each(this.childForms || [], function (f) {
45107             f.reset();
45108         });
45109        
45110         
45111         return this;
45112     },
45113
45114     /**
45115      * Add Roo.form components to this form.
45116      * @param {Field} field1
45117      * @param {Field} field2 (optional)
45118      * @param {Field} etc (optional)
45119      * @return {BasicForm} this
45120      */
45121     add : function(){
45122         this.items.addAll(Array.prototype.slice.call(arguments, 0));
45123         return this;
45124     },
45125
45126
45127     /**
45128      * Removes a field from the items collection (does NOT remove its markup).
45129      * @param {Field} field
45130      * @return {BasicForm} this
45131      */
45132     remove : function(field){
45133         this.items.remove(field);
45134         return this;
45135     },
45136
45137     /**
45138      * Looks at the fields in this form, checks them for an id attribute,
45139      * and calls applyTo on the existing dom element with that id.
45140      * @return {BasicForm} this
45141      */
45142     render : function(){
45143         this.items.each(function(f){
45144             if(f.isFormField && !f.rendered && document.getElementById(f.id)){ // if the element exists
45145                 f.applyTo(f.id);
45146             }
45147         });
45148         return this;
45149     },
45150
45151     /**
45152      * Calls {@link Ext#apply} for all fields in this form with the passed object.
45153      * @param {Object} values
45154      * @return {BasicForm} this
45155      */
45156     applyToFields : function(o){
45157         this.items.each(function(f){
45158            Roo.apply(f, o);
45159         });
45160         return this;
45161     },
45162
45163     /**
45164      * Calls {@link Ext#applyIf} for all field in this form with the passed object.
45165      * @param {Object} values
45166      * @return {BasicForm} this
45167      */
45168     applyIfToFields : function(o){
45169         this.items.each(function(f){
45170            Roo.applyIf(f, o);
45171         });
45172         return this;
45173     }
45174 });
45175
45176 // back compat
45177 Roo.BasicForm = Roo.form.BasicForm;/*
45178  * Based on:
45179  * Ext JS Library 1.1.1
45180  * Copyright(c) 2006-2007, Ext JS, LLC.
45181  *
45182  * Originally Released Under LGPL - original licence link has changed is not relivant.
45183  *
45184  * Fork - LGPL
45185  * <script type="text/javascript">
45186  */
45187
45188 /**
45189  * @class Roo.form.Form
45190  * @extends Roo.form.BasicForm
45191  * Adds the ability to dynamically render forms with JavaScript to {@link Roo.form.BasicForm}.
45192  * @constructor
45193  * @param {Object} config Configuration options
45194  */
45195 Roo.form.Form = function(config){
45196     var xitems =  [];
45197     if (config.items) {
45198         xitems = config.items;
45199         delete config.items;
45200     }
45201    
45202     
45203     Roo.form.Form.superclass.constructor.call(this, null, config);
45204     this.url = this.url || this.action;
45205     if(!this.root){
45206         this.root = new Roo.form.Layout(Roo.applyIf({
45207             id: Roo.id()
45208         }, config));
45209     }
45210     this.active = this.root;
45211     /**
45212      * Array of all the buttons that have been added to this form via {@link addButton}
45213      * @type Array
45214      */
45215     this.buttons = [];
45216     this.allItems = [];
45217     this.addEvents({
45218         /**
45219          * @event clientvalidation
45220          * If the monitorValid config option is true, this event fires repetitively to notify of valid state
45221          * @param {Form} this
45222          * @param {Boolean} valid true if the form has passed client-side validation
45223          */
45224         clientvalidation: true,
45225         /**
45226          * @event rendered
45227          * Fires when the form is rendered
45228          * @param {Roo.form.Form} form
45229          */
45230         rendered : true
45231     });
45232     
45233     if (this.progressUrl) {
45234             // push a hidden field onto the list of fields..
45235             this.addxtype( {
45236                     xns: Roo.form, 
45237                     xtype : 'Hidden', 
45238                     name : 'UPLOAD_IDENTIFIER' 
45239             });
45240         }
45241         
45242     
45243     Roo.each(xitems, this.addxtype, this);
45244     
45245     
45246     
45247 };
45248
45249 Roo.extend(Roo.form.Form, Roo.form.BasicForm, {
45250     /**
45251      * @cfg {Number} labelWidth The width of labels. This property cascades to child containers.
45252      */
45253     /**
45254      * @cfg {String} itemCls A css class to apply to the x-form-item of fields. This property cascades to child containers.
45255      */
45256     /**
45257      * @cfg {String} buttonAlign Valid values are "left," "center" and "right" (defaults to "center")
45258      */
45259     buttonAlign:'center',
45260
45261     /**
45262      * @cfg {Number} minButtonWidth Minimum width of all buttons in pixels (defaults to 75)
45263      */
45264     minButtonWidth:75,
45265
45266     /**
45267      * @cfg {String} labelAlign Valid values are "left," "top" and "right" (defaults to "left").
45268      * This property cascades to child containers if not set.
45269      */
45270     labelAlign:'left',
45271
45272     /**
45273      * @cfg {Boolean} monitorValid If true the form monitors its valid state <b>client-side</b> and
45274      * fires a looping event with that state. This is required to bind buttons to the valid
45275      * state using the config value formBind:true on the button.
45276      */
45277     monitorValid : false,
45278
45279     /**
45280      * @cfg {Number} monitorPoll The milliseconds to poll valid state, ignored if monitorValid is not true (defaults to 200)
45281      */
45282     monitorPoll : 200,
45283     
45284     /**
45285      * @cfg {String} progressUrl - Url to return progress data 
45286      */
45287     
45288     progressUrl : false,
45289   
45290     /**
45291      * Opens a new {@link Roo.form.Column} container in the layout stack. If fields are passed after the config, the
45292      * fields are added and the column is closed. If no fields are passed the column remains open
45293      * until end() is called.
45294      * @param {Object} config The config to pass to the column
45295      * @param {Field} field1 (optional)
45296      * @param {Field} field2 (optional)
45297      * @param {Field} etc (optional)
45298      * @return Column The column container object
45299      */
45300     column : function(c){
45301         var col = new Roo.form.Column(c);
45302         this.start(col);
45303         if(arguments.length > 1){ // duplicate code required because of Opera
45304             this.add.apply(this, Array.prototype.slice.call(arguments, 1));
45305             this.end();
45306         }
45307         return col;
45308     },
45309
45310     /**
45311      * Opens a new {@link Roo.form.FieldSet} container in the layout stack. If fields are passed after the config, the
45312      * fields are added and the fieldset is closed. If no fields are passed the fieldset remains open
45313      * until end() is called.
45314      * @param {Object} config The config to pass to the fieldset
45315      * @param {Field} field1 (optional)
45316      * @param {Field} field2 (optional)
45317      * @param {Field} etc (optional)
45318      * @return FieldSet The fieldset container object
45319      */
45320     fieldset : function(c){
45321         var fs = new Roo.form.FieldSet(c);
45322         this.start(fs);
45323         if(arguments.length > 1){ // duplicate code required because of Opera
45324             this.add.apply(this, Array.prototype.slice.call(arguments, 1));
45325             this.end();
45326         }
45327         return fs;
45328     },
45329
45330     /**
45331      * Opens a new {@link Roo.form.Layout} container in the layout stack. If fields are passed after the config, the
45332      * fields are added and the container is closed. If no fields are passed the container remains open
45333      * until end() is called.
45334      * @param {Object} config The config to pass to the Layout
45335      * @param {Field} field1 (optional)
45336      * @param {Field} field2 (optional)
45337      * @param {Field} etc (optional)
45338      * @return Layout The container object
45339      */
45340     container : function(c){
45341         var l = new Roo.form.Layout(c);
45342         this.start(l);
45343         if(arguments.length > 1){ // duplicate code required because of Opera
45344             this.add.apply(this, Array.prototype.slice.call(arguments, 1));
45345             this.end();
45346         }
45347         return l;
45348     },
45349
45350     /**
45351      * Opens the passed container in the layout stack. The container can be any {@link Roo.form.Layout} or subclass.
45352      * @param {Object} container A Roo.form.Layout or subclass of Layout
45353      * @return {Form} this
45354      */
45355     start : function(c){
45356         // cascade label info
45357         Roo.applyIf(c, {'labelAlign': this.active.labelAlign, 'labelWidth': this.active.labelWidth, 'itemCls': this.active.itemCls});
45358         this.active.stack.push(c);
45359         c.ownerCt = this.active;
45360         this.active = c;
45361         return this;
45362     },
45363
45364     /**
45365      * Closes the current open container
45366      * @return {Form} this
45367      */
45368     end : function(){
45369         if(this.active == this.root){
45370             return this;
45371         }
45372         this.active = this.active.ownerCt;
45373         return this;
45374     },
45375
45376     /**
45377      * Add Roo.form components to the current open container (e.g. column, fieldset, etc.).  Fields added via this method
45378      * can also be passed with an additional property of fieldLabel, which if supplied, will provide the text to display
45379      * as the label of the field.
45380      * @param {Field} field1
45381      * @param {Field} field2 (optional)
45382      * @param {Field} etc. (optional)
45383      * @return {Form} this
45384      */
45385     add : function(){
45386         this.active.stack.push.apply(this.active.stack, arguments);
45387         this.allItems.push.apply(this.allItems,arguments);
45388         var r = [];
45389         for(var i = 0, a = arguments, len = a.length; i < len; i++) {
45390             if(a[i].isFormField){
45391                 r.push(a[i]);
45392             }
45393         }
45394         if(r.length > 0){
45395             Roo.form.Form.superclass.add.apply(this, r);
45396         }
45397         return this;
45398     },
45399     
45400
45401     
45402     
45403     
45404      /**
45405      * Find any element that has been added to a form, using it's ID or name
45406      * This can include framesets, columns etc. along with regular fields..
45407      * @param {String} id - id or name to find.
45408      
45409      * @return {Element} e - or false if nothing found.
45410      */
45411     findbyId : function(id)
45412     {
45413         var ret = false;
45414         if (!id) {
45415             return ret;
45416         }
45417         Roo.each(this.allItems, function(f){
45418             if (f.id == id || f.name == id ){
45419                 ret = f;
45420                 return false;
45421             }
45422         });
45423         return ret;
45424     },
45425
45426     
45427     
45428     /**
45429      * Render this form into the passed container. This should only be called once!
45430      * @param {String/HTMLElement/Element} container The element this component should be rendered into
45431      * @return {Form} this
45432      */
45433     render : function(ct)
45434     {
45435         
45436         
45437         
45438         ct = Roo.get(ct);
45439         var o = this.autoCreate || {
45440             tag: 'form',
45441             method : this.method || 'POST',
45442             id : this.id || Roo.id()
45443         };
45444         this.initEl(ct.createChild(o));
45445
45446         this.root.render(this.el);
45447         
45448        
45449              
45450         this.items.each(function(f){
45451             f.render('x-form-el-'+f.id);
45452         });
45453
45454         if(this.buttons.length > 0){
45455             // tables are required to maintain order and for correct IE layout
45456             var tb = this.el.createChild({cls:'x-form-btns-ct', cn: {
45457                 cls:"x-form-btns x-form-btns-"+this.buttonAlign,
45458                 html:'<table cellspacing="0"><tbody><tr></tr></tbody></table><div class="x-clear"></div>'
45459             }}, null, true);
45460             var tr = tb.getElementsByTagName('tr')[0];
45461             for(var i = 0, len = this.buttons.length; i < len; i++) {
45462                 var b = this.buttons[i];
45463                 var td = document.createElement('td');
45464                 td.className = 'x-form-btn-td';
45465                 b.render(tr.appendChild(td));
45466             }
45467         }
45468         if(this.monitorValid){ // initialize after render
45469             this.startMonitoring();
45470         }
45471         this.fireEvent('rendered', this);
45472         return this;
45473     },
45474
45475     /**
45476      * Adds a button to the footer of the form - this <b>must</b> be called before the form is rendered.
45477      * @param {String/Object} config A string becomes the button text, an object can either be a Button config
45478      * object or a valid Roo.DomHelper element config
45479      * @param {Function} handler The function called when the button is clicked
45480      * @param {Object} scope (optional) The scope of the handler function
45481      * @return {Roo.Button}
45482      */
45483     addButton : function(config, handler, scope){
45484         var bc = {
45485             handler: handler,
45486             scope: scope,
45487             minWidth: this.minButtonWidth,
45488             hideParent:true
45489         };
45490         if(typeof config == "string"){
45491             bc.text = config;
45492         }else{
45493             Roo.apply(bc, config);
45494         }
45495         var btn = new Roo.Button(null, bc);
45496         this.buttons.push(btn);
45497         return btn;
45498     },
45499
45500      /**
45501      * Adds a series of form elements (using the xtype property as the factory method.
45502      * Valid xtypes are:  TextField, TextArea .... Button, Layout, FieldSet, Column, (and 'end' to close a block)
45503      * @param {Object} config 
45504      */
45505     
45506     addxtype : function()
45507     {
45508         var ar = Array.prototype.slice.call(arguments, 0);
45509         var ret = false;
45510         for(var i = 0; i < ar.length; i++) {
45511             if (!ar[i]) {
45512                 continue; // skip -- if this happends something invalid got sent, we 
45513                 // should ignore it, as basically that interface element will not show up
45514                 // and that should be pretty obvious!!
45515             }
45516             
45517             if (Roo.form[ar[i].xtype]) {
45518                 ar[i].form = this;
45519                 var fe = Roo.factory(ar[i], Roo.form);
45520                 if (!ret) {
45521                     ret = fe;
45522                 }
45523                 fe.form = this;
45524                 if (fe.store) {
45525                     fe.store.form = this;
45526                 }
45527                 if (fe.isLayout) {  
45528                          
45529                     this.start(fe);
45530                     this.allItems.push(fe);
45531                     if (fe.items && fe.addxtype) {
45532                         fe.addxtype.apply(fe, fe.items);
45533                         delete fe.items;
45534                     }
45535                      this.end();
45536                     continue;
45537                 }
45538                 
45539                 
45540                  
45541                 this.add(fe);
45542               //  console.log('adding ' + ar[i].xtype);
45543             }
45544             if (ar[i].xtype == 'Button') {  
45545                 //console.log('adding button');
45546                 //console.log(ar[i]);
45547                 this.addButton(ar[i]);
45548                 this.allItems.push(fe);
45549                 continue;
45550             }
45551             
45552             if (ar[i].xtype == 'end') { // so we can add fieldsets... / layout etc.
45553                 alert('end is not supported on xtype any more, use items');
45554             //    this.end();
45555             //    //console.log('adding end');
45556             }
45557             
45558         }
45559         return ret;
45560     },
45561     
45562     /**
45563      * Starts monitoring of the valid state of this form. Usually this is done by passing the config
45564      * option "monitorValid"
45565      */
45566     startMonitoring : function(){
45567         if(!this.bound){
45568             this.bound = true;
45569             Roo.TaskMgr.start({
45570                 run : this.bindHandler,
45571                 interval : this.monitorPoll || 200,
45572                 scope: this
45573             });
45574         }
45575     },
45576
45577     /**
45578      * Stops monitoring of the valid state of this form
45579      */
45580     stopMonitoring : function(){
45581         this.bound = false;
45582     },
45583
45584     // private
45585     bindHandler : function(){
45586         if(!this.bound){
45587             return false; // stops binding
45588         }
45589         var valid = true;
45590         this.items.each(function(f){
45591             if(!f.isValid(true)){
45592                 valid = false;
45593                 return false;
45594             }
45595         });
45596         for(var i = 0, len = this.buttons.length; i < len; i++){
45597             var btn = this.buttons[i];
45598             if(btn.formBind === true && btn.disabled === valid){
45599                 btn.setDisabled(!valid);
45600             }
45601         }
45602         this.fireEvent('clientvalidation', this, valid);
45603     }
45604     
45605     
45606     
45607     
45608     
45609     
45610     
45611     
45612 });
45613
45614
45615 // back compat
45616 Roo.Form = Roo.form.Form;
45617 /*
45618  * Based on:
45619  * Ext JS Library 1.1.1
45620  * Copyright(c) 2006-2007, Ext JS, LLC.
45621  *
45622  * Originally Released Under LGPL - original licence link has changed is not relivant.
45623  *
45624  * Fork - LGPL
45625  * <script type="text/javascript">
45626  */
45627
45628 // as we use this in bootstrap.
45629 Roo.namespace('Roo.form');
45630  /**
45631  * @class Roo.form.Action
45632  * Internal Class used to handle form actions
45633  * @constructor
45634  * @param {Roo.form.BasicForm} el The form element or its id
45635  * @param {Object} config Configuration options
45636  */
45637
45638  
45639  
45640 // define the action interface
45641 Roo.form.Action = function(form, options){
45642     this.form = form;
45643     this.options = options || {};
45644 };
45645 /**
45646  * Client Validation Failed
45647  * @const 
45648  */
45649 Roo.form.Action.CLIENT_INVALID = 'client';
45650 /**
45651  * Server Validation Failed
45652  * @const 
45653  */
45654 Roo.form.Action.SERVER_INVALID = 'server';
45655  /**
45656  * Connect to Server Failed
45657  * @const 
45658  */
45659 Roo.form.Action.CONNECT_FAILURE = 'connect';
45660 /**
45661  * Reading Data from Server Failed
45662  * @const 
45663  */
45664 Roo.form.Action.LOAD_FAILURE = 'load';
45665
45666 Roo.form.Action.prototype = {
45667     type : 'default',
45668     failureType : undefined,
45669     response : undefined,
45670     result : undefined,
45671
45672     // interface method
45673     run : function(options){
45674
45675     },
45676
45677     // interface method
45678     success : function(response){
45679
45680     },
45681
45682     // interface method
45683     handleResponse : function(response){
45684
45685     },
45686
45687     // default connection failure
45688     failure : function(response){
45689         
45690         this.response = response;
45691         this.failureType = Roo.form.Action.CONNECT_FAILURE;
45692         this.form.afterAction(this, false);
45693     },
45694
45695     processResponse : function(response){
45696         this.response = response;
45697         if(!response.responseText){
45698             return true;
45699         }
45700         this.result = this.handleResponse(response);
45701         return this.result;
45702     },
45703
45704     // utility functions used internally
45705     getUrl : function(appendParams){
45706         var url = this.options.url || this.form.url || this.form.el.dom.action;
45707         if(appendParams){
45708             var p = this.getParams();
45709             if(p){
45710                 url += (url.indexOf('?') != -1 ? '&' : '?') + p;
45711             }
45712         }
45713         return url;
45714     },
45715
45716     getMethod : function(){
45717         return (this.options.method || this.form.method || this.form.el.dom.method || 'POST').toUpperCase();
45718     },
45719
45720     getParams : function(){
45721         var bp = this.form.baseParams;
45722         var p = this.options.params;
45723         if(p){
45724             if(typeof p == "object"){
45725                 p = Roo.urlEncode(Roo.applyIf(p, bp));
45726             }else if(typeof p == 'string' && bp){
45727                 p += '&' + Roo.urlEncode(bp);
45728             }
45729         }else if(bp){
45730             p = Roo.urlEncode(bp);
45731         }
45732         return p;
45733     },
45734
45735     createCallback : function(){
45736         return {
45737             success: this.success,
45738             failure: this.failure,
45739             scope: this,
45740             timeout: (this.form.timeout*1000),
45741             upload: this.form.fileUpload ? this.success : undefined
45742         };
45743     }
45744 };
45745
45746 Roo.form.Action.Submit = function(form, options){
45747     Roo.form.Action.Submit.superclass.constructor.call(this, form, options);
45748 };
45749
45750 Roo.extend(Roo.form.Action.Submit, Roo.form.Action, {
45751     type : 'submit',
45752
45753     haveProgress : false,
45754     uploadComplete : false,
45755     
45756     // uploadProgress indicator.
45757     uploadProgress : function()
45758     {
45759         if (!this.form.progressUrl) {
45760             return;
45761         }
45762         
45763         if (!this.haveProgress) {
45764             Roo.MessageBox.progress("Uploading", "Uploading");
45765         }
45766         if (this.uploadComplete) {
45767            Roo.MessageBox.hide();
45768            return;
45769         }
45770         
45771         this.haveProgress = true;
45772    
45773         var uid = this.form.findField('UPLOAD_IDENTIFIER').getValue();
45774         
45775         var c = new Roo.data.Connection();
45776         c.request({
45777             url : this.form.progressUrl,
45778             params: {
45779                 id : uid
45780             },
45781             method: 'GET',
45782             success : function(req){
45783                //console.log(data);
45784                 var rdata = false;
45785                 var edata;
45786                 try  {
45787                    rdata = Roo.decode(req.responseText)
45788                 } catch (e) {
45789                     Roo.log("Invalid data from server..");
45790                     Roo.log(edata);
45791                     return;
45792                 }
45793                 if (!rdata || !rdata.success) {
45794                     Roo.log(rdata);
45795                     Roo.MessageBox.alert(Roo.encode(rdata));
45796                     return;
45797                 }
45798                 var data = rdata.data;
45799                 
45800                 if (this.uploadComplete) {
45801                    Roo.MessageBox.hide();
45802                    return;
45803                 }
45804                    
45805                 if (data){
45806                     Roo.MessageBox.updateProgress(data.bytes_uploaded/data.bytes_total,
45807                        Math.floor((data.bytes_total - data.bytes_uploaded)/1000) + 'k remaining'
45808                     );
45809                 }
45810                 this.uploadProgress.defer(2000,this);
45811             },
45812        
45813             failure: function(data) {
45814                 Roo.log('progress url failed ');
45815                 Roo.log(data);
45816             },
45817             scope : this
45818         });
45819            
45820     },
45821     
45822     
45823     run : function()
45824     {
45825         // run get Values on the form, so it syncs any secondary forms.
45826         this.form.getValues();
45827         
45828         var o = this.options;
45829         var method = this.getMethod();
45830         var isPost = method == 'POST';
45831         if(o.clientValidation === false || this.form.isValid()){
45832             
45833             if (this.form.progressUrl) {
45834                 this.form.findField('UPLOAD_IDENTIFIER').setValue(
45835                     (new Date() * 1) + '' + Math.random());
45836                     
45837             } 
45838             
45839             
45840             Roo.Ajax.request(Roo.apply(this.createCallback(), {
45841                 form:this.form.el.dom,
45842                 url:this.getUrl(!isPost),
45843                 method: method,
45844                 params:isPost ? this.getParams() : null,
45845                 isUpload: this.form.fileUpload
45846             }));
45847             
45848             this.uploadProgress();
45849
45850         }else if (o.clientValidation !== false){ // client validation failed
45851             this.failureType = Roo.form.Action.CLIENT_INVALID;
45852             this.form.afterAction(this, false);
45853         }
45854     },
45855
45856     success : function(response)
45857     {
45858         this.uploadComplete= true;
45859         if (this.haveProgress) {
45860             Roo.MessageBox.hide();
45861         }
45862         
45863         
45864         var result = this.processResponse(response);
45865         if(result === true || result.success){
45866             this.form.afterAction(this, true);
45867             return;
45868         }
45869         if(result.errors){
45870             this.form.markInvalid(result.errors);
45871             this.failureType = Roo.form.Action.SERVER_INVALID;
45872         }
45873         this.form.afterAction(this, false);
45874     },
45875     failure : function(response)
45876     {
45877         this.uploadComplete= true;
45878         if (this.haveProgress) {
45879             Roo.MessageBox.hide();
45880         }
45881         
45882         this.response = response;
45883         this.failureType = Roo.form.Action.CONNECT_FAILURE;
45884         this.form.afterAction(this, false);
45885     },
45886     
45887     handleResponse : function(response){
45888         if(this.form.errorReader){
45889             var rs = this.form.errorReader.read(response);
45890             var errors = [];
45891             if(rs.records){
45892                 for(var i = 0, len = rs.records.length; i < len; i++) {
45893                     var r = rs.records[i];
45894                     errors[i] = r.data;
45895                 }
45896             }
45897             if(errors.length < 1){
45898                 errors = null;
45899             }
45900             return {
45901                 success : rs.success,
45902                 errors : errors
45903             };
45904         }
45905         var ret = false;
45906         try {
45907             ret = Roo.decode(response.responseText);
45908         } catch (e) {
45909             ret = {
45910                 success: false,
45911                 errorMsg: "Failed to read server message: " + (response ? response.responseText : ' - no message'),
45912                 errors : []
45913             };
45914         }
45915         return ret;
45916         
45917     }
45918 });
45919
45920
45921 Roo.form.Action.Load = function(form, options){
45922     Roo.form.Action.Load.superclass.constructor.call(this, form, options);
45923     this.reader = this.form.reader;
45924 };
45925
45926 Roo.extend(Roo.form.Action.Load, Roo.form.Action, {
45927     type : 'load',
45928
45929     run : function(){
45930         
45931         Roo.Ajax.request(Roo.apply(
45932                 this.createCallback(), {
45933                     method:this.getMethod(),
45934                     url:this.getUrl(false),
45935                     params:this.getParams()
45936         }));
45937     },
45938
45939     success : function(response){
45940         
45941         var result = this.processResponse(response);
45942         if(result === true || !result.success || !result.data){
45943             this.failureType = Roo.form.Action.LOAD_FAILURE;
45944             this.form.afterAction(this, false);
45945             return;
45946         }
45947         this.form.clearInvalid();
45948         this.form.setValues(result.data);
45949         this.form.afterAction(this, true);
45950     },
45951
45952     handleResponse : function(response){
45953         if(this.form.reader){
45954             var rs = this.form.reader.read(response);
45955             var data = rs.records && rs.records[0] ? rs.records[0].data : null;
45956             return {
45957                 success : rs.success,
45958                 data : data
45959             };
45960         }
45961         return Roo.decode(response.responseText);
45962     }
45963 });
45964
45965 Roo.form.Action.ACTION_TYPES = {
45966     'load' : Roo.form.Action.Load,
45967     'submit' : Roo.form.Action.Submit
45968 };/*
45969  * Based on:
45970  * Ext JS Library 1.1.1
45971  * Copyright(c) 2006-2007, Ext JS, LLC.
45972  *
45973  * Originally Released Under LGPL - original licence link has changed is not relivant.
45974  *
45975  * Fork - LGPL
45976  * <script type="text/javascript">
45977  */
45978  
45979 /**
45980  * @class Roo.form.Layout
45981  * @extends Roo.Component
45982  * Creates a container for layout and rendering of fields in an {@link Roo.form.Form}.
45983  * @constructor
45984  * @param {Object} config Configuration options
45985  */
45986 Roo.form.Layout = function(config){
45987     var xitems = [];
45988     if (config.items) {
45989         xitems = config.items;
45990         delete config.items;
45991     }
45992     Roo.form.Layout.superclass.constructor.call(this, config);
45993     this.stack = [];
45994     Roo.each(xitems, this.addxtype, this);
45995      
45996 };
45997
45998 Roo.extend(Roo.form.Layout, Roo.Component, {
45999     /**
46000      * @cfg {String/Object} autoCreate
46001      * A DomHelper element spec used to autocreate the layout (defaults to {tag: 'div', cls: 'x-form-ct'})
46002      */
46003     /**
46004      * @cfg {String/Object/Function} style
46005      * A style specification string, e.g. "width:100px", or object in the form {width:"100px"}, or
46006      * a function which returns such a specification.
46007      */
46008     /**
46009      * @cfg {String} labelAlign
46010      * Valid values are "left," "top" and "right" (defaults to "left")
46011      */
46012     /**
46013      * @cfg {Number} labelWidth
46014      * Fixed width in pixels of all field labels (defaults to undefined)
46015      */
46016     /**
46017      * @cfg {Boolean} clear
46018      * True to add a clearing element at the end of this layout, equivalent to CSS clear: both (defaults to true)
46019      */
46020     clear : true,
46021     /**
46022      * @cfg {String} labelSeparator
46023      * The separator to use after field labels (defaults to ':')
46024      */
46025     labelSeparator : ':',
46026     /**
46027      * @cfg {Boolean} hideLabels
46028      * True to suppress the display of field labels in this layout (defaults to false)
46029      */
46030     hideLabels : false,
46031
46032     // private
46033     defaultAutoCreate : {tag: 'div', cls: 'x-form-ct'},
46034     
46035     isLayout : true,
46036     
46037     // private
46038     onRender : function(ct, position){
46039         if(this.el){ // from markup
46040             this.el = Roo.get(this.el);
46041         }else {  // generate
46042             var cfg = this.getAutoCreate();
46043             this.el = ct.createChild(cfg, position);
46044         }
46045         if(this.style){
46046             this.el.applyStyles(this.style);
46047         }
46048         if(this.labelAlign){
46049             this.el.addClass('x-form-label-'+this.labelAlign);
46050         }
46051         if(this.hideLabels){
46052             this.labelStyle = "display:none";
46053             this.elementStyle = "padding-left:0;";
46054         }else{
46055             if(typeof this.labelWidth == 'number'){
46056                 this.labelStyle = "width:"+this.labelWidth+"px;";
46057                 this.elementStyle = "padding-left:"+((this.labelWidth+(typeof this.labelPad == 'number' ? this.labelPad : 5))+'px')+";";
46058             }
46059             if(this.labelAlign == 'top'){
46060                 this.labelStyle = "width:auto;";
46061                 this.elementStyle = "padding-left:0;";
46062             }
46063         }
46064         var stack = this.stack;
46065         var slen = stack.length;
46066         if(slen > 0){
46067             if(!this.fieldTpl){
46068                 var t = new Roo.Template(
46069                     '<div class="x-form-item {5}">',
46070                         '<label for="{0}" style="{2}">{1}{4}</label>',
46071                         '<div class="x-form-element" id="x-form-el-{0}" style="{3}">',
46072                         '</div>',
46073                     '</div><div class="x-form-clear-left"></div>'
46074                 );
46075                 t.disableFormats = true;
46076                 t.compile();
46077                 Roo.form.Layout.prototype.fieldTpl = t;
46078             }
46079             for(var i = 0; i < slen; i++) {
46080                 if(stack[i].isFormField){
46081                     this.renderField(stack[i]);
46082                 }else{
46083                     this.renderComponent(stack[i]);
46084                 }
46085             }
46086         }
46087         if(this.clear){
46088             this.el.createChild({cls:'x-form-clear'});
46089         }
46090     },
46091
46092     // private
46093     renderField : function(f){
46094         f.fieldEl = Roo.get(this.fieldTpl.append(this.el, [
46095                f.id, //0
46096                f.fieldLabel, //1
46097                f.labelStyle||this.labelStyle||'', //2
46098                this.elementStyle||'', //3
46099                typeof f.labelSeparator == 'undefined' ? this.labelSeparator : f.labelSeparator, //4
46100                f.itemCls||this.itemCls||''  //5
46101        ], true).getPrevSibling());
46102     },
46103
46104     // private
46105     renderComponent : function(c){
46106         c.render(c.isLayout ? this.el : this.el.createChild());    
46107     },
46108     /**
46109      * Adds a object form elements (using the xtype property as the factory method.)
46110      * Valid xtypes are:  TextField, TextArea .... Button, Layout, FieldSet, Column
46111      * @param {Object} config 
46112      */
46113     addxtype : function(o)
46114     {
46115         // create the lement.
46116         o.form = this.form;
46117         var fe = Roo.factory(o, Roo.form);
46118         this.form.allItems.push(fe);
46119         this.stack.push(fe);
46120         
46121         if (fe.isFormField) {
46122             this.form.items.add(fe);
46123         }
46124          
46125         return fe;
46126     }
46127 });
46128
46129 /**
46130  * @class Roo.form.Column
46131  * @extends Roo.form.Layout
46132  * Creates a column container for layout and rendering of fields in an {@link Roo.form.Form}.
46133  * @constructor
46134  * @param {Object} config Configuration options
46135  */
46136 Roo.form.Column = function(config){
46137     Roo.form.Column.superclass.constructor.call(this, config);
46138 };
46139
46140 Roo.extend(Roo.form.Column, Roo.form.Layout, {
46141     /**
46142      * @cfg {Number/String} width
46143      * The fixed width of the column in pixels or CSS value (defaults to "auto")
46144      */
46145     /**
46146      * @cfg {String/Object} autoCreate
46147      * A DomHelper element spec used to autocreate the column (defaults to {tag: 'div', cls: 'x-form-ct x-form-column'})
46148      */
46149
46150     // private
46151     defaultAutoCreate : {tag: 'div', cls: 'x-form-ct x-form-column'},
46152
46153     // private
46154     onRender : function(ct, position){
46155         Roo.form.Column.superclass.onRender.call(this, ct, position);
46156         if(this.width){
46157             this.el.setWidth(this.width);
46158         }
46159     }
46160 });
46161
46162
46163 /**
46164  * @class Roo.form.Row
46165  * @extends Roo.form.Layout
46166  * Creates a row container for layout and rendering of fields in an {@link Roo.form.Form}.
46167  * @constructor
46168  * @param {Object} config Configuration options
46169  */
46170
46171  
46172 Roo.form.Row = function(config){
46173     Roo.form.Row.superclass.constructor.call(this, config);
46174 };
46175  
46176 Roo.extend(Roo.form.Row, Roo.form.Layout, {
46177       /**
46178      * @cfg {Number/String} width
46179      * The fixed width of the column in pixels or CSS value (defaults to "auto")
46180      */
46181     /**
46182      * @cfg {Number/String} height
46183      * The fixed height of the column in pixels or CSS value (defaults to "auto")
46184      */
46185     defaultAutoCreate : {tag: 'div', cls: 'x-form-ct x-form-row'},
46186     
46187     padWidth : 20,
46188     // private
46189     onRender : function(ct, position){
46190         //console.log('row render');
46191         if(!this.rowTpl){
46192             var t = new Roo.Template(
46193                 '<div class="x-form-item {5}" style="float:left;width:{6}px">',
46194                     '<label for="{0}" style="{2}">{1}{4}</label>',
46195                     '<div class="x-form-element" id="x-form-el-{0}" style="{3}">',
46196                     '</div>',
46197                 '</div>'
46198             );
46199             t.disableFormats = true;
46200             t.compile();
46201             Roo.form.Layout.prototype.rowTpl = t;
46202         }
46203         this.fieldTpl = this.rowTpl;
46204         
46205         //console.log('lw' + this.labelWidth +', la:' + this.labelAlign);
46206         var labelWidth = 100;
46207         
46208         if ((this.labelAlign != 'top')) {
46209             if (typeof this.labelWidth == 'number') {
46210                 labelWidth = this.labelWidth
46211             }
46212             this.padWidth =  20 + labelWidth;
46213             
46214         }
46215         
46216         Roo.form.Column.superclass.onRender.call(this, ct, position);
46217         if(this.width){
46218             this.el.setWidth(this.width);
46219         }
46220         if(this.height){
46221             this.el.setHeight(this.height);
46222         }
46223     },
46224     
46225     // private
46226     renderField : function(f){
46227         f.fieldEl = this.fieldTpl.append(this.el, [
46228                f.id, f.fieldLabel,
46229                f.labelStyle||this.labelStyle||'',
46230                this.elementStyle||'',
46231                typeof f.labelSeparator == 'undefined' ? this.labelSeparator : f.labelSeparator,
46232                f.itemCls||this.itemCls||'',
46233                f.width ? f.width + this.padWidth : 160 + this.padWidth
46234        ],true);
46235     }
46236 });
46237  
46238
46239 /**
46240  * @class Roo.form.FieldSet
46241  * @extends Roo.form.Layout
46242  * Creates a fieldset container for layout and rendering of fields in an {@link Roo.form.Form}.
46243  * @constructor
46244  * @param {Object} config Configuration options
46245  */
46246 Roo.form.FieldSet = function(config){
46247     Roo.form.FieldSet.superclass.constructor.call(this, config);
46248 };
46249
46250 Roo.extend(Roo.form.FieldSet, Roo.form.Layout, {
46251     /**
46252      * @cfg {String} legend
46253      * The text to display as the legend for the FieldSet (defaults to '')
46254      */
46255     /**
46256      * @cfg {String/Object} autoCreate
46257      * A DomHelper element spec used to autocreate the fieldset (defaults to {tag: 'fieldset', cn: {tag:'legend'}})
46258      */
46259
46260     // private
46261     defaultAutoCreate : {tag: 'fieldset', cn: {tag:'legend'}},
46262
46263     // private
46264     onRender : function(ct, position){
46265         Roo.form.FieldSet.superclass.onRender.call(this, ct, position);
46266         if(this.legend){
46267             this.setLegend(this.legend);
46268         }
46269     },
46270
46271     // private
46272     setLegend : function(text){
46273         if(this.rendered){
46274             this.el.child('legend').update(text);
46275         }
46276     }
46277 });/*
46278  * Based on:
46279  * Ext JS Library 1.1.1
46280  * Copyright(c) 2006-2007, Ext JS, LLC.
46281  *
46282  * Originally Released Under LGPL - original licence link has changed is not relivant.
46283  *
46284  * Fork - LGPL
46285  * <script type="text/javascript">
46286  */
46287 /**
46288  * @class Roo.form.VTypes
46289  * Overridable validation definitions. The validations provided are basic and intended to be easily customizable and extended.
46290  * @singleton
46291  */
46292 Roo.form.VTypes = function(){
46293     // closure these in so they are only created once.
46294     var alpha = /^[a-zA-Z_]+$/;
46295     var alphanum = /^[a-zA-Z0-9_]+$/;
46296     var email = /^([\w]+)(.[\w]+)*@([\w-]+\.){1,5}([A-Za-z]){2,4}$/;
46297     var url = /(((https?)|(ftp)):\/\/([\-\w]+\.)+\w{2,3}(\/[%\-\w]+(\.\w{2,})?)*(([\w\-\.\?\\\/+@&#;`~=%!]*)(\.\w{2,})?)*\/?)/i;
46298
46299     // All these messages and functions are configurable
46300     return {
46301         /**
46302          * The function used to validate email addresses
46303          * @param {String} value The email address
46304          */
46305         'email' : function(v){
46306             return email.test(v);
46307         },
46308         /**
46309          * The error text to display when the email validation function returns false
46310          * @type String
46311          */
46312         'emailText' : 'This field should be an e-mail address in the format "user@domain.com"',
46313         /**
46314          * The keystroke filter mask to be applied on email input
46315          * @type RegExp
46316          */
46317         'emailMask' : /[a-z0-9_\.\-@]/i,
46318
46319         /**
46320          * The function used to validate URLs
46321          * @param {String} value The URL
46322          */
46323         'url' : function(v){
46324             return url.test(v);
46325         },
46326         /**
46327          * The error text to display when the url validation function returns false
46328          * @type String
46329          */
46330         'urlText' : 'This field should be a URL in the format "http:/'+'/www.domain.com"',
46331         
46332         /**
46333          * The function used to validate alpha values
46334          * @param {String} value The value
46335          */
46336         'alpha' : function(v){
46337             return alpha.test(v);
46338         },
46339         /**
46340          * The error text to display when the alpha validation function returns false
46341          * @type String
46342          */
46343         'alphaText' : 'This field should only contain letters and _',
46344         /**
46345          * The keystroke filter mask to be applied on alpha input
46346          * @type RegExp
46347          */
46348         'alphaMask' : /[a-z_]/i,
46349
46350         /**
46351          * The function used to validate alphanumeric values
46352          * @param {String} value The value
46353          */
46354         'alphanum' : function(v){
46355             return alphanum.test(v);
46356         },
46357         /**
46358          * The error text to display when the alphanumeric validation function returns false
46359          * @type String
46360          */
46361         'alphanumText' : 'This field should only contain letters, numbers and _',
46362         /**
46363          * The keystroke filter mask to be applied on alphanumeric input
46364          * @type RegExp
46365          */
46366         'alphanumMask' : /[a-z0-9_]/i
46367     };
46368 }();//<script type="text/javascript">
46369
46370 /**
46371  * @class Roo.form.FCKeditor
46372  * @extends Roo.form.TextArea
46373  * Wrapper around the FCKEditor http://www.fckeditor.net
46374  * @constructor
46375  * Creates a new FCKeditor
46376  * @param {Object} config Configuration options
46377  */
46378 Roo.form.FCKeditor = function(config){
46379     Roo.form.FCKeditor.superclass.constructor.call(this, config);
46380     this.addEvents({
46381          /**
46382          * @event editorinit
46383          * Fired when the editor is initialized - you can add extra handlers here..
46384          * @param {FCKeditor} this
46385          * @param {Object} the FCK object.
46386          */
46387         editorinit : true
46388     });
46389     
46390     
46391 };
46392 Roo.form.FCKeditor.editors = { };
46393 Roo.extend(Roo.form.FCKeditor, Roo.form.TextArea,
46394 {
46395     //defaultAutoCreate : {
46396     //    tag : "textarea",style   : "width:100px;height:60px;" ,autocomplete    : "off"
46397     //},
46398     // private
46399     /**
46400      * @cfg {Object} fck options - see fck manual for details.
46401      */
46402     fckconfig : false,
46403     
46404     /**
46405      * @cfg {Object} fck toolbar set (Basic or Default)
46406      */
46407     toolbarSet : 'Basic',
46408     /**
46409      * @cfg {Object} fck BasePath
46410      */ 
46411     basePath : '/fckeditor/',
46412     
46413     
46414     frame : false,
46415     
46416     value : '',
46417     
46418    
46419     onRender : function(ct, position)
46420     {
46421         if(!this.el){
46422             this.defaultAutoCreate = {
46423                 tag: "textarea",
46424                 style:"width:300px;height:60px;",
46425                 autocomplete: "off"
46426             };
46427         }
46428         Roo.form.FCKeditor.superclass.onRender.call(this, ct, position);
46429         /*
46430         if(this.grow){
46431             this.textSizeEl = Roo.DomHelper.append(document.body, {tag: "pre", cls: "x-form-grow-sizer"});
46432             if(this.preventScrollbars){
46433                 this.el.setStyle("overflow", "hidden");
46434             }
46435             this.el.setHeight(this.growMin);
46436         }
46437         */
46438         //console.log('onrender' + this.getId() );
46439         Roo.form.FCKeditor.editors[this.getId()] = this;
46440          
46441
46442         this.replaceTextarea() ;
46443         
46444     },
46445     
46446     getEditor : function() {
46447         return this.fckEditor;
46448     },
46449     /**
46450      * Sets a data value into the field and validates it.  To set the value directly without validation see {@link #setRawValue}.
46451      * @param {Mixed} value The value to set
46452      */
46453     
46454     
46455     setValue : function(value)
46456     {
46457         //console.log('setValue: ' + value);
46458         
46459         if(typeof(value) == 'undefined') { // not sure why this is happending...
46460             return;
46461         }
46462         Roo.form.FCKeditor.superclass.setValue.apply(this,[value]);
46463         
46464         //if(!this.el || !this.getEditor()) {
46465         //    this.value = value;
46466             //this.setValue.defer(100,this,[value]);    
46467         //    return;
46468         //} 
46469         
46470         if(!this.getEditor()) {
46471             return;
46472         }
46473         
46474         this.getEditor().SetData(value);
46475         
46476         //
46477
46478     },
46479
46480     /**
46481      * Returns the normalized data value (undefined or emptyText will be returned as '').  To return the raw value see {@link #getRawValue}.
46482      * @return {Mixed} value The field value
46483      */
46484     getValue : function()
46485     {
46486         
46487         if (this.frame && this.frame.dom.style.display == 'none') {
46488             return Roo.form.FCKeditor.superclass.getValue.call(this);
46489         }
46490         
46491         if(!this.el || !this.getEditor()) {
46492            
46493            // this.getValue.defer(100,this); 
46494             return this.value;
46495         }
46496        
46497         
46498         var value=this.getEditor().GetData();
46499         Roo.form.FCKeditor.superclass.setValue.apply(this,[value]);
46500         return Roo.form.FCKeditor.superclass.getValue.call(this);
46501         
46502
46503     },
46504
46505     /**
46506      * Returns the raw data value which may or may not be a valid, defined value.  To return a normalized value see {@link #getValue}.
46507      * @return {Mixed} value The field value
46508      */
46509     getRawValue : function()
46510     {
46511         if (this.frame && this.frame.dom.style.display == 'none') {
46512             return Roo.form.FCKeditor.superclass.getRawValue.call(this);
46513         }
46514         
46515         if(!this.el || !this.getEditor()) {
46516             //this.getRawValue.defer(100,this); 
46517             return this.value;
46518             return;
46519         }
46520         
46521         
46522         
46523         var value=this.getEditor().GetData();
46524         Roo.form.FCKeditor.superclass.setRawValue.apply(this,[value]);
46525         return Roo.form.FCKeditor.superclass.getRawValue.call(this);
46526          
46527     },
46528     
46529     setSize : function(w,h) {
46530         
46531         
46532         
46533         //if (this.frame && this.frame.dom.style.display == 'none') {
46534         //    Roo.form.FCKeditor.superclass.setSize.apply(this, [w, h]);
46535         //    return;
46536         //}
46537         //if(!this.el || !this.getEditor()) {
46538         //    this.setSize.defer(100,this, [w,h]); 
46539         //    return;
46540         //}
46541         
46542         
46543         
46544         Roo.form.FCKeditor.superclass.setSize.apply(this, [w, h]);
46545         
46546         this.frame.dom.setAttribute('width', w);
46547         this.frame.dom.setAttribute('height', h);
46548         this.frame.setSize(w,h);
46549         
46550     },
46551     
46552     toggleSourceEdit : function(value) {
46553         
46554       
46555          
46556         this.el.dom.style.display = value ? '' : 'none';
46557         this.frame.dom.style.display = value ?  'none' : '';
46558         
46559     },
46560     
46561     
46562     focus: function(tag)
46563     {
46564         if (this.frame.dom.style.display == 'none') {
46565             return Roo.form.FCKeditor.superclass.focus.call(this);
46566         }
46567         if(!this.el || !this.getEditor()) {
46568             this.focus.defer(100,this, [tag]); 
46569             return;
46570         }
46571         
46572         
46573         
46574         
46575         var tgs = this.getEditor().EditorDocument.getElementsByTagName(tag);
46576         this.getEditor().Focus();
46577         if (tgs.length) {
46578             if (!this.getEditor().Selection.GetSelection()) {
46579                 this.focus.defer(100,this, [tag]); 
46580                 return;
46581             }
46582             
46583             
46584             var r = this.getEditor().EditorDocument.createRange();
46585             r.setStart(tgs[0],0);
46586             r.setEnd(tgs[0],0);
46587             this.getEditor().Selection.GetSelection().removeAllRanges();
46588             this.getEditor().Selection.GetSelection().addRange(r);
46589             this.getEditor().Focus();
46590         }
46591         
46592     },
46593     
46594     
46595     
46596     replaceTextarea : function()
46597     {
46598         if ( document.getElementById( this.getId() + '___Frame' ) )
46599             return ;
46600         //if ( !this.checkBrowser || this._isCompatibleBrowser() )
46601         //{
46602             // We must check the elements firstly using the Id and then the name.
46603         var oTextarea = document.getElementById( this.getId() );
46604         
46605         var colElementsByName = document.getElementsByName( this.getId() ) ;
46606          
46607         oTextarea.style.display = 'none' ;
46608
46609         if ( oTextarea.tabIndex ) {            
46610             this.TabIndex = oTextarea.tabIndex ;
46611         }
46612         
46613         this._insertHtmlBefore( this._getConfigHtml(), oTextarea ) ;
46614         this._insertHtmlBefore( this._getIFrameHtml(), oTextarea ) ;
46615         this.frame = Roo.get(this.getId() + '___Frame')
46616     },
46617     
46618     _getConfigHtml : function()
46619     {
46620         var sConfig = '' ;
46621
46622         for ( var o in this.fckconfig ) {
46623             sConfig += sConfig.length > 0  ? '&amp;' : '';
46624             sConfig += encodeURIComponent( o ) + '=' + encodeURIComponent( this.fckconfig[o] ) ;
46625         }
46626
46627         return '<input type="hidden" id="' + this.getId() + '___Config" value="' + sConfig + '" style="display:none" />' ;
46628     },
46629     
46630     
46631     _getIFrameHtml : function()
46632     {
46633         var sFile = 'fckeditor.html' ;
46634         /* no idea what this is about..
46635         try
46636         {
46637             if ( (/fcksource=true/i).test( window.top.location.search ) )
46638                 sFile = 'fckeditor.original.html' ;
46639         }
46640         catch (e) { 
46641         */
46642
46643         var sLink = this.basePath + 'editor/' + sFile + '?InstanceName=' + encodeURIComponent( this.getId() ) ;
46644         sLink += this.toolbarSet ? ( '&amp;Toolbar=' + this.toolbarSet)  : '';
46645         
46646         
46647         var html = '<iframe id="' + this.getId() +
46648             '___Frame" src="' + sLink +
46649             '" width="' + this.width +
46650             '" height="' + this.height + '"' +
46651             (this.tabIndex ?  ' tabindex="' + this.tabIndex + '"' :'' ) +
46652             ' frameborder="0" scrolling="no"></iframe>' ;
46653
46654         return html ;
46655     },
46656     
46657     _insertHtmlBefore : function( html, element )
46658     {
46659         if ( element.insertAdjacentHTML )       {
46660             // IE
46661             element.insertAdjacentHTML( 'beforeBegin', html ) ;
46662         } else { // Gecko
46663             var oRange = document.createRange() ;
46664             oRange.setStartBefore( element ) ;
46665             var oFragment = oRange.createContextualFragment( html );
46666             element.parentNode.insertBefore( oFragment, element ) ;
46667         }
46668     }
46669     
46670     
46671   
46672     
46673     
46674     
46675     
46676
46677 });
46678
46679 //Roo.reg('fckeditor', Roo.form.FCKeditor);
46680
46681 function FCKeditor_OnComplete(editorInstance){
46682     var f = Roo.form.FCKeditor.editors[editorInstance.Name];
46683     f.fckEditor = editorInstance;
46684     //console.log("loaded");
46685     f.fireEvent('editorinit', f, editorInstance);
46686
46687   
46688
46689  
46690
46691
46692
46693
46694
46695
46696
46697
46698
46699
46700
46701
46702
46703
46704
46705 //<script type="text/javascript">
46706 /**
46707  * @class Roo.form.GridField
46708  * @extends Roo.form.Field
46709  * Embed a grid (or editable grid into a form)
46710  * STATUS ALPHA
46711  * 
46712  * This embeds a grid in a form, the value of the field should be the json encoded array of rows
46713  * it needs 
46714  * xgrid.store = Roo.data.Store
46715  * xgrid.store.proxy = Roo.data.MemoryProxy (data = [] )
46716  * xgrid.store.reader = Roo.data.JsonReader 
46717  * 
46718  * 
46719  * @constructor
46720  * Creates a new GridField
46721  * @param {Object} config Configuration options
46722  */
46723 Roo.form.GridField = function(config){
46724     Roo.form.GridField.superclass.constructor.call(this, config);
46725      
46726 };
46727
46728 Roo.extend(Roo.form.GridField, Roo.form.Field,  {
46729     /**
46730      * @cfg {Number} width  - used to restrict width of grid..
46731      */
46732     width : 100,
46733     /**
46734      * @cfg {Number} height - used to restrict height of grid..
46735      */
46736     height : 50,
46737      /**
46738      * @cfg {Object} xgrid (xtype'd description of grid) { xtype : 'Grid', dataSource: .... }
46739          * 
46740          *}
46741      */
46742     xgrid : false, 
46743     /**
46744      * @cfg {String/Object} autoCreate A DomHelper element spec, or true for a default element spec (defaults to
46745      * {tag: "input", type: "checkbox", autocomplete: "off"})
46746      */
46747    // defaultAutoCreate : { tag: 'div' },
46748     defaultAutoCreate : { tag: 'input', type: 'hidden', autocomplete: 'off'},
46749     /**
46750      * @cfg {String} addTitle Text to include for adding a title.
46751      */
46752     addTitle : false,
46753     //
46754     onResize : function(){
46755         Roo.form.Field.superclass.onResize.apply(this, arguments);
46756     },
46757
46758     initEvents : function(){
46759         // Roo.form.Checkbox.superclass.initEvents.call(this);
46760         // has no events...
46761        
46762     },
46763
46764
46765     getResizeEl : function(){
46766         return this.wrap;
46767     },
46768
46769     getPositionEl : function(){
46770         return this.wrap;
46771     },
46772
46773     // private
46774     onRender : function(ct, position){
46775         
46776         this.style = this.style || 'overflow: hidden; border:1px solid #c3daf9;';
46777         var style = this.style;
46778         delete this.style;
46779         
46780         Roo.form.GridField.superclass.onRender.call(this, ct, position);
46781         this.wrap = this.el.wrap({cls: ''}); // not sure why ive done thsi...
46782         this.viewEl = this.wrap.createChild({ tag: 'div' });
46783         if (style) {
46784             this.viewEl.applyStyles(style);
46785         }
46786         if (this.width) {
46787             this.viewEl.setWidth(this.width);
46788         }
46789         if (this.height) {
46790             this.viewEl.setHeight(this.height);
46791         }
46792         //if(this.inputValue !== undefined){
46793         //this.setValue(this.value);
46794         
46795         
46796         this.grid = new Roo.grid[this.xgrid.xtype](this.viewEl, this.xgrid);
46797         
46798         
46799         this.grid.render();
46800         this.grid.getDataSource().on('remove', this.refreshValue, this);
46801         this.grid.getDataSource().on('update', this.refreshValue, this);
46802         this.grid.on('afteredit', this.refreshValue, this);
46803  
46804     },
46805      
46806     
46807     /**
46808      * Sets the value of the item. 
46809      * @param {String} either an object  or a string..
46810      */
46811     setValue : function(v){
46812         //this.value = v;
46813         v = v || []; // empty set..
46814         // this does not seem smart - it really only affects memoryproxy grids..
46815         if (this.grid && this.grid.getDataSource() && typeof(v) != 'undefined') {
46816             var ds = this.grid.getDataSource();
46817             // assumes a json reader..
46818             var data = {}
46819             data[ds.reader.meta.root ] =  typeof(v) == 'string' ? Roo.decode(v) : v;
46820             ds.loadData( data);
46821         }
46822         // clear selection so it does not get stale.
46823         if (this.grid.sm) { 
46824             this.grid.sm.clearSelections();
46825         }
46826         
46827         Roo.form.GridField.superclass.setValue.call(this, v);
46828         this.refreshValue();
46829         // should load data in the grid really....
46830     },
46831     
46832     // private
46833     refreshValue: function() {
46834          var val = [];
46835         this.grid.getDataSource().each(function(r) {
46836             val.push(r.data);
46837         });
46838         this.el.dom.value = Roo.encode(val);
46839     }
46840     
46841      
46842     
46843     
46844 });/*
46845  * Based on:
46846  * Ext JS Library 1.1.1
46847  * Copyright(c) 2006-2007, Ext JS, LLC.
46848  *
46849  * Originally Released Under LGPL - original licence link has changed is not relivant.
46850  *
46851  * Fork - LGPL
46852  * <script type="text/javascript">
46853  */
46854 /**
46855  * @class Roo.form.DisplayField
46856  * @extends Roo.form.Field
46857  * A generic Field to display non-editable data.
46858  * @constructor
46859  * Creates a new Display Field item.
46860  * @param {Object} config Configuration options
46861  */
46862 Roo.form.DisplayField = function(config){
46863     Roo.form.DisplayField.superclass.constructor.call(this, config);
46864     
46865 };
46866
46867 Roo.extend(Roo.form.DisplayField, Roo.form.TextField,  {
46868     inputType:      'hidden',
46869     allowBlank:     true,
46870     readOnly:         true,
46871     
46872  
46873     /**
46874      * @cfg {String} focusClass The CSS class to use when the checkbox receives focus (defaults to undefined)
46875      */
46876     focusClass : undefined,
46877     /**
46878      * @cfg {String} fieldClass The default CSS class for the checkbox (defaults to "x-form-field")
46879      */
46880     fieldClass: 'x-form-field',
46881     
46882      /**
46883      * @cfg {Function} valueRenderer The renderer for the field (so you can reformat output). should return raw HTML
46884      */
46885     valueRenderer: undefined,
46886     
46887     width: 100,
46888     /**
46889      * @cfg {String/Object} autoCreate A DomHelper element spec, or true for a default element spec (defaults to
46890      * {tag: "input", type: "checkbox", autocomplete: "off"})
46891      */
46892      
46893  //   defaultAutoCreate : { tag: 'input', type: 'hidden', autocomplete: 'off'},
46894
46895     onResize : function(){
46896         Roo.form.DisplayField.superclass.onResize.apply(this, arguments);
46897         
46898     },
46899
46900     initEvents : function(){
46901         // Roo.form.Checkbox.superclass.initEvents.call(this);
46902         // has no events...
46903        
46904     },
46905
46906
46907     getResizeEl : function(){
46908         return this.wrap;
46909     },
46910
46911     getPositionEl : function(){
46912         return this.wrap;
46913     },
46914
46915     // private
46916     onRender : function(ct, position){
46917         
46918         Roo.form.DisplayField.superclass.onRender.call(this, ct, position);
46919         //if(this.inputValue !== undefined){
46920         this.wrap = this.el.wrap();
46921         
46922         this.viewEl = this.wrap.createChild({ tag: 'div', cls: 'x-form-displayfield'});
46923         
46924         if (this.bodyStyle) {
46925             this.viewEl.applyStyles(this.bodyStyle);
46926         }
46927         //this.viewEl.setStyle('padding', '2px');
46928         
46929         this.setValue(this.value);
46930         
46931     },
46932 /*
46933     // private
46934     initValue : Roo.emptyFn,
46935
46936   */
46937
46938         // private
46939     onClick : function(){
46940         
46941     },
46942
46943     /**
46944      * Sets the checked state of the checkbox.
46945      * @param {Boolean/String} checked True, 'true', '1', or 'on' to check the checkbox, any other value will uncheck it.
46946      */
46947     setValue : function(v){
46948         this.value = v;
46949         var html = this.valueRenderer ?  this.valueRenderer(v) : String.format('{0}', v);
46950         // this might be called before we have a dom element..
46951         if (!this.viewEl) {
46952             return;
46953         }
46954         this.viewEl.dom.innerHTML = html;
46955         Roo.form.DisplayField.superclass.setValue.call(this, v);
46956
46957     }
46958 });/*
46959  * 
46960  * Licence- LGPL
46961  * 
46962  */
46963
46964 /**
46965  * @class Roo.form.DayPicker
46966  * @extends Roo.form.Field
46967  * A Day picker show [M] [T] [W] ....
46968  * @constructor
46969  * Creates a new Day Picker
46970  * @param {Object} config Configuration options
46971  */
46972 Roo.form.DayPicker= function(config){
46973     Roo.form.DayPicker.superclass.constructor.call(this, config);
46974      
46975 };
46976
46977 Roo.extend(Roo.form.DayPicker, Roo.form.Field,  {
46978     /**
46979      * @cfg {String} focusClass The CSS class to use when the checkbox receives focus (defaults to undefined)
46980      */
46981     focusClass : undefined,
46982     /**
46983      * @cfg {String} fieldClass The default CSS class for the checkbox (defaults to "x-form-field")
46984      */
46985     fieldClass: "x-form-field",
46986    
46987     /**
46988      * @cfg {String/Object} autoCreate A DomHelper element spec, or true for a default element spec (defaults to
46989      * {tag: "input", type: "checkbox", autocomplete: "off"})
46990      */
46991     defaultAutoCreate : { tag: "input", type: 'hidden', autocomplete: "off"},
46992     
46993    
46994     actionMode : 'viewEl', 
46995     //
46996     // private
46997  
46998     inputType : 'hidden',
46999     
47000      
47001     inputElement: false, // real input element?
47002     basedOn: false, // ????
47003     
47004     isFormField: true, // not sure where this is needed!!!!
47005
47006     onResize : function(){
47007         Roo.form.Checkbox.superclass.onResize.apply(this, arguments);
47008         if(!this.boxLabel){
47009             this.el.alignTo(this.wrap, 'c-c');
47010         }
47011     },
47012
47013     initEvents : function(){
47014         Roo.form.Checkbox.superclass.initEvents.call(this);
47015         this.el.on("click", this.onClick,  this);
47016         this.el.on("change", this.onClick,  this);
47017     },
47018
47019
47020     getResizeEl : function(){
47021         return this.wrap;
47022     },
47023
47024     getPositionEl : function(){
47025         return this.wrap;
47026     },
47027
47028     
47029     // private
47030     onRender : function(ct, position){
47031         Roo.form.Checkbox.superclass.onRender.call(this, ct, position);
47032        
47033         this.wrap = this.el.wrap({cls: 'x-form-daypick-item '});
47034         
47035         var r1 = '<table><tr>';
47036         var r2 = '<tr class="x-form-daypick-icons">';
47037         for (var i=0; i < 7; i++) {
47038             r1+= '<td><div>' + Date.dayNames[i].substring(0,3) + '</div></td>';
47039             r2+= '<td><img class="x-menu-item-icon" src="' + Roo.BLANK_IMAGE_URL  +'"></td>';
47040         }
47041         
47042         var viewEl = this.wrap.createChild( r1 + '</tr>' + r2 + '</tr></table>');
47043         viewEl.select('img').on('click', this.onClick, this);
47044         this.viewEl = viewEl;   
47045         
47046         
47047         // this will not work on Chrome!!!
47048         this.el.on('DOMAttrModified', this.setFromHidden,  this); //ff
47049         this.el.on('propertychange', this.setFromHidden,  this);  //ie
47050         
47051         
47052           
47053
47054     },
47055
47056     // private
47057     initValue : Roo.emptyFn,
47058
47059     /**
47060      * Returns the checked state of the checkbox.
47061      * @return {Boolean} True if checked, else false
47062      */
47063     getValue : function(){
47064         return this.el.dom.value;
47065         
47066     },
47067
47068         // private
47069     onClick : function(e){ 
47070         //this.setChecked(!this.checked);
47071         Roo.get(e.target).toggleClass('x-menu-item-checked');
47072         this.refreshValue();
47073         //if(this.el.dom.checked != this.checked){
47074         //    this.setValue(this.el.dom.checked);
47075        // }
47076     },
47077     
47078     // private
47079     refreshValue : function()
47080     {
47081         var val = '';
47082         this.viewEl.select('img',true).each(function(e,i,n)  {
47083             val += e.is(".x-menu-item-checked") ? String(n) : '';
47084         });
47085         this.setValue(val, true);
47086     },
47087
47088     /**
47089      * Sets the checked state of the checkbox.
47090      * On is always based on a string comparison between inputValue and the param.
47091      * @param {Boolean/String} value - the value to set 
47092      * @param {Boolean/String} suppressEvent - whether to suppress the checkchange event.
47093      */
47094     setValue : function(v,suppressEvent){
47095         if (!this.el.dom) {
47096             return;
47097         }
47098         var old = this.el.dom.value ;
47099         this.el.dom.value = v;
47100         if (suppressEvent) {
47101             return ;
47102         }
47103          
47104         // update display..
47105         this.viewEl.select('img',true).each(function(e,i,n)  {
47106             
47107             var on = e.is(".x-menu-item-checked");
47108             var newv = v.indexOf(String(n)) > -1;
47109             if (on != newv) {
47110                 e.toggleClass('x-menu-item-checked');
47111             }
47112             
47113         });
47114         
47115         
47116         this.fireEvent('change', this, v, old);
47117         
47118         
47119     },
47120    
47121     // handle setting of hidden value by some other method!!?!?
47122     setFromHidden: function()
47123     {
47124         if(!this.el){
47125             return;
47126         }
47127         //console.log("SET FROM HIDDEN");
47128         //alert('setFrom hidden');
47129         this.setValue(this.el.dom.value);
47130     },
47131     
47132     onDestroy : function()
47133     {
47134         if(this.viewEl){
47135             Roo.get(this.viewEl).remove();
47136         }
47137          
47138         Roo.form.DayPicker.superclass.onDestroy.call(this);
47139     }
47140
47141 });/*
47142  * RooJS Library 1.1.1
47143  * Copyright(c) 2008-2011  Alan Knowles
47144  *
47145  * License - LGPL
47146  */
47147  
47148
47149 /**
47150  * @class Roo.form.ComboCheck
47151  * @extends Roo.form.ComboBox
47152  * A combobox for multiple select items.
47153  *
47154  * FIXME - could do with a reset button..
47155  * 
47156  * @constructor
47157  * Create a new ComboCheck
47158  * @param {Object} config Configuration options
47159  */
47160 Roo.form.ComboCheck = function(config){
47161     Roo.form.ComboCheck.superclass.constructor.call(this, config);
47162     // should verify some data...
47163     // like
47164     // hiddenName = required..
47165     // displayField = required
47166     // valudField == required
47167     var req= [ 'hiddenName', 'displayField', 'valueField' ];
47168     var _t = this;
47169     Roo.each(req, function(e) {
47170         if ((typeof(_t[e]) == 'undefined' ) || !_t[e].length) {
47171             throw "Roo.form.ComboCheck : missing value for: " + e;
47172         }
47173     });
47174     
47175     
47176 };
47177
47178 Roo.extend(Roo.form.ComboCheck, Roo.form.ComboBox, {
47179      
47180      
47181     editable : false,
47182      
47183     selectedClass: 'x-menu-item-checked', 
47184     
47185     // private
47186     onRender : function(ct, position){
47187         var _t = this;
47188         
47189         
47190         
47191         if(!this.tpl){
47192             var cls = 'x-combo-list';
47193
47194             
47195             this.tpl =  new Roo.Template({
47196                 html :  '<div class="'+cls+'-item x-menu-check-item">' +
47197                    '<img class="x-menu-item-icon" style="margin: 0px;" src="' + Roo.BLANK_IMAGE_URL + '">' + 
47198                    '<span>{' + this.displayField + '}</span>' +
47199                     '</div>' 
47200                 
47201             });
47202         }
47203  
47204         
47205         Roo.form.ComboCheck.superclass.onRender.call(this, ct, position);
47206         this.view.singleSelect = false;
47207         this.view.multiSelect = true;
47208         this.view.toggleSelect = true;
47209         this.pageTb.add(new Roo.Toolbar.Fill(), {
47210             
47211             text: 'Done',
47212             handler: function()
47213             {
47214                 _t.collapse();
47215             }
47216         });
47217     },
47218     
47219     onViewOver : function(e, t){
47220         // do nothing...
47221         return;
47222         
47223     },
47224     
47225     onViewClick : function(doFocus,index){
47226         return;
47227         
47228     },
47229     select: function () {
47230         //Roo.log("SELECT CALLED");
47231     },
47232      
47233     selectByValue : function(xv, scrollIntoView){
47234         var ar = this.getValueArray();
47235         var sels = [];
47236         
47237         Roo.each(ar, function(v) {
47238             if(v === undefined || v === null){
47239                 return;
47240             }
47241             var r = this.findRecord(this.valueField, v);
47242             if(r){
47243                 sels.push(this.store.indexOf(r))
47244                 
47245             }
47246         },this);
47247         this.view.select(sels);
47248         return false;
47249     },
47250     
47251     
47252     
47253     onSelect : function(record, index){
47254        // Roo.log("onselect Called");
47255        // this is only called by the clear button now..
47256         this.view.clearSelections();
47257         this.setValue('[]');
47258         if (this.value != this.valueBefore) {
47259             this.fireEvent('change', this, this.value, this.valueBefore);
47260             this.valueBefore = this.value;
47261         }
47262     },
47263     getValueArray : function()
47264     {
47265         var ar = [] ;
47266         
47267         try {
47268             //Roo.log(this.value);
47269             if (typeof(this.value) == 'undefined') {
47270                 return [];
47271             }
47272             var ar = Roo.decode(this.value);
47273             return  ar instanceof Array ? ar : []; //?? valid?
47274             
47275         } catch(e) {
47276             Roo.log(e + "\nRoo.form.ComboCheck:getValueArray  invalid data:" + this.getValue());
47277             return [];
47278         }
47279          
47280     },
47281     expand : function ()
47282     {
47283         
47284         Roo.form.ComboCheck.superclass.expand.call(this);
47285         this.valueBefore = typeof(this.value) == 'undefined' ? '' : this.value;
47286         //this.valueBefore = typeof(this.valueBefore) == 'undefined' ? '' : this.valueBefore;
47287         
47288
47289     },
47290     
47291     collapse : function(){
47292         Roo.form.ComboCheck.superclass.collapse.call(this);
47293         var sl = this.view.getSelectedIndexes();
47294         var st = this.store;
47295         var nv = [];
47296         var tv = [];
47297         var r;
47298         Roo.each(sl, function(i) {
47299             r = st.getAt(i);
47300             nv.push(r.get(this.valueField));
47301         },this);
47302         this.setValue(Roo.encode(nv));
47303         if (this.value != this.valueBefore) {
47304
47305             this.fireEvent('change', this, this.value, this.valueBefore);
47306             this.valueBefore = this.value;
47307         }
47308         
47309     },
47310     
47311     setValue : function(v){
47312         // Roo.log(v);
47313         this.value = v;
47314         
47315         var vals = this.getValueArray();
47316         var tv = [];
47317         Roo.each(vals, function(k) {
47318             var r = this.findRecord(this.valueField, k);
47319             if(r){
47320                 tv.push(r.data[this.displayField]);
47321             }else if(this.valueNotFoundText !== undefined){
47322                 tv.push( this.valueNotFoundText );
47323             }
47324         },this);
47325        // Roo.log(tv);
47326         
47327         Roo.form.ComboBox.superclass.setValue.call(this, tv.join(', '));
47328         this.hiddenField.value = v;
47329         this.value = v;
47330     }
47331     
47332 });/*
47333  * Based on:
47334  * Ext JS Library 1.1.1
47335  * Copyright(c) 2006-2007, Ext JS, LLC.
47336  *
47337  * Originally Released Under LGPL - original licence link has changed is not relivant.
47338  *
47339  * Fork - LGPL
47340  * <script type="text/javascript">
47341  */
47342  
47343 /**
47344  * @class Roo.form.Signature
47345  * @extends Roo.form.Field
47346  * Signature field.  
47347  * @constructor
47348  * 
47349  * @param {Object} config Configuration options
47350  */
47351
47352 Roo.form.Signature = function(config){
47353     Roo.form.Signature.superclass.constructor.call(this, config);
47354     
47355     this.addEvents({// not in used??
47356          /**
47357          * @event confirm
47358          * Fires when the 'confirm' icon is pressed (add a listener to enable add button)
47359              * @param {Roo.form.Signature} combo This combo box
47360              */
47361         'confirm' : true,
47362         /**
47363          * @event reset
47364          * Fires when the 'edit' icon is pressed (add a listener to enable add button)
47365              * @param {Roo.form.ComboBox} combo This combo box
47366              * @param {Roo.data.Record|false} record The data record returned from the underlying store (or false on nothing selected)
47367              */
47368         'reset' : true
47369     });
47370 };
47371
47372 Roo.extend(Roo.form.Signature, Roo.form.Field,  {
47373     /**
47374      * @cfg {Object} labels Label to use when rendering a form.
47375      * defaults to 
47376      * labels : { 
47377      *      clear : "Clear",
47378      *      confirm : "Confirm"
47379      *  }
47380      */
47381     labels : { 
47382         clear : "Clear",
47383         confirm : "Confirm"
47384     },
47385     /**
47386      * @cfg {Number} width The signature panel width (defaults to 300)
47387      */
47388     width: 300,
47389     /**
47390      * @cfg {Number} height The signature panel height (defaults to 100)
47391      */
47392     height : 100,
47393     /**
47394      * @cfg {Boolean} allowBlank False to validate that the value length > 0 (defaults to false)
47395      */
47396     allowBlank : false,
47397     
47398     //private
47399     // {Object} signPanel The signature SVG panel element (defaults to {})
47400     signPanel : {},
47401     // {Boolean} isMouseDown False to validate that the mouse down event (defaults to false)
47402     isMouseDown : false,
47403     // {Boolean} isConfirmed validate the signature is confirmed or not for submitting form (defaults to false)
47404     isConfirmed : false,
47405     // {String} signatureTmp SVG mapping string (defaults to empty string)
47406     signatureTmp : '',
47407     
47408     
47409     defaultAutoCreate : { // modified by initCompnoent..
47410         tag: "input",
47411         type:"hidden"
47412     },
47413
47414     // private
47415     onRender : function(ct, position){
47416         
47417         Roo.form.Signature.superclass.onRender.call(this, ct, position);
47418         
47419         this.wrap = this.el.wrap({
47420             cls:'x-form-signature-wrap', style : 'width: ' + this.width + 'px', cn:{cls:'x-form-signature'}
47421         });
47422         
47423         this.createToolbar(this);
47424         this.signPanel = this.wrap.createChild({
47425                 tag: 'div',
47426                 style: 'width: ' + this.width + 'px; height: ' + this.height + 'px; border: 0;'
47427             }, this.el
47428         );
47429             
47430         this.svgID = Roo.id();
47431         this.svgEl = this.signPanel.createChild({
47432               xmlns : 'http://www.w3.org/2000/svg',
47433               tag : 'svg',
47434               id : this.svgID + "-svg",
47435               width: this.width,
47436               height: this.height,
47437               viewBox: '0 0 '+this.width+' '+this.height,
47438               cn : [
47439                 {
47440                     tag: "rect",
47441                     id: this.svgID + "-svg-r",
47442                     width: this.width,
47443                     height: this.height,
47444                     fill: "#ffa"
47445                 },
47446                 {
47447                     tag: "line",
47448                     id: this.svgID + "-svg-l",
47449                     x1: "0", // start
47450                     y1: (this.height*0.8), // start set the line in 80% of height
47451                     x2: this.width, // end
47452                     y2: (this.height*0.8), // end set the line in 80% of height
47453                     'stroke': "#666",
47454                     'stroke-width': "1",
47455                     'stroke-dasharray': "3",
47456                     'shape-rendering': "crispEdges",
47457                     'pointer-events': "none"
47458                 },
47459                 {
47460                     tag: "path",
47461                     id: this.svgID + "-svg-p",
47462                     'stroke': "navy",
47463                     'stroke-width': "3",
47464                     'fill': "none",
47465                     'pointer-events': 'none'
47466                 }
47467               ]
47468         });
47469         this.createSVG();
47470         this.svgBox = this.svgEl.dom.getScreenCTM();
47471     },
47472     createSVG : function(){ 
47473         var svg = this.signPanel;
47474         var r = svg.select('#'+ this.svgID + '-svg-r', true).first().dom;
47475         var t = this;
47476
47477         r.addEventListener('mousedown', function(e) { return t.down(e); }, false);
47478         r.addEventListener('mousemove', function(e) { return t.move(e); }, false);
47479         r.addEventListener('mouseup', function(e) { return t.up(e); }, false);
47480         r.addEventListener('mouseout', function(e) { return t.up(e); }, false);
47481         r.addEventListener('touchstart', function(e) { return t.down(e); }, false);
47482         r.addEventListener('touchmove', function(e) { return t.move(e); }, false);
47483         r.addEventListener('touchend', function(e) { return t.up(e); }, false);
47484         
47485     },
47486     isTouchEvent : function(e){
47487         return e.type.match(/^touch/);
47488     },
47489     getCoords : function (e) {
47490         var pt    = this.svgEl.dom.createSVGPoint();
47491         pt.x = e.clientX; 
47492         pt.y = e.clientY;
47493         if (this.isTouchEvent(e)) {
47494             pt.x =  e.targetTouches[0].clientX 
47495             pt.y = e.targetTouches[0].clientY;
47496         }
47497         var a = this.svgEl.dom.getScreenCTM();
47498         var b = a.inverse();
47499         var mx = pt.matrixTransform(b);
47500         return mx.x + ',' + mx.y;
47501     },
47502     //mouse event headler 
47503     down : function (e) {
47504         this.signatureTmp += 'M' + this.getCoords(e) + ' ';
47505         this.signPanel.select('#'+ this.svgID + '-svg-p', true).first().attr('d', this.signatureTmp);
47506         
47507         this.isMouseDown = true;
47508         
47509         e.preventDefault();
47510     },
47511     move : function (e) {
47512         if (this.isMouseDown) {
47513             this.signatureTmp += 'L' + this.getCoords(e) + ' ';
47514             this.signPanel.select('#'+ this.svgID + '-svg-p', true).first().attr( 'd', this.signatureTmp);
47515         }
47516         
47517         e.preventDefault();
47518     },
47519     up : function (e) {
47520         this.isMouseDown = false;
47521         var sp = this.signatureTmp.split(' ');
47522         
47523         if(sp.length > 1){
47524             if(!sp[sp.length-2].match(/^L/)){
47525                 sp.pop();
47526                 sp.pop();
47527                 sp.push("");
47528                 this.signatureTmp = sp.join(" ");
47529             }
47530         }
47531         if(this.getValue() != this.signatureTmp){
47532             this.signPanel.select('#'+ this.svgID + '-svg-r', true).first().attr('fill', '#ffa');
47533             this.isConfirmed = false;
47534         }
47535         e.preventDefault();
47536     },
47537     
47538     /**
47539      * Protected method that will not generally be called directly. It
47540      * is called when the editor creates its toolbar. Override this method if you need to
47541      * add custom toolbar buttons.
47542      * @param {HtmlEditor} editor
47543      */
47544     createToolbar : function(editor){
47545          function btn(id, toggle, handler){
47546             var xid = fid + '-'+ id ;
47547             return {
47548                 id : xid,
47549                 cmd : id,
47550                 cls : 'x-btn-icon x-edit-'+id,
47551                 enableToggle:toggle !== false,
47552                 scope: editor, // was editor...
47553                 handler:handler||editor.relayBtnCmd,
47554                 clickEvent:'mousedown',
47555                 tooltip: etb.buttonTips[id] || undefined, ///tips ???
47556                 tabIndex:-1
47557             };
47558         }
47559         
47560         
47561         var tb = new Roo.Toolbar(editor.wrap.dom.firstChild);
47562         this.tb = tb;
47563         this.tb.add(
47564            {
47565                 cls : ' x-signature-btn x-signature-'+id,
47566                 scope: editor, // was editor...
47567                 handler: this.reset,
47568                 clickEvent:'mousedown',
47569                 text: this.labels.clear
47570             },
47571             {
47572                  xtype : 'Fill',
47573                  xns: Roo.Toolbar
47574             }, 
47575             {
47576                 cls : '  x-signature-btn x-signature-'+id,
47577                 scope: editor, // was editor...
47578                 handler: this.confirmHandler,
47579                 clickEvent:'mousedown',
47580                 text: this.labels.confirm
47581             }
47582         );
47583     
47584     },
47585     //public
47586     /**
47587      * when user is clicked confirm then show this image.....
47588      * 
47589      * @return {String} Image Data URI
47590      */
47591     getImageDataURI : function(){
47592         var svg = this.svgEl.dom.parentNode.innerHTML;
47593         var src = 'data:image/svg+xml;base64,'+window.btoa(svg);
47594         return src; 
47595     },
47596     /**
47597      * 
47598      * @return {Boolean} this.isConfirmed
47599      */
47600     getConfirmed : function(){
47601         return this.isConfirmed;
47602     },
47603     /**
47604      * 
47605      * @return {Number} this.width
47606      */
47607     getWidth : function(){
47608         return this.width;
47609     },
47610     /**
47611      * 
47612      * @return {Number} this.height
47613      */
47614     getHeight : function(){
47615         return this.height;
47616     },
47617     // private
47618     getSignature : function(){
47619         return this.signatureTmp;
47620     },
47621     // private
47622     reset : function(){
47623         this.signatureTmp = '';
47624         this.signPanel.select('#'+ this.svgID + '-svg-r', true).first().attr('fill', '#ffa');
47625         this.signPanel.select('#'+ this.svgID + '-svg-p', true).first().attr( 'd', '');
47626         this.isConfirmed = false;
47627         Roo.form.Signature.superclass.reset.call(this);
47628     },
47629     setSignature : function(s){
47630         this.signatureTmp = s;
47631         this.signPanel.select('#'+ this.svgID + '-svg-r', true).first().attr('fill', '#ffa');
47632         this.signPanel.select('#'+ this.svgID + '-svg-p', true).first().attr( 'd', s);
47633         this.setValue(s);
47634         this.isConfirmed = false;
47635         Roo.form.Signature.superclass.reset.call(this);
47636     }, 
47637     test : function(){
47638 //        Roo.log(this.signPanel.dom.contentWindow.up())
47639     },
47640     //private
47641     setConfirmed : function(){
47642         
47643         
47644         
47645 //        Roo.log(Roo.get(this.signPanel.dom.contentWindow.r).attr('fill', '#cfc'));
47646     },
47647     // private
47648     confirmHandler : function(){
47649         if(!this.getSignature()){
47650             return;
47651         }
47652         
47653         this.signPanel.select('#'+ this.svgID + '-svg-r', true).first().attr('fill', '#cfc');
47654         this.setValue(this.getSignature());
47655         this.isConfirmed = true;
47656         
47657         this.fireEvent('confirm', this);
47658     },
47659     // private
47660     // Subclasses should provide the validation implementation by overriding this
47661     validateValue : function(value){
47662         if(this.allowBlank){
47663             return true;
47664         }
47665         
47666         if(this.isConfirmed){
47667             return true;
47668         }
47669         return false;
47670     }
47671 });/*
47672  * Based on:
47673  * Ext JS Library 1.1.1
47674  * Copyright(c) 2006-2007, Ext JS, LLC.
47675  *
47676  * Originally Released Under LGPL - original licence link has changed is not relivant.
47677  *
47678  * Fork - LGPL
47679  * <script type="text/javascript">
47680  */
47681  
47682
47683 /**
47684  * @class Roo.form.ComboBox
47685  * @extends Roo.form.TriggerField
47686  * A combobox control with support for autocomplete, remote-loading, paging and many other features.
47687  * @constructor
47688  * Create a new ComboBox.
47689  * @param {Object} config Configuration options
47690  */
47691 Roo.form.Select = function(config){
47692     Roo.form.Select.superclass.constructor.call(this, config);
47693      
47694 };
47695
47696 Roo.extend(Roo.form.Select , Roo.form.ComboBox, {
47697     /**
47698      * @cfg {String/HTMLElement/Element} transform The id, DOM node or element of an existing select to convert to a ComboBox
47699      */
47700     /**
47701      * @cfg {Boolean} lazyRender True to prevent the ComboBox from rendering until requested (should always be used when
47702      * rendering into an Roo.Editor, defaults to false)
47703      */
47704     /**
47705      * @cfg {Boolean/Object} autoCreate A DomHelper element spec, or true for a default element spec (defaults to:
47706      * {tag: "input", type: "text", size: "24", autocomplete: "off"})
47707      */
47708     /**
47709      * @cfg {Roo.data.Store} store The data store to which this combo is bound (defaults to undefined)
47710      */
47711     /**
47712      * @cfg {String} title If supplied, a header element is created containing this text and added into the top of
47713      * the dropdown list (defaults to undefined, with no header element)
47714      */
47715
47716      /**
47717      * @cfg {String/Roo.Template} tpl The template to use to render the output
47718      */
47719      
47720     // private
47721     defaultAutoCreate : {tag: "select"  },
47722     /**
47723      * @cfg {Number} listWidth The width in pixels of the dropdown list (defaults to the width of the ComboBox field)
47724      */
47725     listWidth: undefined,
47726     /**
47727      * @cfg {String} displayField The underlying data field name to bind to this CombBox (defaults to undefined if
47728      * mode = 'remote' or 'text' if mode = 'local')
47729      */
47730     displayField: undefined,
47731     /**
47732      * @cfg {String} valueField The underlying data value name to bind to this CombBox (defaults to undefined if
47733      * mode = 'remote' or 'value' if mode = 'local'). 
47734      * Note: use of a valueField requires the user make a selection
47735      * in order for a value to be mapped.
47736      */
47737     valueField: undefined,
47738     
47739     
47740     /**
47741      * @cfg {String} hiddenName If specified, a hidden form field with this name is dynamically generated to store the
47742      * field's data value (defaults to the underlying DOM element's name)
47743      */
47744     hiddenName: undefined,
47745     /**
47746      * @cfg {String} listClass CSS class to apply to the dropdown list element (defaults to '')
47747      */
47748     listClass: '',
47749     /**
47750      * @cfg {String} selectedClass CSS class to apply to the selected item in the dropdown list (defaults to 'x-combo-selected')
47751      */
47752     selectedClass: 'x-combo-selected',
47753     /**
47754      * @cfg {String} triggerClass An additional CSS class used to style the trigger button.  The trigger will always get the
47755      * class 'x-form-trigger' and triggerClass will be <b>appended</b> if specified (defaults to 'x-form-arrow-trigger'
47756      * which displays a downward arrow icon).
47757      */
47758     triggerClass : 'x-form-arrow-trigger',
47759     /**
47760      * @cfg {Boolean/String} shadow True or "sides" for the default effect, "frame" for 4-way shadow, and "drop" for bottom-right
47761      */
47762     shadow:'sides',
47763     /**
47764      * @cfg {String} listAlign A valid anchor position value. See {@link Roo.Element#alignTo} for details on supported
47765      * anchor positions (defaults to 'tl-bl')
47766      */
47767     listAlign: 'tl-bl?',
47768     /**
47769      * @cfg {Number} maxHeight The maximum height in pixels of the dropdown list before scrollbars are shown (defaults to 300)
47770      */
47771     maxHeight: 300,
47772     /**
47773      * @cfg {String} triggerAction The action to execute when the trigger field is activated.  Use 'all' to run the
47774      * query specified by the allQuery config option (defaults to 'query')
47775      */
47776     triggerAction: 'query',
47777     /**
47778      * @cfg {Number} minChars The minimum number of characters the user must type before autocomplete and typeahead activate
47779      * (defaults to 4, does not apply if editable = false)
47780      */
47781     minChars : 4,
47782     /**
47783      * @cfg {Boolean} typeAhead True to populate and autoselect the remainder of the text being typed after a configurable
47784      * delay (typeAheadDelay) if it matches a known value (defaults to false)
47785      */
47786     typeAhead: false,
47787     /**
47788      * @cfg {Number} queryDelay The length of time in milliseconds to delay between the start of typing and sending the
47789      * query to filter the dropdown list (defaults to 500 if mode = 'remote' or 10 if mode = 'local')
47790      */
47791     queryDelay: 500,
47792     /**
47793      * @cfg {Number} pageSize If greater than 0, a paging toolbar is displayed in the footer of the dropdown list and the
47794      * filter queries will execute with page start and limit parameters.  Only applies when mode = 'remote' (defaults to 0)
47795      */
47796     pageSize: 0,
47797     /**
47798      * @cfg {Boolean} selectOnFocus True to select any existing text in the field immediately on focus.  Only applies
47799      * when editable = true (defaults to false)
47800      */
47801     selectOnFocus:false,
47802     /**
47803      * @cfg {String} queryParam Name of the query as it will be passed on the querystring (defaults to 'query')
47804      */
47805     queryParam: 'query',
47806     /**
47807      * @cfg {String} loadingText The text to display in the dropdown list while data is loading.  Only applies
47808      * when mode = 'remote' (defaults to 'Loading...')
47809      */
47810     loadingText: 'Loading...',
47811     /**
47812      * @cfg {Boolean} resizable True to add a resize handle to the bottom of the dropdown list (defaults to false)
47813      */
47814     resizable: false,
47815     /**
47816      * @cfg {Number} handleHeight The height in pixels of the dropdown list resize handle if resizable = true (defaults to 8)
47817      */
47818     handleHeight : 8,
47819     /**
47820      * @cfg {Boolean} editable False to prevent the user from typing text directly into the field, just like a
47821      * traditional select (defaults to true)
47822      */
47823     editable: true,
47824     /**
47825      * @cfg {String} allQuery The text query to send to the server to return all records for the list with no filtering (defaults to '')
47826      */
47827     allQuery: '',
47828     /**
47829      * @cfg {String} mode Set to 'local' if the ComboBox loads local data (defaults to 'remote' which loads from the server)
47830      */
47831     mode: 'remote',
47832     /**
47833      * @cfg {Number} minListWidth The minimum width of the dropdown list in pixels (defaults to 70, will be ignored if
47834      * listWidth has a higher value)
47835      */
47836     minListWidth : 70,
47837     /**
47838      * @cfg {Boolean} forceSelection True to restrict the selected value to one of the values in the list, false to
47839      * allow the user to set arbitrary text into the field (defaults to false)
47840      */
47841     forceSelection:false,
47842     /**
47843      * @cfg {Number} typeAheadDelay The length of time in milliseconds to wait until the typeahead text is displayed
47844      * if typeAhead = true (defaults to 250)
47845      */
47846     typeAheadDelay : 250,
47847     /**
47848      * @cfg {String} valueNotFoundText When using a name/value combo, if the value passed to setValue is not found in
47849      * the store, valueNotFoundText will be displayed as the field text if defined (defaults to undefined)
47850      */
47851     valueNotFoundText : undefined,
47852     
47853     /**
47854      * @cfg {String} defaultValue The value displayed after loading the store.
47855      */
47856     defaultValue: '',
47857     
47858     /**
47859      * @cfg {Boolean} blockFocus Prevents all focus calls, so it can work with things like HTML edtor bar
47860      */
47861     blockFocus : false,
47862     
47863     /**
47864      * @cfg {Boolean} disableClear Disable showing of clear button.
47865      */
47866     disableClear : false,
47867     /**
47868      * @cfg {Boolean} alwaysQuery  Disable caching of results, and always send query
47869      */
47870     alwaysQuery : false,
47871     
47872     //private
47873     addicon : false,
47874     editicon: false,
47875     
47876     // element that contains real text value.. (when hidden is used..)
47877      
47878     // private
47879     onRender : function(ct, position){
47880         Roo.form.Field.prototype.onRender.call(this, ct, position);
47881         
47882         if(this.store){
47883             this.store.on('beforeload', this.onBeforeLoad, this);
47884             this.store.on('load', this.onLoad, this);
47885             this.store.on('loadexception', this.onLoadException, this);
47886             this.store.load({});
47887         }
47888         
47889         
47890         
47891     },
47892
47893     // private
47894     initEvents : function(){
47895         //Roo.form.ComboBox.superclass.initEvents.call(this);
47896  
47897     },
47898
47899     onDestroy : function(){
47900        
47901         if(this.store){
47902             this.store.un('beforeload', this.onBeforeLoad, this);
47903             this.store.un('load', this.onLoad, this);
47904             this.store.un('loadexception', this.onLoadException, this);
47905         }
47906         //Roo.form.ComboBox.superclass.onDestroy.call(this);
47907     },
47908
47909     // private
47910     fireKey : function(e){
47911         if(e.isNavKeyPress() && !this.list.isVisible()){
47912             this.fireEvent("specialkey", this, e);
47913         }
47914     },
47915
47916     // private
47917     onResize: function(w, h){
47918         
47919         return; 
47920     
47921         
47922     },
47923
47924     /**
47925      * Allow or prevent the user from directly editing the field text.  If false is passed,
47926      * the user will only be able to select from the items defined in the dropdown list.  This method
47927      * is the runtime equivalent of setting the 'editable' config option at config time.
47928      * @param {Boolean} value True to allow the user to directly edit the field text
47929      */
47930     setEditable : function(value){
47931          
47932     },
47933
47934     // private
47935     onBeforeLoad : function(){
47936         
47937         Roo.log("Select before load");
47938         return;
47939     
47940         this.innerList.update(this.loadingText ?
47941                '<div class="loading-indicator">'+this.loadingText+'</div>' : '');
47942         //this.restrictHeight();
47943         this.selectedIndex = -1;
47944     },
47945
47946     // private
47947     onLoad : function(){
47948
47949     
47950         var dom = this.el.dom;
47951         dom.innerHTML = '';
47952          var od = dom.ownerDocument;
47953          
47954         if (this.emptyText) {
47955             var op = od.createElement('option');
47956             op.setAttribute('value', '');
47957             op.innerHTML = String.format('{0}', this.emptyText);
47958             dom.appendChild(op);
47959         }
47960         if(this.store.getCount() > 0){
47961            
47962             var vf = this.valueField;
47963             var df = this.displayField;
47964             this.store.data.each(function(r) {
47965                 // which colmsn to use... testing - cdoe / title..
47966                 var op = od.createElement('option');
47967                 op.setAttribute('value', r.data[vf]);
47968                 op.innerHTML = String.format('{0}', r.data[df]);
47969                 dom.appendChild(op);
47970             });
47971             if (typeof(this.defaultValue != 'undefined')) {
47972                 this.setValue(this.defaultValue);
47973             }
47974             
47975              
47976         }else{
47977             //this.onEmptyResults();
47978         }
47979         //this.el.focus();
47980     },
47981     // private
47982     onLoadException : function()
47983     {
47984         dom.innerHTML = '';
47985             
47986         Roo.log("Select on load exception");
47987         return;
47988     
47989         this.collapse();
47990         Roo.log(this.store.reader.jsonData);
47991         if (this.store && typeof(this.store.reader.jsonData.errorMsg) != 'undefined') {
47992             Roo.MessageBox.alert("Error loading",this.store.reader.jsonData.errorMsg);
47993         }
47994         
47995         
47996     },
47997     // private
47998     onTypeAhead : function(){
47999          
48000     },
48001
48002     // private
48003     onSelect : function(record, index){
48004         Roo.log('on select?');
48005         return;
48006         if(this.fireEvent('beforeselect', this, record, index) !== false){
48007             this.setFromData(index > -1 ? record.data : false);
48008             this.collapse();
48009             this.fireEvent('select', this, record, index);
48010         }
48011     },
48012
48013     /**
48014      * Returns the currently selected field value or empty string if no value is set.
48015      * @return {String} value The selected value
48016      */
48017     getValue : function(){
48018         var dom = this.el.dom;
48019         this.value = dom.options[dom.selectedIndex].value;
48020         return this.value;
48021         
48022     },
48023
48024     /**
48025      * Clears any text/value currently set in the field
48026      */
48027     clearValue : function(){
48028         this.value = '';
48029         this.el.dom.selectedIndex = this.emptyText ? 0 : -1;
48030         
48031     },
48032
48033     /**
48034      * Sets the specified value into the field.  If the value finds a match, the corresponding record text
48035      * will be displayed in the field.  If the value does not match the data value of an existing item,
48036      * and the valueNotFoundText config option is defined, it will be displayed as the default field text.
48037      * Otherwise the field will be blank (although the value will still be set).
48038      * @param {String} value The value to match
48039      */
48040     setValue : function(v){
48041         var d = this.el.dom;
48042         for (var i =0; i < d.options.length;i++) {
48043             if (v == d.options[i].value) {
48044                 d.selectedIndex = i;
48045                 this.value = v;
48046                 return;
48047             }
48048         }
48049         this.clearValue();
48050     },
48051     /**
48052      * @property {Object} the last set data for the element
48053      */
48054     
48055     lastData : false,
48056     /**
48057      * Sets the value of the field based on a object which is related to the record format for the store.
48058      * @param {Object} value the value to set as. or false on reset?
48059      */
48060     setFromData : function(o){
48061         Roo.log('setfrom data?');
48062          
48063         
48064         
48065     },
48066     // private
48067     reset : function(){
48068         this.clearValue();
48069     },
48070     // private
48071     findRecord : function(prop, value){
48072         
48073         return false;
48074     
48075         var record;
48076         if(this.store.getCount() > 0){
48077             this.store.each(function(r){
48078                 if(r.data[prop] == value){
48079                     record = r;
48080                     return false;
48081                 }
48082                 return true;
48083             });
48084         }
48085         return record;
48086     },
48087     
48088     getName: function()
48089     {
48090         // returns hidden if it's set..
48091         if (!this.rendered) {return ''};
48092         return !this.hiddenName && this.el.dom.name  ? this.el.dom.name : (this.hiddenName || '');
48093         
48094     },
48095      
48096
48097     
48098
48099     // private
48100     onEmptyResults : function(){
48101         Roo.log('empty results');
48102         //this.collapse();
48103     },
48104
48105     /**
48106      * Returns true if the dropdown list is expanded, else false.
48107      */
48108     isExpanded : function(){
48109         return false;
48110     },
48111
48112     /**
48113      * Select an item in the dropdown list by its data value. This function does NOT cause the select event to fire.
48114      * The store must be loaded and the list expanded for this function to work, otherwise use setValue.
48115      * @param {String} value The data value of the item to select
48116      * @param {Boolean} scrollIntoView False to prevent the dropdown list from autoscrolling to display the
48117      * selected item if it is not currently in view (defaults to true)
48118      * @return {Boolean} True if the value matched an item in the list, else false
48119      */
48120     selectByValue : function(v, scrollIntoView){
48121         Roo.log('select By Value');
48122         return false;
48123     
48124         if(v !== undefined && v !== null){
48125             var r = this.findRecord(this.valueField || this.displayField, v);
48126             if(r){
48127                 this.select(this.store.indexOf(r), scrollIntoView);
48128                 return true;
48129             }
48130         }
48131         return false;
48132     },
48133
48134     /**
48135      * Select an item in the dropdown list by its numeric index in the list. This function does NOT cause the select event to fire.
48136      * The store must be loaded and the list expanded for this function to work, otherwise use setValue.
48137      * @param {Number} index The zero-based index of the list item to select
48138      * @param {Boolean} scrollIntoView False to prevent the dropdown list from autoscrolling to display the
48139      * selected item if it is not currently in view (defaults to true)
48140      */
48141     select : function(index, scrollIntoView){
48142         Roo.log('select ');
48143         return  ;
48144         
48145         this.selectedIndex = index;
48146         this.view.select(index);
48147         if(scrollIntoView !== false){
48148             var el = this.view.getNode(index);
48149             if(el){
48150                 this.innerList.scrollChildIntoView(el, false);
48151             }
48152         }
48153     },
48154
48155       
48156
48157     // private
48158     validateBlur : function(){
48159         
48160         return;
48161         
48162     },
48163
48164     // private
48165     initQuery : function(){
48166         this.doQuery(this.getRawValue());
48167     },
48168
48169     // private
48170     doForce : function(){
48171         if(this.el.dom.value.length > 0){
48172             this.el.dom.value =
48173                 this.lastSelectionText === undefined ? '' : this.lastSelectionText;
48174              
48175         }
48176     },
48177
48178     /**
48179      * Execute a query to filter the dropdown list.  Fires the beforequery event prior to performing the
48180      * query allowing the query action to be canceled if needed.
48181      * @param {String} query The SQL query to execute
48182      * @param {Boolean} forceAll True to force the query to execute even if there are currently fewer characters
48183      * in the field than the minimum specified by the minChars config option.  It also clears any filter previously
48184      * saved in the current store (defaults to false)
48185      */
48186     doQuery : function(q, forceAll){
48187         
48188         Roo.log('doQuery?');
48189         if(q === undefined || q === null){
48190             q = '';
48191         }
48192         var qe = {
48193             query: q,
48194             forceAll: forceAll,
48195             combo: this,
48196             cancel:false
48197         };
48198         if(this.fireEvent('beforequery', qe)===false || qe.cancel){
48199             return false;
48200         }
48201         q = qe.query;
48202         forceAll = qe.forceAll;
48203         if(forceAll === true || (q.length >= this.minChars)){
48204             if(this.lastQuery != q || this.alwaysQuery){
48205                 this.lastQuery = q;
48206                 if(this.mode == 'local'){
48207                     this.selectedIndex = -1;
48208                     if(forceAll){
48209                         this.store.clearFilter();
48210                     }else{
48211                         this.store.filter(this.displayField, q);
48212                     }
48213                     this.onLoad();
48214                 }else{
48215                     this.store.baseParams[this.queryParam] = q;
48216                     this.store.load({
48217                         params: this.getParams(q)
48218                     });
48219                     this.expand();
48220                 }
48221             }else{
48222                 this.selectedIndex = -1;
48223                 this.onLoad();   
48224             }
48225         }
48226     },
48227
48228     // private
48229     getParams : function(q){
48230         var p = {};
48231         //p[this.queryParam] = q;
48232         if(this.pageSize){
48233             p.start = 0;
48234             p.limit = this.pageSize;
48235         }
48236         return p;
48237     },
48238
48239     /**
48240      * Hides the dropdown list if it is currently expanded. Fires the 'collapse' event on completion.
48241      */
48242     collapse : function(){
48243         
48244     },
48245
48246     // private
48247     collapseIf : function(e){
48248         
48249     },
48250
48251     /**
48252      * Expands the dropdown list if it is currently hidden. Fires the 'expand' event on completion.
48253      */
48254     expand : function(){
48255         
48256     } ,
48257
48258     // private
48259      
48260
48261     /** 
48262     * @cfg {Boolean} grow 
48263     * @hide 
48264     */
48265     /** 
48266     * @cfg {Number} growMin 
48267     * @hide 
48268     */
48269     /** 
48270     * @cfg {Number} growMax 
48271     * @hide 
48272     */
48273     /**
48274      * @hide
48275      * @method autoSize
48276      */
48277     
48278     setWidth : function()
48279     {
48280         
48281     },
48282     getResizeEl : function(){
48283         return this.el;
48284     }
48285 });//<script type="text/javasscript">
48286  
48287
48288 /**
48289  * @class Roo.DDView
48290  * A DnD enabled version of Roo.View.
48291  * @param {Element/String} container The Element in which to create the View.
48292  * @param {String} tpl The template string used to create the markup for each element of the View
48293  * @param {Object} config The configuration properties. These include all the config options of
48294  * {@link Roo.View} plus some specific to this class.<br>
48295  * <p>
48296  * Drag/drop is implemented by adding {@link Roo.data.Record}s to the target DDView. If copying is
48297  * not being performed, the original {@link Roo.data.Record} is removed from the source DDView.<br>
48298  * <p>
48299  * The following extra CSS rules are needed to provide insertion point highlighting:<pre><code>
48300 .x-view-drag-insert-above {
48301         border-top:1px dotted #3366cc;
48302 }
48303 .x-view-drag-insert-below {
48304         border-bottom:1px dotted #3366cc;
48305 }
48306 </code></pre>
48307  * 
48308  */
48309  
48310 Roo.DDView = function(container, tpl, config) {
48311     Roo.DDView.superclass.constructor.apply(this, arguments);
48312     this.getEl().setStyle("outline", "0px none");
48313     this.getEl().unselectable();
48314     if (this.dragGroup) {
48315                 this.setDraggable(this.dragGroup.split(","));
48316     }
48317     if (this.dropGroup) {
48318                 this.setDroppable(this.dropGroup.split(","));
48319     }
48320     if (this.deletable) {
48321         this.setDeletable();
48322     }
48323     this.isDirtyFlag = false;
48324         this.addEvents({
48325                 "drop" : true
48326         });
48327 };
48328
48329 Roo.extend(Roo.DDView, Roo.View, {
48330 /**     @cfg {String/Array} dragGroup The ddgroup name(s) for the View's DragZone. */
48331 /**     @cfg {String/Array} dropGroup The ddgroup name(s) for the View's DropZone. */
48332 /**     @cfg {Boolean} copy Causes drag operations to copy nodes rather than move. */
48333 /**     @cfg {Boolean} allowCopy Causes ctrl/drag operations to copy nodes rather than move. */
48334
48335         isFormField: true,
48336
48337         reset: Roo.emptyFn,
48338         
48339         clearInvalid: Roo.form.Field.prototype.clearInvalid,
48340
48341         validate: function() {
48342                 return true;
48343         },
48344         
48345         destroy: function() {
48346                 this.purgeListeners();
48347                 this.getEl.removeAllListeners();
48348                 this.getEl().remove();
48349                 if (this.dragZone) {
48350                         if (this.dragZone.destroy) {
48351                                 this.dragZone.destroy();
48352                         }
48353                 }
48354                 if (this.dropZone) {
48355                         if (this.dropZone.destroy) {
48356                                 this.dropZone.destroy();
48357                         }
48358                 }
48359         },
48360
48361 /**     Allows this class to be an Roo.form.Field so it can be found using {@link Roo.form.BasicForm#findField}. */
48362         getName: function() {
48363                 return this.name;
48364         },
48365
48366 /**     Loads the View from a JSON string representing the Records to put into the Store. */
48367         setValue: function(v) {
48368                 if (!this.store) {
48369                         throw "DDView.setValue(). DDView must be constructed with a valid Store";
48370                 }
48371                 var data = {};
48372                 data[this.store.reader.meta.root] = v ? [].concat(v) : [];
48373                 this.store.proxy = new Roo.data.MemoryProxy(data);
48374                 this.store.load();
48375         },
48376
48377 /**     @return {String} a parenthesised list of the ids of the Records in the View. */
48378         getValue: function() {
48379                 var result = '(';
48380                 this.store.each(function(rec) {
48381                         result += rec.id + ',';
48382                 });
48383                 return result.substr(0, result.length - 1) + ')';
48384         },
48385         
48386         getIds: function() {
48387                 var i = 0, result = new Array(this.store.getCount());
48388                 this.store.each(function(rec) {
48389                         result[i++] = rec.id;
48390                 });
48391                 return result;
48392         },
48393         
48394         isDirty: function() {
48395                 return this.isDirtyFlag;
48396         },
48397
48398 /**
48399  *      Part of the Roo.dd.DropZone interface. If no target node is found, the
48400  *      whole Element becomes the target, and this causes the drop gesture to append.
48401  */
48402     getTargetFromEvent : function(e) {
48403                 var target = e.getTarget();
48404                 while ((target !== null) && (target.parentNode != this.el.dom)) {
48405                 target = target.parentNode;
48406                 }
48407                 if (!target) {
48408                         target = this.el.dom.lastChild || this.el.dom;
48409                 }
48410                 return target;
48411     },
48412
48413 /**
48414  *      Create the drag data which consists of an object which has the property "ddel" as
48415  *      the drag proxy element. 
48416  */
48417     getDragData : function(e) {
48418         var target = this.findItemFromChild(e.getTarget());
48419                 if(target) {
48420                         this.handleSelection(e);
48421                         var selNodes = this.getSelectedNodes();
48422             var dragData = {
48423                 source: this,
48424                 copy: this.copy || (this.allowCopy && e.ctrlKey),
48425                 nodes: selNodes,
48426                 records: []
48427                         };
48428                         var selectedIndices = this.getSelectedIndexes();
48429                         for (var i = 0; i < selectedIndices.length; i++) {
48430                                 dragData.records.push(this.store.getAt(selectedIndices[i]));
48431                         }
48432                         if (selNodes.length == 1) {
48433                                 dragData.ddel = target.cloneNode(true); // the div element
48434                         } else {
48435                                 var div = document.createElement('div'); // create the multi element drag "ghost"
48436                                 div.className = 'multi-proxy';
48437                                 for (var i = 0, len = selNodes.length; i < len; i++) {
48438                                         div.appendChild(selNodes[i].cloneNode(true));
48439                                 }
48440                                 dragData.ddel = div;
48441                         }
48442             //console.log(dragData)
48443             //console.log(dragData.ddel.innerHTML)
48444                         return dragData;
48445                 }
48446         //console.log('nodragData')
48447                 return false;
48448     },
48449     
48450 /**     Specify to which ddGroup items in this DDView may be dragged. */
48451     setDraggable: function(ddGroup) {
48452         if (ddGroup instanceof Array) {
48453                 Roo.each(ddGroup, this.setDraggable, this);
48454                 return;
48455         }
48456         if (this.dragZone) {
48457                 this.dragZone.addToGroup(ddGroup);
48458         } else {
48459                         this.dragZone = new Roo.dd.DragZone(this.getEl(), {
48460                                 containerScroll: true,
48461                                 ddGroup: ddGroup 
48462
48463                         });
48464 //                      Draggability implies selection. DragZone's mousedown selects the element.
48465                         if (!this.multiSelect) { this.singleSelect = true; }
48466
48467 //                      Wire the DragZone's handlers up to methods in *this*
48468                         this.dragZone.getDragData = this.getDragData.createDelegate(this);
48469                 }
48470     },
48471
48472 /**     Specify from which ddGroup this DDView accepts drops. */
48473     setDroppable: function(ddGroup) {
48474         if (ddGroup instanceof Array) {
48475                 Roo.each(ddGroup, this.setDroppable, this);
48476                 return;
48477         }
48478         if (this.dropZone) {
48479                 this.dropZone.addToGroup(ddGroup);
48480         } else {
48481                         this.dropZone = new Roo.dd.DropZone(this.getEl(), {
48482                                 containerScroll: true,
48483                                 ddGroup: ddGroup
48484                         });
48485
48486 //                      Wire the DropZone's handlers up to methods in *this*
48487                         this.dropZone.getTargetFromEvent = this.getTargetFromEvent.createDelegate(this);
48488                         this.dropZone.onNodeEnter = this.onNodeEnter.createDelegate(this);
48489                         this.dropZone.onNodeOver = this.onNodeOver.createDelegate(this);
48490                         this.dropZone.onNodeOut = this.onNodeOut.createDelegate(this);
48491                         this.dropZone.onNodeDrop = this.onNodeDrop.createDelegate(this);
48492                 }
48493     },
48494
48495 /**     Decide whether to drop above or below a View node. */
48496     getDropPoint : function(e, n, dd){
48497         if (n == this.el.dom) { return "above"; }
48498                 var t = Roo.lib.Dom.getY(n), b = t + n.offsetHeight;
48499                 var c = t + (b - t) / 2;
48500                 var y = Roo.lib.Event.getPageY(e);
48501                 if(y <= c) {
48502                         return "above";
48503                 }else{
48504                         return "below";
48505                 }
48506     },
48507
48508     onNodeEnter : function(n, dd, e, data){
48509                 return false;
48510     },
48511     
48512     onNodeOver : function(n, dd, e, data){
48513                 var pt = this.getDropPoint(e, n, dd);
48514                 // set the insert point style on the target node
48515                 var dragElClass = this.dropNotAllowed;
48516                 if (pt) {
48517                         var targetElClass;
48518                         if (pt == "above"){
48519                                 dragElClass = n.previousSibling ? "x-tree-drop-ok-between" : "x-tree-drop-ok-above";
48520                                 targetElClass = "x-view-drag-insert-above";
48521                         } else {
48522                                 dragElClass = n.nextSibling ? "x-tree-drop-ok-between" : "x-tree-drop-ok-below";
48523                                 targetElClass = "x-view-drag-insert-below";
48524                         }
48525                         if (this.lastInsertClass != targetElClass){
48526                                 Roo.fly(n).replaceClass(this.lastInsertClass, targetElClass);
48527                                 this.lastInsertClass = targetElClass;
48528                         }
48529                 }
48530                 return dragElClass;
48531         },
48532
48533     onNodeOut : function(n, dd, e, data){
48534                 this.removeDropIndicators(n);
48535     },
48536
48537     onNodeDrop : function(n, dd, e, data){
48538         if (this.fireEvent("drop", this, n, dd, e, data) === false) {
48539                 return false;
48540         }
48541         var pt = this.getDropPoint(e, n, dd);
48542                 var insertAt = (n == this.el.dom) ? this.nodes.length : n.nodeIndex;
48543                 if (pt == "below") { insertAt++; }
48544                 for (var i = 0; i < data.records.length; i++) {
48545                         var r = data.records[i];
48546                         var dup = this.store.getById(r.id);
48547                         if (dup && (dd != this.dragZone)) {
48548                                 Roo.fly(this.getNode(this.store.indexOf(dup))).frame("red", 1);
48549                         } else {
48550                                 if (data.copy) {
48551                                         this.store.insert(insertAt++, r.copy());
48552                                 } else {
48553                                         data.source.isDirtyFlag = true;
48554                                         r.store.remove(r);
48555                                         this.store.insert(insertAt++, r);
48556                                 }
48557                                 this.isDirtyFlag = true;
48558                         }
48559                 }
48560                 this.dragZone.cachedTarget = null;
48561                 return true;
48562     },
48563
48564     removeDropIndicators : function(n){
48565                 if(n){
48566                         Roo.fly(n).removeClass([
48567                                 "x-view-drag-insert-above",
48568                                 "x-view-drag-insert-below"]);
48569                         this.lastInsertClass = "_noclass";
48570                 }
48571     },
48572
48573 /**
48574  *      Utility method. Add a delete option to the DDView's context menu.
48575  *      @param {String} imageUrl The URL of the "delete" icon image.
48576  */
48577         setDeletable: function(imageUrl) {
48578                 if (!this.singleSelect && !this.multiSelect) {
48579                         this.singleSelect = true;
48580                 }
48581                 var c = this.getContextMenu();
48582                 this.contextMenu.on("itemclick", function(item) {
48583                         switch (item.id) {
48584                                 case "delete":
48585                                         this.remove(this.getSelectedIndexes());
48586                                         break;
48587                         }
48588                 }, this);
48589                 this.contextMenu.add({
48590                         icon: imageUrl,
48591                         id: "delete",
48592                         text: 'Delete'
48593                 });
48594         },
48595         
48596 /**     Return the context menu for this DDView. */
48597         getContextMenu: function() {
48598                 if (!this.contextMenu) {
48599 //                      Create the View's context menu
48600                         this.contextMenu = new Roo.menu.Menu({
48601                                 id: this.id + "-contextmenu"
48602                         });
48603                         this.el.on("contextmenu", this.showContextMenu, this);
48604                 }
48605                 return this.contextMenu;
48606         },
48607         
48608         disableContextMenu: function() {
48609                 if (this.contextMenu) {
48610                         this.el.un("contextmenu", this.showContextMenu, this);
48611                 }
48612         },
48613
48614         showContextMenu: function(e, item) {
48615         item = this.findItemFromChild(e.getTarget());
48616                 if (item) {
48617                         e.stopEvent();
48618                         this.select(this.getNode(item), this.multiSelect && e.ctrlKey, true);
48619                         this.contextMenu.showAt(e.getXY());
48620             }
48621     },
48622
48623 /**
48624  *      Remove {@link Roo.data.Record}s at the specified indices.
48625  *      @param {Array/Number} selectedIndices The index (or Array of indices) of Records to remove.
48626  */
48627     remove: function(selectedIndices) {
48628                 selectedIndices = [].concat(selectedIndices);
48629                 for (var i = 0; i < selectedIndices.length; i++) {
48630                         var rec = this.store.getAt(selectedIndices[i]);
48631                         this.store.remove(rec);
48632                 }
48633     },
48634
48635 /**
48636  *      Double click fires the event, but also, if this is draggable, and there is only one other
48637  *      related DropZone, it transfers the selected node.
48638  */
48639     onDblClick : function(e){
48640         var item = this.findItemFromChild(e.getTarget());
48641         if(item){
48642             if (this.fireEvent("dblclick", this, this.indexOf(item), item, e) === false) {
48643                 return false;
48644             }
48645             if (this.dragGroup) {
48646                     var targets = Roo.dd.DragDropMgr.getRelated(this.dragZone, true);
48647                     while (targets.indexOf(this.dropZone) > -1) {
48648                             targets.remove(this.dropZone);
48649                                 }
48650                     if (targets.length == 1) {
48651                                         this.dragZone.cachedTarget = null;
48652                         var el = Roo.get(targets[0].getEl());
48653                         var box = el.getBox(true);
48654                         targets[0].onNodeDrop(el.dom, {
48655                                 target: el.dom,
48656                                 xy: [box.x, box.y + box.height - 1]
48657                         }, null, this.getDragData(e));
48658                     }
48659                 }
48660         }
48661     },
48662     
48663     handleSelection: function(e) {
48664                 this.dragZone.cachedTarget = null;
48665         var item = this.findItemFromChild(e.getTarget());
48666         if (!item) {
48667                 this.clearSelections(true);
48668                 return;
48669         }
48670                 if (item && (this.multiSelect || this.singleSelect)){
48671                         if(this.multiSelect && e.shiftKey && (!e.ctrlKey) && this.lastSelection){
48672                                 this.select(this.getNodes(this.indexOf(this.lastSelection), item.nodeIndex), false);
48673                         }else if (this.isSelected(this.getNode(item)) && e.ctrlKey){
48674                                 this.unselect(item);
48675                         } else {
48676                                 this.select(item, this.multiSelect && e.ctrlKey);
48677                                 this.lastSelection = item;
48678                         }
48679                 }
48680     },
48681
48682     onItemClick : function(item, index, e){
48683                 if(this.fireEvent("beforeclick", this, index, item, e) === false){
48684                         return false;
48685                 }
48686                 return true;
48687     },
48688
48689     unselect : function(nodeInfo, suppressEvent){
48690                 var node = this.getNode(nodeInfo);
48691                 if(node && this.isSelected(node)){
48692                         if(this.fireEvent("beforeselect", this, node, this.selections) !== false){
48693                                 Roo.fly(node).removeClass(this.selectedClass);
48694                                 this.selections.remove(node);
48695                                 if(!suppressEvent){
48696                                         this.fireEvent("selectionchange", this, this.selections);
48697                                 }
48698                         }
48699                 }
48700     }
48701 });
48702 /*
48703  * Based on:
48704  * Ext JS Library 1.1.1
48705  * Copyright(c) 2006-2007, Ext JS, LLC.
48706  *
48707  * Originally Released Under LGPL - original licence link has changed is not relivant.
48708  *
48709  * Fork - LGPL
48710  * <script type="text/javascript">
48711  */
48712  
48713 /**
48714  * @class Roo.LayoutManager
48715  * @extends Roo.util.Observable
48716  * Base class for layout managers.
48717  */
48718 Roo.LayoutManager = function(container, config){
48719     Roo.LayoutManager.superclass.constructor.call(this);
48720     this.el = Roo.get(container);
48721     // ie scrollbar fix
48722     if(this.el.dom == document.body && Roo.isIE && !config.allowScroll){
48723         document.body.scroll = "no";
48724     }else if(this.el.dom != document.body && this.el.getStyle('position') == 'static'){
48725         this.el.position('relative');
48726     }
48727     this.id = this.el.id;
48728     this.el.addClass("x-layout-container");
48729     /** false to disable window resize monitoring @type Boolean */
48730     this.monitorWindowResize = true;
48731     this.regions = {};
48732     this.addEvents({
48733         /**
48734          * @event layout
48735          * Fires when a layout is performed. 
48736          * @param {Roo.LayoutManager} this
48737          */
48738         "layout" : true,
48739         /**
48740          * @event regionresized
48741          * Fires when the user resizes a region. 
48742          * @param {Roo.LayoutRegion} region The resized region
48743          * @param {Number} newSize The new size (width for east/west, height for north/south)
48744          */
48745         "regionresized" : true,
48746         /**
48747          * @event regioncollapsed
48748          * Fires when a region is collapsed. 
48749          * @param {Roo.LayoutRegion} region The collapsed region
48750          */
48751         "regioncollapsed" : true,
48752         /**
48753          * @event regionexpanded
48754          * Fires when a region is expanded.  
48755          * @param {Roo.LayoutRegion} region The expanded region
48756          */
48757         "regionexpanded" : true
48758     });
48759     this.updating = false;
48760     Roo.EventManager.onWindowResize(this.onWindowResize, this, true);
48761 };
48762
48763 Roo.extend(Roo.LayoutManager, Roo.util.Observable, {
48764     /**
48765      * Returns true if this layout is currently being updated
48766      * @return {Boolean}
48767      */
48768     isUpdating : function(){
48769         return this.updating; 
48770     },
48771     
48772     /**
48773      * Suspend the LayoutManager from doing auto-layouts while
48774      * making multiple add or remove calls
48775      */
48776     beginUpdate : function(){
48777         this.updating = true;    
48778     },
48779     
48780     /**
48781      * Restore auto-layouts and optionally disable the manager from performing a layout
48782      * @param {Boolean} noLayout true to disable a layout update 
48783      */
48784     endUpdate : function(noLayout){
48785         this.updating = false;
48786         if(!noLayout){
48787             this.layout();
48788         }    
48789     },
48790     
48791     layout: function(){
48792         
48793     },
48794     
48795     onRegionResized : function(region, newSize){
48796         this.fireEvent("regionresized", region, newSize);
48797         this.layout();
48798     },
48799     
48800     onRegionCollapsed : function(region){
48801         this.fireEvent("regioncollapsed", region);
48802     },
48803     
48804     onRegionExpanded : function(region){
48805         this.fireEvent("regionexpanded", region);
48806     },
48807         
48808     /**
48809      * Returns the size of the current view. This method normalizes document.body and element embedded layouts and
48810      * performs box-model adjustments.
48811      * @return {Object} The size as an object {width: (the width), height: (the height)}
48812      */
48813     getViewSize : function(){
48814         var size;
48815         if(this.el.dom != document.body){
48816             size = this.el.getSize();
48817         }else{
48818             size = {width: Roo.lib.Dom.getViewWidth(), height: Roo.lib.Dom.getViewHeight()};
48819         }
48820         size.width -= this.el.getBorderWidth("lr")-this.el.getPadding("lr");
48821         size.height -= this.el.getBorderWidth("tb")-this.el.getPadding("tb");
48822         return size;
48823     },
48824     
48825     /**
48826      * Returns the Element this layout is bound to.
48827      * @return {Roo.Element}
48828      */
48829     getEl : function(){
48830         return this.el;
48831     },
48832     
48833     /**
48834      * Returns the specified region.
48835      * @param {String} target The region key ('center', 'north', 'south', 'east' or 'west')
48836      * @return {Roo.LayoutRegion}
48837      */
48838     getRegion : function(target){
48839         return this.regions[target.toLowerCase()];
48840     },
48841     
48842     onWindowResize : function(){
48843         if(this.monitorWindowResize){
48844             this.layout();
48845         }
48846     }
48847 });/*
48848  * Based on:
48849  * Ext JS Library 1.1.1
48850  * Copyright(c) 2006-2007, Ext JS, LLC.
48851  *
48852  * Originally Released Under LGPL - original licence link has changed is not relivant.
48853  *
48854  * Fork - LGPL
48855  * <script type="text/javascript">
48856  */
48857 /**
48858  * @class Roo.BorderLayout
48859  * @extends Roo.LayoutManager
48860  * This class represents a common layout manager used in desktop applications. For screenshots and more details,
48861  * please see: <br><br>
48862  * <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>
48863  * <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>
48864  * Example:
48865  <pre><code>
48866  var layout = new Roo.BorderLayout(document.body, {
48867     north: {
48868         initialSize: 25,
48869         titlebar: false
48870     },
48871     west: {
48872         split:true,
48873         initialSize: 200,
48874         minSize: 175,
48875         maxSize: 400,
48876         titlebar: true,
48877         collapsible: true
48878     },
48879     east: {
48880         split:true,
48881         initialSize: 202,
48882         minSize: 175,
48883         maxSize: 400,
48884         titlebar: true,
48885         collapsible: true
48886     },
48887     south: {
48888         split:true,
48889         initialSize: 100,
48890         minSize: 100,
48891         maxSize: 200,
48892         titlebar: true,
48893         collapsible: true
48894     },
48895     center: {
48896         titlebar: true,
48897         autoScroll:true,
48898         resizeTabs: true,
48899         minTabWidth: 50,
48900         preferredTabWidth: 150
48901     }
48902 });
48903
48904 // shorthand
48905 var CP = Roo.ContentPanel;
48906
48907 layout.beginUpdate();
48908 layout.add("north", new CP("north", "North"));
48909 layout.add("south", new CP("south", {title: "South", closable: true}));
48910 layout.add("west", new CP("west", {title: "West"}));
48911 layout.add("east", new CP("autoTabs", {title: "Auto Tabs", closable: true}));
48912 layout.add("center", new CP("center1", {title: "Close Me", closable: true}));
48913 layout.add("center", new CP("center2", {title: "Center Panel", closable: false}));
48914 layout.getRegion("center").showPanel("center1");
48915 layout.endUpdate();
48916 </code></pre>
48917
48918 <b>The container the layout is rendered into can be either the body element or any other element.
48919 If it is not the body element, the container needs to either be an absolute positioned element,
48920 or you will need to add "position:relative" to the css of the container.  You will also need to specify
48921 the container size if it is not the body element.</b>
48922
48923 * @constructor
48924 * Create a new BorderLayout
48925 * @param {String/HTMLElement/Element} container The container this layout is bound to
48926 * @param {Object} config Configuration options
48927  */
48928 Roo.BorderLayout = function(container, config){
48929     config = config || {};
48930     Roo.BorderLayout.superclass.constructor.call(this, container, config);
48931     this.factory = config.factory || Roo.BorderLayout.RegionFactory;
48932     for(var i = 0, len = this.factory.validRegions.length; i < len; i++) {
48933         var target = this.factory.validRegions[i];
48934         if(config[target]){
48935             this.addRegion(target, config[target]);
48936         }
48937     }
48938 };
48939
48940 Roo.extend(Roo.BorderLayout, Roo.LayoutManager, {
48941     /**
48942      * Creates and adds a new region if it doesn't already exist.
48943      * @param {String} target The target region key (north, south, east, west or center).
48944      * @param {Object} config The regions config object
48945      * @return {BorderLayoutRegion} The new region
48946      */
48947     addRegion : function(target, config){
48948         if(!this.regions[target]){
48949             var r = this.factory.create(target, this, config);
48950             this.bindRegion(target, r);
48951         }
48952         return this.regions[target];
48953     },
48954
48955     // private (kinda)
48956     bindRegion : function(name, r){
48957         this.regions[name] = r;
48958         r.on("visibilitychange", this.layout, this);
48959         r.on("paneladded", this.layout, this);
48960         r.on("panelremoved", this.layout, this);
48961         r.on("invalidated", this.layout, this);
48962         r.on("resized", this.onRegionResized, this);
48963         r.on("collapsed", this.onRegionCollapsed, this);
48964         r.on("expanded", this.onRegionExpanded, this);
48965     },
48966
48967     /**
48968      * Performs a layout update.
48969      */
48970     layout : function(){
48971         if(this.updating) return;
48972         var size = this.getViewSize();
48973         var w = size.width;
48974         var h = size.height;
48975         var centerW = w;
48976         var centerH = h;
48977         var centerY = 0;
48978         var centerX = 0;
48979         //var x = 0, y = 0;
48980
48981         var rs = this.regions;
48982         var north = rs["north"];
48983         var south = rs["south"]; 
48984         var west = rs["west"];
48985         var east = rs["east"];
48986         var center = rs["center"];
48987         //if(this.hideOnLayout){ // not supported anymore
48988             //c.el.setStyle("display", "none");
48989         //}
48990         if(north && north.isVisible()){
48991             var b = north.getBox();
48992             var m = north.getMargins();
48993             b.width = w - (m.left+m.right);
48994             b.x = m.left;
48995             b.y = m.top;
48996             centerY = b.height + b.y + m.bottom;
48997             centerH -= centerY;
48998             north.updateBox(this.safeBox(b));
48999         }
49000         if(south && south.isVisible()){
49001             var b = south.getBox();
49002             var m = south.getMargins();
49003             b.width = w - (m.left+m.right);
49004             b.x = m.left;
49005             var totalHeight = (b.height + m.top + m.bottom);
49006             b.y = h - totalHeight + m.top;
49007             centerH -= totalHeight;
49008             south.updateBox(this.safeBox(b));
49009         }
49010         if(west && west.isVisible()){
49011             var b = west.getBox();
49012             var m = west.getMargins();
49013             b.height = centerH - (m.top+m.bottom);
49014             b.x = m.left;
49015             b.y = centerY + m.top;
49016             var totalWidth = (b.width + m.left + m.right);
49017             centerX += totalWidth;
49018             centerW -= totalWidth;
49019             west.updateBox(this.safeBox(b));
49020         }
49021         if(east && east.isVisible()){
49022             var b = east.getBox();
49023             var m = east.getMargins();
49024             b.height = centerH - (m.top+m.bottom);
49025             var totalWidth = (b.width + m.left + m.right);
49026             b.x = w - totalWidth + m.left;
49027             b.y = centerY + m.top;
49028             centerW -= totalWidth;
49029             east.updateBox(this.safeBox(b));
49030         }
49031         if(center){
49032             var m = center.getMargins();
49033             var centerBox = {
49034                 x: centerX + m.left,
49035                 y: centerY + m.top,
49036                 width: centerW - (m.left+m.right),
49037                 height: centerH - (m.top+m.bottom)
49038             };
49039             //if(this.hideOnLayout){
49040                 //center.el.setStyle("display", "block");
49041             //}
49042             center.updateBox(this.safeBox(centerBox));
49043         }
49044         this.el.repaint();
49045         this.fireEvent("layout", this);
49046     },
49047
49048     // private
49049     safeBox : function(box){
49050         box.width = Math.max(0, box.width);
49051         box.height = Math.max(0, box.height);
49052         return box;
49053     },
49054
49055     /**
49056      * Adds a ContentPanel (or subclass) to this layout.
49057      * @param {String} target The target region key (north, south, east, west or center).
49058      * @param {Roo.ContentPanel} panel The panel to add
49059      * @return {Roo.ContentPanel} The added panel
49060      */
49061     add : function(target, panel){
49062          
49063         target = target.toLowerCase();
49064         return this.regions[target].add(panel);
49065     },
49066
49067     /**
49068      * Remove a ContentPanel (or subclass) to this layout.
49069      * @param {String} target The target region key (north, south, east, west or center).
49070      * @param {Number/String/Roo.ContentPanel} panel The index, id or panel to remove
49071      * @return {Roo.ContentPanel} The removed panel
49072      */
49073     remove : function(target, panel){
49074         target = target.toLowerCase();
49075         return this.regions[target].remove(panel);
49076     },
49077
49078     /**
49079      * Searches all regions for a panel with the specified id
49080      * @param {String} panelId
49081      * @return {Roo.ContentPanel} The panel or null if it wasn't found
49082      */
49083     findPanel : function(panelId){
49084         var rs = this.regions;
49085         for(var target in rs){
49086             if(typeof rs[target] != "function"){
49087                 var p = rs[target].getPanel(panelId);
49088                 if(p){
49089                     return p;
49090                 }
49091             }
49092         }
49093         return null;
49094     },
49095
49096     /**
49097      * Searches all regions for a panel with the specified id and activates (shows) it.
49098      * @param {String/ContentPanel} panelId The panels id or the panel itself
49099      * @return {Roo.ContentPanel} The shown panel or null
49100      */
49101     showPanel : function(panelId) {
49102       var rs = this.regions;
49103       for(var target in rs){
49104          var r = rs[target];
49105          if(typeof r != "function"){
49106             if(r.hasPanel(panelId)){
49107                return r.showPanel(panelId);
49108             }
49109          }
49110       }
49111       return null;
49112    },
49113
49114    /**
49115      * Restores this layout's state using Roo.state.Manager or the state provided by the passed provider.
49116      * @param {Roo.state.Provider} provider (optional) An alternate state provider
49117      */
49118     restoreState : function(provider){
49119         if(!provider){
49120             provider = Roo.state.Manager;
49121         }
49122         var sm = new Roo.LayoutStateManager();
49123         sm.init(this, provider);
49124     },
49125
49126     /**
49127      * Adds a batch of multiple ContentPanels dynamically by passing a special regions config object.  This config
49128      * object should contain properties for each region to add ContentPanels to, and each property's value should be
49129      * a valid ContentPanel config object.  Example:
49130      * <pre><code>
49131 // Create the main layout
49132 var layout = new Roo.BorderLayout('main-ct', {
49133     west: {
49134         split:true,
49135         minSize: 175,
49136         titlebar: true
49137     },
49138     center: {
49139         title:'Components'
49140     }
49141 }, 'main-ct');
49142
49143 // Create and add multiple ContentPanels at once via configs
49144 layout.batchAdd({
49145    west: {
49146        id: 'source-files',
49147        autoCreate:true,
49148        title:'Ext Source Files',
49149        autoScroll:true,
49150        fitToFrame:true
49151    },
49152    center : {
49153        el: cview,
49154        autoScroll:true,
49155        fitToFrame:true,
49156        toolbar: tb,
49157        resizeEl:'cbody'
49158    }
49159 });
49160 </code></pre>
49161      * @param {Object} regions An object containing ContentPanel configs by region name
49162      */
49163     batchAdd : function(regions){
49164         this.beginUpdate();
49165         for(var rname in regions){
49166             var lr = this.regions[rname];
49167             if(lr){
49168                 this.addTypedPanels(lr, regions[rname]);
49169             }
49170         }
49171         this.endUpdate();
49172     },
49173
49174     // private
49175     addTypedPanels : function(lr, ps){
49176         if(typeof ps == 'string'){
49177             lr.add(new Roo.ContentPanel(ps));
49178         }
49179         else if(ps instanceof Array){
49180             for(var i =0, len = ps.length; i < len; i++){
49181                 this.addTypedPanels(lr, ps[i]);
49182             }
49183         }
49184         else if(!ps.events){ // raw config?
49185             var el = ps.el;
49186             delete ps.el; // prevent conflict
49187             lr.add(new Roo.ContentPanel(el || Roo.id(), ps));
49188         }
49189         else {  // panel object assumed!
49190             lr.add(ps);
49191         }
49192     },
49193     /**
49194      * Adds a xtype elements to the layout.
49195      * <pre><code>
49196
49197 layout.addxtype({
49198        xtype : 'ContentPanel',
49199        region: 'west',
49200        items: [ .... ]
49201    }
49202 );
49203
49204 layout.addxtype({
49205         xtype : 'NestedLayoutPanel',
49206         region: 'west',
49207         layout: {
49208            center: { },
49209            west: { }   
49210         },
49211         items : [ ... list of content panels or nested layout panels.. ]
49212    }
49213 );
49214 </code></pre>
49215      * @param {Object} cfg Xtype definition of item to add.
49216      */
49217     addxtype : function(cfg)
49218     {
49219         // basically accepts a pannel...
49220         // can accept a layout region..!?!?
49221         //Roo.log('Roo.BorderLayout add ' + cfg.xtype)
49222         
49223         if (!cfg.xtype.match(/Panel$/)) {
49224             return false;
49225         }
49226         var ret = false;
49227         
49228         if (typeof(cfg.region) == 'undefined') {
49229             Roo.log("Failed to add Panel, region was not set");
49230             Roo.log(cfg);
49231             return false;
49232         }
49233         var region = cfg.region;
49234         delete cfg.region;
49235         
49236           
49237         var xitems = [];
49238         if (cfg.items) {
49239             xitems = cfg.items;
49240             delete cfg.items;
49241         }
49242         var nb = false;
49243         
49244         switch(cfg.xtype) 
49245         {
49246             case 'ContentPanel':  // ContentPanel (el, cfg)
49247             case 'ScrollPanel':  // ContentPanel (el, cfg)
49248             case 'ViewPanel': 
49249                 if(cfg.autoCreate) {
49250                     ret = new Roo[cfg.xtype](cfg); // new panel!!!!!
49251                 } else {
49252                     var el = this.el.createChild();
49253                     ret = new Roo[cfg.xtype](el, cfg); // new panel!!!!!
49254                 }
49255                 
49256                 this.add(region, ret);
49257                 break;
49258             
49259             
49260             case 'TreePanel': // our new panel!
49261                 cfg.el = this.el.createChild();
49262                 ret = new Roo[cfg.xtype](cfg); // new panel!!!!!
49263                 this.add(region, ret);
49264                 break;
49265             
49266             case 'NestedLayoutPanel': 
49267                 // create a new Layout (which is  a Border Layout...
49268                 var el = this.el.createChild();
49269                 var clayout = cfg.layout;
49270                 delete cfg.layout;
49271                 clayout.items   = clayout.items  || [];
49272                 // replace this exitems with the clayout ones..
49273                 xitems = clayout.items;
49274                  
49275                 
49276                 if (region == 'center' && this.active && this.getRegion('center').panels.length < 1) {
49277                     cfg.background = false;
49278                 }
49279                 var layout = new Roo.BorderLayout(el, clayout);
49280                 
49281                 ret = new Roo[cfg.xtype](layout, cfg); // new panel!!!!!
49282                 //console.log('adding nested layout panel '  + cfg.toSource());
49283                 this.add(region, ret);
49284                 nb = {}; /// find first...
49285                 break;
49286                 
49287             case 'GridPanel': 
49288             
49289                 // needs grid and region
49290                 
49291                 //var el = this.getRegion(region).el.createChild();
49292                 var el = this.el.createChild();
49293                 // create the grid first...
49294                 
49295                 var grid = new Roo.grid[cfg.grid.xtype](el, cfg.grid);
49296                 delete cfg.grid;
49297                 if (region == 'center' && this.active ) {
49298                     cfg.background = false;
49299                 }
49300                 ret = new Roo[cfg.xtype](grid, cfg); // new panel!!!!!
49301                 
49302                 this.add(region, ret);
49303                 if (cfg.background) {
49304                     ret.on('activate', function(gp) {
49305                         if (!gp.grid.rendered) {
49306                             gp.grid.render();
49307                         }
49308                     });
49309                 } else {
49310                     grid.render();
49311                 }
49312                 break;
49313            
49314            
49315            
49316                 
49317                 
49318                 
49319             default:
49320                 if (typeof(Roo[cfg.xtype]) != 'undefined') {
49321                     
49322                     ret = new Roo[cfg.xtype](cfg); // new panel!!!!!
49323                     this.add(region, ret);
49324                 } else {
49325                 
49326                     alert("Can not add '" + cfg.xtype + "' to BorderLayout");
49327                     return null;
49328                 }
49329                 
49330              // GridPanel (grid, cfg)
49331             
49332         }
49333         this.beginUpdate();
49334         // add children..
49335         var region = '';
49336         var abn = {};
49337         Roo.each(xitems, function(i)  {
49338             region = nb && i.region ? i.region : false;
49339             
49340             var add = ret.addxtype(i);
49341            
49342             if (region) {
49343                 nb[region] = nb[region] == undefined ? 0 : nb[region]+1;
49344                 if (!i.background) {
49345                     abn[region] = nb[region] ;
49346                 }
49347             }
49348             
49349         });
49350         this.endUpdate();
49351
49352         // make the last non-background panel active..
49353         //if (nb) { Roo.log(abn); }
49354         if (nb) {
49355             
49356             for(var r in abn) {
49357                 region = this.getRegion(r);
49358                 if (region) {
49359                     // tried using nb[r], but it does not work..
49360                      
49361                     region.showPanel(abn[r]);
49362                    
49363                 }
49364             }
49365         }
49366         return ret;
49367         
49368     }
49369 });
49370
49371 /**
49372  * Shortcut for creating a new BorderLayout object and adding one or more ContentPanels to it in a single step, handling
49373  * the beginUpdate and endUpdate calls internally.  The key to this method is the <b>panels</b> property that can be
49374  * provided with each region config, which allows you to add ContentPanel configs in addition to the region configs
49375  * during creation.  The following code is equivalent to the constructor-based example at the beginning of this class:
49376  * <pre><code>
49377 // shorthand
49378 var CP = Roo.ContentPanel;
49379
49380 var layout = Roo.BorderLayout.create({
49381     north: {
49382         initialSize: 25,
49383         titlebar: false,
49384         panels: [new CP("north", "North")]
49385     },
49386     west: {
49387         split:true,
49388         initialSize: 200,
49389         minSize: 175,
49390         maxSize: 400,
49391         titlebar: true,
49392         collapsible: true,
49393         panels: [new CP("west", {title: "West"})]
49394     },
49395     east: {
49396         split:true,
49397         initialSize: 202,
49398         minSize: 175,
49399         maxSize: 400,
49400         titlebar: true,
49401         collapsible: true,
49402         panels: [new CP("autoTabs", {title: "Auto Tabs", closable: true})]
49403     },
49404     south: {
49405         split:true,
49406         initialSize: 100,
49407         minSize: 100,
49408         maxSize: 200,
49409         titlebar: true,
49410         collapsible: true,
49411         panels: [new CP("south", {title: "South", closable: true})]
49412     },
49413     center: {
49414         titlebar: true,
49415         autoScroll:true,
49416         resizeTabs: true,
49417         minTabWidth: 50,
49418         preferredTabWidth: 150,
49419         panels: [
49420             new CP("center1", {title: "Close Me", closable: true}),
49421             new CP("center2", {title: "Center Panel", closable: false})
49422         ]
49423     }
49424 }, document.body);
49425
49426 layout.getRegion("center").showPanel("center1");
49427 </code></pre>
49428  * @param config
49429  * @param targetEl
49430  */
49431 Roo.BorderLayout.create = function(config, targetEl){
49432     var layout = new Roo.BorderLayout(targetEl || document.body, config);
49433     layout.beginUpdate();
49434     var regions = Roo.BorderLayout.RegionFactory.validRegions;
49435     for(var j = 0, jlen = regions.length; j < jlen; j++){
49436         var lr = regions[j];
49437         if(layout.regions[lr] && config[lr].panels){
49438             var r = layout.regions[lr];
49439             var ps = config[lr].panels;
49440             layout.addTypedPanels(r, ps);
49441         }
49442     }
49443     layout.endUpdate();
49444     return layout;
49445 };
49446
49447 // private
49448 Roo.BorderLayout.RegionFactory = {
49449     // private
49450     validRegions : ["north","south","east","west","center"],
49451
49452     // private
49453     create : function(target, mgr, config){
49454         target = target.toLowerCase();
49455         if(config.lightweight || config.basic){
49456             return new Roo.BasicLayoutRegion(mgr, config, target);
49457         }
49458         switch(target){
49459             case "north":
49460                 return new Roo.NorthLayoutRegion(mgr, config);
49461             case "south":
49462                 return new Roo.SouthLayoutRegion(mgr, config);
49463             case "east":
49464                 return new Roo.EastLayoutRegion(mgr, config);
49465             case "west":
49466                 return new Roo.WestLayoutRegion(mgr, config);
49467             case "center":
49468                 return new Roo.CenterLayoutRegion(mgr, config);
49469         }
49470         throw 'Layout region "'+target+'" not supported.';
49471     }
49472 };/*
49473  * Based on:
49474  * Ext JS Library 1.1.1
49475  * Copyright(c) 2006-2007, Ext JS, LLC.
49476  *
49477  * Originally Released Under LGPL - original licence link has changed is not relivant.
49478  *
49479  * Fork - LGPL
49480  * <script type="text/javascript">
49481  */
49482  
49483 /**
49484  * @class Roo.BasicLayoutRegion
49485  * @extends Roo.util.Observable
49486  * This class represents a lightweight region in a layout manager. This region does not move dom nodes
49487  * and does not have a titlebar, tabs or any other features. All it does is size and position 
49488  * panels. To create a BasicLayoutRegion, add lightweight:true or basic:true to your regions config.
49489  */
49490 Roo.BasicLayoutRegion = function(mgr, config, pos, skipConfig){
49491     this.mgr = mgr;
49492     this.position  = pos;
49493     this.events = {
49494         /**
49495          * @scope Roo.BasicLayoutRegion
49496          */
49497         
49498         /**
49499          * @event beforeremove
49500          * Fires before a panel is removed (or closed). To cancel the removal set "e.cancel = true" on the event argument.
49501          * @param {Roo.LayoutRegion} this
49502          * @param {Roo.ContentPanel} panel The panel
49503          * @param {Object} e The cancel event object
49504          */
49505         "beforeremove" : true,
49506         /**
49507          * @event invalidated
49508          * Fires when the layout for this region is changed.
49509          * @param {Roo.LayoutRegion} this
49510          */
49511         "invalidated" : true,
49512         /**
49513          * @event visibilitychange
49514          * Fires when this region is shown or hidden 
49515          * @param {Roo.LayoutRegion} this
49516          * @param {Boolean} visibility true or false
49517          */
49518         "visibilitychange" : true,
49519         /**
49520          * @event paneladded
49521          * Fires when a panel is added. 
49522          * @param {Roo.LayoutRegion} this
49523          * @param {Roo.ContentPanel} panel The panel
49524          */
49525         "paneladded" : true,
49526         /**
49527          * @event panelremoved
49528          * Fires when a panel is removed. 
49529          * @param {Roo.LayoutRegion} this
49530          * @param {Roo.ContentPanel} panel The panel
49531          */
49532         "panelremoved" : true,
49533         /**
49534          * @event collapsed
49535          * Fires when this region is collapsed.
49536          * @param {Roo.LayoutRegion} this
49537          */
49538         "collapsed" : true,
49539         /**
49540          * @event expanded
49541          * Fires when this region is expanded.
49542          * @param {Roo.LayoutRegion} this
49543          */
49544         "expanded" : true,
49545         /**
49546          * @event slideshow
49547          * Fires when this region is slid into view.
49548          * @param {Roo.LayoutRegion} this
49549          */
49550         "slideshow" : true,
49551         /**
49552          * @event slidehide
49553          * Fires when this region slides out of view. 
49554          * @param {Roo.LayoutRegion} this
49555          */
49556         "slidehide" : true,
49557         /**
49558          * @event panelactivated
49559          * Fires when a panel is activated. 
49560          * @param {Roo.LayoutRegion} this
49561          * @param {Roo.ContentPanel} panel The activated panel
49562          */
49563         "panelactivated" : true,
49564         /**
49565          * @event resized
49566          * Fires when the user resizes this region. 
49567          * @param {Roo.LayoutRegion} this
49568          * @param {Number} newSize The new size (width for east/west, height for north/south)
49569          */
49570         "resized" : true
49571     };
49572     /** A collection of panels in this region. @type Roo.util.MixedCollection */
49573     this.panels = new Roo.util.MixedCollection();
49574     this.panels.getKey = this.getPanelId.createDelegate(this);
49575     this.box = null;
49576     this.activePanel = null;
49577     // ensure listeners are added...
49578     
49579     if (config.listeners || config.events) {
49580         Roo.BasicLayoutRegion.superclass.constructor.call(this, {
49581             listeners : config.listeners || {},
49582             events : config.events || {}
49583         });
49584     }
49585     
49586     if(skipConfig !== true){
49587         this.applyConfig(config);
49588     }
49589 };
49590
49591 Roo.extend(Roo.BasicLayoutRegion, Roo.util.Observable, {
49592     getPanelId : function(p){
49593         return p.getId();
49594     },
49595     
49596     applyConfig : function(config){
49597         this.margins = config.margins || this.margins || {top: 0, left: 0, right:0, bottom: 0};
49598         this.config = config;
49599         
49600     },
49601     
49602     /**
49603      * Resizes the region to the specified size. For vertical regions (west, east) this adjusts 
49604      * the width, for horizontal (north, south) the height.
49605      * @param {Number} newSize The new width or height
49606      */
49607     resizeTo : function(newSize){
49608         var el = this.el ? this.el :
49609                  (this.activePanel ? this.activePanel.getEl() : null);
49610         if(el){
49611             switch(this.position){
49612                 case "east":
49613                 case "west":
49614                     el.setWidth(newSize);
49615                     this.fireEvent("resized", this, newSize);
49616                 break;
49617                 case "north":
49618                 case "south":
49619                     el.setHeight(newSize);
49620                     this.fireEvent("resized", this, newSize);
49621                 break;                
49622             }
49623         }
49624     },
49625     
49626     getBox : function(){
49627         return this.activePanel ? this.activePanel.getEl().getBox(false, true) : null;
49628     },
49629     
49630     getMargins : function(){
49631         return this.margins;
49632     },
49633     
49634     updateBox : function(box){
49635         this.box = box;
49636         var el = this.activePanel.getEl();
49637         el.dom.style.left = box.x + "px";
49638         el.dom.style.top = box.y + "px";
49639         this.activePanel.setSize(box.width, box.height);
49640     },
49641     
49642     /**
49643      * Returns the container element for this region.
49644      * @return {Roo.Element}
49645      */
49646     getEl : function(){
49647         return this.activePanel;
49648     },
49649     
49650     /**
49651      * Returns true if this region is currently visible.
49652      * @return {Boolean}
49653      */
49654     isVisible : function(){
49655         return this.activePanel ? true : false;
49656     },
49657     
49658     setActivePanel : function(panel){
49659         panel = this.getPanel(panel);
49660         if(this.activePanel && this.activePanel != panel){
49661             this.activePanel.setActiveState(false);
49662             this.activePanel.getEl().setLeftTop(-10000,-10000);
49663         }
49664         this.activePanel = panel;
49665         panel.setActiveState(true);
49666         if(this.box){
49667             panel.setSize(this.box.width, this.box.height);
49668         }
49669         this.fireEvent("panelactivated", this, panel);
49670         this.fireEvent("invalidated");
49671     },
49672     
49673     /**
49674      * Show the specified panel.
49675      * @param {Number/String/ContentPanel} panelId The panels index, id or the panel itself
49676      * @return {Roo.ContentPanel} The shown panel or null
49677      */
49678     showPanel : function(panel){
49679         if(panel = this.getPanel(panel)){
49680             this.setActivePanel(panel);
49681         }
49682         return panel;
49683     },
49684     
49685     /**
49686      * Get the active panel for this region.
49687      * @return {Roo.ContentPanel} The active panel or null
49688      */
49689     getActivePanel : function(){
49690         return this.activePanel;
49691     },
49692     
49693     /**
49694      * Add the passed ContentPanel(s)
49695      * @param {ContentPanel...} panel The ContentPanel(s) to add (you can pass more than one)
49696      * @return {Roo.ContentPanel} The panel added (if only one was added)
49697      */
49698     add : function(panel){
49699         if(arguments.length > 1){
49700             for(var i = 0, len = arguments.length; i < len; i++) {
49701                 this.add(arguments[i]);
49702             }
49703             return null;
49704         }
49705         if(this.hasPanel(panel)){
49706             this.showPanel(panel);
49707             return panel;
49708         }
49709         var el = panel.getEl();
49710         if(el.dom.parentNode != this.mgr.el.dom){
49711             this.mgr.el.dom.appendChild(el.dom);
49712         }
49713         if(panel.setRegion){
49714             panel.setRegion(this);
49715         }
49716         this.panels.add(panel);
49717         el.setStyle("position", "absolute");
49718         if(!panel.background){
49719             this.setActivePanel(panel);
49720             if(this.config.initialSize && this.panels.getCount()==1){
49721                 this.resizeTo(this.config.initialSize);
49722             }
49723         }
49724         this.fireEvent("paneladded", this, panel);
49725         return panel;
49726     },
49727     
49728     /**
49729      * Returns true if the panel is in this region.
49730      * @param {Number/String/ContentPanel} panel The panels index, id or the panel itself
49731      * @return {Boolean}
49732      */
49733     hasPanel : function(panel){
49734         if(typeof panel == "object"){ // must be panel obj
49735             panel = panel.getId();
49736         }
49737         return this.getPanel(panel) ? true : false;
49738     },
49739     
49740     /**
49741      * Removes the specified panel. If preservePanel is not true (either here or in the config), the panel is destroyed.
49742      * @param {Number/String/ContentPanel} panel The panels index, id or the panel itself
49743      * @param {Boolean} preservePanel Overrides the config preservePanel option
49744      * @return {Roo.ContentPanel} The panel that was removed
49745      */
49746     remove : function(panel, preservePanel){
49747         panel = this.getPanel(panel);
49748         if(!panel){
49749             return null;
49750         }
49751         var e = {};
49752         this.fireEvent("beforeremove", this, panel, e);
49753         if(e.cancel === true){
49754             return null;
49755         }
49756         var panelId = panel.getId();
49757         this.panels.removeKey(panelId);
49758         return panel;
49759     },
49760     
49761     /**
49762      * Returns the panel specified or null if it's not in this region.
49763      * @param {Number/String/ContentPanel} panel The panels index, id or the panel itself
49764      * @return {Roo.ContentPanel}
49765      */
49766     getPanel : function(id){
49767         if(typeof id == "object"){ // must be panel obj
49768             return id;
49769         }
49770         return this.panels.get(id);
49771     },
49772     
49773     /**
49774      * Returns this regions position (north/south/east/west/center).
49775      * @return {String} 
49776      */
49777     getPosition: function(){
49778         return this.position;    
49779     }
49780 });/*
49781  * Based on:
49782  * Ext JS Library 1.1.1
49783  * Copyright(c) 2006-2007, Ext JS, LLC.
49784  *
49785  * Originally Released Under LGPL - original licence link has changed is not relivant.
49786  *
49787  * Fork - LGPL
49788  * <script type="text/javascript">
49789  */
49790  
49791 /**
49792  * @class Roo.LayoutRegion
49793  * @extends Roo.BasicLayoutRegion
49794  * This class represents a region in a layout manager.
49795  * @cfg {Boolean}   collapsible     False to disable collapsing (defaults to true)
49796  * @cfg {Boolean}   collapsed       True to set the initial display to collapsed (defaults to false)
49797  * @cfg {Boolean}   floatable       False to disable floating (defaults to true)
49798  * @cfg {Object}    margins         Margins for the element (defaults to {top: 0, left: 0, right:0, bottom: 0})
49799  * @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})
49800  * @cfg {String}    tabPosition     "top" or "bottom" (defaults to "bottom")
49801  * @cfg {String}    collapsedTitle  Optional string message to display in the collapsed block of a north or south region
49802  * @cfg {Boolean}   alwaysShowTabs  True to always display tabs even when there is only 1 panel (defaults to false)
49803  * @cfg {Boolean}   autoScroll      True to enable overflow scrolling (defaults to false)
49804  * @cfg {Boolean}   titlebar        True to display a title bar (defaults to true)
49805  * @cfg {String}    title           The title for the region (overrides panel titles)
49806  * @cfg {Boolean}   animate         True to animate expand/collapse (defaults to false)
49807  * @cfg {Boolean}   autoHide        False to disable auto hiding when the mouse leaves the "floated" region (defaults to true)
49808  * @cfg {Boolean}   preservePanels  True to preserve removed panels so they can be readded later (defaults to false)
49809  * @cfg {Boolean}   closeOnTab      True to place the close icon on the tabs instead of the region titlebar (defaults to false)
49810  * @cfg {Boolean}   hideTabs        True to hide the tab strip (defaults to false)
49811  * @cfg {Boolean}   resizeTabs      True to enable automatic tab resizing. This will resize the tabs so they are all the same size and fit within
49812  *                      the space available, similar to FireFox 1.5 tabs (defaults to false)
49813  * @cfg {Number}    minTabWidth     The minimum tab width (defaults to 40)
49814  * @cfg {Number}    preferredTabWidth The preferred tab width (defaults to 150)
49815  * @cfg {Boolean}   showPin         True to show a pin button
49816  * @cfg {Boolean}   hidden          True to start the region hidden (defaults to false)
49817  * @cfg {Boolean}   hideWhenEmpty   True to hide the region when it has no panels
49818  * @cfg {Boolean}   disableTabTips  True to disable tab tooltips
49819  * @cfg {Number}    width           For East/West panels
49820  * @cfg {Number}    height          For North/South panels
49821  * @cfg {Boolean}   split           To show the splitter
49822  * @cfg {Boolean}   toolbar         xtype configuration for a toolbar - shows on right of tabbar
49823  */
49824 Roo.LayoutRegion = function(mgr, config, pos){
49825     Roo.LayoutRegion.superclass.constructor.call(this, mgr, config, pos, true);
49826     var dh = Roo.DomHelper;
49827     /** This region's container element 
49828     * @type Roo.Element */
49829     this.el = dh.append(mgr.el.dom, {tag: "div", cls: "x-layout-panel x-layout-panel-" + this.position}, true);
49830     /** This region's title element 
49831     * @type Roo.Element */
49832
49833     this.titleEl = dh.append(this.el.dom, {tag: "div", unselectable: "on", cls: "x-unselectable x-layout-panel-hd x-layout-title-"+this.position, children:[
49834         {tag: "span", cls: "x-unselectable x-layout-panel-hd-text", unselectable: "on", html: "&#160;"},
49835         {tag: "div", cls: "x-unselectable x-layout-panel-hd-tools", unselectable: "on"}
49836     ]}, true);
49837     this.titleEl.enableDisplayMode();
49838     /** This region's title text element 
49839     * @type HTMLElement */
49840     this.titleTextEl = this.titleEl.dom.firstChild;
49841     this.tools = Roo.get(this.titleEl.dom.childNodes[1], true);
49842     this.closeBtn = this.createTool(this.tools.dom, "x-layout-close");
49843     this.closeBtn.enableDisplayMode();
49844     this.closeBtn.on("click", this.closeClicked, this);
49845     this.closeBtn.hide();
49846
49847     this.createBody(config);
49848     this.visible = true;
49849     this.collapsed = false;
49850
49851     if(config.hideWhenEmpty){
49852         this.hide();
49853         this.on("paneladded", this.validateVisibility, this);
49854         this.on("panelremoved", this.validateVisibility, this);
49855     }
49856     this.applyConfig(config);
49857 };
49858
49859 Roo.extend(Roo.LayoutRegion, Roo.BasicLayoutRegion, {
49860
49861     createBody : function(){
49862         /** This region's body element 
49863         * @type Roo.Element */
49864         this.bodyEl = this.el.createChild({tag: "div", cls: "x-layout-panel-body"});
49865     },
49866
49867     applyConfig : function(c){
49868         if(c.collapsible && this.position != "center" && !this.collapsedEl){
49869             var dh = Roo.DomHelper;
49870             if(c.titlebar !== false){
49871                 this.collapseBtn = this.createTool(this.tools.dom, "x-layout-collapse-"+this.position);
49872                 this.collapseBtn.on("click", this.collapse, this);
49873                 this.collapseBtn.enableDisplayMode();
49874
49875                 if(c.showPin === true || this.showPin){
49876                     this.stickBtn = this.createTool(this.tools.dom, "x-layout-stick");
49877                     this.stickBtn.enableDisplayMode();
49878                     this.stickBtn.on("click", this.expand, this);
49879                     this.stickBtn.hide();
49880                 }
49881             }
49882             /** This region's collapsed element
49883             * @type Roo.Element */
49884             this.collapsedEl = dh.append(this.mgr.el.dom, {cls: "x-layout-collapsed x-layout-collapsed-"+this.position, children:[
49885                 {cls: "x-layout-collapsed-tools", children:[{cls: "x-layout-ctools-inner"}]}
49886             ]}, true);
49887             if(c.floatable !== false){
49888                this.collapsedEl.addClassOnOver("x-layout-collapsed-over");
49889                this.collapsedEl.on("click", this.collapseClick, this);
49890             }
49891
49892             if(c.collapsedTitle && (this.position == "north" || this.position== "south")) {
49893                 this.collapsedTitleTextEl = dh.append(this.collapsedEl.dom, {tag: "div", cls: "x-unselectable x-layout-panel-hd-text",
49894                    id: "message", unselectable: "on", style:{"float":"left"}});
49895                this.collapsedTitleTextEl.innerHTML = c.collapsedTitle;
49896              }
49897             this.expandBtn = this.createTool(this.collapsedEl.dom.firstChild.firstChild, "x-layout-expand-"+this.position);
49898             this.expandBtn.on("click", this.expand, this);
49899         }
49900         if(this.collapseBtn){
49901             this.collapseBtn.setVisible(c.collapsible == true);
49902         }
49903         this.cmargins = c.cmargins || this.cmargins ||
49904                          (this.position == "west" || this.position == "east" ?
49905                              {top: 0, left: 2, right:2, bottom: 0} :
49906                              {top: 2, left: 0, right:0, bottom: 2});
49907         this.margins = c.margins || this.margins || {top: 0, left: 0, right:0, bottom: 0};
49908         this.bottomTabs = c.tabPosition != "top";
49909         this.autoScroll = c.autoScroll || false;
49910         if(this.autoScroll){
49911             this.bodyEl.setStyle("overflow", "auto");
49912         }else{
49913             this.bodyEl.setStyle("overflow", "hidden");
49914         }
49915         //if(c.titlebar !== false){
49916             if((!c.titlebar && !c.title) || c.titlebar === false){
49917                 this.titleEl.hide();
49918             }else{
49919                 this.titleEl.show();
49920                 if(c.title){
49921                     this.titleTextEl.innerHTML = c.title;
49922                 }
49923             }
49924         //}
49925         this.duration = c.duration || .30;
49926         this.slideDuration = c.slideDuration || .45;
49927         this.config = c;
49928         if(c.collapsed){
49929             this.collapse(true);
49930         }
49931         if(c.hidden){
49932             this.hide();
49933         }
49934     },
49935     /**
49936      * Returns true if this region is currently visible.
49937      * @return {Boolean}
49938      */
49939     isVisible : function(){
49940         return this.visible;
49941     },
49942
49943     /**
49944      * Updates the title for collapsed north/south regions (used with {@link #collapsedTitle} config option)
49945      * @param {String} title (optional) The title text (accepts HTML markup, defaults to the numeric character reference for a non-breaking space, "&amp;#160;")
49946      */
49947     setCollapsedTitle : function(title){
49948         title = title || "&#160;";
49949         if(this.collapsedTitleTextEl){
49950             this.collapsedTitleTextEl.innerHTML = title;
49951         }
49952     },
49953
49954     getBox : function(){
49955         var b;
49956         if(!this.collapsed){
49957             b = this.el.getBox(false, true);
49958         }else{
49959             b = this.collapsedEl.getBox(false, true);
49960         }
49961         return b;
49962     },
49963
49964     getMargins : function(){
49965         return this.collapsed ? this.cmargins : this.margins;
49966     },
49967
49968     highlight : function(){
49969         this.el.addClass("x-layout-panel-dragover");
49970     },
49971
49972     unhighlight : function(){
49973         this.el.removeClass("x-layout-panel-dragover");
49974     },
49975
49976     updateBox : function(box){
49977         this.box = box;
49978         if(!this.collapsed){
49979             this.el.dom.style.left = box.x + "px";
49980             this.el.dom.style.top = box.y + "px";
49981             this.updateBody(box.width, box.height);
49982         }else{
49983             this.collapsedEl.dom.style.left = box.x + "px";
49984             this.collapsedEl.dom.style.top = box.y + "px";
49985             this.collapsedEl.setSize(box.width, box.height);
49986         }
49987         if(this.tabs){
49988             this.tabs.autoSizeTabs();
49989         }
49990     },
49991
49992     updateBody : function(w, h){
49993         if(w !== null){
49994             this.el.setWidth(w);
49995             w -= this.el.getBorderWidth("rl");
49996             if(this.config.adjustments){
49997                 w += this.config.adjustments[0];
49998             }
49999         }
50000         if(h !== null){
50001             this.el.setHeight(h);
50002             h = this.titleEl && this.titleEl.isDisplayed() ? h - (this.titleEl.getHeight()||0) : h;
50003             h -= this.el.getBorderWidth("tb");
50004             if(this.config.adjustments){
50005                 h += this.config.adjustments[1];
50006             }
50007             this.bodyEl.setHeight(h);
50008             if(this.tabs){
50009                 h = this.tabs.syncHeight(h);
50010             }
50011         }
50012         if(this.panelSize){
50013             w = w !== null ? w : this.panelSize.width;
50014             h = h !== null ? h : this.panelSize.height;
50015         }
50016         if(this.activePanel){
50017             var el = this.activePanel.getEl();
50018             w = w !== null ? w : el.getWidth();
50019             h = h !== null ? h : el.getHeight();
50020             this.panelSize = {width: w, height: h};
50021             this.activePanel.setSize(w, h);
50022         }
50023         if(Roo.isIE && this.tabs){
50024             this.tabs.el.repaint();
50025         }
50026     },
50027
50028     /**
50029      * Returns the container element for this region.
50030      * @return {Roo.Element}
50031      */
50032     getEl : function(){
50033         return this.el;
50034     },
50035
50036     /**
50037      * Hides this region.
50038      */
50039     hide : function(){
50040         if(!this.collapsed){
50041             this.el.dom.style.left = "-2000px";
50042             this.el.hide();
50043         }else{
50044             this.collapsedEl.dom.style.left = "-2000px";
50045             this.collapsedEl.hide();
50046         }
50047         this.visible = false;
50048         this.fireEvent("visibilitychange", this, false);
50049     },
50050
50051     /**
50052      * Shows this region if it was previously hidden.
50053      */
50054     show : function(){
50055         if(!this.collapsed){
50056             this.el.show();
50057         }else{
50058             this.collapsedEl.show();
50059         }
50060         this.visible = true;
50061         this.fireEvent("visibilitychange", this, true);
50062     },
50063
50064     closeClicked : function(){
50065         if(this.activePanel){
50066             this.remove(this.activePanel);
50067         }
50068     },
50069
50070     collapseClick : function(e){
50071         if(this.isSlid){
50072            e.stopPropagation();
50073            this.slideIn();
50074         }else{
50075            e.stopPropagation();
50076            this.slideOut();
50077         }
50078     },
50079
50080     /**
50081      * Collapses this region.
50082      * @param {Boolean} skipAnim (optional) true to collapse the element without animation (if animate is true)
50083      */
50084     collapse : function(skipAnim){
50085         if(this.collapsed) return;
50086         this.collapsed = true;
50087         if(this.split){
50088             this.split.el.hide();
50089         }
50090         if(this.config.animate && skipAnim !== true){
50091             this.fireEvent("invalidated", this);
50092             this.animateCollapse();
50093         }else{
50094             this.el.setLocation(-20000,-20000);
50095             this.el.hide();
50096             this.collapsedEl.show();
50097             this.fireEvent("collapsed", this);
50098             this.fireEvent("invalidated", this);
50099         }
50100     },
50101
50102     animateCollapse : function(){
50103         // overridden
50104     },
50105
50106     /**
50107      * Expands this region if it was previously collapsed.
50108      * @param {Roo.EventObject} e The event that triggered the expand (or null if calling manually)
50109      * @param {Boolean} skipAnim (optional) true to expand the element without animation (if animate is true)
50110      */
50111     expand : function(e, skipAnim){
50112         if(e) e.stopPropagation();
50113         if(!this.collapsed || this.el.hasActiveFx()) return;
50114         if(this.isSlid){
50115             this.afterSlideIn();
50116             skipAnim = true;
50117         }
50118         this.collapsed = false;
50119         if(this.config.animate && skipAnim !== true){
50120             this.animateExpand();
50121         }else{
50122             this.el.show();
50123             if(this.split){
50124                 this.split.el.show();
50125             }
50126             this.collapsedEl.setLocation(-2000,-2000);
50127             this.collapsedEl.hide();
50128             this.fireEvent("invalidated", this);
50129             this.fireEvent("expanded", this);
50130         }
50131     },
50132
50133     animateExpand : function(){
50134         // overridden
50135     },
50136
50137     initTabs : function()
50138     {
50139         this.bodyEl.setStyle("overflow", "hidden");
50140         var ts = new Roo.TabPanel(
50141                 this.bodyEl.dom,
50142                 {
50143                     tabPosition: this.bottomTabs ? 'bottom' : 'top',
50144                     disableTooltips: this.config.disableTabTips,
50145                     toolbar : this.config.toolbar
50146                 }
50147         );
50148         if(this.config.hideTabs){
50149             ts.stripWrap.setDisplayed(false);
50150         }
50151         this.tabs = ts;
50152         ts.resizeTabs = this.config.resizeTabs === true;
50153         ts.minTabWidth = this.config.minTabWidth || 40;
50154         ts.maxTabWidth = this.config.maxTabWidth || 250;
50155         ts.preferredTabWidth = this.config.preferredTabWidth || 150;
50156         ts.monitorResize = false;
50157         ts.bodyEl.setStyle("overflow", this.config.autoScroll ? "auto" : "hidden");
50158         ts.bodyEl.addClass('x-layout-tabs-body');
50159         this.panels.each(this.initPanelAsTab, this);
50160     },
50161
50162     initPanelAsTab : function(panel){
50163         var ti = this.tabs.addTab(panel.getEl().id, panel.getTitle(), null,
50164                     this.config.closeOnTab && panel.isClosable());
50165         if(panel.tabTip !== undefined){
50166             ti.setTooltip(panel.tabTip);
50167         }
50168         ti.on("activate", function(){
50169               this.setActivePanel(panel);
50170         }, this);
50171         if(this.config.closeOnTab){
50172             ti.on("beforeclose", function(t, e){
50173                 e.cancel = true;
50174                 this.remove(panel);
50175             }, this);
50176         }
50177         return ti;
50178     },
50179
50180     updatePanelTitle : function(panel, title){
50181         if(this.activePanel == panel){
50182             this.updateTitle(title);
50183         }
50184         if(this.tabs){
50185             var ti = this.tabs.getTab(panel.getEl().id);
50186             ti.setText(title);
50187             if(panel.tabTip !== undefined){
50188                 ti.setTooltip(panel.tabTip);
50189             }
50190         }
50191     },
50192
50193     updateTitle : function(title){
50194         if(this.titleTextEl && !this.config.title){
50195             this.titleTextEl.innerHTML = (typeof title != "undefined" && title.length > 0 ? title : "&#160;");
50196         }
50197     },
50198
50199     setActivePanel : function(panel){
50200         panel = this.getPanel(panel);
50201         if(this.activePanel && this.activePanel != panel){
50202             this.activePanel.setActiveState(false);
50203         }
50204         this.activePanel = panel;
50205         panel.setActiveState(true);
50206         if(this.panelSize){
50207             panel.setSize(this.panelSize.width, this.panelSize.height);
50208         }
50209         if(this.closeBtn){
50210             this.closeBtn.setVisible(!this.config.closeOnTab && !this.isSlid && panel.isClosable());
50211         }
50212         this.updateTitle(panel.getTitle());
50213         if(this.tabs){
50214             this.fireEvent("invalidated", this);
50215         }
50216         this.fireEvent("panelactivated", this, panel);
50217     },
50218
50219     /**
50220      * Shows the specified panel.
50221      * @param {Number/String/ContentPanel} panelId The panel's index, id or the panel itself
50222      * @return {Roo.ContentPanel} The shown panel, or null if a panel could not be found from panelId
50223      */
50224     showPanel : function(panel){
50225         if(panel = this.getPanel(panel)){
50226             if(this.tabs){
50227                 var tab = this.tabs.getTab(panel.getEl().id);
50228                 if(tab.isHidden()){
50229                     this.tabs.unhideTab(tab.id);
50230                 }
50231                 tab.activate();
50232             }else{
50233                 this.setActivePanel(panel);
50234             }
50235         }
50236         return panel;
50237     },
50238
50239     /**
50240      * Get the active panel for this region.
50241      * @return {Roo.ContentPanel} The active panel or null
50242      */
50243     getActivePanel : function(){
50244         return this.activePanel;
50245     },
50246
50247     validateVisibility : function(){
50248         if(this.panels.getCount() < 1){
50249             this.updateTitle("&#160;");
50250             this.closeBtn.hide();
50251             this.hide();
50252         }else{
50253             if(!this.isVisible()){
50254                 this.show();
50255             }
50256         }
50257     },
50258
50259     /**
50260      * Adds the passed ContentPanel(s) to this region.
50261      * @param {ContentPanel...} panel The ContentPanel(s) to add (you can pass more than one)
50262      * @return {Roo.ContentPanel} The panel added (if only one was added; null otherwise)
50263      */
50264     add : function(panel){
50265         if(arguments.length > 1){
50266             for(var i = 0, len = arguments.length; i < len; i++) {
50267                 this.add(arguments[i]);
50268             }
50269             return null;
50270         }
50271         if(this.hasPanel(panel)){
50272             this.showPanel(panel);
50273             return panel;
50274         }
50275         panel.setRegion(this);
50276         this.panels.add(panel);
50277         if(this.panels.getCount() == 1 && !this.config.alwaysShowTabs){
50278             this.bodyEl.dom.appendChild(panel.getEl().dom);
50279             if(panel.background !== true){
50280                 this.setActivePanel(panel);
50281             }
50282             this.fireEvent("paneladded", this, panel);
50283             return panel;
50284         }
50285         if(!this.tabs){
50286             this.initTabs();
50287         }else{
50288             this.initPanelAsTab(panel);
50289         }
50290         if(panel.background !== true){
50291             this.tabs.activate(panel.getEl().id);
50292         }
50293         this.fireEvent("paneladded", this, panel);
50294         return panel;
50295     },
50296
50297     /**
50298      * Hides the tab for the specified panel.
50299      * @param {Number/String/ContentPanel} panel The panel's index, id or the panel itself
50300      */
50301     hidePanel : function(panel){
50302         if(this.tabs && (panel = this.getPanel(panel))){
50303             this.tabs.hideTab(panel.getEl().id);
50304         }
50305     },
50306
50307     /**
50308      * Unhides the tab for a previously hidden panel.
50309      * @param {Number/String/ContentPanel} panel The panel's index, id or the panel itself
50310      */
50311     unhidePanel : function(panel){
50312         if(this.tabs && (panel = this.getPanel(panel))){
50313             this.tabs.unhideTab(panel.getEl().id);
50314         }
50315     },
50316
50317     clearPanels : function(){
50318         while(this.panels.getCount() > 0){
50319              this.remove(this.panels.first());
50320         }
50321     },
50322
50323     /**
50324      * Removes the specified panel. If preservePanel is not true (either here or in the config), the panel is destroyed.
50325      * @param {Number/String/ContentPanel} panel The panel's index, id or the panel itself
50326      * @param {Boolean} preservePanel Overrides the config preservePanel option
50327      * @return {Roo.ContentPanel} The panel that was removed
50328      */
50329     remove : function(panel, preservePanel){
50330         panel = this.getPanel(panel);
50331         if(!panel){
50332             return null;
50333         }
50334         var e = {};
50335         this.fireEvent("beforeremove", this, panel, e);
50336         if(e.cancel === true){
50337             return null;
50338         }
50339         preservePanel = (typeof preservePanel != "undefined" ? preservePanel : (this.config.preservePanels === true || panel.preserve === true));
50340         var panelId = panel.getId();
50341         this.panels.removeKey(panelId);
50342         if(preservePanel){
50343             document.body.appendChild(panel.getEl().dom);
50344         }
50345         if(this.tabs){
50346             this.tabs.removeTab(panel.getEl().id);
50347         }else if (!preservePanel){
50348             this.bodyEl.dom.removeChild(panel.getEl().dom);
50349         }
50350         if(this.panels.getCount() == 1 && this.tabs && !this.config.alwaysShowTabs){
50351             var p = this.panels.first();
50352             var tempEl = document.createElement("div"); // temp holder to keep IE from deleting the node
50353             tempEl.appendChild(p.getEl().dom);
50354             this.bodyEl.update("");
50355             this.bodyEl.dom.appendChild(p.getEl().dom);
50356             tempEl = null;
50357             this.updateTitle(p.getTitle());
50358             this.tabs = null;
50359             this.bodyEl.setStyle("overflow", this.config.autoScroll ? "auto" : "hidden");
50360             this.setActivePanel(p);
50361         }
50362         panel.setRegion(null);
50363         if(this.activePanel == panel){
50364             this.activePanel = null;
50365         }
50366         if(this.config.autoDestroy !== false && preservePanel !== true){
50367             try{panel.destroy();}catch(e){}
50368         }
50369         this.fireEvent("panelremoved", this, panel);
50370         return panel;
50371     },
50372
50373     /**
50374      * Returns the TabPanel component used by this region
50375      * @return {Roo.TabPanel}
50376      */
50377     getTabs : function(){
50378         return this.tabs;
50379     },
50380
50381     createTool : function(parentEl, className){
50382         var btn = Roo.DomHelper.append(parentEl, {tag: "div", cls: "x-layout-tools-button",
50383             children: [{tag: "div", cls: "x-layout-tools-button-inner " + className, html: "&#160;"}]}, true);
50384         btn.addClassOnOver("x-layout-tools-button-over");
50385         return btn;
50386     }
50387 });/*
50388  * Based on:
50389  * Ext JS Library 1.1.1
50390  * Copyright(c) 2006-2007, Ext JS, LLC.
50391  *
50392  * Originally Released Under LGPL - original licence link has changed is not relivant.
50393  *
50394  * Fork - LGPL
50395  * <script type="text/javascript">
50396  */
50397  
50398
50399
50400 /**
50401  * @class Roo.SplitLayoutRegion
50402  * @extends Roo.LayoutRegion
50403  * Adds a splitbar and other (private) useful functionality to a {@link Roo.LayoutRegion}.
50404  */
50405 Roo.SplitLayoutRegion = function(mgr, config, pos, cursor){
50406     this.cursor = cursor;
50407     Roo.SplitLayoutRegion.superclass.constructor.call(this, mgr, config, pos);
50408 };
50409
50410 Roo.extend(Roo.SplitLayoutRegion, Roo.LayoutRegion, {
50411     splitTip : "Drag to resize.",
50412     collapsibleSplitTip : "Drag to resize. Double click to hide.",
50413     useSplitTips : false,
50414
50415     applyConfig : function(config){
50416         Roo.SplitLayoutRegion.superclass.applyConfig.call(this, config);
50417         if(config.split){
50418             if(!this.split){
50419                 var splitEl = Roo.DomHelper.append(this.mgr.el.dom, 
50420                         {tag: "div", id: this.el.id + "-split", cls: "x-layout-split x-layout-split-"+this.position, html: "&#160;"});
50421                 /** The SplitBar for this region 
50422                 * @type Roo.SplitBar */
50423                 this.split = new Roo.SplitBar(splitEl, this.el, this.orientation);
50424                 this.split.on("moved", this.onSplitMove, this);
50425                 this.split.useShim = config.useShim === true;
50426                 this.split.getMaximumSize = this[this.position == 'north' || this.position == 'south' ? 'getVMaxSize' : 'getHMaxSize'].createDelegate(this);
50427                 if(this.useSplitTips){
50428                     this.split.el.dom.title = config.collapsible ? this.collapsibleSplitTip : this.splitTip;
50429                 }
50430                 if(config.collapsible){
50431                     this.split.el.on("dblclick", this.collapse,  this);
50432                 }
50433             }
50434             if(typeof config.minSize != "undefined"){
50435                 this.split.minSize = config.minSize;
50436             }
50437             if(typeof config.maxSize != "undefined"){
50438                 this.split.maxSize = config.maxSize;
50439             }
50440             if(config.hideWhenEmpty || config.hidden || config.collapsed){
50441                 this.hideSplitter();
50442             }
50443         }
50444     },
50445
50446     getHMaxSize : function(){
50447          var cmax = this.config.maxSize || 10000;
50448          var center = this.mgr.getRegion("center");
50449          return Math.min(cmax, (this.el.getWidth()+center.getEl().getWidth())-center.getMinWidth());
50450     },
50451
50452     getVMaxSize : function(){
50453          var cmax = this.config.maxSize || 10000;
50454          var center = this.mgr.getRegion("center");
50455          return Math.min(cmax, (this.el.getHeight()+center.getEl().getHeight())-center.getMinHeight());
50456     },
50457
50458     onSplitMove : function(split, newSize){
50459         this.fireEvent("resized", this, newSize);
50460     },
50461     
50462     /** 
50463      * Returns the {@link Roo.SplitBar} for this region.
50464      * @return {Roo.SplitBar}
50465      */
50466     getSplitBar : function(){
50467         return this.split;
50468     },
50469     
50470     hide : function(){
50471         this.hideSplitter();
50472         Roo.SplitLayoutRegion.superclass.hide.call(this);
50473     },
50474
50475     hideSplitter : function(){
50476         if(this.split){
50477             this.split.el.setLocation(-2000,-2000);
50478             this.split.el.hide();
50479         }
50480     },
50481
50482     show : function(){
50483         if(this.split){
50484             this.split.el.show();
50485         }
50486         Roo.SplitLayoutRegion.superclass.show.call(this);
50487     },
50488     
50489     beforeSlide: function(){
50490         if(Roo.isGecko){// firefox overflow auto bug workaround
50491             this.bodyEl.clip();
50492             if(this.tabs) this.tabs.bodyEl.clip();
50493             if(this.activePanel){
50494                 this.activePanel.getEl().clip();
50495                 
50496                 if(this.activePanel.beforeSlide){
50497                     this.activePanel.beforeSlide();
50498                 }
50499             }
50500         }
50501     },
50502     
50503     afterSlide : function(){
50504         if(Roo.isGecko){// firefox overflow auto bug workaround
50505             this.bodyEl.unclip();
50506             if(this.tabs) this.tabs.bodyEl.unclip();
50507             if(this.activePanel){
50508                 this.activePanel.getEl().unclip();
50509                 if(this.activePanel.afterSlide){
50510                     this.activePanel.afterSlide();
50511                 }
50512             }
50513         }
50514     },
50515
50516     initAutoHide : function(){
50517         if(this.autoHide !== false){
50518             if(!this.autoHideHd){
50519                 var st = new Roo.util.DelayedTask(this.slideIn, this);
50520                 this.autoHideHd = {
50521                     "mouseout": function(e){
50522                         if(!e.within(this.el, true)){
50523                             st.delay(500);
50524                         }
50525                     },
50526                     "mouseover" : function(e){
50527                         st.cancel();
50528                     },
50529                     scope : this
50530                 };
50531             }
50532             this.el.on(this.autoHideHd);
50533         }
50534     },
50535
50536     clearAutoHide : function(){
50537         if(this.autoHide !== false){
50538             this.el.un("mouseout", this.autoHideHd.mouseout);
50539             this.el.un("mouseover", this.autoHideHd.mouseover);
50540         }
50541     },
50542
50543     clearMonitor : function(){
50544         Roo.get(document).un("click", this.slideInIf, this);
50545     },
50546
50547     // these names are backwards but not changed for compat
50548     slideOut : function(){
50549         if(this.isSlid || this.el.hasActiveFx()){
50550             return;
50551         }
50552         this.isSlid = true;
50553         if(this.collapseBtn){
50554             this.collapseBtn.hide();
50555         }
50556         this.closeBtnState = this.closeBtn.getStyle('display');
50557         this.closeBtn.hide();
50558         if(this.stickBtn){
50559             this.stickBtn.show();
50560         }
50561         this.el.show();
50562         this.el.alignTo(this.collapsedEl, this.getCollapseAnchor());
50563         this.beforeSlide();
50564         this.el.setStyle("z-index", 10001);
50565         this.el.slideIn(this.getSlideAnchor(), {
50566             callback: function(){
50567                 this.afterSlide();
50568                 this.initAutoHide();
50569                 Roo.get(document).on("click", this.slideInIf, this);
50570                 this.fireEvent("slideshow", this);
50571             },
50572             scope: this,
50573             block: true
50574         });
50575     },
50576
50577     afterSlideIn : function(){
50578         this.clearAutoHide();
50579         this.isSlid = false;
50580         this.clearMonitor();
50581         this.el.setStyle("z-index", "");
50582         if(this.collapseBtn){
50583             this.collapseBtn.show();
50584         }
50585         this.closeBtn.setStyle('display', this.closeBtnState);
50586         if(this.stickBtn){
50587             this.stickBtn.hide();
50588         }
50589         this.fireEvent("slidehide", this);
50590     },
50591
50592     slideIn : function(cb){
50593         if(!this.isSlid || this.el.hasActiveFx()){
50594             Roo.callback(cb);
50595             return;
50596         }
50597         this.isSlid = false;
50598         this.beforeSlide();
50599         this.el.slideOut(this.getSlideAnchor(), {
50600             callback: function(){
50601                 this.el.setLeftTop(-10000, -10000);
50602                 this.afterSlide();
50603                 this.afterSlideIn();
50604                 Roo.callback(cb);
50605             },
50606             scope: this,
50607             block: true
50608         });
50609     },
50610     
50611     slideInIf : function(e){
50612         if(!e.within(this.el)){
50613             this.slideIn();
50614         }
50615     },
50616
50617     animateCollapse : function(){
50618         this.beforeSlide();
50619         this.el.setStyle("z-index", 20000);
50620         var anchor = this.getSlideAnchor();
50621         this.el.slideOut(anchor, {
50622             callback : function(){
50623                 this.el.setStyle("z-index", "");
50624                 this.collapsedEl.slideIn(anchor, {duration:.3});
50625                 this.afterSlide();
50626                 this.el.setLocation(-10000,-10000);
50627                 this.el.hide();
50628                 this.fireEvent("collapsed", this);
50629             },
50630             scope: this,
50631             block: true
50632         });
50633     },
50634
50635     animateExpand : function(){
50636         this.beforeSlide();
50637         this.el.alignTo(this.collapsedEl, this.getCollapseAnchor(), this.getExpandAdj());
50638         this.el.setStyle("z-index", 20000);
50639         this.collapsedEl.hide({
50640             duration:.1
50641         });
50642         this.el.slideIn(this.getSlideAnchor(), {
50643             callback : function(){
50644                 this.el.setStyle("z-index", "");
50645                 this.afterSlide();
50646                 if(this.split){
50647                     this.split.el.show();
50648                 }
50649                 this.fireEvent("invalidated", this);
50650                 this.fireEvent("expanded", this);
50651             },
50652             scope: this,
50653             block: true
50654         });
50655     },
50656
50657     anchors : {
50658         "west" : "left",
50659         "east" : "right",
50660         "north" : "top",
50661         "south" : "bottom"
50662     },
50663
50664     sanchors : {
50665         "west" : "l",
50666         "east" : "r",
50667         "north" : "t",
50668         "south" : "b"
50669     },
50670
50671     canchors : {
50672         "west" : "tl-tr",
50673         "east" : "tr-tl",
50674         "north" : "tl-bl",
50675         "south" : "bl-tl"
50676     },
50677
50678     getAnchor : function(){
50679         return this.anchors[this.position];
50680     },
50681
50682     getCollapseAnchor : function(){
50683         return this.canchors[this.position];
50684     },
50685
50686     getSlideAnchor : function(){
50687         return this.sanchors[this.position];
50688     },
50689
50690     getAlignAdj : function(){
50691         var cm = this.cmargins;
50692         switch(this.position){
50693             case "west":
50694                 return [0, 0];
50695             break;
50696             case "east":
50697                 return [0, 0];
50698             break;
50699             case "north":
50700                 return [0, 0];
50701             break;
50702             case "south":
50703                 return [0, 0];
50704             break;
50705         }
50706     },
50707
50708     getExpandAdj : function(){
50709         var c = this.collapsedEl, cm = this.cmargins;
50710         switch(this.position){
50711             case "west":
50712                 return [-(cm.right+c.getWidth()+cm.left), 0];
50713             break;
50714             case "east":
50715                 return [cm.right+c.getWidth()+cm.left, 0];
50716             break;
50717             case "north":
50718                 return [0, -(cm.top+cm.bottom+c.getHeight())];
50719             break;
50720             case "south":
50721                 return [0, cm.top+cm.bottom+c.getHeight()];
50722             break;
50723         }
50724     }
50725 });/*
50726  * Based on:
50727  * Ext JS Library 1.1.1
50728  * Copyright(c) 2006-2007, Ext JS, LLC.
50729  *
50730  * Originally Released Under LGPL - original licence link has changed is not relivant.
50731  *
50732  * Fork - LGPL
50733  * <script type="text/javascript">
50734  */
50735 /*
50736  * These classes are private internal classes
50737  */
50738 Roo.CenterLayoutRegion = function(mgr, config){
50739     Roo.LayoutRegion.call(this, mgr, config, "center");
50740     this.visible = true;
50741     this.minWidth = config.minWidth || 20;
50742     this.minHeight = config.minHeight || 20;
50743 };
50744
50745 Roo.extend(Roo.CenterLayoutRegion, Roo.LayoutRegion, {
50746     hide : function(){
50747         // center panel can't be hidden
50748     },
50749     
50750     show : function(){
50751         // center panel can't be hidden
50752     },
50753     
50754     getMinWidth: function(){
50755         return this.minWidth;
50756     },
50757     
50758     getMinHeight: function(){
50759         return this.minHeight;
50760     }
50761 });
50762
50763
50764 Roo.NorthLayoutRegion = function(mgr, config){
50765     Roo.LayoutRegion.call(this, mgr, config, "north", "n-resize");
50766     if(this.split){
50767         this.split.placement = Roo.SplitBar.TOP;
50768         this.split.orientation = Roo.SplitBar.VERTICAL;
50769         this.split.el.addClass("x-layout-split-v");
50770     }
50771     var size = config.initialSize || config.height;
50772     if(typeof size != "undefined"){
50773         this.el.setHeight(size);
50774     }
50775 };
50776 Roo.extend(Roo.NorthLayoutRegion, Roo.SplitLayoutRegion, {
50777     orientation: Roo.SplitBar.VERTICAL,
50778     getBox : function(){
50779         if(this.collapsed){
50780             return this.collapsedEl.getBox();
50781         }
50782         var box = this.el.getBox();
50783         if(this.split){
50784             box.height += this.split.el.getHeight();
50785         }
50786         return box;
50787     },
50788     
50789     updateBox : function(box){
50790         if(this.split && !this.collapsed){
50791             box.height -= this.split.el.getHeight();
50792             this.split.el.setLeft(box.x);
50793             this.split.el.setTop(box.y+box.height);
50794             this.split.el.setWidth(box.width);
50795         }
50796         if(this.collapsed){
50797             this.updateBody(box.width, null);
50798         }
50799         Roo.LayoutRegion.prototype.updateBox.call(this, box);
50800     }
50801 });
50802
50803 Roo.SouthLayoutRegion = function(mgr, config){
50804     Roo.SplitLayoutRegion.call(this, mgr, config, "south", "s-resize");
50805     if(this.split){
50806         this.split.placement = Roo.SplitBar.BOTTOM;
50807         this.split.orientation = Roo.SplitBar.VERTICAL;
50808         this.split.el.addClass("x-layout-split-v");
50809     }
50810     var size = config.initialSize || config.height;
50811     if(typeof size != "undefined"){
50812         this.el.setHeight(size);
50813     }
50814 };
50815 Roo.extend(Roo.SouthLayoutRegion, Roo.SplitLayoutRegion, {
50816     orientation: Roo.SplitBar.VERTICAL,
50817     getBox : function(){
50818         if(this.collapsed){
50819             return this.collapsedEl.getBox();
50820         }
50821         var box = this.el.getBox();
50822         if(this.split){
50823             var sh = this.split.el.getHeight();
50824             box.height += sh;
50825             box.y -= sh;
50826         }
50827         return box;
50828     },
50829     
50830     updateBox : function(box){
50831         if(this.split && !this.collapsed){
50832             var sh = this.split.el.getHeight();
50833             box.height -= sh;
50834             box.y += sh;
50835             this.split.el.setLeft(box.x);
50836             this.split.el.setTop(box.y-sh);
50837             this.split.el.setWidth(box.width);
50838         }
50839         if(this.collapsed){
50840             this.updateBody(box.width, null);
50841         }
50842         Roo.LayoutRegion.prototype.updateBox.call(this, box);
50843     }
50844 });
50845
50846 Roo.EastLayoutRegion = function(mgr, config){
50847     Roo.SplitLayoutRegion.call(this, mgr, config, "east", "e-resize");
50848     if(this.split){
50849         this.split.placement = Roo.SplitBar.RIGHT;
50850         this.split.orientation = Roo.SplitBar.HORIZONTAL;
50851         this.split.el.addClass("x-layout-split-h");
50852     }
50853     var size = config.initialSize || config.width;
50854     if(typeof size != "undefined"){
50855         this.el.setWidth(size);
50856     }
50857 };
50858 Roo.extend(Roo.EastLayoutRegion, Roo.SplitLayoutRegion, {
50859     orientation: Roo.SplitBar.HORIZONTAL,
50860     getBox : function(){
50861         if(this.collapsed){
50862             return this.collapsedEl.getBox();
50863         }
50864         var box = this.el.getBox();
50865         if(this.split){
50866             var sw = this.split.el.getWidth();
50867             box.width += sw;
50868             box.x -= sw;
50869         }
50870         return box;
50871     },
50872
50873     updateBox : function(box){
50874         if(this.split && !this.collapsed){
50875             var sw = this.split.el.getWidth();
50876             box.width -= sw;
50877             this.split.el.setLeft(box.x);
50878             this.split.el.setTop(box.y);
50879             this.split.el.setHeight(box.height);
50880             box.x += sw;
50881         }
50882         if(this.collapsed){
50883             this.updateBody(null, box.height);
50884         }
50885         Roo.LayoutRegion.prototype.updateBox.call(this, box);
50886     }
50887 });
50888
50889 Roo.WestLayoutRegion = function(mgr, config){
50890     Roo.SplitLayoutRegion.call(this, mgr, config, "west", "w-resize");
50891     if(this.split){
50892         this.split.placement = Roo.SplitBar.LEFT;
50893         this.split.orientation = Roo.SplitBar.HORIZONTAL;
50894         this.split.el.addClass("x-layout-split-h");
50895     }
50896     var size = config.initialSize || config.width;
50897     if(typeof size != "undefined"){
50898         this.el.setWidth(size);
50899     }
50900 };
50901 Roo.extend(Roo.WestLayoutRegion, Roo.SplitLayoutRegion, {
50902     orientation: Roo.SplitBar.HORIZONTAL,
50903     getBox : function(){
50904         if(this.collapsed){
50905             return this.collapsedEl.getBox();
50906         }
50907         var box = this.el.getBox();
50908         if(this.split){
50909             box.width += this.split.el.getWidth();
50910         }
50911         return box;
50912     },
50913     
50914     updateBox : function(box){
50915         if(this.split && !this.collapsed){
50916             var sw = this.split.el.getWidth();
50917             box.width -= sw;
50918             this.split.el.setLeft(box.x+box.width);
50919             this.split.el.setTop(box.y);
50920             this.split.el.setHeight(box.height);
50921         }
50922         if(this.collapsed){
50923             this.updateBody(null, box.height);
50924         }
50925         Roo.LayoutRegion.prototype.updateBox.call(this, box);
50926     }
50927 });
50928 /*
50929  * Based on:
50930  * Ext JS Library 1.1.1
50931  * Copyright(c) 2006-2007, Ext JS, LLC.
50932  *
50933  * Originally Released Under LGPL - original licence link has changed is not relivant.
50934  *
50935  * Fork - LGPL
50936  * <script type="text/javascript">
50937  */
50938  
50939  
50940 /*
50941  * Private internal class for reading and applying state
50942  */
50943 Roo.LayoutStateManager = function(layout){
50944      // default empty state
50945      this.state = {
50946         north: {},
50947         south: {},
50948         east: {},
50949         west: {}       
50950     };
50951 };
50952
50953 Roo.LayoutStateManager.prototype = {
50954     init : function(layout, provider){
50955         this.provider = provider;
50956         var state = provider.get(layout.id+"-layout-state");
50957         if(state){
50958             var wasUpdating = layout.isUpdating();
50959             if(!wasUpdating){
50960                 layout.beginUpdate();
50961             }
50962             for(var key in state){
50963                 if(typeof state[key] != "function"){
50964                     var rstate = state[key];
50965                     var r = layout.getRegion(key);
50966                     if(r && rstate){
50967                         if(rstate.size){
50968                             r.resizeTo(rstate.size);
50969                         }
50970                         if(rstate.collapsed == true){
50971                             r.collapse(true);
50972                         }else{
50973                             r.expand(null, true);
50974                         }
50975                     }
50976                 }
50977             }
50978             if(!wasUpdating){
50979                 layout.endUpdate();
50980             }
50981             this.state = state; 
50982         }
50983         this.layout = layout;
50984         layout.on("regionresized", this.onRegionResized, this);
50985         layout.on("regioncollapsed", this.onRegionCollapsed, this);
50986         layout.on("regionexpanded", this.onRegionExpanded, this);
50987     },
50988     
50989     storeState : function(){
50990         this.provider.set(this.layout.id+"-layout-state", this.state);
50991     },
50992     
50993     onRegionResized : function(region, newSize){
50994         this.state[region.getPosition()].size = newSize;
50995         this.storeState();
50996     },
50997     
50998     onRegionCollapsed : function(region){
50999         this.state[region.getPosition()].collapsed = true;
51000         this.storeState();
51001     },
51002     
51003     onRegionExpanded : function(region){
51004         this.state[region.getPosition()].collapsed = false;
51005         this.storeState();
51006     }
51007 };/*
51008  * Based on:
51009  * Ext JS Library 1.1.1
51010  * Copyright(c) 2006-2007, Ext JS, LLC.
51011  *
51012  * Originally Released Under LGPL - original licence link has changed is not relivant.
51013  *
51014  * Fork - LGPL
51015  * <script type="text/javascript">
51016  */
51017 /**
51018  * @class Roo.ContentPanel
51019  * @extends Roo.util.Observable
51020  * A basic ContentPanel element.
51021  * @cfg {Boolean}   fitToFrame    True for this panel to adjust its size to fit when the region resizes  (defaults to false)
51022  * @cfg {Boolean}   fitContainer   When using {@link #fitToFrame} and {@link #resizeEl}, you can also fit the parent container  (defaults to false)
51023  * @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
51024  * @cfg {Boolean}   closable      True if the panel can be closed/removed
51025  * @cfg {Boolean}   background    True if the panel should not be activated when it is added (defaults to false)
51026  * @cfg {String/HTMLElement/Element} resizeEl An element to resize if {@link #fitToFrame} is true (instead of this panel's element)
51027  * @cfg {Toolbar}   toolbar       A toolbar for this panel
51028  * @cfg {Boolean} autoScroll    True to scroll overflow in this panel (use with {@link #fitToFrame})
51029  * @cfg {String} title          The title for this panel
51030  * @cfg {Array} adjustments     Values to <b>add</b> to the width/height when doing a {@link #fitToFrame} (default is [0, 0])
51031  * @cfg {String} url            Calls {@link #setUrl} with this value
51032  * @cfg {String} region         (center|north|south|east|west) which region to put this panel on (when used with xtype constructors)
51033  * @cfg {String/Object} params  When used with {@link #url}, calls {@link #setUrl} with this value
51034  * @cfg {Boolean} loadOnce      When used with {@link #url}, calls {@link #setUrl} with this value
51035  * @cfg {String}    content        Raw content to fill content panel with (uses setContent on construction.)
51036
51037  * @constructor
51038  * Create a new ContentPanel.
51039  * @param {String/HTMLElement/Roo.Element} el The container element for this panel
51040  * @param {String/Object} config A string to set only the title or a config object
51041  * @param {String} content (optional) Set the HTML content for this panel
51042  * @param {String} region (optional) Used by xtype constructors to add to regions. (values center,east,west,south,north)
51043  */
51044 Roo.ContentPanel = function(el, config, content){
51045     
51046      
51047     /*
51048     if(el.autoCreate || el.xtype){ // xtype is available if this is called from factory
51049         config = el;
51050         el = Roo.id();
51051     }
51052     if (config && config.parentLayout) { 
51053         el = config.parentLayout.el.createChild(); 
51054     }
51055     */
51056     if(el.autoCreate){ // xtype is available if this is called from factory
51057         config = el;
51058         el = Roo.id();
51059     }
51060     this.el = Roo.get(el);
51061     if(!this.el && config && config.autoCreate){
51062         if(typeof config.autoCreate == "object"){
51063             if(!config.autoCreate.id){
51064                 config.autoCreate.id = config.id||el;
51065             }
51066             this.el = Roo.DomHelper.append(document.body,
51067                         config.autoCreate, true);
51068         }else{
51069             this.el = Roo.DomHelper.append(document.body,
51070                         {tag: "div", cls: "x-layout-inactive-content", id: config.id||el}, true);
51071         }
51072     }
51073     this.closable = false;
51074     this.loaded = false;
51075     this.active = false;
51076     if(typeof config == "string"){
51077         this.title = config;
51078     }else{
51079         Roo.apply(this, config);
51080     }
51081     
51082     if (this.toolbar && !this.toolbar.el && this.toolbar.xtype) {
51083         this.wrapEl = this.el.wrap();
51084         this.toolbar.container = this.el.insertSibling(false, 'before');
51085         this.toolbar = new Roo.Toolbar(this.toolbar);
51086     }
51087     
51088     // xtype created footer. - not sure if will work as we normally have to render first..
51089     if (this.footer && !this.footer.el && this.footer.xtype) {
51090         if (!this.wrapEl) {
51091             this.wrapEl = this.el.wrap();
51092         }
51093     
51094         this.footer.container = this.wrapEl.createChild();
51095          
51096         this.footer = Roo.factory(this.footer, Roo);
51097         
51098     }
51099     
51100     if(this.resizeEl){
51101         this.resizeEl = Roo.get(this.resizeEl, true);
51102     }else{
51103         this.resizeEl = this.el;
51104     }
51105     // handle view.xtype
51106     
51107  
51108     
51109     
51110     this.addEvents({
51111         /**
51112          * @event activate
51113          * Fires when this panel is activated. 
51114          * @param {Roo.ContentPanel} this
51115          */
51116         "activate" : true,
51117         /**
51118          * @event deactivate
51119          * Fires when this panel is activated. 
51120          * @param {Roo.ContentPanel} this
51121          */
51122         "deactivate" : true,
51123
51124         /**
51125          * @event resize
51126          * Fires when this panel is resized if fitToFrame is true.
51127          * @param {Roo.ContentPanel} this
51128          * @param {Number} width The width after any component adjustments
51129          * @param {Number} height The height after any component adjustments
51130          */
51131         "resize" : true,
51132         
51133          /**
51134          * @event render
51135          * Fires when this tab is created
51136          * @param {Roo.ContentPanel} this
51137          */
51138         "render" : true
51139         
51140         
51141         
51142     });
51143     
51144
51145     
51146     
51147     if(this.autoScroll){
51148         this.resizeEl.setStyle("overflow", "auto");
51149     } else {
51150         // fix randome scrolling
51151         this.el.on('scroll', function() {
51152             Roo.log('fix random scolling');
51153             this.scrollTo('top',0); 
51154         });
51155     }
51156     content = content || this.content;
51157     if(content){
51158         this.setContent(content);
51159     }
51160     if(config && config.url){
51161         this.setUrl(this.url, this.params, this.loadOnce);
51162     }
51163     
51164     
51165     
51166     Roo.ContentPanel.superclass.constructor.call(this);
51167     
51168     if (this.view && typeof(this.view.xtype) != 'undefined') {
51169         this.view.el = this.el.appendChild(document.createElement("div"));
51170         this.view = Roo.factory(this.view); 
51171         this.view.render  &&  this.view.render(false, '');  
51172     }
51173     
51174     
51175     this.fireEvent('render', this);
51176 };
51177
51178 Roo.extend(Roo.ContentPanel, Roo.util.Observable, {
51179     tabTip:'',
51180     setRegion : function(region){
51181         this.region = region;
51182         if(region){
51183            this.el.replaceClass("x-layout-inactive-content", "x-layout-active-content");
51184         }else{
51185            this.el.replaceClass("x-layout-active-content", "x-layout-inactive-content");
51186         } 
51187     },
51188     
51189     /**
51190      * Returns the toolbar for this Panel if one was configured. 
51191      * @return {Roo.Toolbar} 
51192      */
51193     getToolbar : function(){
51194         return this.toolbar;
51195     },
51196     
51197     setActiveState : function(active){
51198         this.active = active;
51199         if(!active){
51200             this.fireEvent("deactivate", this);
51201         }else{
51202             this.fireEvent("activate", this);
51203         }
51204     },
51205     /**
51206      * Updates this panel's element
51207      * @param {String} content The new content
51208      * @param {Boolean} loadScripts (optional) true to look for and process scripts
51209     */
51210     setContent : function(content, loadScripts){
51211         this.el.update(content, loadScripts);
51212     },
51213
51214     ignoreResize : function(w, h){
51215         if(this.lastSize && this.lastSize.width == w && this.lastSize.height == h){
51216             return true;
51217         }else{
51218             this.lastSize = {width: w, height: h};
51219             return false;
51220         }
51221     },
51222     /**
51223      * Get the {@link Roo.UpdateManager} for this panel. Enables you to perform Ajax updates.
51224      * @return {Roo.UpdateManager} The UpdateManager
51225      */
51226     getUpdateManager : function(){
51227         return this.el.getUpdateManager();
51228     },
51229      /**
51230      * Loads this content panel immediately with content from XHR. Note: to delay loading until the panel is activated, use {@link #setUrl}.
51231      * @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:
51232 <pre><code>
51233 panel.load({
51234     url: "your-url.php",
51235     params: {param1: "foo", param2: "bar"}, // or a URL encoded string
51236     callback: yourFunction,
51237     scope: yourObject, //(optional scope)
51238     discardUrl: false,
51239     nocache: false,
51240     text: "Loading...",
51241     timeout: 30,
51242     scripts: false
51243 });
51244 </code></pre>
51245      * The only required property is <i>url</i>. The optional properties <i>nocache</i>, <i>text</i> and <i>scripts</i>
51246      * 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.
51247      * @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}
51248      * @param {Function} callback (optional) Callback when transaction is complete -- called with signature (oElement, bSuccess, oResponse)
51249      * @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.
51250      * @return {Roo.ContentPanel} this
51251      */
51252     load : function(){
51253         var um = this.el.getUpdateManager();
51254         um.update.apply(um, arguments);
51255         return this;
51256     },
51257
51258
51259     /**
51260      * 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.
51261      * @param {String/Function} url The URL to load the content from or a function to call to get the URL
51262      * @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)
51263      * @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)
51264      * @return {Roo.UpdateManager} The UpdateManager
51265      */
51266     setUrl : function(url, params, loadOnce){
51267         if(this.refreshDelegate){
51268             this.removeListener("activate", this.refreshDelegate);
51269         }
51270         this.refreshDelegate = this._handleRefresh.createDelegate(this, [url, params, loadOnce]);
51271         this.on("activate", this.refreshDelegate);
51272         return this.el.getUpdateManager();
51273     },
51274     
51275     _handleRefresh : function(url, params, loadOnce){
51276         if(!loadOnce || !this.loaded){
51277             var updater = this.el.getUpdateManager();
51278             updater.update(url, params, this._setLoaded.createDelegate(this));
51279         }
51280     },
51281     
51282     _setLoaded : function(){
51283         this.loaded = true;
51284     }, 
51285     
51286     /**
51287      * Returns this panel's id
51288      * @return {String} 
51289      */
51290     getId : function(){
51291         return this.el.id;
51292     },
51293     
51294     /** 
51295      * Returns this panel's element - used by regiosn to add.
51296      * @return {Roo.Element} 
51297      */
51298     getEl : function(){
51299         return this.wrapEl || this.el;
51300     },
51301     
51302     adjustForComponents : function(width, height)
51303     {
51304         //Roo.log('adjustForComponents ');
51305         if(this.resizeEl != this.el){
51306             width -= this.el.getFrameWidth('lr');
51307             height -= this.el.getFrameWidth('tb');
51308         }
51309         if(this.toolbar){
51310             var te = this.toolbar.getEl();
51311             height -= te.getHeight();
51312             te.setWidth(width);
51313         }
51314         if(this.footer){
51315             var te = this.footer.getEl();
51316             Roo.log("footer:" + te.getHeight());
51317             
51318             height -= te.getHeight();
51319             te.setWidth(width);
51320         }
51321         
51322         
51323         if(this.adjustments){
51324             width += this.adjustments[0];
51325             height += this.adjustments[1];
51326         }
51327         return {"width": width, "height": height};
51328     },
51329     
51330     setSize : function(width, height){
51331         if(this.fitToFrame && !this.ignoreResize(width, height)){
51332             if(this.fitContainer && this.resizeEl != this.el){
51333                 this.el.setSize(width, height);
51334             }
51335             var size = this.adjustForComponents(width, height);
51336             this.resizeEl.setSize(this.autoWidth ? "auto" : size.width, this.autoHeight ? "auto" : size.height);
51337             this.fireEvent('resize', this, size.width, size.height);
51338         }
51339     },
51340     
51341     /**
51342      * Returns this panel's title
51343      * @return {String} 
51344      */
51345     getTitle : function(){
51346         return this.title;
51347     },
51348     
51349     /**
51350      * Set this panel's title
51351      * @param {String} title
51352      */
51353     setTitle : function(title){
51354         this.title = title;
51355         if(this.region){
51356             this.region.updatePanelTitle(this, title);
51357         }
51358     },
51359     
51360     /**
51361      * Returns true is this panel was configured to be closable
51362      * @return {Boolean} 
51363      */
51364     isClosable : function(){
51365         return this.closable;
51366     },
51367     
51368     beforeSlide : function(){
51369         this.el.clip();
51370         this.resizeEl.clip();
51371     },
51372     
51373     afterSlide : function(){
51374         this.el.unclip();
51375         this.resizeEl.unclip();
51376     },
51377     
51378     /**
51379      *   Force a content refresh from the URL specified in the {@link #setUrl} method.
51380      *   Will fail silently if the {@link #setUrl} method has not been called.
51381      *   This does not activate the panel, just updates its content.
51382      */
51383     refresh : function(){
51384         if(this.refreshDelegate){
51385            this.loaded = false;
51386            this.refreshDelegate();
51387         }
51388     },
51389     
51390     /**
51391      * Destroys this panel
51392      */
51393     destroy : function(){
51394         this.el.removeAllListeners();
51395         var tempEl = document.createElement("span");
51396         tempEl.appendChild(this.el.dom);
51397         tempEl.innerHTML = "";
51398         this.el.remove();
51399         this.el = null;
51400     },
51401     
51402     /**
51403      * form - if the content panel contains a form - this is a reference to it.
51404      * @type {Roo.form.Form}
51405      */
51406     form : false,
51407     /**
51408      * view - if the content panel contains a view (Roo.DatePicker / Roo.View / Roo.JsonView)
51409      *    This contains a reference to it.
51410      * @type {Roo.View}
51411      */
51412     view : false,
51413     
51414       /**
51415      * Adds a xtype elements to the panel - currently only supports Forms, View, JsonView.
51416      * <pre><code>
51417
51418 layout.addxtype({
51419        xtype : 'Form',
51420        items: [ .... ]
51421    }
51422 );
51423
51424 </code></pre>
51425      * @param {Object} cfg Xtype definition of item to add.
51426      */
51427     
51428     addxtype : function(cfg) {
51429         // add form..
51430         if (cfg.xtype.match(/^Form$/)) {
51431             
51432             var el;
51433             //if (this.footer) {
51434             //    el = this.footer.container.insertSibling(false, 'before');
51435             //} else {
51436                 el = this.el.createChild();
51437             //}
51438
51439             this.form = new  Roo.form.Form(cfg);
51440             
51441             
51442             if ( this.form.allItems.length) this.form.render(el.dom);
51443             return this.form;
51444         }
51445         // should only have one of theses..
51446         if ([ 'View', 'JsonView', 'DatePicker'].indexOf(cfg.xtype) > -1) {
51447             // views.. should not be just added - used named prop 'view''
51448             
51449             cfg.el = this.el.appendChild(document.createElement("div"));
51450             // factory?
51451             
51452             var ret = new Roo.factory(cfg);
51453              
51454              ret.render && ret.render(false, ''); // render blank..
51455             this.view = ret;
51456             return ret;
51457         }
51458         return false;
51459     }
51460 });
51461
51462 /**
51463  * @class Roo.GridPanel
51464  * @extends Roo.ContentPanel
51465  * @constructor
51466  * Create a new GridPanel.
51467  * @param {Roo.grid.Grid} grid The grid for this panel
51468  * @param {String/Object} config A string to set only the panel's title, or a config object
51469  */
51470 Roo.GridPanel = function(grid, config){
51471     
51472   
51473     this.wrapper = Roo.DomHelper.append(document.body, // wrapper for IE7 strict & safari scroll issue
51474         {tag: "div", cls: "x-layout-grid-wrapper x-layout-inactive-content"}, true);
51475         
51476     this.wrapper.dom.appendChild(grid.getGridEl().dom);
51477     
51478     Roo.GridPanel.superclass.constructor.call(this, this.wrapper, config);
51479     
51480     if(this.toolbar){
51481         this.toolbar.el.insertBefore(this.wrapper.dom.firstChild);
51482     }
51483     // xtype created footer. - not sure if will work as we normally have to render first..
51484     if (this.footer && !this.footer.el && this.footer.xtype) {
51485         
51486         this.footer.container = this.grid.getView().getFooterPanel(true);
51487         this.footer.dataSource = this.grid.dataSource;
51488         this.footer = Roo.factory(this.footer, Roo);
51489         
51490     }
51491     
51492     grid.monitorWindowResize = false; // turn off autosizing
51493     grid.autoHeight = false;
51494     grid.autoWidth = false;
51495     this.grid = grid;
51496     this.grid.getGridEl().replaceClass("x-layout-inactive-content", "x-layout-component-panel");
51497 };
51498
51499 Roo.extend(Roo.GridPanel, Roo.ContentPanel, {
51500     getId : function(){
51501         return this.grid.id;
51502     },
51503     
51504     /**
51505      * Returns the grid for this panel
51506      * @return {Roo.grid.Grid} 
51507      */
51508     getGrid : function(){
51509         return this.grid;    
51510     },
51511     
51512     setSize : function(width, height){
51513         if(!this.ignoreResize(width, height)){
51514             var grid = this.grid;
51515             var size = this.adjustForComponents(width, height);
51516             grid.getGridEl().setSize(size.width, size.height);
51517             grid.autoSize();
51518         }
51519     },
51520     
51521     beforeSlide : function(){
51522         this.grid.getView().scroller.clip();
51523     },
51524     
51525     afterSlide : function(){
51526         this.grid.getView().scroller.unclip();
51527     },
51528     
51529     destroy : function(){
51530         this.grid.destroy();
51531         delete this.grid;
51532         Roo.GridPanel.superclass.destroy.call(this); 
51533     }
51534 });
51535
51536
51537 /**
51538  * @class Roo.NestedLayoutPanel
51539  * @extends Roo.ContentPanel
51540  * @constructor
51541  * Create a new NestedLayoutPanel.
51542  * 
51543  * 
51544  * @param {Roo.BorderLayout} layout The layout for this panel
51545  * @param {String/Object} config A string to set only the title or a config object
51546  */
51547 Roo.NestedLayoutPanel = function(layout, config)
51548 {
51549     // construct with only one argument..
51550     /* FIXME - implement nicer consturctors
51551     if (layout.layout) {
51552         config = layout;
51553         layout = config.layout;
51554         delete config.layout;
51555     }
51556     if (layout.xtype && !layout.getEl) {
51557         // then layout needs constructing..
51558         layout = Roo.factory(layout, Roo);
51559     }
51560     */
51561     
51562     
51563     Roo.NestedLayoutPanel.superclass.constructor.call(this, layout.getEl(), config);
51564     
51565     layout.monitorWindowResize = false; // turn off autosizing
51566     this.layout = layout;
51567     this.layout.getEl().addClass("x-layout-nested-layout");
51568     
51569     
51570     
51571     
51572 };
51573
51574 Roo.extend(Roo.NestedLayoutPanel, Roo.ContentPanel, {
51575
51576     setSize : function(width, height){
51577         if(!this.ignoreResize(width, height)){
51578             var size = this.adjustForComponents(width, height);
51579             var el = this.layout.getEl();
51580             el.setSize(size.width, size.height);
51581             var touch = el.dom.offsetWidth;
51582             this.layout.layout();
51583             // ie requires a double layout on the first pass
51584             if(Roo.isIE && !this.initialized){
51585                 this.initialized = true;
51586                 this.layout.layout();
51587             }
51588         }
51589     },
51590     
51591     // activate all subpanels if not currently active..
51592     
51593     setActiveState : function(active){
51594         this.active = active;
51595         if(!active){
51596             this.fireEvent("deactivate", this);
51597             return;
51598         }
51599         
51600         this.fireEvent("activate", this);
51601         // not sure if this should happen before or after..
51602         if (!this.layout) {
51603             return; // should not happen..
51604         }
51605         var reg = false;
51606         for (var r in this.layout.regions) {
51607             reg = this.layout.getRegion(r);
51608             if (reg.getActivePanel()) {
51609                 //reg.showPanel(reg.getActivePanel()); // force it to activate.. 
51610                 reg.setActivePanel(reg.getActivePanel());
51611                 continue;
51612             }
51613             if (!reg.panels.length) {
51614                 continue;
51615             }
51616             reg.showPanel(reg.getPanel(0));
51617         }
51618         
51619         
51620         
51621         
51622     },
51623     
51624     /**
51625      * Returns the nested BorderLayout for this panel
51626      * @return {Roo.BorderLayout} 
51627      */
51628     getLayout : function(){
51629         return this.layout;
51630     },
51631     
51632      /**
51633      * Adds a xtype elements to the layout of the nested panel
51634      * <pre><code>
51635
51636 panel.addxtype({
51637        xtype : 'ContentPanel',
51638        region: 'west',
51639        items: [ .... ]
51640    }
51641 );
51642
51643 panel.addxtype({
51644         xtype : 'NestedLayoutPanel',
51645         region: 'west',
51646         layout: {
51647            center: { },
51648            west: { }   
51649         },
51650         items : [ ... list of content panels or nested layout panels.. ]
51651    }
51652 );
51653 </code></pre>
51654      * @param {Object} cfg Xtype definition of item to add.
51655      */
51656     addxtype : function(cfg) {
51657         return this.layout.addxtype(cfg);
51658     
51659     }
51660 });
51661
51662 Roo.ScrollPanel = function(el, config, content){
51663     config = config || {};
51664     config.fitToFrame = true;
51665     Roo.ScrollPanel.superclass.constructor.call(this, el, config, content);
51666     
51667     this.el.dom.style.overflow = "hidden";
51668     var wrap = this.el.wrap({cls: "x-scroller x-layout-inactive-content"});
51669     this.el.removeClass("x-layout-inactive-content");
51670     this.el.on("mousewheel", this.onWheel, this);
51671
51672     var up = wrap.createChild({cls: "x-scroller-up", html: "&#160;"}, this.el.dom);
51673     var down = wrap.createChild({cls: "x-scroller-down", html: "&#160;"});
51674     up.unselectable(); down.unselectable();
51675     up.on("click", this.scrollUp, this);
51676     down.on("click", this.scrollDown, this);
51677     up.addClassOnOver("x-scroller-btn-over");
51678     down.addClassOnOver("x-scroller-btn-over");
51679     up.addClassOnClick("x-scroller-btn-click");
51680     down.addClassOnClick("x-scroller-btn-click");
51681     this.adjustments = [0, -(up.getHeight() + down.getHeight())];
51682
51683     this.resizeEl = this.el;
51684     this.el = wrap; this.up = up; this.down = down;
51685 };
51686
51687 Roo.extend(Roo.ScrollPanel, Roo.ContentPanel, {
51688     increment : 100,
51689     wheelIncrement : 5,
51690     scrollUp : function(){
51691         this.resizeEl.scroll("up", this.increment, {callback: this.afterScroll, scope: this});
51692     },
51693
51694     scrollDown : function(){
51695         this.resizeEl.scroll("down", this.increment, {callback: this.afterScroll, scope: this});
51696     },
51697
51698     afterScroll : function(){
51699         var el = this.resizeEl;
51700         var t = el.dom.scrollTop, h = el.dom.scrollHeight, ch = el.dom.clientHeight;
51701         this.up[t == 0 ? "addClass" : "removeClass"]("x-scroller-btn-disabled");
51702         this.down[h - t <= ch ? "addClass" : "removeClass"]("x-scroller-btn-disabled");
51703     },
51704
51705     setSize : function(){
51706         Roo.ScrollPanel.superclass.setSize.apply(this, arguments);
51707         this.afterScroll();
51708     },
51709
51710     onWheel : function(e){
51711         var d = e.getWheelDelta();
51712         this.resizeEl.dom.scrollTop -= (d*this.wheelIncrement);
51713         this.afterScroll();
51714         e.stopEvent();
51715     },
51716
51717     setContent : function(content, loadScripts){
51718         this.resizeEl.update(content, loadScripts);
51719     }
51720
51721 });
51722
51723
51724
51725
51726
51727
51728
51729
51730
51731 /**
51732  * @class Roo.TreePanel
51733  * @extends Roo.ContentPanel
51734  * @constructor
51735  * Create a new TreePanel. - defaults to fit/scoll contents.
51736  * @param {String/Object} config A string to set only the panel's title, or a config object
51737  * @cfg {Roo.tree.TreePanel} tree The tree TreePanel, with config etc.
51738  */
51739 Roo.TreePanel = function(config){
51740     var el = config.el;
51741     var tree = config.tree;
51742     delete config.tree; 
51743     delete config.el; // hopefull!
51744     
51745     // wrapper for IE7 strict & safari scroll issue
51746     
51747     var treeEl = el.createChild();
51748     config.resizeEl = treeEl;
51749     
51750     
51751     
51752     Roo.TreePanel.superclass.constructor.call(this, el, config);
51753  
51754  
51755     this.tree = new Roo.tree.TreePanel(treeEl , tree);
51756     //console.log(tree);
51757     this.on('activate', function()
51758     {
51759         if (this.tree.rendered) {
51760             return;
51761         }
51762         //console.log('render tree');
51763         this.tree.render();
51764     });
51765     // this should not be needed.. - it's actually the 'el' that resizes?
51766     // actuall it breaks the containerScroll - dragging nodes auto scroll at top
51767     
51768     //this.on('resize',  function (cp, w, h) {
51769     //        this.tree.innerCt.setWidth(w);
51770     //        this.tree.innerCt.setHeight(h);
51771     //        //this.tree.innerCt.setStyle('overflow-y', 'auto');
51772     //});
51773
51774         
51775     
51776 };
51777
51778 Roo.extend(Roo.TreePanel, Roo.ContentPanel, {   
51779     fitToFrame : true,
51780     autoScroll : true
51781 });
51782
51783
51784
51785
51786
51787
51788
51789
51790
51791
51792
51793 /*
51794  * Based on:
51795  * Ext JS Library 1.1.1
51796  * Copyright(c) 2006-2007, Ext JS, LLC.
51797  *
51798  * Originally Released Under LGPL - original licence link has changed is not relivant.
51799  *
51800  * Fork - LGPL
51801  * <script type="text/javascript">
51802  */
51803  
51804
51805 /**
51806  * @class Roo.ReaderLayout
51807  * @extends Roo.BorderLayout
51808  * This is a pre-built layout that represents a classic, 5-pane application.  It consists of a header, a primary
51809  * center region containing two nested regions (a top one for a list view and one for item preview below),
51810  * and regions on either side that can be used for navigation, application commands, informational displays, etc.
51811  * The setup and configuration work exactly the same as it does for a {@link Roo.BorderLayout} - this class simply
51812  * expedites the setup of the overall layout and regions for this common application style.
51813  * Example:
51814  <pre><code>
51815 var reader = new Roo.ReaderLayout();
51816 var CP = Roo.ContentPanel;  // shortcut for adding
51817
51818 reader.beginUpdate();
51819 reader.add("north", new CP("north", "North"));
51820 reader.add("west", new CP("west", {title: "West"}));
51821 reader.add("east", new CP("east", {title: "East"}));
51822
51823 reader.regions.listView.add(new CP("listView", "List"));
51824 reader.regions.preview.add(new CP("preview", "Preview"));
51825 reader.endUpdate();
51826 </code></pre>
51827 * @constructor
51828 * Create a new ReaderLayout
51829 * @param {Object} config Configuration options
51830 * @param {String/HTMLElement/Element} container (optional) The container this layout is bound to (defaults to
51831 * document.body if omitted)
51832 */
51833 Roo.ReaderLayout = function(config, renderTo){
51834     var c = config || {size:{}};
51835     Roo.ReaderLayout.superclass.constructor.call(this, renderTo || document.body, {
51836         north: c.north !== false ? Roo.apply({
51837             split:false,
51838             initialSize: 32,
51839             titlebar: false
51840         }, c.north) : false,
51841         west: c.west !== false ? Roo.apply({
51842             split:true,
51843             initialSize: 200,
51844             minSize: 175,
51845             maxSize: 400,
51846             titlebar: true,
51847             collapsible: true,
51848             animate: true,
51849             margins:{left:5,right:0,bottom:5,top:5},
51850             cmargins:{left:5,right:5,bottom:5,top:5}
51851         }, c.west) : false,
51852         east: c.east !== false ? Roo.apply({
51853             split:true,
51854             initialSize: 200,
51855             minSize: 175,
51856             maxSize: 400,
51857             titlebar: true,
51858             collapsible: true,
51859             animate: true,
51860             margins:{left:0,right:5,bottom:5,top:5},
51861             cmargins:{left:5,right:5,bottom:5,top:5}
51862         }, c.east) : false,
51863         center: Roo.apply({
51864             tabPosition: 'top',
51865             autoScroll:false,
51866             closeOnTab: true,
51867             titlebar:false,
51868             margins:{left:c.west!==false ? 0 : 5,right:c.east!==false ? 0 : 5,bottom:5,top:2}
51869         }, c.center)
51870     });
51871
51872     this.el.addClass('x-reader');
51873
51874     this.beginUpdate();
51875
51876     var inner = new Roo.BorderLayout(Roo.get(document.body).createChild(), {
51877         south: c.preview !== false ? Roo.apply({
51878             split:true,
51879             initialSize: 200,
51880             minSize: 100,
51881             autoScroll:true,
51882             collapsible:true,
51883             titlebar: true,
51884             cmargins:{top:5,left:0, right:0, bottom:0}
51885         }, c.preview) : false,
51886         center: Roo.apply({
51887             autoScroll:false,
51888             titlebar:false,
51889             minHeight:200
51890         }, c.listView)
51891     });
51892     this.add('center', new Roo.NestedLayoutPanel(inner,
51893             Roo.apply({title: c.mainTitle || '',tabTip:''},c.innerPanelCfg)));
51894
51895     this.endUpdate();
51896
51897     this.regions.preview = inner.getRegion('south');
51898     this.regions.listView = inner.getRegion('center');
51899 };
51900
51901 Roo.extend(Roo.ReaderLayout, Roo.BorderLayout);/*
51902  * Based on:
51903  * Ext JS Library 1.1.1
51904  * Copyright(c) 2006-2007, Ext JS, LLC.
51905  *
51906  * Originally Released Under LGPL - original licence link has changed is not relivant.
51907  *
51908  * Fork - LGPL
51909  * <script type="text/javascript">
51910  */
51911  
51912 /**
51913  * @class Roo.grid.Grid
51914  * @extends Roo.util.Observable
51915  * This class represents the primary interface of a component based grid control.
51916  * <br><br>Usage:<pre><code>
51917  var grid = new Roo.grid.Grid("my-container-id", {
51918      ds: myDataStore,
51919      cm: myColModel,
51920      selModel: mySelectionModel,
51921      autoSizeColumns: true,
51922      monitorWindowResize: false,
51923      trackMouseOver: true
51924  });
51925  // set any options
51926  grid.render();
51927  * </code></pre>
51928  * <b>Common Problems:</b><br/>
51929  * - Grid does not resize properly when going smaller: Setting overflow hidden on the container
51930  * element will correct this<br/>
51931  * - If you get el.style[camel]= NaNpx or -2px or something related, be certain you have given your container element
51932  * dimensions. The grid adapts to your container's size, if your container has no size defined then the results
51933  * are unpredictable.<br/>
51934  * - Do not render the grid into an element with display:none. Try using visibility:hidden. Otherwise there is no way for the
51935  * grid to calculate dimensions/offsets.<br/>
51936   * @constructor
51937  * @param {String/HTMLElement/Roo.Element} container The element into which this grid will be rendered -
51938  * The container MUST have some type of size defined for the grid to fill. The container will be
51939  * automatically set to position relative if it isn't already.
51940  * @param {Object} config A config object that sets properties on this grid.
51941  */
51942 Roo.grid.Grid = function(container, config){
51943         // initialize the container
51944         this.container = Roo.get(container);
51945         this.container.update("");
51946         this.container.setStyle("overflow", "hidden");
51947     this.container.addClass('x-grid-container');
51948
51949     this.id = this.container.id;
51950
51951     Roo.apply(this, config);
51952     // check and correct shorthanded configs
51953     if(this.ds){
51954         this.dataSource = this.ds;
51955         delete this.ds;
51956     }
51957     if(this.cm){
51958         this.colModel = this.cm;
51959         delete this.cm;
51960     }
51961     if(this.sm){
51962         this.selModel = this.sm;
51963         delete this.sm;
51964     }
51965
51966     if (this.selModel) {
51967         this.selModel = Roo.factory(this.selModel, Roo.grid);
51968         this.sm = this.selModel;
51969         this.sm.xmodule = this.xmodule || false;
51970     }
51971     if (typeof(this.colModel.config) == 'undefined') {
51972         this.colModel = new Roo.grid.ColumnModel(this.colModel);
51973         this.cm = this.colModel;
51974         this.cm.xmodule = this.xmodule || false;
51975     }
51976     if (this.dataSource) {
51977         this.dataSource= Roo.factory(this.dataSource, Roo.data);
51978         this.ds = this.dataSource;
51979         this.ds.xmodule = this.xmodule || false;
51980          
51981     }
51982     
51983     
51984     
51985     if(this.width){
51986         this.container.setWidth(this.width);
51987     }
51988
51989     if(this.height){
51990         this.container.setHeight(this.height);
51991     }
51992     /** @private */
51993         this.addEvents({
51994         // raw events
51995         /**
51996          * @event click
51997          * The raw click event for the entire grid.
51998          * @param {Roo.EventObject} e
51999          */
52000         "click" : true,
52001         /**
52002          * @event dblclick
52003          * The raw dblclick event for the entire grid.
52004          * @param {Roo.EventObject} e
52005          */
52006         "dblclick" : true,
52007         /**
52008          * @event contextmenu
52009          * The raw contextmenu event for the entire grid.
52010          * @param {Roo.EventObject} e
52011          */
52012         "contextmenu" : true,
52013         /**
52014          * @event mousedown
52015          * The raw mousedown event for the entire grid.
52016          * @param {Roo.EventObject} e
52017          */
52018         "mousedown" : true,
52019         /**
52020          * @event mouseup
52021          * The raw mouseup event for the entire grid.
52022          * @param {Roo.EventObject} e
52023          */
52024         "mouseup" : true,
52025         /**
52026          * @event mouseover
52027          * The raw mouseover event for the entire grid.
52028          * @param {Roo.EventObject} e
52029          */
52030         "mouseover" : true,
52031         /**
52032          * @event mouseout
52033          * The raw mouseout event for the entire grid.
52034          * @param {Roo.EventObject} e
52035          */
52036         "mouseout" : true,
52037         /**
52038          * @event keypress
52039          * The raw keypress event for the entire grid.
52040          * @param {Roo.EventObject} e
52041          */
52042         "keypress" : true,
52043         /**
52044          * @event keydown
52045          * The raw keydown event for the entire grid.
52046          * @param {Roo.EventObject} e
52047          */
52048         "keydown" : true,
52049
52050         // custom events
52051
52052         /**
52053          * @event cellclick
52054          * Fires when a cell is clicked
52055          * @param {Grid} this
52056          * @param {Number} rowIndex
52057          * @param {Number} columnIndex
52058          * @param {Roo.EventObject} e
52059          */
52060         "cellclick" : true,
52061         /**
52062          * @event celldblclick
52063          * Fires when a cell is double clicked
52064          * @param {Grid} this
52065          * @param {Number} rowIndex
52066          * @param {Number} columnIndex
52067          * @param {Roo.EventObject} e
52068          */
52069         "celldblclick" : true,
52070         /**
52071          * @event rowclick
52072          * Fires when a row is clicked
52073          * @param {Grid} this
52074          * @param {Number} rowIndex
52075          * @param {Roo.EventObject} e
52076          */
52077         "rowclick" : true,
52078         /**
52079          * @event rowdblclick
52080          * Fires when a row is double clicked
52081          * @param {Grid} this
52082          * @param {Number} rowIndex
52083          * @param {Roo.EventObject} e
52084          */
52085         "rowdblclick" : true,
52086         /**
52087          * @event headerclick
52088          * Fires when a header is clicked
52089          * @param {Grid} this
52090          * @param {Number} columnIndex
52091          * @param {Roo.EventObject} e
52092          */
52093         "headerclick" : true,
52094         /**
52095          * @event headerdblclick
52096          * Fires when a header cell is double clicked
52097          * @param {Grid} this
52098          * @param {Number} columnIndex
52099          * @param {Roo.EventObject} e
52100          */
52101         "headerdblclick" : true,
52102         /**
52103          * @event rowcontextmenu
52104          * Fires when a row is right clicked
52105          * @param {Grid} this
52106          * @param {Number} rowIndex
52107          * @param {Roo.EventObject} e
52108          */
52109         "rowcontextmenu" : true,
52110         /**
52111          * @event cellcontextmenu
52112          * Fires when a cell is right clicked
52113          * @param {Grid} this
52114          * @param {Number} rowIndex
52115          * @param {Number} cellIndex
52116          * @param {Roo.EventObject} e
52117          */
52118          "cellcontextmenu" : true,
52119         /**
52120          * @event headercontextmenu
52121          * Fires when a header is right clicked
52122          * @param {Grid} this
52123          * @param {Number} columnIndex
52124          * @param {Roo.EventObject} e
52125          */
52126         "headercontextmenu" : true,
52127         /**
52128          * @event bodyscroll
52129          * Fires when the body element is scrolled
52130          * @param {Number} scrollLeft
52131          * @param {Number} scrollTop
52132          */
52133         "bodyscroll" : true,
52134         /**
52135          * @event columnresize
52136          * Fires when the user resizes a column
52137          * @param {Number} columnIndex
52138          * @param {Number} newSize
52139          */
52140         "columnresize" : true,
52141         /**
52142          * @event columnmove
52143          * Fires when the user moves a column
52144          * @param {Number} oldIndex
52145          * @param {Number} newIndex
52146          */
52147         "columnmove" : true,
52148         /**
52149          * @event startdrag
52150          * Fires when row(s) start being dragged
52151          * @param {Grid} this
52152          * @param {Roo.GridDD} dd The drag drop object
52153          * @param {event} e The raw browser event
52154          */
52155         "startdrag" : true,
52156         /**
52157          * @event enddrag
52158          * Fires when a drag operation is complete
52159          * @param {Grid} this
52160          * @param {Roo.GridDD} dd The drag drop object
52161          * @param {event} e The raw browser event
52162          */
52163         "enddrag" : true,
52164         /**
52165          * @event dragdrop
52166          * Fires when dragged row(s) are dropped on a valid DD target
52167          * @param {Grid} this
52168          * @param {Roo.GridDD} dd The drag drop object
52169          * @param {String} targetId The target drag drop object
52170          * @param {event} e The raw browser event
52171          */
52172         "dragdrop" : true,
52173         /**
52174          * @event dragover
52175          * Fires while row(s) are being dragged. "targetId" is the id of the Yahoo.util.DD object the selected rows are being dragged over.
52176          * @param {Grid} this
52177          * @param {Roo.GridDD} dd The drag drop object
52178          * @param {String} targetId The target drag drop object
52179          * @param {event} e The raw browser event
52180          */
52181         "dragover" : true,
52182         /**
52183          * @event dragenter
52184          *  Fires when the dragged row(s) first cross another DD target while being dragged
52185          * @param {Grid} this
52186          * @param {Roo.GridDD} dd The drag drop object
52187          * @param {String} targetId The target drag drop object
52188          * @param {event} e The raw browser event
52189          */
52190         "dragenter" : true,
52191         /**
52192          * @event dragout
52193          * Fires when the dragged row(s) leave another DD target while being dragged
52194          * @param {Grid} this
52195          * @param {Roo.GridDD} dd The drag drop object
52196          * @param {String} targetId The target drag drop object
52197          * @param {event} e The raw browser event
52198          */
52199         "dragout" : true,
52200         /**
52201          * @event rowclass
52202          * Fires when a row is rendered, so you can change add a style to it.
52203          * @param {GridView} gridview   The grid view
52204          * @param {Object} rowcfg   contains record  rowIndex and rowClass - set rowClass to add a style.
52205          */
52206         'rowclass' : true,
52207
52208         /**
52209          * @event render
52210          * Fires when the grid is rendered
52211          * @param {Grid} grid
52212          */
52213         'render' : true
52214     });
52215
52216     Roo.grid.Grid.superclass.constructor.call(this);
52217 };
52218 Roo.extend(Roo.grid.Grid, Roo.util.Observable, {
52219     
52220     /**
52221      * @cfg {String} ddGroup - drag drop group.
52222      */
52223
52224     /**
52225      * @cfg {Number} minColumnWidth The minimum width a column can be resized to. Default is 25.
52226      */
52227     minColumnWidth : 25,
52228
52229     /**
52230      * @cfg {Boolean} autoSizeColumns True to automatically resize the columns to fit their content
52231      * <b>on initial render.</b> It is more efficient to explicitly size the columns
52232      * through the ColumnModel's {@link Roo.grid.ColumnModel#width} config option.  Default is false.
52233      */
52234     autoSizeColumns : false,
52235
52236     /**
52237      * @cfg {Boolean} autoSizeHeaders True to measure headers with column data when auto sizing columns. Default is true.
52238      */
52239     autoSizeHeaders : true,
52240
52241     /**
52242      * @cfg {Boolean} monitorWindowResize True to autoSize the grid when the window resizes. Default is true.
52243      */
52244     monitorWindowResize : true,
52245
52246     /**
52247      * @cfg {Boolean} maxRowsToMeasure If autoSizeColumns is on, maxRowsToMeasure can be used to limit the number of
52248      * rows measured to get a columns size. Default is 0 (all rows).
52249      */
52250     maxRowsToMeasure : 0,
52251
52252     /**
52253      * @cfg {Boolean} trackMouseOver True to highlight rows when the mouse is over. Default is true.
52254      */
52255     trackMouseOver : true,
52256
52257     /**
52258     * @cfg {Boolean} enableDrag  True to enable drag of rows. Default is false. (double check if this is needed?)
52259     */
52260     
52261     /**
52262     * @cfg {Boolean} enableDragDrop True to enable drag and drop of rows. Default is false.
52263     */
52264     enableDragDrop : false,
52265     
52266     /**
52267     * @cfg {Boolean} enableColumnMove True to enable drag and drop reorder of columns. Default is true.
52268     */
52269     enableColumnMove : true,
52270     
52271     /**
52272     * @cfg {Boolean} enableColumnHide True to enable hiding of columns with the header context menu. Default is true.
52273     */
52274     enableColumnHide : true,
52275     
52276     /**
52277     * @cfg {Boolean} enableRowHeightSync True to manually sync row heights across locked and not locked rows. Default is false.
52278     */
52279     enableRowHeightSync : false,
52280     
52281     /**
52282     * @cfg {Boolean} stripeRows True to stripe the rows.  Default is true.
52283     */
52284     stripeRows : true,
52285     
52286     /**
52287     * @cfg {Boolean} autoHeight True to fit the height of the grid container to the height of the data. Default is false.
52288     */
52289     autoHeight : false,
52290
52291     /**
52292      * @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.
52293      */
52294     autoExpandColumn : false,
52295
52296     /**
52297     * @cfg {Number} autoExpandMin The minimum width the autoExpandColumn can have (if enabled).
52298     * Default is 50.
52299     */
52300     autoExpandMin : 50,
52301
52302     /**
52303     * @cfg {Number} autoExpandMax The maximum width the autoExpandColumn can have (if enabled). Default is 1000.
52304     */
52305     autoExpandMax : 1000,
52306
52307     /**
52308     * @cfg {Object} view The {@link Roo.grid.GridView} used by the grid. This can be set before a call to render().
52309     */
52310     view : null,
52311
52312     /**
52313     * @cfg {Object} loadMask An {@link Roo.LoadMask} config or true to mask the grid while loading. Default is false.
52314     */
52315     loadMask : false,
52316     /**
52317     * @cfg {Roo.dd.DropTarget} dropTarget An {@link Roo.dd.DropTarget} config
52318     */
52319     dropTarget: false,
52320     
52321    
52322     
52323     // private
52324     rendered : false,
52325
52326     /**
52327     * @cfg {Boolean} autoWidth True to set the grid's width to the default total width of the grid's columns instead
52328     * of a fixed width. Default is false.
52329     */
52330     /**
52331     * @cfg {Number} maxHeight Sets the maximum height of the grid - ignored if autoHeight is not on.
52332     */
52333     /**
52334      * Called once after all setup has been completed and the grid is ready to be rendered.
52335      * @return {Roo.grid.Grid} this
52336      */
52337     render : function()
52338     {
52339         var c = this.container;
52340         // try to detect autoHeight/width mode
52341         if((!c.dom.offsetHeight || c.dom.offsetHeight < 20) || c.getStyle("height") == "auto"){
52342             this.autoHeight = true;
52343         }
52344         var view = this.getView();
52345         view.init(this);
52346
52347         c.on("click", this.onClick, this);
52348         c.on("dblclick", this.onDblClick, this);
52349         c.on("contextmenu", this.onContextMenu, this);
52350         c.on("keydown", this.onKeyDown, this);
52351         if (Roo.isTouch) {
52352             c.on("touchstart", this.onTouchStart, this);
52353         }
52354
52355         this.relayEvents(c, ["mousedown","mouseup","mouseover","mouseout","keypress"]);
52356
52357         this.getSelectionModel().init(this);
52358
52359         view.render();
52360
52361         if(this.loadMask){
52362             this.loadMask = new Roo.LoadMask(this.container,
52363                     Roo.apply({store:this.dataSource}, this.loadMask));
52364         }
52365         
52366         
52367         if (this.toolbar && this.toolbar.xtype) {
52368             this.toolbar.container = this.getView().getHeaderPanel(true);
52369             this.toolbar = new Roo.Toolbar(this.toolbar);
52370         }
52371         if (this.footer && this.footer.xtype) {
52372             this.footer.dataSource = this.getDataSource();
52373             this.footer.container = this.getView().getFooterPanel(true);
52374             this.footer = Roo.factory(this.footer, Roo);
52375         }
52376         if (this.dropTarget && this.dropTarget.xtype) {
52377             delete this.dropTarget.xtype;
52378             this.dropTarget =  new Roo.dd.DropTarget(this.getView().mainBody, this.dropTarget);
52379         }
52380         
52381         
52382         this.rendered = true;
52383         this.fireEvent('render', this);
52384         return this;
52385     },
52386
52387         /**
52388          * Reconfigures the grid to use a different Store and Column Model.
52389          * The View will be bound to the new objects and refreshed.
52390          * @param {Roo.data.Store} dataSource The new {@link Roo.data.Store} object
52391          * @param {Roo.grid.ColumnModel} The new {@link Roo.grid.ColumnModel} object
52392          */
52393     reconfigure : function(dataSource, colModel){
52394         if(this.loadMask){
52395             this.loadMask.destroy();
52396             this.loadMask = new Roo.LoadMask(this.container,
52397                     Roo.apply({store:dataSource}, this.loadMask));
52398         }
52399         this.view.bind(dataSource, colModel);
52400         this.dataSource = dataSource;
52401         this.colModel = colModel;
52402         this.view.refresh(true);
52403     },
52404
52405     // private
52406     onKeyDown : function(e){
52407         this.fireEvent("keydown", e);
52408     },
52409
52410     /**
52411      * Destroy this grid.
52412      * @param {Boolean} removeEl True to remove the element
52413      */
52414     destroy : function(removeEl, keepListeners){
52415         if(this.loadMask){
52416             this.loadMask.destroy();
52417         }
52418         var c = this.container;
52419         c.removeAllListeners();
52420         this.view.destroy();
52421         this.colModel.purgeListeners();
52422         if(!keepListeners){
52423             this.purgeListeners();
52424         }
52425         c.update("");
52426         if(removeEl === true){
52427             c.remove();
52428         }
52429     },
52430
52431     // private
52432     processEvent : function(name, e){
52433         // does this fire select???
52434         Roo.log('grid:processEvent '  + name);
52435         
52436         if (name != 'touchstart' ) {
52437             this.fireEvent(name, e);    
52438         }
52439         
52440         var t = e.getTarget();
52441         var v = this.view;
52442         var header = v.findHeaderIndex(t);
52443         if(header !== false){
52444             var ename = name == 'touchstart' ? 'click' : name;
52445              
52446             this.fireEvent("header" + ename, this, header, e);
52447         }else{
52448             var row = v.findRowIndex(t);
52449             var cell = v.findCellIndex(t);
52450             if (name == 'touchstart') {
52451                 // first touch is always a click.
52452                 // hopefull this happens after selection is updated.?
52453                 name = false;
52454                 
52455                 if (typeof(this.selModel.getSelectedCell) != 'undefined') {
52456                     var cs = this.selModel.getSelectedCell();
52457                     if (row == cs[0] && cell == cs[1]){
52458                         name = 'dblclick';
52459                     }
52460                 }
52461                 if (typeof(this.selModel.getSelections) != 'undefined') {
52462                     var cs = this.selModel.getSelections();
52463                     var ds = this.dataSource;
52464                     if (cs.length == 1 && ds.getAt(row) == cs[0]){
52465                         name = 'dblclick';
52466                     }
52467                 }
52468                 if (!name) {
52469                     return;
52470                 }
52471             }
52472             
52473             
52474             if(row !== false){
52475                 this.fireEvent("row" + name, this, row, e);
52476                 if(cell !== false){
52477                     this.fireEvent("cell" + name, this, row, cell, e);
52478                 }
52479             }
52480         }
52481     },
52482
52483     // private
52484     onClick : function(e){
52485         this.processEvent("click", e);
52486     },
52487    // private
52488     onTouchStart : function(e){
52489         this.processEvent("touchstart", e);
52490     },
52491
52492     // private
52493     onContextMenu : function(e, t){
52494         this.processEvent("contextmenu", e);
52495     },
52496
52497     // private
52498     onDblClick : function(e){
52499         this.processEvent("dblclick", e);
52500     },
52501
52502     // private
52503     walkCells : function(row, col, step, fn, scope){
52504         var cm = this.colModel, clen = cm.getColumnCount();
52505         var ds = this.dataSource, rlen = ds.getCount(), first = true;
52506         if(step < 0){
52507             if(col < 0){
52508                 row--;
52509                 first = false;
52510             }
52511             while(row >= 0){
52512                 if(!first){
52513                     col = clen-1;
52514                 }
52515                 first = false;
52516                 while(col >= 0){
52517                     if(fn.call(scope || this, row, col, cm) === true){
52518                         return [row, col];
52519                     }
52520                     col--;
52521                 }
52522                 row--;
52523             }
52524         } else {
52525             if(col >= clen){
52526                 row++;
52527                 first = false;
52528             }
52529             while(row < rlen){
52530                 if(!first){
52531                     col = 0;
52532                 }
52533                 first = false;
52534                 while(col < clen){
52535                     if(fn.call(scope || this, row, col, cm) === true){
52536                         return [row, col];
52537                     }
52538                     col++;
52539                 }
52540                 row++;
52541             }
52542         }
52543         return null;
52544     },
52545
52546     // private
52547     getSelections : function(){
52548         return this.selModel.getSelections();
52549     },
52550
52551     /**
52552      * Causes the grid to manually recalculate its dimensions. Generally this is done automatically,
52553      * but if manual update is required this method will initiate it.
52554      */
52555     autoSize : function(){
52556         if(this.rendered){
52557             this.view.layout();
52558             if(this.view.adjustForScroll){
52559                 this.view.adjustForScroll();
52560             }
52561         }
52562     },
52563
52564     /**
52565      * Returns the grid's underlying element.
52566      * @return {Element} The element
52567      */
52568     getGridEl : function(){
52569         return this.container;
52570     },
52571
52572     // private for compatibility, overridden by editor grid
52573     stopEditing : function(){},
52574
52575     /**
52576      * Returns the grid's SelectionModel.
52577      * @return {SelectionModel}
52578      */
52579     getSelectionModel : function(){
52580         if(!this.selModel){
52581             this.selModel = new Roo.grid.RowSelectionModel();
52582         }
52583         return this.selModel;
52584     },
52585
52586     /**
52587      * Returns the grid's DataSource.
52588      * @return {DataSource}
52589      */
52590     getDataSource : function(){
52591         return this.dataSource;
52592     },
52593
52594     /**
52595      * Returns the grid's ColumnModel.
52596      * @return {ColumnModel}
52597      */
52598     getColumnModel : function(){
52599         return this.colModel;
52600     },
52601
52602     /**
52603      * Returns the grid's GridView object.
52604      * @return {GridView}
52605      */
52606     getView : function(){
52607         if(!this.view){
52608             this.view = new Roo.grid.GridView(this.viewConfig);
52609         }
52610         return this.view;
52611     },
52612     /**
52613      * Called to get grid's drag proxy text, by default returns this.ddText.
52614      * @return {String}
52615      */
52616     getDragDropText : function(){
52617         var count = this.selModel.getCount();
52618         return String.format(this.ddText, count, count == 1 ? '' : 's');
52619     }
52620 });
52621 /**
52622  * Configures the text is the drag proxy (defaults to "%0 selected row(s)").
52623  * %0 is replaced with the number of selected rows.
52624  * @type String
52625  */
52626 Roo.grid.Grid.prototype.ddText = "{0} selected row{1}";/*
52627  * Based on:
52628  * Ext JS Library 1.1.1
52629  * Copyright(c) 2006-2007, Ext JS, LLC.
52630  *
52631  * Originally Released Under LGPL - original licence link has changed is not relivant.
52632  *
52633  * Fork - LGPL
52634  * <script type="text/javascript">
52635  */
52636  
52637 Roo.grid.AbstractGridView = function(){
52638         this.grid = null;
52639         
52640         this.events = {
52641             "beforerowremoved" : true,
52642             "beforerowsinserted" : true,
52643             "beforerefresh" : true,
52644             "rowremoved" : true,
52645             "rowsinserted" : true,
52646             "rowupdated" : true,
52647             "refresh" : true
52648         };
52649     Roo.grid.AbstractGridView.superclass.constructor.call(this);
52650 };
52651
52652 Roo.extend(Roo.grid.AbstractGridView, Roo.util.Observable, {
52653     rowClass : "x-grid-row",
52654     cellClass : "x-grid-cell",
52655     tdClass : "x-grid-td",
52656     hdClass : "x-grid-hd",
52657     splitClass : "x-grid-hd-split",
52658     
52659     init: function(grid){
52660         this.grid = grid;
52661                 var cid = this.grid.getGridEl().id;
52662         this.colSelector = "#" + cid + " ." + this.cellClass + "-";
52663         this.tdSelector = "#" + cid + " ." + this.tdClass + "-";
52664         this.hdSelector = "#" + cid + " ." + this.hdClass + "-";
52665         this.splitSelector = "#" + cid + " ." + this.splitClass + "-";
52666         },
52667         
52668     getColumnRenderers : function(){
52669         var renderers = [];
52670         var cm = this.grid.colModel;
52671         var colCount = cm.getColumnCount();
52672         for(var i = 0; i < colCount; i++){
52673             renderers[i] = cm.getRenderer(i);
52674         }
52675         return renderers;
52676     },
52677     
52678     getColumnIds : function(){
52679         var ids = [];
52680         var cm = this.grid.colModel;
52681         var colCount = cm.getColumnCount();
52682         for(var i = 0; i < colCount; i++){
52683             ids[i] = cm.getColumnId(i);
52684         }
52685         return ids;
52686     },
52687     
52688     getDataIndexes : function(){
52689         if(!this.indexMap){
52690             this.indexMap = this.buildIndexMap();
52691         }
52692         return this.indexMap.colToData;
52693     },
52694     
52695     getColumnIndexByDataIndex : function(dataIndex){
52696         if(!this.indexMap){
52697             this.indexMap = this.buildIndexMap();
52698         }
52699         return this.indexMap.dataToCol[dataIndex];
52700     },
52701     
52702     /**
52703      * Set a css style for a column dynamically. 
52704      * @param {Number} colIndex The index of the column
52705      * @param {String} name The css property name
52706      * @param {String} value The css value
52707      */
52708     setCSSStyle : function(colIndex, name, value){
52709         var selector = "#" + this.grid.id + " .x-grid-col-" + colIndex;
52710         Roo.util.CSS.updateRule(selector, name, value);
52711     },
52712     
52713     generateRules : function(cm){
52714         var ruleBuf = [], rulesId = this.grid.id + '-cssrules';
52715         Roo.util.CSS.removeStyleSheet(rulesId);
52716         for(var i = 0, len = cm.getColumnCount(); i < len; i++){
52717             var cid = cm.getColumnId(i);
52718             ruleBuf.push(this.colSelector, cid, " {\n", cm.config[i].css, "}\n",
52719                          this.tdSelector, cid, " {\n}\n",
52720                          this.hdSelector, cid, " {\n}\n",
52721                          this.splitSelector, cid, " {\n}\n");
52722         }
52723         return Roo.util.CSS.createStyleSheet(ruleBuf.join(""), rulesId);
52724     }
52725 });/*
52726  * Based on:
52727  * Ext JS Library 1.1.1
52728  * Copyright(c) 2006-2007, Ext JS, LLC.
52729  *
52730  * Originally Released Under LGPL - original licence link has changed is not relivant.
52731  *
52732  * Fork - LGPL
52733  * <script type="text/javascript">
52734  */
52735
52736 // private
52737 // This is a support class used internally by the Grid components
52738 Roo.grid.HeaderDragZone = function(grid, hd, hd2){
52739     this.grid = grid;
52740     this.view = grid.getView();
52741     this.ddGroup = "gridHeader" + this.grid.getGridEl().id;
52742     Roo.grid.HeaderDragZone.superclass.constructor.call(this, hd);
52743     if(hd2){
52744         this.setHandleElId(Roo.id(hd));
52745         this.setOuterHandleElId(Roo.id(hd2));
52746     }
52747     this.scroll = false;
52748 };
52749 Roo.extend(Roo.grid.HeaderDragZone, Roo.dd.DragZone, {
52750     maxDragWidth: 120,
52751     getDragData : function(e){
52752         var t = Roo.lib.Event.getTarget(e);
52753         var h = this.view.findHeaderCell(t);
52754         if(h){
52755             return {ddel: h.firstChild, header:h};
52756         }
52757         return false;
52758     },
52759
52760     onInitDrag : function(e){
52761         this.view.headersDisabled = true;
52762         var clone = this.dragData.ddel.cloneNode(true);
52763         clone.id = Roo.id();
52764         clone.style.width = Math.min(this.dragData.header.offsetWidth,this.maxDragWidth) + "px";
52765         this.proxy.update(clone);
52766         return true;
52767     },
52768
52769     afterValidDrop : function(){
52770         var v = this.view;
52771         setTimeout(function(){
52772             v.headersDisabled = false;
52773         }, 50);
52774     },
52775
52776     afterInvalidDrop : function(){
52777         var v = this.view;
52778         setTimeout(function(){
52779             v.headersDisabled = false;
52780         }, 50);
52781     }
52782 });
52783 /*
52784  * Based on:
52785  * Ext JS Library 1.1.1
52786  * Copyright(c) 2006-2007, Ext JS, LLC.
52787  *
52788  * Originally Released Under LGPL - original licence link has changed is not relivant.
52789  *
52790  * Fork - LGPL
52791  * <script type="text/javascript">
52792  */
52793 // private
52794 // This is a support class used internally by the Grid components
52795 Roo.grid.HeaderDropZone = function(grid, hd, hd2){
52796     this.grid = grid;
52797     this.view = grid.getView();
52798     // split the proxies so they don't interfere with mouse events
52799     this.proxyTop = Roo.DomHelper.append(document.body, {
52800         cls:"col-move-top", html:"&#160;"
52801     }, true);
52802     this.proxyBottom = Roo.DomHelper.append(document.body, {
52803         cls:"col-move-bottom", html:"&#160;"
52804     }, true);
52805     this.proxyTop.hide = this.proxyBottom.hide = function(){
52806         this.setLeftTop(-100,-100);
52807         this.setStyle("visibility", "hidden");
52808     };
52809     this.ddGroup = "gridHeader" + this.grid.getGridEl().id;
52810     // temporarily disabled
52811     //Roo.dd.ScrollManager.register(this.view.scroller.dom);
52812     Roo.grid.HeaderDropZone.superclass.constructor.call(this, grid.getGridEl().dom);
52813 };
52814 Roo.extend(Roo.grid.HeaderDropZone, Roo.dd.DropZone, {
52815     proxyOffsets : [-4, -9],
52816     fly: Roo.Element.fly,
52817
52818     getTargetFromEvent : function(e){
52819         var t = Roo.lib.Event.getTarget(e);
52820         var cindex = this.view.findCellIndex(t);
52821         if(cindex !== false){
52822             return this.view.getHeaderCell(cindex);
52823         }
52824         return null;
52825     },
52826
52827     nextVisible : function(h){
52828         var v = this.view, cm = this.grid.colModel;
52829         h = h.nextSibling;
52830         while(h){
52831             if(!cm.isHidden(v.getCellIndex(h))){
52832                 return h;
52833             }
52834             h = h.nextSibling;
52835         }
52836         return null;
52837     },
52838
52839     prevVisible : function(h){
52840         var v = this.view, cm = this.grid.colModel;
52841         h = h.prevSibling;
52842         while(h){
52843             if(!cm.isHidden(v.getCellIndex(h))){
52844                 return h;
52845             }
52846             h = h.prevSibling;
52847         }
52848         return null;
52849     },
52850
52851     positionIndicator : function(h, n, e){
52852         var x = Roo.lib.Event.getPageX(e);
52853         var r = Roo.lib.Dom.getRegion(n.firstChild);
52854         var px, pt, py = r.top + this.proxyOffsets[1];
52855         if((r.right - x) <= (r.right-r.left)/2){
52856             px = r.right+this.view.borderWidth;
52857             pt = "after";
52858         }else{
52859             px = r.left;
52860             pt = "before";
52861         }
52862         var oldIndex = this.view.getCellIndex(h);
52863         var newIndex = this.view.getCellIndex(n);
52864
52865         if(this.grid.colModel.isFixed(newIndex)){
52866             return false;
52867         }
52868
52869         var locked = this.grid.colModel.isLocked(newIndex);
52870
52871         if(pt == "after"){
52872             newIndex++;
52873         }
52874         if(oldIndex < newIndex){
52875             newIndex--;
52876         }
52877         if(oldIndex == newIndex && (locked == this.grid.colModel.isLocked(oldIndex))){
52878             return false;
52879         }
52880         px +=  this.proxyOffsets[0];
52881         this.proxyTop.setLeftTop(px, py);
52882         this.proxyTop.show();
52883         if(!this.bottomOffset){
52884             this.bottomOffset = this.view.mainHd.getHeight();
52885         }
52886         this.proxyBottom.setLeftTop(px, py+this.proxyTop.dom.offsetHeight+this.bottomOffset);
52887         this.proxyBottom.show();
52888         return pt;
52889     },
52890
52891     onNodeEnter : function(n, dd, e, data){
52892         if(data.header != n){
52893             this.positionIndicator(data.header, n, e);
52894         }
52895     },
52896
52897     onNodeOver : function(n, dd, e, data){
52898         var result = false;
52899         if(data.header != n){
52900             result = this.positionIndicator(data.header, n, e);
52901         }
52902         if(!result){
52903             this.proxyTop.hide();
52904             this.proxyBottom.hide();
52905         }
52906         return result ? this.dropAllowed : this.dropNotAllowed;
52907     },
52908
52909     onNodeOut : function(n, dd, e, data){
52910         this.proxyTop.hide();
52911         this.proxyBottom.hide();
52912     },
52913
52914     onNodeDrop : function(n, dd, e, data){
52915         var h = data.header;
52916         if(h != n){
52917             var cm = this.grid.colModel;
52918             var x = Roo.lib.Event.getPageX(e);
52919             var r = Roo.lib.Dom.getRegion(n.firstChild);
52920             var pt = (r.right - x) <= ((r.right-r.left)/2) ? "after" : "before";
52921             var oldIndex = this.view.getCellIndex(h);
52922             var newIndex = this.view.getCellIndex(n);
52923             var locked = cm.isLocked(newIndex);
52924             if(pt == "after"){
52925                 newIndex++;
52926             }
52927             if(oldIndex < newIndex){
52928                 newIndex--;
52929             }
52930             if(oldIndex == newIndex && (locked == cm.isLocked(oldIndex))){
52931                 return false;
52932             }
52933             cm.setLocked(oldIndex, locked, true);
52934             cm.moveColumn(oldIndex, newIndex);
52935             this.grid.fireEvent("columnmove", oldIndex, newIndex);
52936             return true;
52937         }
52938         return false;
52939     }
52940 });
52941 /*
52942  * Based on:
52943  * Ext JS Library 1.1.1
52944  * Copyright(c) 2006-2007, Ext JS, LLC.
52945  *
52946  * Originally Released Under LGPL - original licence link has changed is not relivant.
52947  *
52948  * Fork - LGPL
52949  * <script type="text/javascript">
52950  */
52951   
52952 /**
52953  * @class Roo.grid.GridView
52954  * @extends Roo.util.Observable
52955  *
52956  * @constructor
52957  * @param {Object} config
52958  */
52959 Roo.grid.GridView = function(config){
52960     Roo.grid.GridView.superclass.constructor.call(this);
52961     this.el = null;
52962
52963     Roo.apply(this, config);
52964 };
52965
52966 Roo.extend(Roo.grid.GridView, Roo.grid.AbstractGridView, {
52967
52968     unselectable :  'unselectable="on"',
52969     unselectableCls :  'x-unselectable',
52970     
52971     
52972     rowClass : "x-grid-row",
52973
52974     cellClass : "x-grid-col",
52975
52976     tdClass : "x-grid-td",
52977
52978     hdClass : "x-grid-hd",
52979
52980     splitClass : "x-grid-split",
52981
52982     sortClasses : ["sort-asc", "sort-desc"],
52983
52984     enableMoveAnim : false,
52985
52986     hlColor: "C3DAF9",
52987
52988     dh : Roo.DomHelper,
52989
52990     fly : Roo.Element.fly,
52991
52992     css : Roo.util.CSS,
52993
52994     borderWidth: 1,
52995
52996     splitOffset: 3,
52997
52998     scrollIncrement : 22,
52999
53000     cellRE: /(?:.*?)x-grid-(?:hd|cell|csplit)-(?:[\d]+)-([\d]+)(?:.*?)/,
53001
53002     findRE: /\s?(?:x-grid-hd|x-grid-col|x-grid-csplit)\s/,
53003
53004     bind : function(ds, cm){
53005         if(this.ds){
53006             this.ds.un("load", this.onLoad, this);
53007             this.ds.un("datachanged", this.onDataChange, this);
53008             this.ds.un("add", this.onAdd, this);
53009             this.ds.un("remove", this.onRemove, this);
53010             this.ds.un("update", this.onUpdate, this);
53011             this.ds.un("clear", this.onClear, this);
53012         }
53013         if(ds){
53014             ds.on("load", this.onLoad, this);
53015             ds.on("datachanged", this.onDataChange, this);
53016             ds.on("add", this.onAdd, this);
53017             ds.on("remove", this.onRemove, this);
53018             ds.on("update", this.onUpdate, this);
53019             ds.on("clear", this.onClear, this);
53020         }
53021         this.ds = ds;
53022
53023         if(this.cm){
53024             this.cm.un("widthchange", this.onColWidthChange, this);
53025             this.cm.un("headerchange", this.onHeaderChange, this);
53026             this.cm.un("hiddenchange", this.onHiddenChange, this);
53027             this.cm.un("columnmoved", this.onColumnMove, this);
53028             this.cm.un("columnlockchange", this.onColumnLock, this);
53029         }
53030         if(cm){
53031             this.generateRules(cm);
53032             cm.on("widthchange", this.onColWidthChange, this);
53033             cm.on("headerchange", this.onHeaderChange, this);
53034             cm.on("hiddenchange", this.onHiddenChange, this);
53035             cm.on("columnmoved", this.onColumnMove, this);
53036             cm.on("columnlockchange", this.onColumnLock, this);
53037         }
53038         this.cm = cm;
53039     },
53040
53041     init: function(grid){
53042         Roo.grid.GridView.superclass.init.call(this, grid);
53043
53044         this.bind(grid.dataSource, grid.colModel);
53045
53046         grid.on("headerclick", this.handleHeaderClick, this);
53047
53048         if(grid.trackMouseOver){
53049             grid.on("mouseover", this.onRowOver, this);
53050             grid.on("mouseout", this.onRowOut, this);
53051         }
53052         grid.cancelTextSelection = function(){};
53053         this.gridId = grid.id;
53054
53055         var tpls = this.templates || {};
53056
53057         if(!tpls.master){
53058             tpls.master = new Roo.Template(
53059                '<div class="x-grid" hidefocus="true">',
53060                 '<a href="#" class="x-grid-focus" tabIndex="-1"></a>',
53061                   '<div class="x-grid-topbar"></div>',
53062                   '<div class="x-grid-scroller"><div></div></div>',
53063                   '<div class="x-grid-locked">',
53064                       '<div class="x-grid-header">{lockedHeader}</div>',
53065                       '<div class="x-grid-body">{lockedBody}</div>',
53066                   "</div>",
53067                   '<div class="x-grid-viewport">',
53068                       '<div class="x-grid-header">{header}</div>',
53069                       '<div class="x-grid-body">{body}</div>',
53070                   "</div>",
53071                   '<div class="x-grid-bottombar"></div>',
53072                  
53073                   '<div class="x-grid-resize-proxy">&#160;</div>',
53074                "</div>"
53075             );
53076             tpls.master.disableformats = true;
53077         }
53078
53079         if(!tpls.header){
53080             tpls.header = new Roo.Template(
53081                '<table border="0" cellspacing="0" cellpadding="0">',
53082                '<tbody><tr class="x-grid-hd-row">{cells}</tr></tbody>',
53083                "</table>{splits}"
53084             );
53085             tpls.header.disableformats = true;
53086         }
53087         tpls.header.compile();
53088
53089         if(!tpls.hcell){
53090             tpls.hcell = new Roo.Template(
53091                 '<td class="x-grid-hd x-grid-td-{id} {cellId}"><div title="{title}" class="x-grid-hd-inner x-grid-hd-{id}">',
53092                 '<div class="x-grid-hd-text ' + this.unselectableCls +  '" ' + this.unselectable +'>{value}<img class="x-grid-sort-icon" src="', Roo.BLANK_IMAGE_URL, '" /></div>',
53093                 "</div></td>"
53094              );
53095              tpls.hcell.disableFormats = true;
53096         }
53097         tpls.hcell.compile();
53098
53099         if(!tpls.hsplit){
53100             tpls.hsplit = new Roo.Template('<div class="x-grid-split {splitId} x-grid-split-{id}" style="{style} ' +
53101                                             this.unselectableCls +  '" ' + this.unselectable +'>&#160;</div>');
53102             tpls.hsplit.disableFormats = true;
53103         }
53104         tpls.hsplit.compile();
53105
53106         if(!tpls.body){
53107             tpls.body = new Roo.Template(
53108                '<table border="0" cellspacing="0" cellpadding="0">',
53109                "<tbody>{rows}</tbody>",
53110                "</table>"
53111             );
53112             tpls.body.disableFormats = true;
53113         }
53114         tpls.body.compile();
53115
53116         if(!tpls.row){
53117             tpls.row = new Roo.Template('<tr class="x-grid-row {alt}">{cells}</tr>');
53118             tpls.row.disableFormats = true;
53119         }
53120         tpls.row.compile();
53121
53122         if(!tpls.cell){
53123             tpls.cell = new Roo.Template(
53124                 '<td class="x-grid-col x-grid-td-{id} {cellId} {css}" tabIndex="0">',
53125                 '<div class="x-grid-col-{id} x-grid-cell-inner"><div class="x-grid-cell-text ' +
53126                     this.unselectableCls +  '" ' + this.unselectable +'" {attr}>{value}</div></div>',
53127                 "</td>"
53128             );
53129             tpls.cell.disableFormats = true;
53130         }
53131         tpls.cell.compile();
53132
53133         this.templates = tpls;
53134     },
53135
53136     // remap these for backwards compat
53137     onColWidthChange : function(){
53138         this.updateColumns.apply(this, arguments);
53139     },
53140     onHeaderChange : function(){
53141         this.updateHeaders.apply(this, arguments);
53142     }, 
53143     onHiddenChange : function(){
53144         this.handleHiddenChange.apply(this, arguments);
53145     },
53146     onColumnMove : function(){
53147         this.handleColumnMove.apply(this, arguments);
53148     },
53149     onColumnLock : function(){
53150         this.handleLockChange.apply(this, arguments);
53151     },
53152
53153     onDataChange : function(){
53154         this.refresh();
53155         this.updateHeaderSortState();
53156     },
53157
53158     onClear : function(){
53159         this.refresh();
53160     },
53161
53162     onUpdate : function(ds, record){
53163         this.refreshRow(record);
53164     },
53165
53166     refreshRow : function(record){
53167         var ds = this.ds, index;
53168         if(typeof record == 'number'){
53169             index = record;
53170             record = ds.getAt(index);
53171         }else{
53172             index = ds.indexOf(record);
53173         }
53174         this.insertRows(ds, index, index, true);
53175         this.onRemove(ds, record, index+1, true);
53176         this.syncRowHeights(index, index);
53177         this.layout();
53178         this.fireEvent("rowupdated", this, index, record);
53179     },
53180
53181     onAdd : function(ds, records, index){
53182         this.insertRows(ds, index, index + (records.length-1));
53183     },
53184
53185     onRemove : function(ds, record, index, isUpdate){
53186         if(isUpdate !== true){
53187             this.fireEvent("beforerowremoved", this, index, record);
53188         }
53189         var bt = this.getBodyTable(), lt = this.getLockedTable();
53190         if(bt.rows[index]){
53191             bt.firstChild.removeChild(bt.rows[index]);
53192         }
53193         if(lt.rows[index]){
53194             lt.firstChild.removeChild(lt.rows[index]);
53195         }
53196         if(isUpdate !== true){
53197             this.stripeRows(index);
53198             this.syncRowHeights(index, index);
53199             this.layout();
53200             this.fireEvent("rowremoved", this, index, record);
53201         }
53202     },
53203
53204     onLoad : function(){
53205         this.scrollToTop();
53206     },
53207
53208     /**
53209      * Scrolls the grid to the top
53210      */
53211     scrollToTop : function(){
53212         if(this.scroller){
53213             this.scroller.dom.scrollTop = 0;
53214             this.syncScroll();
53215         }
53216     },
53217
53218     /**
53219      * Gets a panel in the header of the grid that can be used for toolbars etc.
53220      * After modifying the contents of this panel a call to grid.autoSize() may be
53221      * required to register any changes in size.
53222      * @param {Boolean} doShow By default the header is hidden. Pass true to show the panel
53223      * @return Roo.Element
53224      */
53225     getHeaderPanel : function(doShow){
53226         if(doShow){
53227             this.headerPanel.show();
53228         }
53229         return this.headerPanel;
53230     },
53231
53232     /**
53233      * Gets a panel in the footer of the grid that can be used for toolbars etc.
53234      * After modifying the contents of this panel a call to grid.autoSize() may be
53235      * required to register any changes in size.
53236      * @param {Boolean} doShow By default the footer is hidden. Pass true to show the panel
53237      * @return Roo.Element
53238      */
53239     getFooterPanel : function(doShow){
53240         if(doShow){
53241             this.footerPanel.show();
53242         }
53243         return this.footerPanel;
53244     },
53245
53246     initElements : function(){
53247         var E = Roo.Element;
53248         var el = this.grid.getGridEl().dom.firstChild;
53249         var cs = el.childNodes;
53250
53251         this.el = new E(el);
53252         
53253          this.focusEl = new E(el.firstChild);
53254         this.focusEl.swallowEvent("click", true);
53255         
53256         this.headerPanel = new E(cs[1]);
53257         this.headerPanel.enableDisplayMode("block");
53258
53259         this.scroller = new E(cs[2]);
53260         this.scrollSizer = new E(this.scroller.dom.firstChild);
53261
53262         this.lockedWrap = new E(cs[3]);
53263         this.lockedHd = new E(this.lockedWrap.dom.firstChild);
53264         this.lockedBody = new E(this.lockedWrap.dom.childNodes[1]);
53265
53266         this.mainWrap = new E(cs[4]);
53267         this.mainHd = new E(this.mainWrap.dom.firstChild);
53268         this.mainBody = new E(this.mainWrap.dom.childNodes[1]);
53269
53270         this.footerPanel = new E(cs[5]);
53271         this.footerPanel.enableDisplayMode("block");
53272
53273         this.resizeProxy = new E(cs[6]);
53274
53275         this.headerSelector = String.format(
53276            '#{0} td.x-grid-hd, #{1} td.x-grid-hd',
53277            this.lockedHd.id, this.mainHd.id
53278         );
53279
53280         this.splitterSelector = String.format(
53281            '#{0} div.x-grid-split, #{1} div.x-grid-split',
53282            this.idToCssName(this.lockedHd.id), this.idToCssName(this.mainHd.id)
53283         );
53284     },
53285     idToCssName : function(s)
53286     {
53287         return s.replace(/[^a-z0-9]+/ig, '-');
53288     },
53289
53290     getHeaderCell : function(index){
53291         return Roo.DomQuery.select(this.headerSelector)[index];
53292     },
53293
53294     getHeaderCellMeasure : function(index){
53295         return this.getHeaderCell(index).firstChild;
53296     },
53297
53298     getHeaderCellText : function(index){
53299         return this.getHeaderCell(index).firstChild.firstChild;
53300     },
53301
53302     getLockedTable : function(){
53303         return this.lockedBody.dom.firstChild;
53304     },
53305
53306     getBodyTable : function(){
53307         return this.mainBody.dom.firstChild;
53308     },
53309
53310     getLockedRow : function(index){
53311         return this.getLockedTable().rows[index];
53312     },
53313
53314     getRow : function(index){
53315         return this.getBodyTable().rows[index];
53316     },
53317
53318     getRowComposite : function(index){
53319         if(!this.rowEl){
53320             this.rowEl = new Roo.CompositeElementLite();
53321         }
53322         var els = [], lrow, mrow;
53323         if(lrow = this.getLockedRow(index)){
53324             els.push(lrow);
53325         }
53326         if(mrow = this.getRow(index)){
53327             els.push(mrow);
53328         }
53329         this.rowEl.elements = els;
53330         return this.rowEl;
53331     },
53332     /**
53333      * Gets the 'td' of the cell
53334      * 
53335      * @param {Integer} rowIndex row to select
53336      * @param {Integer} colIndex column to select
53337      * 
53338      * @return {Object} 
53339      */
53340     getCell : function(rowIndex, colIndex){
53341         var locked = this.cm.getLockedCount();
53342         var source;
53343         if(colIndex < locked){
53344             source = this.lockedBody.dom.firstChild;
53345         }else{
53346             source = this.mainBody.dom.firstChild;
53347             colIndex -= locked;
53348         }
53349         return source.rows[rowIndex].childNodes[colIndex];
53350     },
53351
53352     getCellText : function(rowIndex, colIndex){
53353         return this.getCell(rowIndex, colIndex).firstChild.firstChild;
53354     },
53355
53356     getCellBox : function(cell){
53357         var b = this.fly(cell).getBox();
53358         if(Roo.isOpera){ // opera fails to report the Y
53359             b.y = cell.offsetTop + this.mainBody.getY();
53360         }
53361         return b;
53362     },
53363
53364     getCellIndex : function(cell){
53365         var id = String(cell.className).match(this.cellRE);
53366         if(id){
53367             return parseInt(id[1], 10);
53368         }
53369         return 0;
53370     },
53371
53372     findHeaderIndex : function(n){
53373         var r = Roo.fly(n).findParent("td." + this.hdClass, 6);
53374         return r ? this.getCellIndex(r) : false;
53375     },
53376
53377     findHeaderCell : function(n){
53378         var r = Roo.fly(n).findParent("td." + this.hdClass, 6);
53379         return r ? r : false;
53380     },
53381
53382     findRowIndex : function(n){
53383         if(!n){
53384             return false;
53385         }
53386         var r = Roo.fly(n).findParent("tr." + this.rowClass, 6);
53387         return r ? r.rowIndex : false;
53388     },
53389
53390     findCellIndex : function(node){
53391         var stop = this.el.dom;
53392         while(node && node != stop){
53393             if(this.findRE.test(node.className)){
53394                 return this.getCellIndex(node);
53395             }
53396             node = node.parentNode;
53397         }
53398         return false;
53399     },
53400
53401     getColumnId : function(index){
53402         return this.cm.getColumnId(index);
53403     },
53404
53405     getSplitters : function()
53406     {
53407         if(this.splitterSelector){
53408            return Roo.DomQuery.select(this.splitterSelector);
53409         }else{
53410             return null;
53411       }
53412     },
53413
53414     getSplitter : function(index){
53415         return this.getSplitters()[index];
53416     },
53417
53418     onRowOver : function(e, t){
53419         var row;
53420         if((row = this.findRowIndex(t)) !== false){
53421             this.getRowComposite(row).addClass("x-grid-row-over");
53422         }
53423     },
53424
53425     onRowOut : function(e, t){
53426         var row;
53427         if((row = this.findRowIndex(t)) !== false && row !== this.findRowIndex(e.getRelatedTarget())){
53428             this.getRowComposite(row).removeClass("x-grid-row-over");
53429         }
53430     },
53431
53432     renderHeaders : function(){
53433         var cm = this.cm;
53434         var ct = this.templates.hcell, ht = this.templates.header, st = this.templates.hsplit;
53435         var cb = [], lb = [], sb = [], lsb = [], p = {};
53436         for(var i = 0, len = cm.getColumnCount(); i < len; i++){
53437             p.cellId = "x-grid-hd-0-" + i;
53438             p.splitId = "x-grid-csplit-0-" + i;
53439             p.id = cm.getColumnId(i);
53440             p.title = cm.getColumnTooltip(i) || "";
53441             p.value = cm.getColumnHeader(i) || "";
53442             p.style = (this.grid.enableColumnResize === false || !cm.isResizable(i) || cm.isFixed(i)) ? 'cursor:default' : '';
53443             if(!cm.isLocked(i)){
53444                 cb[cb.length] = ct.apply(p);
53445                 sb[sb.length] = st.apply(p);
53446             }else{
53447                 lb[lb.length] = ct.apply(p);
53448                 lsb[lsb.length] = st.apply(p);
53449             }
53450         }
53451         return [ht.apply({cells: lb.join(""), splits:lsb.join("")}),
53452                 ht.apply({cells: cb.join(""), splits:sb.join("")})];
53453     },
53454
53455     updateHeaders : function(){
53456         var html = this.renderHeaders();
53457         this.lockedHd.update(html[0]);
53458         this.mainHd.update(html[1]);
53459     },
53460
53461     /**
53462      * Focuses the specified row.
53463      * @param {Number} row The row index
53464      */
53465     focusRow : function(row)
53466     {
53467         //Roo.log('GridView.focusRow');
53468         var x = this.scroller.dom.scrollLeft;
53469         this.focusCell(row, 0, false);
53470         this.scroller.dom.scrollLeft = x;
53471     },
53472
53473     /**
53474      * Focuses the specified cell.
53475      * @param {Number} row The row index
53476      * @param {Number} col The column index
53477      * @param {Boolean} hscroll false to disable horizontal scrolling
53478      */
53479     focusCell : function(row, col, hscroll)
53480     {
53481         //Roo.log('GridView.focusCell');
53482         var el = this.ensureVisible(row, col, hscroll);
53483         this.focusEl.alignTo(el, "tl-tl");
53484         if(Roo.isGecko){
53485             this.focusEl.focus();
53486         }else{
53487             this.focusEl.focus.defer(1, this.focusEl);
53488         }
53489     },
53490
53491     /**
53492      * Scrolls the specified cell into view
53493      * @param {Number} row The row index
53494      * @param {Number} col The column index
53495      * @param {Boolean} hscroll false to disable horizontal scrolling
53496      */
53497     ensureVisible : function(row, col, hscroll)
53498     {
53499         //Roo.log('GridView.ensureVisible,' + row + ',' + col);
53500         //return null; //disable for testing.
53501         if(typeof row != "number"){
53502             row = row.rowIndex;
53503         }
53504         if(row < 0 && row >= this.ds.getCount()){
53505             return  null;
53506         }
53507         col = (col !== undefined ? col : 0);
53508         var cm = this.grid.colModel;
53509         while(cm.isHidden(col)){
53510             col++;
53511         }
53512
53513         var el = this.getCell(row, col);
53514         if(!el){
53515             return null;
53516         }
53517         var c = this.scroller.dom;
53518
53519         var ctop = parseInt(el.offsetTop, 10);
53520         var cleft = parseInt(el.offsetLeft, 10);
53521         var cbot = ctop + el.offsetHeight;
53522         var cright = cleft + el.offsetWidth;
53523         
53524         var ch = c.clientHeight - this.mainHd.dom.offsetHeight;
53525         var stop = parseInt(c.scrollTop, 10);
53526         var sleft = parseInt(c.scrollLeft, 10);
53527         var sbot = stop + ch;
53528         var sright = sleft + c.clientWidth;
53529         /*
53530         Roo.log('GridView.ensureVisible:' +
53531                 ' ctop:' + ctop +
53532                 ' c.clientHeight:' + c.clientHeight +
53533                 ' this.mainHd.dom.offsetHeight:' + this.mainHd.dom.offsetHeight +
53534                 ' stop:' + stop +
53535                 ' cbot:' + cbot +
53536                 ' sbot:' + sbot +
53537                 ' ch:' + ch  
53538                 );
53539         */
53540         if(ctop < stop){
53541              c.scrollTop = ctop;
53542             //Roo.log("set scrolltop to ctop DISABLE?");
53543         }else if(cbot > sbot){
53544             //Roo.log("set scrolltop to cbot-ch");
53545             c.scrollTop = cbot-ch;
53546         }
53547         
53548         if(hscroll !== false){
53549             if(cleft < sleft){
53550                 c.scrollLeft = cleft;
53551             }else if(cright > sright){
53552                 c.scrollLeft = cright-c.clientWidth;
53553             }
53554         }
53555          
53556         return el;
53557     },
53558
53559     updateColumns : function(){
53560         this.grid.stopEditing();
53561         var cm = this.grid.colModel, colIds = this.getColumnIds();
53562         //var totalWidth = cm.getTotalWidth();
53563         var pos = 0;
53564         for(var i = 0, len = cm.getColumnCount(); i < len; i++){
53565             //if(cm.isHidden(i)) continue;
53566             var w = cm.getColumnWidth(i);
53567             this.css.updateRule(this.colSelector+this.idToCssName(colIds[i]), "width", (w - this.borderWidth) + "px");
53568             this.css.updateRule(this.hdSelector+this.idToCssName(colIds[i]), "width", (w - this.borderWidth) + "px");
53569         }
53570         this.updateSplitters();
53571     },
53572
53573     generateRules : function(cm){
53574         var ruleBuf = [], rulesId = this.idToCssName(this.grid.id)+ '-cssrules';
53575         Roo.util.CSS.removeStyleSheet(rulesId);
53576         for(var i = 0, len = cm.getColumnCount(); i < len; i++){
53577             var cid = cm.getColumnId(i);
53578             var align = '';
53579             if(cm.config[i].align){
53580                 align = 'text-align:'+cm.config[i].align+';';
53581             }
53582             var hidden = '';
53583             if(cm.isHidden(i)){
53584                 hidden = 'display:none;';
53585             }
53586             var width = "width:" + (cm.getColumnWidth(i) - this.borderWidth) + "px;";
53587             ruleBuf.push(
53588                     this.colSelector, cid, " {\n", cm.config[i].css, align, width, "\n}\n",
53589                     this.hdSelector, cid, " {\n", align, width, "}\n",
53590                     this.tdSelector, cid, " {\n",hidden,"\n}\n",
53591                     this.splitSelector, cid, " {\n", hidden , "\n}\n");
53592         }
53593         return Roo.util.CSS.createStyleSheet(ruleBuf.join(""), rulesId);
53594     },
53595
53596     updateSplitters : function(){
53597         var cm = this.cm, s = this.getSplitters();
53598         if(s){ // splitters not created yet
53599             var pos = 0, locked = true;
53600             for(var i = 0, len = cm.getColumnCount(); i < len; i++){
53601                 if(cm.isHidden(i)) continue;
53602                 var w = cm.getColumnWidth(i); // make sure it's a number
53603                 if(!cm.isLocked(i) && locked){
53604                     pos = 0;
53605                     locked = false;
53606                 }
53607                 pos += w;
53608                 s[i].style.left = (pos-this.splitOffset) + "px";
53609             }
53610         }
53611     },
53612
53613     handleHiddenChange : function(colModel, colIndex, hidden){
53614         if(hidden){
53615             this.hideColumn(colIndex);
53616         }else{
53617             this.unhideColumn(colIndex);
53618         }
53619     },
53620
53621     hideColumn : function(colIndex){
53622         var cid = this.getColumnId(colIndex);
53623         this.css.updateRule(this.tdSelector+this.idToCssName(cid), "display", "none");
53624         this.css.updateRule(this.splitSelector+this.idToCssName(cid), "display", "none");
53625         if(Roo.isSafari){
53626             this.updateHeaders();
53627         }
53628         this.updateSplitters();
53629         this.layout();
53630     },
53631
53632     unhideColumn : function(colIndex){
53633         var cid = this.getColumnId(colIndex);
53634         this.css.updateRule(this.tdSelector+this.idToCssName(cid), "display", "");
53635         this.css.updateRule(this.splitSelector+this.idToCssName(cid), "display", "");
53636
53637         if(Roo.isSafari){
53638             this.updateHeaders();
53639         }
53640         this.updateSplitters();
53641         this.layout();
53642     },
53643
53644     insertRows : function(dm, firstRow, lastRow, isUpdate){
53645         if(firstRow == 0 && lastRow == dm.getCount()-1){
53646             this.refresh();
53647         }else{
53648             if(!isUpdate){
53649                 this.fireEvent("beforerowsinserted", this, firstRow, lastRow);
53650             }
53651             var s = this.getScrollState();
53652             var markup = this.renderRows(firstRow, lastRow);
53653             this.bufferRows(markup[0], this.getLockedTable(), firstRow);
53654             this.bufferRows(markup[1], this.getBodyTable(), firstRow);
53655             this.restoreScroll(s);
53656             if(!isUpdate){
53657                 this.fireEvent("rowsinserted", this, firstRow, lastRow);
53658                 this.syncRowHeights(firstRow, lastRow);
53659                 this.stripeRows(firstRow);
53660                 this.layout();
53661             }
53662         }
53663     },
53664
53665     bufferRows : function(markup, target, index){
53666         var before = null, trows = target.rows, tbody = target.tBodies[0];
53667         if(index < trows.length){
53668             before = trows[index];
53669         }
53670         var b = document.createElement("div");
53671         b.innerHTML = "<table><tbody>"+markup+"</tbody></table>";
53672         var rows = b.firstChild.rows;
53673         for(var i = 0, len = rows.length; i < len; i++){
53674             if(before){
53675                 tbody.insertBefore(rows[0], before);
53676             }else{
53677                 tbody.appendChild(rows[0]);
53678             }
53679         }
53680         b.innerHTML = "";
53681         b = null;
53682     },
53683
53684     deleteRows : function(dm, firstRow, lastRow){
53685         if(dm.getRowCount()<1){
53686             this.fireEvent("beforerefresh", this);
53687             this.mainBody.update("");
53688             this.lockedBody.update("");
53689             this.fireEvent("refresh", this);
53690         }else{
53691             this.fireEvent("beforerowsdeleted", this, firstRow, lastRow);
53692             var bt = this.getBodyTable();
53693             var tbody = bt.firstChild;
53694             var rows = bt.rows;
53695             for(var rowIndex = firstRow; rowIndex <= lastRow; rowIndex++){
53696                 tbody.removeChild(rows[firstRow]);
53697             }
53698             this.stripeRows(firstRow);
53699             this.fireEvent("rowsdeleted", this, firstRow, lastRow);
53700         }
53701     },
53702
53703     updateRows : function(dataSource, firstRow, lastRow){
53704         var s = this.getScrollState();
53705         this.refresh();
53706         this.restoreScroll(s);
53707     },
53708
53709     handleSort : function(dataSource, sortColumnIndex, sortDir, noRefresh){
53710         if(!noRefresh){
53711            this.refresh();
53712         }
53713         this.updateHeaderSortState();
53714     },
53715
53716     getScrollState : function(){
53717         
53718         var sb = this.scroller.dom;
53719         return {left: sb.scrollLeft, top: sb.scrollTop};
53720     },
53721
53722     stripeRows : function(startRow){
53723         if(!this.grid.stripeRows || this.ds.getCount() < 1){
53724             return;
53725         }
53726         startRow = startRow || 0;
53727         var rows = this.getBodyTable().rows;
53728         var lrows = this.getLockedTable().rows;
53729         var cls = ' x-grid-row-alt ';
53730         for(var i = startRow, len = rows.length; i < len; i++){
53731             var row = rows[i], lrow = lrows[i];
53732             var isAlt = ((i+1) % 2 == 0);
53733             var hasAlt = (' '+row.className + ' ').indexOf(cls) != -1;
53734             if(isAlt == hasAlt){
53735                 continue;
53736             }
53737             if(isAlt){
53738                 row.className += " x-grid-row-alt";
53739             }else{
53740                 row.className = row.className.replace("x-grid-row-alt", "");
53741             }
53742             if(lrow){
53743                 lrow.className = row.className;
53744             }
53745         }
53746     },
53747
53748     restoreScroll : function(state){
53749         //Roo.log('GridView.restoreScroll');
53750         var sb = this.scroller.dom;
53751         sb.scrollLeft = state.left;
53752         sb.scrollTop = state.top;
53753         this.syncScroll();
53754     },
53755
53756     syncScroll : function(){
53757         //Roo.log('GridView.syncScroll');
53758         var sb = this.scroller.dom;
53759         var sh = this.mainHd.dom;
53760         var bs = this.mainBody.dom;
53761         var lv = this.lockedBody.dom;
53762         sh.scrollLeft = bs.scrollLeft = sb.scrollLeft;
53763         lv.scrollTop = bs.scrollTop = sb.scrollTop;
53764     },
53765
53766     handleScroll : function(e){
53767         this.syncScroll();
53768         var sb = this.scroller.dom;
53769         this.grid.fireEvent("bodyscroll", sb.scrollLeft, sb.scrollTop);
53770         e.stopEvent();
53771     },
53772
53773     handleWheel : function(e){
53774         var d = e.getWheelDelta();
53775         this.scroller.dom.scrollTop -= d*22;
53776         // set this here to prevent jumpy scrolling on large tables
53777         this.lockedBody.dom.scrollTop = this.mainBody.dom.scrollTop = this.scroller.dom.scrollTop;
53778         e.stopEvent();
53779     },
53780
53781     renderRows : function(startRow, endRow){
53782         // pull in all the crap needed to render rows
53783         var g = this.grid, cm = g.colModel, ds = g.dataSource, stripe = g.stripeRows;
53784         var colCount = cm.getColumnCount();
53785
53786         if(ds.getCount() < 1){
53787             return ["", ""];
53788         }
53789
53790         // build a map for all the columns
53791         var cs = [];
53792         for(var i = 0; i < colCount; i++){
53793             var name = cm.getDataIndex(i);
53794             cs[i] = {
53795                 name : typeof name == 'undefined' ? ds.fields.get(i).name : name,
53796                 renderer : cm.getRenderer(i),
53797                 id : cm.getColumnId(i),
53798                 locked : cm.isLocked(i)
53799             };
53800         }
53801
53802         startRow = startRow || 0;
53803         endRow = typeof endRow == "undefined"? ds.getCount()-1 : endRow;
53804
53805         // records to render
53806         var rs = ds.getRange(startRow, endRow);
53807
53808         return this.doRender(cs, rs, ds, startRow, colCount, stripe);
53809     },
53810
53811     // As much as I hate to duplicate code, this was branched because FireFox really hates
53812     // [].join("") on strings. The performance difference was substantial enough to
53813     // branch this function
53814     doRender : Roo.isGecko ?
53815             function(cs, rs, ds, startRow, colCount, stripe){
53816                 var ts = this.templates, ct = ts.cell, rt = ts.row;
53817                 // buffers
53818                 var buf = "", lbuf = "", cb, lcb, c, p = {}, rp = {}, r, rowIndex;
53819                 
53820                 var hasListener = this.grid.hasListener('rowclass');
53821                 var rowcfg = {};
53822                 for(var j = 0, len = rs.length; j < len; j++){
53823                     r = rs[j]; cb = ""; lcb = ""; rowIndex = (j+startRow);
53824                     for(var i = 0; i < colCount; i++){
53825                         c = cs[i];
53826                         p.cellId = "x-grid-cell-" + rowIndex + "-" + i;
53827                         p.id = c.id;
53828                         p.css = p.attr = "";
53829                         p.value = c.renderer(r.data[c.name], p, r, rowIndex, i, ds);
53830                         if(p.value == undefined || p.value === "") p.value = "&#160;";
53831                         if(r.dirty && typeof r.modified[c.name] !== 'undefined'){
53832                             p.css += p.css ? ' x-grid-dirty-cell' : 'x-grid-dirty-cell';
53833                         }
53834                         var markup = ct.apply(p);
53835                         if(!c.locked){
53836                             cb+= markup;
53837                         }else{
53838                             lcb+= markup;
53839                         }
53840                     }
53841                     var alt = [];
53842                     if(stripe && ((rowIndex+1) % 2 == 0)){
53843                         alt.push("x-grid-row-alt")
53844                     }
53845                     if(r.dirty){
53846                         alt.push(  " x-grid-dirty-row");
53847                     }
53848                     rp.cells = lcb;
53849                     if(this.getRowClass){
53850                         alt.push(this.getRowClass(r, rowIndex));
53851                     }
53852                     if (hasListener) {
53853                         rowcfg = {
53854                              
53855                             record: r,
53856                             rowIndex : rowIndex,
53857                             rowClass : ''
53858                         }
53859                         this.grid.fireEvent('rowclass', this, rowcfg);
53860                         alt.push(rowcfg.rowClass);
53861                     }
53862                     rp.alt = alt.join(" ");
53863                     lbuf+= rt.apply(rp);
53864                     rp.cells = cb;
53865                     buf+=  rt.apply(rp);
53866                 }
53867                 return [lbuf, buf];
53868             } :
53869             function(cs, rs, ds, startRow, colCount, stripe){
53870                 var ts = this.templates, ct = ts.cell, rt = ts.row;
53871                 // buffers
53872                 var buf = [], lbuf = [], cb, lcb, c, p = {}, rp = {}, r, rowIndex;
53873                 var hasListener = this.grid.hasListener('rowclass');
53874  
53875                 var rowcfg = {};
53876                 for(var j = 0, len = rs.length; j < len; j++){
53877                     r = rs[j]; cb = []; lcb = []; rowIndex = (j+startRow);
53878                     for(var i = 0; i < colCount; i++){
53879                         c = cs[i];
53880                         p.cellId = "x-grid-cell-" + rowIndex + "-" + i;
53881                         p.id = c.id;
53882                         p.css = p.attr = "";
53883                         p.value = c.renderer(r.data[c.name], p, r, rowIndex, i, ds);
53884                         if(p.value == undefined || p.value === "") p.value = "&#160;";
53885                         if(r.dirty && typeof r.modified[c.name] !== 'undefined'){
53886                             p.css += p.css ? ' x-grid-dirty-cell' : 'x-grid-dirty-cell';
53887                         }
53888                         
53889                         var markup = ct.apply(p);
53890                         if(!c.locked){
53891                             cb[cb.length] = markup;
53892                         }else{
53893                             lcb[lcb.length] = markup;
53894                         }
53895                     }
53896                     var alt = [];
53897                     if(stripe && ((rowIndex+1) % 2 == 0)){
53898                         alt.push( "x-grid-row-alt");
53899                     }
53900                     if(r.dirty){
53901                         alt.push(" x-grid-dirty-row");
53902                     }
53903                     rp.cells = lcb;
53904                     if(this.getRowClass){
53905                         alt.push( this.getRowClass(r, rowIndex));
53906                     }
53907                     if (hasListener) {
53908                         rowcfg = {
53909                              
53910                             record: r,
53911                             rowIndex : rowIndex,
53912                             rowClass : ''
53913                         }
53914                         this.grid.fireEvent('rowclass', this, rowcfg);
53915                         alt.push(rowcfg.rowClass);
53916                     }
53917                     rp.alt = alt.join(" ");
53918                     rp.cells = lcb.join("");
53919                     lbuf[lbuf.length] = rt.apply(rp);
53920                     rp.cells = cb.join("");
53921                     buf[buf.length] =  rt.apply(rp);
53922                 }
53923                 return [lbuf.join(""), buf.join("")];
53924             },
53925
53926     renderBody : function(){
53927         var markup = this.renderRows();
53928         var bt = this.templates.body;
53929         return [bt.apply({rows: markup[0]}), bt.apply({rows: markup[1]})];
53930     },
53931
53932     /**
53933      * Refreshes the grid
53934      * @param {Boolean} headersToo
53935      */
53936     refresh : function(headersToo){
53937         this.fireEvent("beforerefresh", this);
53938         this.grid.stopEditing();
53939         var result = this.renderBody();
53940         this.lockedBody.update(result[0]);
53941         this.mainBody.update(result[1]);
53942         if(headersToo === true){
53943             this.updateHeaders();
53944             this.updateColumns();
53945             this.updateSplitters();
53946             this.updateHeaderSortState();
53947         }
53948         this.syncRowHeights();
53949         this.layout();
53950         this.fireEvent("refresh", this);
53951     },
53952
53953     handleColumnMove : function(cm, oldIndex, newIndex){
53954         this.indexMap = null;
53955         var s = this.getScrollState();
53956         this.refresh(true);
53957         this.restoreScroll(s);
53958         this.afterMove(newIndex);
53959     },
53960
53961     afterMove : function(colIndex){
53962         if(this.enableMoveAnim && Roo.enableFx){
53963             this.fly(this.getHeaderCell(colIndex).firstChild).highlight(this.hlColor);
53964         }
53965         // if multisort - fix sortOrder, and reload..
53966         if (this.grid.dataSource.multiSort) {
53967             // the we can call sort again..
53968             var dm = this.grid.dataSource;
53969             var cm = this.grid.colModel;
53970             var so = [];
53971             for(var i = 0; i < cm.config.length; i++ ) {
53972                 
53973                 if ((typeof(dm.sortToggle[cm.config[i].dataIndex]) == 'undefined')) {
53974                     continue; // dont' bother, it's not in sort list or being set.
53975                 }
53976                 
53977                 so.push(cm.config[i].dataIndex);
53978             };
53979             dm.sortOrder = so;
53980             dm.load(dm.lastOptions);
53981             
53982             
53983         }
53984         
53985     },
53986
53987     updateCell : function(dm, rowIndex, dataIndex){
53988         var colIndex = this.getColumnIndexByDataIndex(dataIndex);
53989         if(typeof colIndex == "undefined"){ // not present in grid
53990             return;
53991         }
53992         var cm = this.grid.colModel;
53993         var cell = this.getCell(rowIndex, colIndex);
53994         var cellText = this.getCellText(rowIndex, colIndex);
53995
53996         var p = {
53997             cellId : "x-grid-cell-" + rowIndex + "-" + colIndex,
53998             id : cm.getColumnId(colIndex),
53999             css: colIndex == cm.getColumnCount()-1 ? "x-grid-col-last" : ""
54000         };
54001         var renderer = cm.getRenderer(colIndex);
54002         var val = renderer(dm.getValueAt(rowIndex, dataIndex), p, rowIndex, colIndex, dm);
54003         if(typeof val == "undefined" || val === "") val = "&#160;";
54004         cellText.innerHTML = val;
54005         cell.className = this.cellClass + " " + this.idToCssName(p.cellId) + " " + p.css;
54006         this.syncRowHeights(rowIndex, rowIndex);
54007     },
54008
54009     calcColumnWidth : function(colIndex, maxRowsToMeasure){
54010         var maxWidth = 0;
54011         if(this.grid.autoSizeHeaders){
54012             var h = this.getHeaderCellMeasure(colIndex);
54013             maxWidth = Math.max(maxWidth, h.scrollWidth);
54014         }
54015         var tb, index;
54016         if(this.cm.isLocked(colIndex)){
54017             tb = this.getLockedTable();
54018             index = colIndex;
54019         }else{
54020             tb = this.getBodyTable();
54021             index = colIndex - this.cm.getLockedCount();
54022         }
54023         if(tb && tb.rows){
54024             var rows = tb.rows;
54025             var stopIndex = Math.min(maxRowsToMeasure || rows.length, rows.length);
54026             for(var i = 0; i < stopIndex; i++){
54027                 var cell = rows[i].childNodes[index].firstChild;
54028                 maxWidth = Math.max(maxWidth, cell.scrollWidth);
54029             }
54030         }
54031         return maxWidth + /*margin for error in IE*/ 5;
54032     },
54033     /**
54034      * Autofit a column to its content.
54035      * @param {Number} colIndex
54036      * @param {Boolean} forceMinSize true to force the column to go smaller if possible
54037      */
54038      autoSizeColumn : function(colIndex, forceMinSize, suppressEvent){
54039          if(this.cm.isHidden(colIndex)){
54040              return; // can't calc a hidden column
54041          }
54042         if(forceMinSize){
54043             var cid = this.cm.getColumnId(colIndex);
54044             this.css.updateRule(this.colSelector +this.idToCssName( cid), "width", this.grid.minColumnWidth + "px");
54045            if(this.grid.autoSizeHeaders){
54046                this.css.updateRule(this.hdSelector + this.idToCssName(cid), "width", this.grid.minColumnWidth + "px");
54047            }
54048         }
54049         var newWidth = this.calcColumnWidth(colIndex);
54050         this.cm.setColumnWidth(colIndex,
54051             Math.max(this.grid.minColumnWidth, newWidth), suppressEvent);
54052         if(!suppressEvent){
54053             this.grid.fireEvent("columnresize", colIndex, newWidth);
54054         }
54055     },
54056
54057     /**
54058      * Autofits all columns to their content and then expands to fit any extra space in the grid
54059      */
54060      autoSizeColumns : function(){
54061         var cm = this.grid.colModel;
54062         var colCount = cm.getColumnCount();
54063         for(var i = 0; i < colCount; i++){
54064             this.autoSizeColumn(i, true, true);
54065         }
54066         if(cm.getTotalWidth() < this.scroller.dom.clientWidth){
54067             this.fitColumns();
54068         }else{
54069             this.updateColumns();
54070             this.layout();
54071         }
54072     },
54073
54074     /**
54075      * Autofits all columns to the grid's width proportionate with their current size
54076      * @param {Boolean} reserveScrollSpace Reserve space for a scrollbar
54077      */
54078     fitColumns : function(reserveScrollSpace){
54079         var cm = this.grid.colModel;
54080         var colCount = cm.getColumnCount();
54081         var cols = [];
54082         var width = 0;
54083         var i, w;
54084         for (i = 0; i < colCount; i++){
54085             if(!cm.isHidden(i) && !cm.isFixed(i)){
54086                 w = cm.getColumnWidth(i);
54087                 cols.push(i);
54088                 cols.push(w);
54089                 width += w;
54090             }
54091         }
54092         var avail = Math.min(this.scroller.dom.clientWidth, this.el.getWidth());
54093         if(reserveScrollSpace){
54094             avail -= 17;
54095         }
54096         var frac = (avail - cm.getTotalWidth())/width;
54097         while (cols.length){
54098             w = cols.pop();
54099             i = cols.pop();
54100             cm.setColumnWidth(i, Math.floor(w + w*frac), true);
54101         }
54102         this.updateColumns();
54103         this.layout();
54104     },
54105
54106     onRowSelect : function(rowIndex){
54107         var row = this.getRowComposite(rowIndex);
54108         row.addClass("x-grid-row-selected");
54109     },
54110
54111     onRowDeselect : function(rowIndex){
54112         var row = this.getRowComposite(rowIndex);
54113         row.removeClass("x-grid-row-selected");
54114     },
54115
54116     onCellSelect : function(row, col){
54117         var cell = this.getCell(row, col);
54118         if(cell){
54119             Roo.fly(cell).addClass("x-grid-cell-selected");
54120         }
54121     },
54122
54123     onCellDeselect : function(row, col){
54124         var cell = this.getCell(row, col);
54125         if(cell){
54126             Roo.fly(cell).removeClass("x-grid-cell-selected");
54127         }
54128     },
54129
54130     updateHeaderSortState : function(){
54131         
54132         // sort state can be single { field: xxx, direction : yyy}
54133         // or   { xxx=>ASC , yyy : DESC ..... }
54134         
54135         var mstate = {};
54136         if (!this.ds.multiSort) { 
54137             var state = this.ds.getSortState();
54138             if(!state){
54139                 return;
54140             }
54141             mstate[state.field] = state.direction;
54142             // FIXME... - this is not used here.. but might be elsewhere..
54143             this.sortState = state;
54144             
54145         } else {
54146             mstate = this.ds.sortToggle;
54147         }
54148         //remove existing sort classes..
54149         
54150         var sc = this.sortClasses;
54151         var hds = this.el.select(this.headerSelector).removeClass(sc);
54152         
54153         for(var f in mstate) {
54154         
54155             var sortColumn = this.cm.findColumnIndex(f);
54156             
54157             if(sortColumn != -1){
54158                 var sortDir = mstate[f];        
54159                 hds.item(sortColumn).addClass(sc[sortDir == "DESC" ? 1 : 0]);
54160             }
54161         }
54162         
54163          
54164         
54165     },
54166
54167
54168     handleHeaderClick : function(g, index,e){
54169         
54170         Roo.log("header click");
54171         
54172         if (Roo.isTouch) {
54173             // touch events on header are handled by context
54174             this.handleHdCtx(g,index,e);
54175             return;
54176         }
54177         
54178         
54179         if(this.headersDisabled){
54180             return;
54181         }
54182         var dm = g.dataSource, cm = g.colModel;
54183         if(!cm.isSortable(index)){
54184             return;
54185         }
54186         g.stopEditing();
54187         
54188         if (dm.multiSort) {
54189             // update the sortOrder
54190             var so = [];
54191             for(var i = 0; i < cm.config.length; i++ ) {
54192                 
54193                 if ((typeof(dm.sortToggle[cm.config[i].dataIndex]) == 'undefined') && (index != i)) {
54194                     continue; // dont' bother, it's not in sort list or being set.
54195                 }
54196                 
54197                 so.push(cm.config[i].dataIndex);
54198             };
54199             dm.sortOrder = so;
54200         }
54201         
54202         
54203         dm.sort(cm.getDataIndex(index));
54204     },
54205
54206
54207     destroy : function(){
54208         if(this.colMenu){
54209             this.colMenu.removeAll();
54210             Roo.menu.MenuMgr.unregister(this.colMenu);
54211             this.colMenu.getEl().remove();
54212             delete this.colMenu;
54213         }
54214         if(this.hmenu){
54215             this.hmenu.removeAll();
54216             Roo.menu.MenuMgr.unregister(this.hmenu);
54217             this.hmenu.getEl().remove();
54218             delete this.hmenu;
54219         }
54220         if(this.grid.enableColumnMove){
54221             var dds = Roo.dd.DDM.ids['gridHeader' + this.grid.getGridEl().id];
54222             if(dds){
54223                 for(var dd in dds){
54224                     if(!dds[dd].config.isTarget && dds[dd].dragElId){
54225                         var elid = dds[dd].dragElId;
54226                         dds[dd].unreg();
54227                         Roo.get(elid).remove();
54228                     } else if(dds[dd].config.isTarget){
54229                         dds[dd].proxyTop.remove();
54230                         dds[dd].proxyBottom.remove();
54231                         dds[dd].unreg();
54232                     }
54233                     if(Roo.dd.DDM.locationCache[dd]){
54234                         delete Roo.dd.DDM.locationCache[dd];
54235                     }
54236                 }
54237                 delete Roo.dd.DDM.ids['gridHeader' + this.grid.getGridEl().id];
54238             }
54239         }
54240         Roo.util.CSS.removeStyleSheet(this.idToCssName(this.grid.id) + '-cssrules');
54241         this.bind(null, null);
54242         Roo.EventManager.removeResizeListener(this.onWindowResize, this);
54243     },
54244
54245     handleLockChange : function(){
54246         this.refresh(true);
54247     },
54248
54249     onDenyColumnLock : function(){
54250
54251     },
54252
54253     onDenyColumnHide : function(){
54254
54255     },
54256
54257     handleHdMenuClick : function(item){
54258         var index = this.hdCtxIndex;
54259         var cm = this.cm, ds = this.ds;
54260         switch(item.id){
54261             case "asc":
54262                 ds.sort(cm.getDataIndex(index), "ASC");
54263                 break;
54264             case "desc":
54265                 ds.sort(cm.getDataIndex(index), "DESC");
54266                 break;
54267             case "lock":
54268                 var lc = cm.getLockedCount();
54269                 if(cm.getColumnCount(true) <= lc+1){
54270                     this.onDenyColumnLock();
54271                     return;
54272                 }
54273                 if(lc != index){
54274                     cm.setLocked(index, true, true);
54275                     cm.moveColumn(index, lc);
54276                     this.grid.fireEvent("columnmove", index, lc);
54277                 }else{
54278                     cm.setLocked(index, true);
54279                 }
54280             break;
54281             case "unlock":
54282                 var lc = cm.getLockedCount();
54283                 if((lc-1) != index){
54284                     cm.setLocked(index, false, true);
54285                     cm.moveColumn(index, lc-1);
54286                     this.grid.fireEvent("columnmove", index, lc-1);
54287                 }else{
54288                     cm.setLocked(index, false);
54289                 }
54290             break;
54291             case 'wider': // used to expand cols on touch..
54292             case 'narrow':
54293                 var cw = cm.getColumnWidth(index);
54294                 cw += (item.id == 'wider' ? 1 : -1) * 50;
54295                 cw = Math.max(0, cw);
54296                 cw = Math.min(cw,4000);
54297                 cm.setColumnWidth(index, cw);
54298                 break;
54299                 
54300             default:
54301                 index = cm.getIndexById(item.id.substr(4));
54302                 if(index != -1){
54303                     if(item.checked && cm.getColumnCount(true) <= 1){
54304                         this.onDenyColumnHide();
54305                         return false;
54306                     }
54307                     cm.setHidden(index, item.checked);
54308                 }
54309         }
54310         return true;
54311     },
54312
54313     beforeColMenuShow : function(){
54314         var cm = this.cm,  colCount = cm.getColumnCount();
54315         this.colMenu.removeAll();
54316         for(var i = 0; i < colCount; i++){
54317             this.colMenu.add(new Roo.menu.CheckItem({
54318                 id: "col-"+cm.getColumnId(i),
54319                 text: cm.getColumnHeader(i),
54320                 checked: !cm.isHidden(i),
54321                 hideOnClick:false
54322             }));
54323         }
54324     },
54325
54326     handleHdCtx : function(g, index, e){
54327         e.stopEvent();
54328         var hd = this.getHeaderCell(index);
54329         this.hdCtxIndex = index;
54330         var ms = this.hmenu.items, cm = this.cm;
54331         ms.get("asc").setDisabled(!cm.isSortable(index));
54332         ms.get("desc").setDisabled(!cm.isSortable(index));
54333         if(this.grid.enableColLock !== false){
54334             ms.get("lock").setDisabled(cm.isLocked(index));
54335             ms.get("unlock").setDisabled(!cm.isLocked(index));
54336         }
54337         this.hmenu.show(hd, "tl-bl");
54338     },
54339
54340     handleHdOver : function(e){
54341         var hd = this.findHeaderCell(e.getTarget());
54342         if(hd && !this.headersDisabled){
54343             if(this.grid.colModel.isSortable(this.getCellIndex(hd))){
54344                this.fly(hd).addClass("x-grid-hd-over");
54345             }
54346         }
54347     },
54348
54349     handleHdOut : function(e){
54350         var hd = this.findHeaderCell(e.getTarget());
54351         if(hd){
54352             this.fly(hd).removeClass("x-grid-hd-over");
54353         }
54354     },
54355
54356     handleSplitDblClick : function(e, t){
54357         var i = this.getCellIndex(t);
54358         if(this.grid.enableColumnResize !== false && this.cm.isResizable(i) && !this.cm.isFixed(i)){
54359             this.autoSizeColumn(i, true);
54360             this.layout();
54361         }
54362     },
54363
54364     render : function(){
54365
54366         var cm = this.cm;
54367         var colCount = cm.getColumnCount();
54368
54369         if(this.grid.monitorWindowResize === true){
54370             Roo.EventManager.onWindowResize(this.onWindowResize, this, true);
54371         }
54372         var header = this.renderHeaders();
54373         var body = this.templates.body.apply({rows:""});
54374         var html = this.templates.master.apply({
54375             lockedBody: body,
54376             body: body,
54377             lockedHeader: header[0],
54378             header: header[1]
54379         });
54380
54381         //this.updateColumns();
54382
54383         this.grid.getGridEl().dom.innerHTML = html;
54384
54385         this.initElements();
54386         
54387         // a kludge to fix the random scolling effect in webkit
54388         this.el.on("scroll", function() {
54389             this.el.dom.scrollTop=0; // hopefully not recursive..
54390         },this);
54391
54392         this.scroller.on("scroll", this.handleScroll, this);
54393         this.lockedBody.on("mousewheel", this.handleWheel, this);
54394         this.mainBody.on("mousewheel", this.handleWheel, this);
54395
54396         this.mainHd.on("mouseover", this.handleHdOver, this);
54397         this.mainHd.on("mouseout", this.handleHdOut, this);
54398         this.mainHd.on("dblclick", this.handleSplitDblClick, this,
54399                 {delegate: "."+this.splitClass});
54400
54401         this.lockedHd.on("mouseover", this.handleHdOver, this);
54402         this.lockedHd.on("mouseout", this.handleHdOut, this);
54403         this.lockedHd.on("dblclick", this.handleSplitDblClick, this,
54404                 {delegate: "."+this.splitClass});
54405
54406         if(this.grid.enableColumnResize !== false && Roo.grid.SplitDragZone){
54407             new Roo.grid.SplitDragZone(this.grid, this.lockedHd.dom, this.mainHd.dom);
54408         }
54409
54410         this.updateSplitters();
54411
54412         if(this.grid.enableColumnMove && Roo.grid.HeaderDragZone){
54413             new Roo.grid.HeaderDragZone(this.grid, this.lockedHd.dom, this.mainHd.dom);
54414             new Roo.grid.HeaderDropZone(this.grid, this.lockedHd.dom, this.mainHd.dom);
54415         }
54416
54417         if(this.grid.enableCtxMenu !== false && Roo.menu.Menu){
54418             this.hmenu = new Roo.menu.Menu({id: this.grid.id + "-hctx"});
54419             this.hmenu.add(
54420                 {id:"asc", text: this.sortAscText, cls: "xg-hmenu-sort-asc"},
54421                 {id:"desc", text: this.sortDescText, cls: "xg-hmenu-sort-desc"}
54422             );
54423             if(this.grid.enableColLock !== false){
54424                 this.hmenu.add('-',
54425                     {id:"lock", text: this.lockText, cls: "xg-hmenu-lock"},
54426                     {id:"unlock", text: this.unlockText, cls: "xg-hmenu-unlock"}
54427                 );
54428             }
54429             if (Roo.isTouch) {
54430                  this.hmenu.add('-',
54431                     {id:"wider", text: this.columnsWiderText},
54432                     {id:"narrow", text: this.columnsNarrowText }
54433                 );
54434                 
54435                  
54436             }
54437             
54438             if(this.grid.enableColumnHide !== false){
54439
54440                 this.colMenu = new Roo.menu.Menu({id:this.grid.id + "-hcols-menu"});
54441                 this.colMenu.on("beforeshow", this.beforeColMenuShow, this);
54442                 this.colMenu.on("itemclick", this.handleHdMenuClick, this);
54443
54444                 this.hmenu.add('-',
54445                     {id:"columns", text: this.columnsText, menu: this.colMenu}
54446                 );
54447             }
54448             this.hmenu.on("itemclick", this.handleHdMenuClick, this);
54449
54450             this.grid.on("headercontextmenu", this.handleHdCtx, this);
54451         }
54452
54453         if((this.grid.enableDragDrop || this.grid.enableDrag) && Roo.grid.GridDragZone){
54454             this.dd = new Roo.grid.GridDragZone(this.grid, {
54455                 ddGroup : this.grid.ddGroup || 'GridDD'
54456             });
54457             
54458         }
54459
54460         /*
54461         for(var i = 0; i < colCount; i++){
54462             if(cm.isHidden(i)){
54463                 this.hideColumn(i);
54464             }
54465             if(cm.config[i].align){
54466                 this.css.updateRule(this.colSelector + i, "textAlign", cm.config[i].align);
54467                 this.css.updateRule(this.hdSelector + i, "textAlign", cm.config[i].align);
54468             }
54469         }*/
54470         
54471         this.updateHeaderSortState();
54472
54473         this.beforeInitialResize();
54474         this.layout(true);
54475
54476         // two part rendering gives faster view to the user
54477         this.renderPhase2.defer(1, this);
54478     },
54479
54480     renderPhase2 : function(){
54481         // render the rows now
54482         this.refresh();
54483         if(this.grid.autoSizeColumns){
54484             this.autoSizeColumns();
54485         }
54486     },
54487
54488     beforeInitialResize : function(){
54489
54490     },
54491
54492     onColumnSplitterMoved : function(i, w){
54493         this.userResized = true;
54494         var cm = this.grid.colModel;
54495         cm.setColumnWidth(i, w, true);
54496         var cid = cm.getColumnId(i);
54497         this.css.updateRule(this.colSelector + this.idToCssName(cid), "width", (w-this.borderWidth) + "px");
54498         this.css.updateRule(this.hdSelector + this.idToCssName(cid), "width", (w-this.borderWidth) + "px");
54499         this.updateSplitters();
54500         this.layout();
54501         this.grid.fireEvent("columnresize", i, w);
54502     },
54503
54504     syncRowHeights : function(startIndex, endIndex){
54505         if(this.grid.enableRowHeightSync === true && this.cm.getLockedCount() > 0){
54506             startIndex = startIndex || 0;
54507             var mrows = this.getBodyTable().rows;
54508             var lrows = this.getLockedTable().rows;
54509             var len = mrows.length-1;
54510             endIndex = Math.min(endIndex || len, len);
54511             for(var i = startIndex; i <= endIndex; i++){
54512                 var m = mrows[i], l = lrows[i];
54513                 var h = Math.max(m.offsetHeight, l.offsetHeight);
54514                 m.style.height = l.style.height = h + "px";
54515             }
54516         }
54517     },
54518
54519     layout : function(initialRender, is2ndPass){
54520         var g = this.grid;
54521         var auto = g.autoHeight;
54522         var scrollOffset = 16;
54523         var c = g.getGridEl(), cm = this.cm,
54524                 expandCol = g.autoExpandColumn,
54525                 gv = this;
54526         //c.beginMeasure();
54527
54528         if(!c.dom.offsetWidth){ // display:none?
54529             if(initialRender){
54530                 this.lockedWrap.show();
54531                 this.mainWrap.show();
54532             }
54533             return;
54534         }
54535
54536         var hasLock = this.cm.isLocked(0);
54537
54538         var tbh = this.headerPanel.getHeight();
54539         var bbh = this.footerPanel.getHeight();
54540
54541         if(auto){
54542             var ch = this.getBodyTable().offsetHeight + tbh + bbh + this.mainHd.getHeight();
54543             var newHeight = ch + c.getBorderWidth("tb");
54544             if(g.maxHeight){
54545                 newHeight = Math.min(g.maxHeight, newHeight);
54546             }
54547             c.setHeight(newHeight);
54548         }
54549
54550         if(g.autoWidth){
54551             c.setWidth(cm.getTotalWidth()+c.getBorderWidth('lr'));
54552         }
54553
54554         var s = this.scroller;
54555
54556         var csize = c.getSize(true);
54557
54558         this.el.setSize(csize.width, csize.height);
54559
54560         this.headerPanel.setWidth(csize.width);
54561         this.footerPanel.setWidth(csize.width);
54562
54563         var hdHeight = this.mainHd.getHeight();
54564         var vw = csize.width;
54565         var vh = csize.height - (tbh + bbh);
54566
54567         s.setSize(vw, vh);
54568
54569         var bt = this.getBodyTable();
54570         var ltWidth = hasLock ?
54571                       Math.max(this.getLockedTable().offsetWidth, this.lockedHd.dom.firstChild.offsetWidth) : 0;
54572
54573         var scrollHeight = bt.offsetHeight;
54574         var scrollWidth = ltWidth + bt.offsetWidth;
54575         var vscroll = false, hscroll = false;
54576
54577         this.scrollSizer.setSize(scrollWidth, scrollHeight+hdHeight);
54578
54579         var lw = this.lockedWrap, mw = this.mainWrap;
54580         var lb = this.lockedBody, mb = this.mainBody;
54581
54582         setTimeout(function(){
54583             var t = s.dom.offsetTop;
54584             var w = s.dom.clientWidth,
54585                 h = s.dom.clientHeight;
54586
54587             lw.setTop(t);
54588             lw.setSize(ltWidth, h);
54589
54590             mw.setLeftTop(ltWidth, t);
54591             mw.setSize(w-ltWidth, h);
54592
54593             lb.setHeight(h-hdHeight);
54594             mb.setHeight(h-hdHeight);
54595
54596             if(is2ndPass !== true && !gv.userResized && expandCol){
54597                 // high speed resize without full column calculation
54598                 
54599                 var ci = cm.getIndexById(expandCol);
54600                 if (ci < 0) {
54601                     ci = cm.findColumnIndex(expandCol);
54602                 }
54603                 ci = Math.max(0, ci); // make sure it's got at least the first col.
54604                 var expandId = cm.getColumnId(ci);
54605                 var  tw = cm.getTotalWidth(false);
54606                 var currentWidth = cm.getColumnWidth(ci);
54607                 var cw = Math.min(Math.max(((w-tw)+currentWidth-2)-/*scrollbar*/(w <= s.dom.offsetWidth ? 0 : 18), g.autoExpandMin), g.autoExpandMax);
54608                 if(currentWidth != cw){
54609                     cm.setColumnWidth(ci, cw, true);
54610                     gv.css.updateRule(gv.colSelector+gv.idToCssName(expandId), "width", (cw - gv.borderWidth) + "px");
54611                     gv.css.updateRule(gv.hdSelector+gv.idToCssName(expandId), "width", (cw - gv.borderWidth) + "px");
54612                     gv.updateSplitters();
54613                     gv.layout(false, true);
54614                 }
54615             }
54616
54617             if(initialRender){
54618                 lw.show();
54619                 mw.show();
54620             }
54621             //c.endMeasure();
54622         }, 10);
54623     },
54624
54625     onWindowResize : function(){
54626         if(!this.grid.monitorWindowResize || this.grid.autoHeight){
54627             return;
54628         }
54629         this.layout();
54630     },
54631
54632     appendFooter : function(parentEl){
54633         return null;
54634     },
54635
54636     sortAscText : "Sort Ascending",
54637     sortDescText : "Sort Descending",
54638     lockText : "Lock Column",
54639     unlockText : "Unlock Column",
54640     columnsText : "Columns",
54641  
54642     columnsWiderText : "Wider",
54643     columnsNarrowText : "Thinner"
54644 });
54645
54646
54647 Roo.grid.GridView.ColumnDragZone = function(grid, hd){
54648     Roo.grid.GridView.ColumnDragZone.superclass.constructor.call(this, grid, hd, null);
54649     this.proxy.el.addClass('x-grid3-col-dd');
54650 };
54651
54652 Roo.extend(Roo.grid.GridView.ColumnDragZone, Roo.grid.HeaderDragZone, {
54653     handleMouseDown : function(e){
54654
54655     },
54656
54657     callHandleMouseDown : function(e){
54658         Roo.grid.GridView.ColumnDragZone.superclass.handleMouseDown.call(this, e);
54659     }
54660 });
54661 /*
54662  * Based on:
54663  * Ext JS Library 1.1.1
54664  * Copyright(c) 2006-2007, Ext JS, LLC.
54665  *
54666  * Originally Released Under LGPL - original licence link has changed is not relivant.
54667  *
54668  * Fork - LGPL
54669  * <script type="text/javascript">
54670  */
54671  
54672 // private
54673 // This is a support class used internally by the Grid components
54674 Roo.grid.SplitDragZone = function(grid, hd, hd2){
54675     this.grid = grid;
54676     this.view = grid.getView();
54677     this.proxy = this.view.resizeProxy;
54678     Roo.grid.SplitDragZone.superclass.constructor.call(this, hd,
54679         "gridSplitters" + this.grid.getGridEl().id, {
54680         dragElId : Roo.id(this.proxy.dom), resizeFrame:false
54681     });
54682     this.setHandleElId(Roo.id(hd));
54683     this.setOuterHandleElId(Roo.id(hd2));
54684     this.scroll = false;
54685 };
54686 Roo.extend(Roo.grid.SplitDragZone, Roo.dd.DDProxy, {
54687     fly: Roo.Element.fly,
54688
54689     b4StartDrag : function(x, y){
54690         this.view.headersDisabled = true;
54691         this.proxy.setHeight(this.view.mainWrap.getHeight());
54692         var w = this.cm.getColumnWidth(this.cellIndex);
54693         var minw = Math.max(w-this.grid.minColumnWidth, 0);
54694         this.resetConstraints();
54695         this.setXConstraint(minw, 1000);
54696         this.setYConstraint(0, 0);
54697         this.minX = x - minw;
54698         this.maxX = x + 1000;
54699         this.startPos = x;
54700         Roo.dd.DDProxy.prototype.b4StartDrag.call(this, x, y);
54701     },
54702
54703
54704     handleMouseDown : function(e){
54705         ev = Roo.EventObject.setEvent(e);
54706         var t = this.fly(ev.getTarget());
54707         if(t.hasClass("x-grid-split")){
54708             this.cellIndex = this.view.getCellIndex(t.dom);
54709             this.split = t.dom;
54710             this.cm = this.grid.colModel;
54711             if(this.cm.isResizable(this.cellIndex) && !this.cm.isFixed(this.cellIndex)){
54712                 Roo.grid.SplitDragZone.superclass.handleMouseDown.apply(this, arguments);
54713             }
54714         }
54715     },
54716
54717     endDrag : function(e){
54718         this.view.headersDisabled = false;
54719         var endX = Math.max(this.minX, Roo.lib.Event.getPageX(e));
54720         var diff = endX - this.startPos;
54721         this.view.onColumnSplitterMoved(this.cellIndex, this.cm.getColumnWidth(this.cellIndex)+diff);
54722     },
54723
54724     autoOffset : function(){
54725         this.setDelta(0,0);
54726     }
54727 });/*
54728  * Based on:
54729  * Ext JS Library 1.1.1
54730  * Copyright(c) 2006-2007, Ext JS, LLC.
54731  *
54732  * Originally Released Under LGPL - original licence link has changed is not relivant.
54733  *
54734  * Fork - LGPL
54735  * <script type="text/javascript">
54736  */
54737  
54738 // private
54739 // This is a support class used internally by the Grid components
54740 Roo.grid.GridDragZone = function(grid, config){
54741     this.view = grid.getView();
54742     Roo.grid.GridDragZone.superclass.constructor.call(this, this.view.mainBody.dom, config);
54743     if(this.view.lockedBody){
54744         this.setHandleElId(Roo.id(this.view.mainBody.dom));
54745         this.setOuterHandleElId(Roo.id(this.view.lockedBody.dom));
54746     }
54747     this.scroll = false;
54748     this.grid = grid;
54749     this.ddel = document.createElement('div');
54750     this.ddel.className = 'x-grid-dd-wrap';
54751 };
54752
54753 Roo.extend(Roo.grid.GridDragZone, Roo.dd.DragZone, {
54754     ddGroup : "GridDD",
54755
54756     getDragData : function(e){
54757         var t = Roo.lib.Event.getTarget(e);
54758         var rowIndex = this.view.findRowIndex(t);
54759         var sm = this.grid.selModel;
54760             
54761         //Roo.log(rowIndex);
54762         
54763         if (sm.getSelectedCell) {
54764             // cell selection..
54765             if (!sm.getSelectedCell()) {
54766                 return false;
54767             }
54768             if (rowIndex != sm.getSelectedCell()[0]) {
54769                 return false;
54770             }
54771         
54772         }
54773         
54774         if(rowIndex !== false){
54775             
54776             // if editorgrid.. 
54777             
54778             
54779             //Roo.log([ sm.getSelectedCell() ? sm.getSelectedCell()[0] : 'NO' , rowIndex ]);
54780                
54781             //if(!sm.isSelected(rowIndex) || e.hasModifier()){
54782               //  
54783             //}
54784             if (e.hasModifier()){
54785                 sm.handleMouseDown(e, t); // non modifier buttons are handled by row select.
54786             }
54787             
54788             Roo.log("getDragData");
54789             
54790             return {
54791                 grid: this.grid,
54792                 ddel: this.ddel,
54793                 rowIndex: rowIndex,
54794                 selections:sm.getSelections ? sm.getSelections() : (
54795                     sm.getSelectedCell() ? [ this.grid.ds.getAt(sm.getSelectedCell()[0]) ] : []
54796                 )
54797             };
54798         }
54799         return false;
54800     },
54801
54802     onInitDrag : function(e){
54803         var data = this.dragData;
54804         this.ddel.innerHTML = this.grid.getDragDropText();
54805         this.proxy.update(this.ddel);
54806         // fire start drag?
54807     },
54808
54809     afterRepair : function(){
54810         this.dragging = false;
54811     },
54812
54813     getRepairXY : function(e, data){
54814         return false;
54815     },
54816
54817     onEndDrag : function(data, e){
54818         // fire end drag?
54819     },
54820
54821     onValidDrop : function(dd, e, id){
54822         // fire drag drop?
54823         this.hideProxy();
54824     },
54825
54826     beforeInvalidDrop : function(e, id){
54827
54828     }
54829 });/*
54830  * Based on:
54831  * Ext JS Library 1.1.1
54832  * Copyright(c) 2006-2007, Ext JS, LLC.
54833  *
54834  * Originally Released Under LGPL - original licence link has changed is not relivant.
54835  *
54836  * Fork - LGPL
54837  * <script type="text/javascript">
54838  */
54839  
54840
54841 /**
54842  * @class Roo.grid.ColumnModel
54843  * @extends Roo.util.Observable
54844  * This is the default implementation of a ColumnModel used by the Grid. It defines
54845  * the columns in the grid.
54846  * <br>Usage:<br>
54847  <pre><code>
54848  var colModel = new Roo.grid.ColumnModel([
54849         {header: "Ticker", width: 60, sortable: true, locked: true},
54850         {header: "Company Name", width: 150, sortable: true},
54851         {header: "Market Cap.", width: 100, sortable: true},
54852         {header: "$ Sales", width: 100, sortable: true, renderer: money},
54853         {header: "Employees", width: 100, sortable: true, resizable: false}
54854  ]);
54855  </code></pre>
54856  * <p>
54857  
54858  * The config options listed for this class are options which may appear in each
54859  * individual column definition.
54860  * <br/>RooJS Fix - column id's are not sequential but use Roo.id() - fixes bugs with layouts.
54861  * @constructor
54862  * @param {Object} config An Array of column config objects. See this class's
54863  * config objects for details.
54864 */
54865 Roo.grid.ColumnModel = function(config){
54866         /**
54867      * The config passed into the constructor
54868      */
54869     this.config = config;
54870     this.lookup = {};
54871
54872     // if no id, create one
54873     // if the column does not have a dataIndex mapping,
54874     // map it to the order it is in the config
54875     for(var i = 0, len = config.length; i < len; i++){
54876         var c = config[i];
54877         if(typeof c.dataIndex == "undefined"){
54878             c.dataIndex = i;
54879         }
54880         if(typeof c.renderer == "string"){
54881             c.renderer = Roo.util.Format[c.renderer];
54882         }
54883         if(typeof c.id == "undefined"){
54884             c.id = Roo.id();
54885         }
54886         if(c.editor && c.editor.xtype){
54887             c.editor  = Roo.factory(c.editor, Roo.grid);
54888         }
54889         if(c.editor && c.editor.isFormField){
54890             c.editor = new Roo.grid.GridEditor(c.editor);
54891         }
54892         this.lookup[c.id] = c;
54893     }
54894
54895     /**
54896      * The width of columns which have no width specified (defaults to 100)
54897      * @type Number
54898      */
54899     this.defaultWidth = 100;
54900
54901     /**
54902      * Default sortable of columns which have no sortable specified (defaults to false)
54903      * @type Boolean
54904      */
54905     this.defaultSortable = false;
54906
54907     this.addEvents({
54908         /**
54909              * @event widthchange
54910              * Fires when the width of a column changes.
54911              * @param {ColumnModel} this
54912              * @param {Number} columnIndex The column index
54913              * @param {Number} newWidth The new width
54914              */
54915             "widthchange": true,
54916         /**
54917              * @event headerchange
54918              * Fires when the text of a header changes.
54919              * @param {ColumnModel} this
54920              * @param {Number} columnIndex The column index
54921              * @param {Number} newText The new header text
54922              */
54923             "headerchange": true,
54924         /**
54925              * @event hiddenchange
54926              * Fires when a column is hidden or "unhidden".
54927              * @param {ColumnModel} this
54928              * @param {Number} columnIndex The column index
54929              * @param {Boolean} hidden true if hidden, false otherwise
54930              */
54931             "hiddenchange": true,
54932             /**
54933          * @event columnmoved
54934          * Fires when a column is moved.
54935          * @param {ColumnModel} this
54936          * @param {Number} oldIndex
54937          * @param {Number} newIndex
54938          */
54939         "columnmoved" : true,
54940         /**
54941          * @event columlockchange
54942          * Fires when a column's locked state is changed
54943          * @param {ColumnModel} this
54944          * @param {Number} colIndex
54945          * @param {Boolean} locked true if locked
54946          */
54947         "columnlockchange" : true
54948     });
54949     Roo.grid.ColumnModel.superclass.constructor.call(this);
54950 };
54951 Roo.extend(Roo.grid.ColumnModel, Roo.util.Observable, {
54952     /**
54953      * @cfg {String} header The header text to display in the Grid view.
54954      */
54955     /**
54956      * @cfg {String} dataIndex (Optional) The name of the field in the grid's {@link Roo.data.Store}'s
54957      * {@link Roo.data.Record} definition from which to draw the column's value. If not
54958      * specified, the column's index is used as an index into the Record's data Array.
54959      */
54960     /**
54961      * @cfg {Number} width (Optional) The initial width in pixels of the column. Using this
54962      * instead of {@link Roo.grid.Grid#autoSizeColumns} is more efficient.
54963      */
54964     /**
54965      * @cfg {Boolean} sortable (Optional) True if sorting is to be allowed on this column.
54966      * Defaults to the value of the {@link #defaultSortable} property.
54967      * Whether local/remote sorting is used is specified in {@link Roo.data.Store#remoteSort}.
54968      */
54969     /**
54970      * @cfg {Boolean} locked (Optional) True to lock the column in place while scrolling the Grid.  Defaults to false.
54971      */
54972     /**
54973      * @cfg {Boolean} fixed (Optional) True if the column width cannot be changed.  Defaults to false.
54974      */
54975     /**
54976      * @cfg {Boolean} resizable (Optional) False to disable column resizing. Defaults to true.
54977      */
54978     /**
54979      * @cfg {Boolean} hidden (Optional) True to hide the column. Defaults to false.
54980      */
54981     /**
54982      * @cfg {Function} renderer (Optional) A function used to generate HTML markup for a cell
54983      * given the cell's data value. See {@link #setRenderer}. If not specified, the
54984      * default renderer uses the raw data value. If an object is returned (bootstrap only)
54985      * then it is treated as a Roo Component object instance, and it is rendered after the initial row is rendered
54986      */
54987        /**
54988      * @cfg {Roo.grid.GridEditor} editor (Optional) For grid editors - returns the grid editor 
54989      */
54990     /**
54991      * @cfg {String} align (Optional) Set the CSS text-align property of the column.  Defaults to undefined.
54992      */
54993
54994     /**
54995      * Returns the id of the column at the specified index.
54996      * @param {Number} index The column index
54997      * @return {String} the id
54998      */
54999     getColumnId : function(index){
55000         return this.config[index].id;
55001     },
55002
55003     /**
55004      * Returns the column for a specified id.
55005      * @param {String} id The column id
55006      * @return {Object} the column
55007      */
55008     getColumnById : function(id){
55009         return this.lookup[id];
55010     },
55011
55012     
55013     /**
55014      * Returns the column for a specified dataIndex.
55015      * @param {String} dataIndex The column dataIndex
55016      * @return {Object|Boolean} the column or false if not found
55017      */
55018     getColumnByDataIndex: function(dataIndex){
55019         var index = this.findColumnIndex(dataIndex);
55020         return index > -1 ? this.config[index] : false;
55021     },
55022     
55023     /**
55024      * Returns the index for a specified column id.
55025      * @param {String} id The column id
55026      * @return {Number} the index, or -1 if not found
55027      */
55028     getIndexById : function(id){
55029         for(var i = 0, len = this.config.length; i < len; i++){
55030             if(this.config[i].id == id){
55031                 return i;
55032             }
55033         }
55034         return -1;
55035     },
55036     
55037     /**
55038      * Returns the index for a specified column dataIndex.
55039      * @param {String} dataIndex The column dataIndex
55040      * @return {Number} the index, or -1 if not found
55041      */
55042     
55043     findColumnIndex : function(dataIndex){
55044         for(var i = 0, len = this.config.length; i < len; i++){
55045             if(this.config[i].dataIndex == dataIndex){
55046                 return i;
55047             }
55048         }
55049         return -1;
55050     },
55051     
55052     
55053     moveColumn : function(oldIndex, newIndex){
55054         var c = this.config[oldIndex];
55055         this.config.splice(oldIndex, 1);
55056         this.config.splice(newIndex, 0, c);
55057         this.dataMap = null;
55058         this.fireEvent("columnmoved", this, oldIndex, newIndex);
55059     },
55060
55061     isLocked : function(colIndex){
55062         return this.config[colIndex].locked === true;
55063     },
55064
55065     setLocked : function(colIndex, value, suppressEvent){
55066         if(this.isLocked(colIndex) == value){
55067             return;
55068         }
55069         this.config[colIndex].locked = value;
55070         if(!suppressEvent){
55071             this.fireEvent("columnlockchange", this, colIndex, value);
55072         }
55073     },
55074
55075     getTotalLockedWidth : function(){
55076         var totalWidth = 0;
55077         for(var i = 0; i < this.config.length; i++){
55078             if(this.isLocked(i) && !this.isHidden(i)){
55079                 this.totalWidth += this.getColumnWidth(i);
55080             }
55081         }
55082         return totalWidth;
55083     },
55084
55085     getLockedCount : function(){
55086         for(var i = 0, len = this.config.length; i < len; i++){
55087             if(!this.isLocked(i)){
55088                 return i;
55089             }
55090         }
55091     },
55092
55093     /**
55094      * Returns the number of columns.
55095      * @return {Number}
55096      */
55097     getColumnCount : function(visibleOnly){
55098         if(visibleOnly === true){
55099             var c = 0;
55100             for(var i = 0, len = this.config.length; i < len; i++){
55101                 if(!this.isHidden(i)){
55102                     c++;
55103                 }
55104             }
55105             return c;
55106         }
55107         return this.config.length;
55108     },
55109
55110     /**
55111      * Returns the column configs that return true by the passed function that is called with (columnConfig, index)
55112      * @param {Function} fn
55113      * @param {Object} scope (optional)
55114      * @return {Array} result
55115      */
55116     getColumnsBy : function(fn, scope){
55117         var r = [];
55118         for(var i = 0, len = this.config.length; i < len; i++){
55119             var c = this.config[i];
55120             if(fn.call(scope||this, c, i) === true){
55121                 r[r.length] = c;
55122             }
55123         }
55124         return r;
55125     },
55126
55127     /**
55128      * Returns true if the specified column is sortable.
55129      * @param {Number} col The column index
55130      * @return {Boolean}
55131      */
55132     isSortable : function(col){
55133         if(typeof this.config[col].sortable == "undefined"){
55134             return this.defaultSortable;
55135         }
55136         return this.config[col].sortable;
55137     },
55138
55139     /**
55140      * Returns the rendering (formatting) function defined for the column.
55141      * @param {Number} col The column index.
55142      * @return {Function} The function used to render the cell. See {@link #setRenderer}.
55143      */
55144     getRenderer : function(col){
55145         if(!this.config[col].renderer){
55146             return Roo.grid.ColumnModel.defaultRenderer;
55147         }
55148         return this.config[col].renderer;
55149     },
55150
55151     /**
55152      * Sets the rendering (formatting) function for a column.
55153      * @param {Number} col The column index
55154      * @param {Function} fn The function to use to process the cell's raw data
55155      * to return HTML markup for the grid view. The render function is called with
55156      * the following parameters:<ul>
55157      * <li>Data value.</li>
55158      * <li>Cell metadata. An object in which you may set the following attributes:<ul>
55159      * <li>css A CSS style string to apply to the table cell.</li>
55160      * <li>attr An HTML attribute definition string to apply to the data container element <i>within</i> the table cell.</li></ul>
55161      * <li>The {@link Roo.data.Record} from which the data was extracted.</li>
55162      * <li>Row index</li>
55163      * <li>Column index</li>
55164      * <li>The {@link Roo.data.Store} object from which the Record was extracted</li></ul>
55165      */
55166     setRenderer : function(col, fn){
55167         this.config[col].renderer = fn;
55168     },
55169
55170     /**
55171      * Returns the width for the specified column.
55172      * @param {Number} col The column index
55173      * @return {Number}
55174      */
55175     getColumnWidth : function(col){
55176         return this.config[col].width * 1 || this.defaultWidth;
55177     },
55178
55179     /**
55180      * Sets the width for a column.
55181      * @param {Number} col The column index
55182      * @param {Number} width The new width
55183      */
55184     setColumnWidth : function(col, width, suppressEvent){
55185         this.config[col].width = width;
55186         this.totalWidth = null;
55187         if(!suppressEvent){
55188              this.fireEvent("widthchange", this, col, width);
55189         }
55190     },
55191
55192     /**
55193      * Returns the total width of all columns.
55194      * @param {Boolean} includeHidden True to include hidden column widths
55195      * @return {Number}
55196      */
55197     getTotalWidth : function(includeHidden){
55198         if(!this.totalWidth){
55199             this.totalWidth = 0;
55200             for(var i = 0, len = this.config.length; i < len; i++){
55201                 if(includeHidden || !this.isHidden(i)){
55202                     this.totalWidth += this.getColumnWidth(i);
55203                 }
55204             }
55205         }
55206         return this.totalWidth;
55207     },
55208
55209     /**
55210      * Returns the header for the specified column.
55211      * @param {Number} col The column index
55212      * @return {String}
55213      */
55214     getColumnHeader : function(col){
55215         return this.config[col].header;
55216     },
55217
55218     /**
55219      * Sets the header for a column.
55220      * @param {Number} col The column index
55221      * @param {String} header The new header
55222      */
55223     setColumnHeader : function(col, header){
55224         this.config[col].header = header;
55225         this.fireEvent("headerchange", this, col, header);
55226     },
55227
55228     /**
55229      * Returns the tooltip for the specified column.
55230      * @param {Number} col The column index
55231      * @return {String}
55232      */
55233     getColumnTooltip : function(col){
55234             return this.config[col].tooltip;
55235     },
55236     /**
55237      * Sets the tooltip for a column.
55238      * @param {Number} col The column index
55239      * @param {String} tooltip The new tooltip
55240      */
55241     setColumnTooltip : function(col, tooltip){
55242             this.config[col].tooltip = tooltip;
55243     },
55244
55245     /**
55246      * Returns the dataIndex for the specified column.
55247      * @param {Number} col The column index
55248      * @return {Number}
55249      */
55250     getDataIndex : function(col){
55251         return this.config[col].dataIndex;
55252     },
55253
55254     /**
55255      * Sets the dataIndex for a column.
55256      * @param {Number} col The column index
55257      * @param {Number} dataIndex The new dataIndex
55258      */
55259     setDataIndex : function(col, dataIndex){
55260         this.config[col].dataIndex = dataIndex;
55261     },
55262
55263     
55264     
55265     /**
55266      * Returns true if the cell is editable.
55267      * @param {Number} colIndex The column index
55268      * @param {Number} rowIndex The row index
55269      * @return {Boolean}
55270      */
55271     isCellEditable : function(colIndex, rowIndex){
55272         return (this.config[colIndex].editable || (typeof this.config[colIndex].editable == "undefined" && this.config[colIndex].editor)) ? true : false;
55273     },
55274
55275     /**
55276      * Returns the editor defined for the cell/column.
55277      * return false or null to disable editing.
55278      * @param {Number} colIndex The column index
55279      * @param {Number} rowIndex The row index
55280      * @return {Object}
55281      */
55282     getCellEditor : function(colIndex, rowIndex){
55283         return this.config[colIndex].editor;
55284     },
55285
55286     /**
55287      * Sets if a column is editable.
55288      * @param {Number} col The column index
55289      * @param {Boolean} editable True if the column is editable
55290      */
55291     setEditable : function(col, editable){
55292         this.config[col].editable = editable;
55293     },
55294
55295
55296     /**
55297      * Returns true if the column is hidden.
55298      * @param {Number} colIndex The column index
55299      * @return {Boolean}
55300      */
55301     isHidden : function(colIndex){
55302         return this.config[colIndex].hidden;
55303     },
55304
55305
55306     /**
55307      * Returns true if the column width cannot be changed
55308      */
55309     isFixed : function(colIndex){
55310         return this.config[colIndex].fixed;
55311     },
55312
55313     /**
55314      * Returns true if the column can be resized
55315      * @return {Boolean}
55316      */
55317     isResizable : function(colIndex){
55318         return colIndex >= 0 && this.config[colIndex].resizable !== false && this.config[colIndex].fixed !== true;
55319     },
55320     /**
55321      * Sets if a column is hidden.
55322      * @param {Number} colIndex The column index
55323      * @param {Boolean} hidden True if the column is hidden
55324      */
55325     setHidden : function(colIndex, hidden){
55326         this.config[colIndex].hidden = hidden;
55327         this.totalWidth = null;
55328         this.fireEvent("hiddenchange", this, colIndex, hidden);
55329     },
55330
55331     /**
55332      * Sets the editor for a column.
55333      * @param {Number} col The column index
55334      * @param {Object} editor The editor object
55335      */
55336     setEditor : function(col, editor){
55337         this.config[col].editor = editor;
55338     }
55339 });
55340
55341 Roo.grid.ColumnModel.defaultRenderer = function(value){
55342         if(typeof value == "string" && value.length < 1){
55343             return "&#160;";
55344         }
55345         return value;
55346 };
55347
55348 // Alias for backwards compatibility
55349 Roo.grid.DefaultColumnModel = Roo.grid.ColumnModel;
55350 /*
55351  * Based on:
55352  * Ext JS Library 1.1.1
55353  * Copyright(c) 2006-2007, Ext JS, LLC.
55354  *
55355  * Originally Released Under LGPL - original licence link has changed is not relivant.
55356  *
55357  * Fork - LGPL
55358  * <script type="text/javascript">
55359  */
55360
55361 /**
55362  * @class Roo.grid.AbstractSelectionModel
55363  * @extends Roo.util.Observable
55364  * Abstract base class for grid SelectionModels.  It provides the interface that should be
55365  * implemented by descendant classes.  This class should not be directly instantiated.
55366  * @constructor
55367  */
55368 Roo.grid.AbstractSelectionModel = function(){
55369     this.locked = false;
55370     Roo.grid.AbstractSelectionModel.superclass.constructor.call(this);
55371 };
55372
55373 Roo.extend(Roo.grid.AbstractSelectionModel, Roo.util.Observable,  {
55374     /** @ignore Called by the grid automatically. Do not call directly. */
55375     init : function(grid){
55376         this.grid = grid;
55377         this.initEvents();
55378     },
55379
55380     /**
55381      * Locks the selections.
55382      */
55383     lock : function(){
55384         this.locked = true;
55385     },
55386
55387     /**
55388      * Unlocks the selections.
55389      */
55390     unlock : function(){
55391         this.locked = false;
55392     },
55393
55394     /**
55395      * Returns true if the selections are locked.
55396      * @return {Boolean}
55397      */
55398     isLocked : function(){
55399         return this.locked;
55400     }
55401 });/*
55402  * Based on:
55403  * Ext JS Library 1.1.1
55404  * Copyright(c) 2006-2007, Ext JS, LLC.
55405  *
55406  * Originally Released Under LGPL - original licence link has changed is not relivant.
55407  *
55408  * Fork - LGPL
55409  * <script type="text/javascript">
55410  */
55411 /**
55412  * @extends Roo.grid.AbstractSelectionModel
55413  * @class Roo.grid.RowSelectionModel
55414  * The default SelectionModel used by {@link Roo.grid.Grid}.
55415  * It supports multiple selections and keyboard selection/navigation. 
55416  * @constructor
55417  * @param {Object} config
55418  */
55419 Roo.grid.RowSelectionModel = function(config){
55420     Roo.apply(this, config);
55421     this.selections = new Roo.util.MixedCollection(false, function(o){
55422         return o.id;
55423     });
55424
55425     this.last = false;
55426     this.lastActive = false;
55427
55428     this.addEvents({
55429         /**
55430              * @event selectionchange
55431              * Fires when the selection changes
55432              * @param {SelectionModel} this
55433              */
55434             "selectionchange" : true,
55435         /**
55436              * @event afterselectionchange
55437              * Fires after the selection changes (eg. by key press or clicking)
55438              * @param {SelectionModel} this
55439              */
55440             "afterselectionchange" : true,
55441         /**
55442              * @event beforerowselect
55443              * Fires when a row is selected being selected, return false to cancel.
55444              * @param {SelectionModel} this
55445              * @param {Number} rowIndex The selected index
55446              * @param {Boolean} keepExisting False if other selections will be cleared
55447              */
55448             "beforerowselect" : true,
55449         /**
55450              * @event rowselect
55451              * Fires when a row is selected.
55452              * @param {SelectionModel} this
55453              * @param {Number} rowIndex The selected index
55454              * @param {Roo.data.Record} r The record
55455              */
55456             "rowselect" : true,
55457         /**
55458              * @event rowdeselect
55459              * Fires when a row is deselected.
55460              * @param {SelectionModel} this
55461              * @param {Number} rowIndex The selected index
55462              */
55463         "rowdeselect" : true
55464     });
55465     Roo.grid.RowSelectionModel.superclass.constructor.call(this);
55466     this.locked = false;
55467 };
55468
55469 Roo.extend(Roo.grid.RowSelectionModel, Roo.grid.AbstractSelectionModel,  {
55470     /**
55471      * @cfg {Boolean} singleSelect
55472      * True to allow selection of only one row at a time (defaults to false)
55473      */
55474     singleSelect : false,
55475
55476     // private
55477     initEvents : function(){
55478
55479         if(!this.grid.enableDragDrop && !this.grid.enableDrag){
55480             this.grid.on("mousedown", this.handleMouseDown, this);
55481         }else{ // allow click to work like normal
55482             this.grid.on("rowclick", this.handleDragableRowClick, this);
55483         }
55484
55485         this.rowNav = new Roo.KeyNav(this.grid.getGridEl(), {
55486             "up" : function(e){
55487                 if(!e.shiftKey){
55488                     this.selectPrevious(e.shiftKey);
55489                 }else if(this.last !== false && this.lastActive !== false){
55490                     var last = this.last;
55491                     this.selectRange(this.last,  this.lastActive-1);
55492                     this.grid.getView().focusRow(this.lastActive);
55493                     if(last !== false){
55494                         this.last = last;
55495                     }
55496                 }else{
55497                     this.selectFirstRow();
55498                 }
55499                 this.fireEvent("afterselectionchange", this);
55500             },
55501             "down" : function(e){
55502                 if(!e.shiftKey){
55503                     this.selectNext(e.shiftKey);
55504                 }else if(this.last !== false && this.lastActive !== false){
55505                     var last = this.last;
55506                     this.selectRange(this.last,  this.lastActive+1);
55507                     this.grid.getView().focusRow(this.lastActive);
55508                     if(last !== false){
55509                         this.last = last;
55510                     }
55511                 }else{
55512                     this.selectFirstRow();
55513                 }
55514                 this.fireEvent("afterselectionchange", this);
55515             },
55516             scope: this
55517         });
55518
55519         var view = this.grid.view;
55520         view.on("refresh", this.onRefresh, this);
55521         view.on("rowupdated", this.onRowUpdated, this);
55522         view.on("rowremoved", this.onRemove, this);
55523     },
55524
55525     // private
55526     onRefresh : function(){
55527         var ds = this.grid.dataSource, i, v = this.grid.view;
55528         var s = this.selections;
55529         s.each(function(r){
55530             if((i = ds.indexOfId(r.id)) != -1){
55531                 v.onRowSelect(i);
55532             }else{
55533                 s.remove(r);
55534             }
55535         });
55536     },
55537
55538     // private
55539     onRemove : function(v, index, r){
55540         this.selections.remove(r);
55541     },
55542
55543     // private
55544     onRowUpdated : function(v, index, r){
55545         if(this.isSelected(r)){
55546             v.onRowSelect(index);
55547         }
55548     },
55549
55550     /**
55551      * Select records.
55552      * @param {Array} records The records to select
55553      * @param {Boolean} keepExisting (optional) True to keep existing selections
55554      */
55555     selectRecords : function(records, keepExisting){
55556         if(!keepExisting){
55557             this.clearSelections();
55558         }
55559         var ds = this.grid.dataSource;
55560         for(var i = 0, len = records.length; i < len; i++){
55561             this.selectRow(ds.indexOf(records[i]), true);
55562         }
55563     },
55564
55565     /**
55566      * Gets the number of selected rows.
55567      * @return {Number}
55568      */
55569     getCount : function(){
55570         return this.selections.length;
55571     },
55572
55573     /**
55574      * Selects the first row in the grid.
55575      */
55576     selectFirstRow : function(){
55577         this.selectRow(0);
55578     },
55579
55580     /**
55581      * Select the last row.
55582      * @param {Boolean} keepExisting (optional) True to keep existing selections
55583      */
55584     selectLastRow : function(keepExisting){
55585         this.selectRow(this.grid.dataSource.getCount() - 1, keepExisting);
55586     },
55587
55588     /**
55589      * Selects the row immediately following the last selected row.
55590      * @param {Boolean} keepExisting (optional) True to keep existing selections
55591      */
55592     selectNext : function(keepExisting){
55593         if(this.last !== false && (this.last+1) < this.grid.dataSource.getCount()){
55594             this.selectRow(this.last+1, keepExisting);
55595             this.grid.getView().focusRow(this.last);
55596         }
55597     },
55598
55599     /**
55600      * Selects the row that precedes the last selected row.
55601      * @param {Boolean} keepExisting (optional) True to keep existing selections
55602      */
55603     selectPrevious : function(keepExisting){
55604         if(this.last){
55605             this.selectRow(this.last-1, keepExisting);
55606             this.grid.getView().focusRow(this.last);
55607         }
55608     },
55609
55610     /**
55611      * Returns the selected records
55612      * @return {Array} Array of selected records
55613      */
55614     getSelections : function(){
55615         return [].concat(this.selections.items);
55616     },
55617
55618     /**
55619      * Returns the first selected record.
55620      * @return {Record}
55621      */
55622     getSelected : function(){
55623         return this.selections.itemAt(0);
55624     },
55625
55626
55627     /**
55628      * Clears all selections.
55629      */
55630     clearSelections : function(fast){
55631         if(this.locked) return;
55632         if(fast !== true){
55633             var ds = this.grid.dataSource;
55634             var s = this.selections;
55635             s.each(function(r){
55636                 this.deselectRow(ds.indexOfId(r.id));
55637             }, this);
55638             s.clear();
55639         }else{
55640             this.selections.clear();
55641         }
55642         this.last = false;
55643     },
55644
55645
55646     /**
55647      * Selects all rows.
55648      */
55649     selectAll : function(){
55650         if(this.locked) return;
55651         this.selections.clear();
55652         for(var i = 0, len = this.grid.dataSource.getCount(); i < len; i++){
55653             this.selectRow(i, true);
55654         }
55655     },
55656
55657     /**
55658      * Returns True if there is a selection.
55659      * @return {Boolean}
55660      */
55661     hasSelection : function(){
55662         return this.selections.length > 0;
55663     },
55664
55665     /**
55666      * Returns True if the specified row is selected.
55667      * @param {Number/Record} record The record or index of the record to check
55668      * @return {Boolean}
55669      */
55670     isSelected : function(index){
55671         var r = typeof index == "number" ? this.grid.dataSource.getAt(index) : index;
55672         return (r && this.selections.key(r.id) ? true : false);
55673     },
55674
55675     /**
55676      * Returns True if the specified record id is selected.
55677      * @param {String} id The id of record to check
55678      * @return {Boolean}
55679      */
55680     isIdSelected : function(id){
55681         return (this.selections.key(id) ? true : false);
55682     },
55683
55684     // private
55685     handleMouseDown : function(e, t){
55686         var view = this.grid.getView(), rowIndex;
55687         if(this.isLocked() || (rowIndex = view.findRowIndex(t)) === false){
55688             return;
55689         };
55690         if(e.shiftKey && this.last !== false){
55691             var last = this.last;
55692             this.selectRange(last, rowIndex, e.ctrlKey);
55693             this.last = last; // reset the last
55694             view.focusRow(rowIndex);
55695         }else{
55696             var isSelected = this.isSelected(rowIndex);
55697             if(e.button !== 0 && isSelected){
55698                 view.focusRow(rowIndex);
55699             }else if(e.ctrlKey && isSelected){
55700                 this.deselectRow(rowIndex);
55701             }else if(!isSelected){
55702                 this.selectRow(rowIndex, e.button === 0 && (e.ctrlKey || e.shiftKey));
55703                 view.focusRow(rowIndex);
55704             }
55705         }
55706         this.fireEvent("afterselectionchange", this);
55707     },
55708     // private
55709     handleDragableRowClick :  function(grid, rowIndex, e) 
55710     {
55711         if(e.button === 0 && !e.shiftKey && !e.ctrlKey) {
55712             this.selectRow(rowIndex, false);
55713             grid.view.focusRow(rowIndex);
55714              this.fireEvent("afterselectionchange", this);
55715         }
55716     },
55717     
55718     /**
55719      * Selects multiple rows.
55720      * @param {Array} rows Array of the indexes of the row to select
55721      * @param {Boolean} keepExisting (optional) True to keep existing selections
55722      */
55723     selectRows : function(rows, keepExisting){
55724         if(!keepExisting){
55725             this.clearSelections();
55726         }
55727         for(var i = 0, len = rows.length; i < len; i++){
55728             this.selectRow(rows[i], true);
55729         }
55730     },
55731
55732     /**
55733      * Selects a range of rows. All rows in between startRow and endRow are also selected.
55734      * @param {Number} startRow The index of the first row in the range
55735      * @param {Number} endRow The index of the last row in the range
55736      * @param {Boolean} keepExisting (optional) True to retain existing selections
55737      */
55738     selectRange : function(startRow, endRow, keepExisting){
55739         if(this.locked) return;
55740         if(!keepExisting){
55741             this.clearSelections();
55742         }
55743         if(startRow <= endRow){
55744             for(var i = startRow; i <= endRow; i++){
55745                 this.selectRow(i, true);
55746             }
55747         }else{
55748             for(var i = startRow; i >= endRow; i--){
55749                 this.selectRow(i, true);
55750             }
55751         }
55752     },
55753
55754     /**
55755      * Deselects a range of rows. All rows in between startRow and endRow are also deselected.
55756      * @param {Number} startRow The index of the first row in the range
55757      * @param {Number} endRow The index of the last row in the range
55758      */
55759     deselectRange : function(startRow, endRow, preventViewNotify){
55760         if(this.locked) return;
55761         for(var i = startRow; i <= endRow; i++){
55762             this.deselectRow(i, preventViewNotify);
55763         }
55764     },
55765
55766     /**
55767      * Selects a row.
55768      * @param {Number} row The index of the row to select
55769      * @param {Boolean} keepExisting (optional) True to keep existing selections
55770      */
55771     selectRow : function(index, keepExisting, preventViewNotify){
55772         if(this.locked || (index < 0 || index >= this.grid.dataSource.getCount())) return;
55773         if(this.fireEvent("beforerowselect", this, index, keepExisting) !== false){
55774             if(!keepExisting || this.singleSelect){
55775                 this.clearSelections();
55776             }
55777             var r = this.grid.dataSource.getAt(index);
55778             this.selections.add(r);
55779             this.last = this.lastActive = index;
55780             if(!preventViewNotify){
55781                 this.grid.getView().onRowSelect(index);
55782             }
55783             this.fireEvent("rowselect", this, index, r);
55784             this.fireEvent("selectionchange", this);
55785         }
55786     },
55787
55788     /**
55789      * Deselects a row.
55790      * @param {Number} row The index of the row to deselect
55791      */
55792     deselectRow : function(index, preventViewNotify){
55793         if(this.locked) return;
55794         if(this.last == index){
55795             this.last = false;
55796         }
55797         if(this.lastActive == index){
55798             this.lastActive = false;
55799         }
55800         var r = this.grid.dataSource.getAt(index);
55801         this.selections.remove(r);
55802         if(!preventViewNotify){
55803             this.grid.getView().onRowDeselect(index);
55804         }
55805         this.fireEvent("rowdeselect", this, index);
55806         this.fireEvent("selectionchange", this);
55807     },
55808
55809     // private
55810     restoreLast : function(){
55811         if(this._last){
55812             this.last = this._last;
55813         }
55814     },
55815
55816     // private
55817     acceptsNav : function(row, col, cm){
55818         return !cm.isHidden(col) && cm.isCellEditable(col, row);
55819     },
55820
55821     // private
55822     onEditorKey : function(field, e){
55823         var k = e.getKey(), newCell, g = this.grid, ed = g.activeEditor;
55824         if(k == e.TAB){
55825             e.stopEvent();
55826             ed.completeEdit();
55827             if(e.shiftKey){
55828                 newCell = g.walkCells(ed.row, ed.col-1, -1, this.acceptsNav, this);
55829             }else{
55830                 newCell = g.walkCells(ed.row, ed.col+1, 1, this.acceptsNav, this);
55831             }
55832         }else if(k == e.ENTER && !e.ctrlKey){
55833             e.stopEvent();
55834             ed.completeEdit();
55835             if(e.shiftKey){
55836                 newCell = g.walkCells(ed.row-1, ed.col, -1, this.acceptsNav, this);
55837             }else{
55838                 newCell = g.walkCells(ed.row+1, ed.col, 1, this.acceptsNav, this);
55839             }
55840         }else if(k == e.ESC){
55841             ed.cancelEdit();
55842         }
55843         if(newCell){
55844             g.startEditing(newCell[0], newCell[1]);
55845         }
55846     }
55847 });/*
55848  * Based on:
55849  * Ext JS Library 1.1.1
55850  * Copyright(c) 2006-2007, Ext JS, LLC.
55851  *
55852  * Originally Released Under LGPL - original licence link has changed is not relivant.
55853  *
55854  * Fork - LGPL
55855  * <script type="text/javascript">
55856  */
55857 /**
55858  * @class Roo.grid.CellSelectionModel
55859  * @extends Roo.grid.AbstractSelectionModel
55860  * This class provides the basic implementation for cell selection in a grid.
55861  * @constructor
55862  * @param {Object} config The object containing the configuration of this model.
55863  * @cfg {Boolean} enter_is_tab Enter behaves the same as tab. (eg. goes to next cell) default: false
55864  */
55865 Roo.grid.CellSelectionModel = function(config){
55866     Roo.apply(this, config);
55867
55868     this.selection = null;
55869
55870     this.addEvents({
55871         /**
55872              * @event beforerowselect
55873              * Fires before a cell is selected.
55874              * @param {SelectionModel} this
55875              * @param {Number} rowIndex The selected row index
55876              * @param {Number} colIndex The selected cell index
55877              */
55878             "beforecellselect" : true,
55879         /**
55880              * @event cellselect
55881              * Fires when a cell is selected.
55882              * @param {SelectionModel} this
55883              * @param {Number} rowIndex The selected row index
55884              * @param {Number} colIndex The selected cell index
55885              */
55886             "cellselect" : true,
55887         /**
55888              * @event selectionchange
55889              * Fires when the active selection changes.
55890              * @param {SelectionModel} this
55891              * @param {Object} selection null for no selection or an object (o) with two properties
55892                 <ul>
55893                 <li>o.record: the record object for the row the selection is in</li>
55894                 <li>o.cell: An array of [rowIndex, columnIndex]</li>
55895                 </ul>
55896              */
55897             "selectionchange" : true,
55898         /**
55899              * @event tabend
55900              * Fires when the tab (or enter) was pressed on the last editable cell
55901              * You can use this to trigger add new row.
55902              * @param {SelectionModel} this
55903              */
55904             "tabend" : true,
55905          /**
55906              * @event beforeeditnext
55907              * Fires before the next editable sell is made active
55908              * You can use this to skip to another cell or fire the tabend
55909              *    if you set cell to false
55910              * @param {Object} eventdata object : { cell : [ row, col ] } 
55911              */
55912             "beforeeditnext" : true
55913     });
55914     Roo.grid.CellSelectionModel.superclass.constructor.call(this);
55915 };
55916
55917 Roo.extend(Roo.grid.CellSelectionModel, Roo.grid.AbstractSelectionModel,  {
55918     
55919     enter_is_tab: false,
55920
55921     /** @ignore */
55922     initEvents : function(){
55923         this.grid.on("mousedown", this.handleMouseDown, this);
55924         this.grid.getGridEl().on(Roo.isIE ? "keydown" : "keypress", this.handleKeyDown, this);
55925         var view = this.grid.view;
55926         view.on("refresh", this.onViewChange, this);
55927         view.on("rowupdated", this.onRowUpdated, this);
55928         view.on("beforerowremoved", this.clearSelections, this);
55929         view.on("beforerowsinserted", this.clearSelections, this);
55930         if(this.grid.isEditor){
55931             this.grid.on("beforeedit", this.beforeEdit,  this);
55932         }
55933     },
55934
55935         //private
55936     beforeEdit : function(e){
55937         this.select(e.row, e.column, false, true, e.record);
55938     },
55939
55940         //private
55941     onRowUpdated : function(v, index, r){
55942         if(this.selection && this.selection.record == r){
55943             v.onCellSelect(index, this.selection.cell[1]);
55944         }
55945     },
55946
55947         //private
55948     onViewChange : function(){
55949         this.clearSelections(true);
55950     },
55951
55952         /**
55953          * Returns the currently selected cell,.
55954          * @return {Array} The selected cell (row, column) or null if none selected.
55955          */
55956     getSelectedCell : function(){
55957         return this.selection ? this.selection.cell : null;
55958     },
55959
55960     /**
55961      * Clears all selections.
55962      * @param {Boolean} true to prevent the gridview from being notified about the change.
55963      */
55964     clearSelections : function(preventNotify){
55965         var s = this.selection;
55966         if(s){
55967             if(preventNotify !== true){
55968                 this.grid.view.onCellDeselect(s.cell[0], s.cell[1]);
55969             }
55970             this.selection = null;
55971             this.fireEvent("selectionchange", this, null);
55972         }
55973     },
55974
55975     /**
55976      * Returns true if there is a selection.
55977      * @return {Boolean}
55978      */
55979     hasSelection : function(){
55980         return this.selection ? true : false;
55981     },
55982
55983     /** @ignore */
55984     handleMouseDown : function(e, t){
55985         var v = this.grid.getView();
55986         if(this.isLocked()){
55987             return;
55988         };
55989         var row = v.findRowIndex(t);
55990         var cell = v.findCellIndex(t);
55991         if(row !== false && cell !== false){
55992             this.select(row, cell);
55993         }
55994     },
55995
55996     /**
55997      * Selects a cell.
55998      * @param {Number} rowIndex
55999      * @param {Number} collIndex
56000      */
56001     select : function(rowIndex, colIndex, preventViewNotify, preventFocus, /*internal*/ r){
56002         if(this.fireEvent("beforecellselect", this, rowIndex, colIndex) !== false){
56003             this.clearSelections();
56004             r = r || this.grid.dataSource.getAt(rowIndex);
56005             this.selection = {
56006                 record : r,
56007                 cell : [rowIndex, colIndex]
56008             };
56009             if(!preventViewNotify){
56010                 var v = this.grid.getView();
56011                 v.onCellSelect(rowIndex, colIndex);
56012                 if(preventFocus !== true){
56013                     v.focusCell(rowIndex, colIndex);
56014                 }
56015             }
56016             this.fireEvent("cellselect", this, rowIndex, colIndex);
56017             this.fireEvent("selectionchange", this, this.selection);
56018         }
56019     },
56020
56021         //private
56022     isSelectable : function(rowIndex, colIndex, cm){
56023         return !cm.isHidden(colIndex);
56024     },
56025
56026     /** @ignore */
56027     handleKeyDown : function(e){
56028         //Roo.log('Cell Sel Model handleKeyDown');
56029         if(!e.isNavKeyPress()){
56030             return;
56031         }
56032         var g = this.grid, s = this.selection;
56033         if(!s){
56034             e.stopEvent();
56035             var cell = g.walkCells(0, 0, 1, this.isSelectable,  this);
56036             if(cell){
56037                 this.select(cell[0], cell[1]);
56038             }
56039             return;
56040         }
56041         var sm = this;
56042         var walk = function(row, col, step){
56043             return g.walkCells(row, col, step, sm.isSelectable,  sm);
56044         };
56045         var k = e.getKey(), r = s.cell[0], c = s.cell[1];
56046         var newCell;
56047
56048       
56049
56050         switch(k){
56051             case e.TAB:
56052                 // handled by onEditorKey
56053                 if (g.isEditor && g.editing) {
56054                     return;
56055                 }
56056                 if(e.shiftKey) {
56057                     newCell = walk(r, c-1, -1);
56058                 } else {
56059                     newCell = walk(r, c+1, 1);
56060                 }
56061                 break;
56062             
56063             case e.DOWN:
56064                newCell = walk(r+1, c, 1);
56065                 break;
56066             
56067             case e.UP:
56068                 newCell = walk(r-1, c, -1);
56069                 break;
56070             
56071             case e.RIGHT:
56072                 newCell = walk(r, c+1, 1);
56073                 break;
56074             
56075             case e.LEFT:
56076                 newCell = walk(r, c-1, -1);
56077                 break;
56078             
56079             case e.ENTER:
56080                 
56081                 if(g.isEditor && !g.editing){
56082                    g.startEditing(r, c);
56083                    e.stopEvent();
56084                    return;
56085                 }
56086                 
56087                 
56088              break;
56089         };
56090         if(newCell){
56091             this.select(newCell[0], newCell[1]);
56092             e.stopEvent();
56093             
56094         }
56095     },
56096
56097     acceptsNav : function(row, col, cm){
56098         return !cm.isHidden(col) && cm.isCellEditable(col, row);
56099     },
56100     /**
56101      * Selects a cell.
56102      * @param {Number} field (not used) - as it's normally used as a listener
56103      * @param {Number} e - event - fake it by using
56104      *
56105      * var e = Roo.EventObjectImpl.prototype;
56106      * e.keyCode = e.TAB
56107      *
56108      * 
56109      */
56110     onEditorKey : function(field, e){
56111         
56112         var k = e.getKey(),
56113             newCell,
56114             g = this.grid,
56115             ed = g.activeEditor,
56116             forward = false;
56117         ///Roo.log('onEditorKey' + k);
56118         
56119         
56120         if (this.enter_is_tab && k == e.ENTER) {
56121             k = e.TAB;
56122         }
56123         
56124         if(k == e.TAB){
56125             if(e.shiftKey){
56126                 newCell = g.walkCells(ed.row, ed.col-1, -1, this.acceptsNav, this);
56127             }else{
56128                 newCell = g.walkCells(ed.row, ed.col+1, 1, this.acceptsNav, this);
56129                 forward = true;
56130             }
56131             
56132             e.stopEvent();
56133             
56134         } else if(k == e.ENTER &&  !e.ctrlKey){
56135             ed.completeEdit();
56136             e.stopEvent();
56137             newCell = g.walkCells(ed.row, ed.col+1, 1, this.acceptsNav, this);
56138         
56139                 } else if(k == e.ESC){
56140             ed.cancelEdit();
56141         }
56142                 
56143         if (newCell) {
56144             var ecall = { cell : newCell, forward : forward };
56145             this.fireEvent('beforeeditnext', ecall );
56146             newCell = ecall.cell;
56147                         forward = ecall.forward;
56148         }
56149                 
56150         if(newCell){
56151             //Roo.log('next cell after edit');
56152             g.startEditing.defer(100, g, [newCell[0], newCell[1]]);
56153         } else if (forward) {
56154             // tabbed past last
56155             this.fireEvent.defer(100, this, ['tabend',this]);
56156         }
56157     }
56158 });/*
56159  * Based on:
56160  * Ext JS Library 1.1.1
56161  * Copyright(c) 2006-2007, Ext JS, LLC.
56162  *
56163  * Originally Released Under LGPL - original licence link has changed is not relivant.
56164  *
56165  * Fork - LGPL
56166  * <script type="text/javascript">
56167  */
56168  
56169 /**
56170  * @class Roo.grid.EditorGrid
56171  * @extends Roo.grid.Grid
56172  * Class for creating and editable grid.
56173  * @param {String/HTMLElement/Roo.Element} container The element into which this grid will be rendered - 
56174  * The container MUST have some type of size defined for the grid to fill. The container will be 
56175  * automatically set to position relative if it isn't already.
56176  * @param {Object} dataSource The data model to bind to
56177  * @param {Object} colModel The column model with info about this grid's columns
56178  */
56179 Roo.grid.EditorGrid = function(container, config){
56180     Roo.grid.EditorGrid.superclass.constructor.call(this, container, config);
56181     this.getGridEl().addClass("xedit-grid");
56182
56183     if(!this.selModel){
56184         this.selModel = new Roo.grid.CellSelectionModel();
56185     }
56186
56187     this.activeEditor = null;
56188
56189         this.addEvents({
56190             /**
56191              * @event beforeedit
56192              * Fires before cell editing is triggered. The edit event object has the following properties <br />
56193              * <ul style="padding:5px;padding-left:16px;">
56194              * <li>grid - This grid</li>
56195              * <li>record - The record being edited</li>
56196              * <li>field - The field name being edited</li>
56197              * <li>value - The value for the field being edited.</li>
56198              * <li>row - The grid row index</li>
56199              * <li>column - The grid column index</li>
56200              * <li>cancel - Set this to true to cancel the edit or return false from your handler.</li>
56201              * </ul>
56202              * @param {Object} e An edit event (see above for description)
56203              */
56204             "beforeedit" : true,
56205             /**
56206              * @event afteredit
56207              * Fires after a cell is edited. <br />
56208              * <ul style="padding:5px;padding-left:16px;">
56209              * <li>grid - This grid</li>
56210              * <li>record - The record being edited</li>
56211              * <li>field - The field name being edited</li>
56212              * <li>value - The value being set</li>
56213              * <li>originalValue - The original value for the field, before the edit.</li>
56214              * <li>row - The grid row index</li>
56215              * <li>column - The grid column index</li>
56216              * </ul>
56217              * @param {Object} e An edit event (see above for description)
56218              */
56219             "afteredit" : true,
56220             /**
56221              * @event validateedit
56222              * Fires after a cell is edited, but before the value is set in the record. 
56223          * You can use this to modify the value being set in the field, Return false
56224              * to cancel the change. The edit event object has the following properties <br />
56225              * <ul style="padding:5px;padding-left:16px;">
56226          * <li>editor - This editor</li>
56227              * <li>grid - This grid</li>
56228              * <li>record - The record being edited</li>
56229              * <li>field - The field name being edited</li>
56230              * <li>value - The value being set</li>
56231              * <li>originalValue - The original value for the field, before the edit.</li>
56232              * <li>row - The grid row index</li>
56233              * <li>column - The grid column index</li>
56234              * <li>cancel - Set this to true to cancel the edit or return false from your handler.</li>
56235              * </ul>
56236              * @param {Object} e An edit event (see above for description)
56237              */
56238             "validateedit" : true
56239         });
56240     this.on("bodyscroll", this.stopEditing,  this);
56241     this.on(this.clicksToEdit == 1 ? "cellclick" : "celldblclick", this.onCellDblClick,  this);
56242 };
56243
56244 Roo.extend(Roo.grid.EditorGrid, Roo.grid.Grid, {
56245     /**
56246      * @cfg {Number} clicksToEdit
56247      * The number of clicks on a cell required to display the cell's editor (defaults to 2)
56248      */
56249     clicksToEdit: 2,
56250
56251     // private
56252     isEditor : true,
56253     // private
56254     trackMouseOver: false, // causes very odd FF errors
56255
56256     onCellDblClick : function(g, row, col){
56257         this.startEditing(row, col);
56258     },
56259
56260     onEditComplete : function(ed, value, startValue){
56261         this.editing = false;
56262         this.activeEditor = null;
56263         ed.un("specialkey", this.selModel.onEditorKey, this.selModel);
56264         var r = ed.record;
56265         var field = this.colModel.getDataIndex(ed.col);
56266         var e = {
56267             grid: this,
56268             record: r,
56269             field: field,
56270             originalValue: startValue,
56271             value: value,
56272             row: ed.row,
56273             column: ed.col,
56274             cancel:false,
56275             editor: ed
56276         };
56277         var cell = Roo.get(this.view.getCell(ed.row,ed.col))
56278         cell.show();
56279           
56280         if(String(value) !== String(startValue)){
56281             
56282             if(this.fireEvent("validateedit", e) !== false && !e.cancel){
56283                 r.set(field, e.value);
56284                 // if we are dealing with a combo box..
56285                 // then we also set the 'name' colum to be the displayField
56286                 if (ed.field.displayField && ed.field.name) {
56287                     r.set(ed.field.name, ed.field.el.dom.value);
56288                 }
56289                 
56290                 delete e.cancel; //?? why!!!
56291                 this.fireEvent("afteredit", e);
56292             }
56293         } else {
56294             this.fireEvent("afteredit", e); // always fire it!
56295         }
56296         this.view.focusCell(ed.row, ed.col);
56297     },
56298
56299     /**
56300      * Starts editing the specified for the specified row/column
56301      * @param {Number} rowIndex
56302      * @param {Number} colIndex
56303      */
56304     startEditing : function(row, col){
56305         this.stopEditing();
56306         if(this.colModel.isCellEditable(col, row)){
56307             this.view.ensureVisible(row, col, true);
56308           
56309             var r = this.dataSource.getAt(row);
56310             var field = this.colModel.getDataIndex(col);
56311             var cell = Roo.get(this.view.getCell(row,col));
56312             var e = {
56313                 grid: this,
56314                 record: r,
56315                 field: field,
56316                 value: r.data[field],
56317                 row: row,
56318                 column: col,
56319                 cancel:false 
56320             };
56321             if(this.fireEvent("beforeedit", e) !== false && !e.cancel){
56322                 this.editing = true;
56323                 var ed = this.colModel.getCellEditor(col, row);
56324                 
56325                 if (!ed) {
56326                     return;
56327                 }
56328                 if(!ed.rendered){
56329                     ed.render(ed.parentEl || document.body);
56330                 }
56331                 ed.field.reset();
56332                
56333                 cell.hide();
56334                 
56335                 (function(){ // complex but required for focus issues in safari, ie and opera
56336                     ed.row = row;
56337                     ed.col = col;
56338                     ed.record = r;
56339                     ed.on("complete",   this.onEditComplete,        this,       {single: true});
56340                     ed.on("specialkey", this.selModel.onEditorKey,  this.selModel);
56341                     this.activeEditor = ed;
56342                     var v = r.data[field];
56343                     ed.startEdit(this.view.getCell(row, col), v);
56344                     // combo's with 'displayField and name set
56345                     if (ed.field.displayField && ed.field.name) {
56346                         ed.field.el.dom.value = r.data[ed.field.name];
56347                     }
56348                     
56349                     
56350                 }).defer(50, this);
56351             }
56352         }
56353     },
56354         
56355     /**
56356      * Stops any active editing
56357      */
56358     stopEditing : function(){
56359         if(this.activeEditor){
56360             this.activeEditor.completeEdit();
56361         }
56362         this.activeEditor = null;
56363     },
56364         
56365          /**
56366      * Called to get grid's drag proxy text, by default returns this.ddText.
56367      * @return {String}
56368      */
56369     getDragDropText : function(){
56370         var count = this.selModel.getSelectedCell() ? 1 : 0;
56371         return String.format(this.ddText, count, count == 1 ? '' : 's');
56372     }
56373         
56374 });/*
56375  * Based on:
56376  * Ext JS Library 1.1.1
56377  * Copyright(c) 2006-2007, Ext JS, LLC.
56378  *
56379  * Originally Released Under LGPL - original licence link has changed is not relivant.
56380  *
56381  * Fork - LGPL
56382  * <script type="text/javascript">
56383  */
56384
56385 // private - not really -- you end up using it !
56386 // This is a support class used internally by the Grid components
56387
56388 /**
56389  * @class Roo.grid.GridEditor
56390  * @extends Roo.Editor
56391  * Class for creating and editable grid elements.
56392  * @param {Object} config any settings (must include field)
56393  */
56394 Roo.grid.GridEditor = function(field, config){
56395     if (!config && field.field) {
56396         config = field;
56397         field = Roo.factory(config.field, Roo.form);
56398     }
56399     Roo.grid.GridEditor.superclass.constructor.call(this, field, config);
56400     field.monitorTab = false;
56401 };
56402
56403 Roo.extend(Roo.grid.GridEditor, Roo.Editor, {
56404     
56405     /**
56406      * @cfg {Roo.form.Field} field Field to wrap (or xtyped)
56407      */
56408     
56409     alignment: "tl-tl",
56410     autoSize: "width",
56411     hideEl : false,
56412     cls: "x-small-editor x-grid-editor",
56413     shim:false,
56414     shadow:"frame"
56415 });/*
56416  * Based on:
56417  * Ext JS Library 1.1.1
56418  * Copyright(c) 2006-2007, Ext JS, LLC.
56419  *
56420  * Originally Released Under LGPL - original licence link has changed is not relivant.
56421  *
56422  * Fork - LGPL
56423  * <script type="text/javascript">
56424  */
56425   
56426
56427   
56428 Roo.grid.PropertyRecord = Roo.data.Record.create([
56429     {name:'name',type:'string'},  'value'
56430 ]);
56431
56432
56433 Roo.grid.PropertyStore = function(grid, source){
56434     this.grid = grid;
56435     this.store = new Roo.data.Store({
56436         recordType : Roo.grid.PropertyRecord
56437     });
56438     this.store.on('update', this.onUpdate,  this);
56439     if(source){
56440         this.setSource(source);
56441     }
56442     Roo.grid.PropertyStore.superclass.constructor.call(this);
56443 };
56444
56445
56446
56447 Roo.extend(Roo.grid.PropertyStore, Roo.util.Observable, {
56448     setSource : function(o){
56449         this.source = o;
56450         this.store.removeAll();
56451         var data = [];
56452         for(var k in o){
56453             if(this.isEditableValue(o[k])){
56454                 data.push(new Roo.grid.PropertyRecord({name: k, value: o[k]}, k));
56455             }
56456         }
56457         this.store.loadRecords({records: data}, {}, true);
56458     },
56459
56460     onUpdate : function(ds, record, type){
56461         if(type == Roo.data.Record.EDIT){
56462             var v = record.data['value'];
56463             var oldValue = record.modified['value'];
56464             if(this.grid.fireEvent('beforepropertychange', this.source, record.id, v, oldValue) !== false){
56465                 this.source[record.id] = v;
56466                 record.commit();
56467                 this.grid.fireEvent('propertychange', this.source, record.id, v, oldValue);
56468             }else{
56469                 record.reject();
56470             }
56471         }
56472     },
56473
56474     getProperty : function(row){
56475        return this.store.getAt(row);
56476     },
56477
56478     isEditableValue: function(val){
56479         if(val && val instanceof Date){
56480             return true;
56481         }else if(typeof val == 'object' || typeof val == 'function'){
56482             return false;
56483         }
56484         return true;
56485     },
56486
56487     setValue : function(prop, value){
56488         this.source[prop] = value;
56489         this.store.getById(prop).set('value', value);
56490     },
56491
56492     getSource : function(){
56493         return this.source;
56494     }
56495 });
56496
56497 Roo.grid.PropertyColumnModel = function(grid, store){
56498     this.grid = grid;
56499     var g = Roo.grid;
56500     g.PropertyColumnModel.superclass.constructor.call(this, [
56501         {header: this.nameText, sortable: true, dataIndex:'name', id: 'name'},
56502         {header: this.valueText, resizable:false, dataIndex: 'value', id: 'value'}
56503     ]);
56504     this.store = store;
56505     this.bselect = Roo.DomHelper.append(document.body, {
56506         tag: 'select', style:'display:none', cls: 'x-grid-editor', children: [
56507             {tag: 'option', value: 'true', html: 'true'},
56508             {tag: 'option', value: 'false', html: 'false'}
56509         ]
56510     });
56511     Roo.id(this.bselect);
56512     var f = Roo.form;
56513     this.editors = {
56514         'date' : new g.GridEditor(new f.DateField({selectOnFocus:true})),
56515         'string' : new g.GridEditor(new f.TextField({selectOnFocus:true})),
56516         'number' : new g.GridEditor(new f.NumberField({selectOnFocus:true, style:'text-align:left;'})),
56517         'int' : new g.GridEditor(new f.NumberField({selectOnFocus:true, allowDecimals:false, style:'text-align:left;'})),
56518         'boolean' : new g.GridEditor(new f.Field({el:this.bselect,selectOnFocus:true}))
56519     };
56520     this.renderCellDelegate = this.renderCell.createDelegate(this);
56521     this.renderPropDelegate = this.renderProp.createDelegate(this);
56522 };
56523
56524 Roo.extend(Roo.grid.PropertyColumnModel, Roo.grid.ColumnModel, {
56525     
56526     
56527     nameText : 'Name',
56528     valueText : 'Value',
56529     
56530     dateFormat : 'm/j/Y',
56531     
56532     
56533     renderDate : function(dateVal){
56534         return dateVal.dateFormat(this.dateFormat);
56535     },
56536
56537     renderBool : function(bVal){
56538         return bVal ? 'true' : 'false';
56539     },
56540
56541     isCellEditable : function(colIndex, rowIndex){
56542         return colIndex == 1;
56543     },
56544
56545     getRenderer : function(col){
56546         return col == 1 ?
56547             this.renderCellDelegate : this.renderPropDelegate;
56548     },
56549
56550     renderProp : function(v){
56551         return this.getPropertyName(v);
56552     },
56553
56554     renderCell : function(val){
56555         var rv = val;
56556         if(val instanceof Date){
56557             rv = this.renderDate(val);
56558         }else if(typeof val == 'boolean'){
56559             rv = this.renderBool(val);
56560         }
56561         return Roo.util.Format.htmlEncode(rv);
56562     },
56563
56564     getPropertyName : function(name){
56565         var pn = this.grid.propertyNames;
56566         return pn && pn[name] ? pn[name] : name;
56567     },
56568
56569     getCellEditor : function(colIndex, rowIndex){
56570         var p = this.store.getProperty(rowIndex);
56571         var n = p.data['name'], val = p.data['value'];
56572         
56573         if(typeof(this.grid.customEditors[n]) == 'string'){
56574             return this.editors[this.grid.customEditors[n]];
56575         }
56576         if(typeof(this.grid.customEditors[n]) != 'undefined'){
56577             return this.grid.customEditors[n];
56578         }
56579         if(val instanceof Date){
56580             return this.editors['date'];
56581         }else if(typeof val == 'number'){
56582             return this.editors['number'];
56583         }else if(typeof val == 'boolean'){
56584             return this.editors['boolean'];
56585         }else{
56586             return this.editors['string'];
56587         }
56588     }
56589 });
56590
56591 /**
56592  * @class Roo.grid.PropertyGrid
56593  * @extends Roo.grid.EditorGrid
56594  * This class represents the  interface of a component based property grid control.
56595  * <br><br>Usage:<pre><code>
56596  var grid = new Roo.grid.PropertyGrid("my-container-id", {
56597       
56598  });
56599  // set any options
56600  grid.render();
56601  * </code></pre>
56602   
56603  * @constructor
56604  * @param {String/HTMLElement/Roo.Element} container The element into which this grid will be rendered -
56605  * The container MUST have some type of size defined for the grid to fill. The container will be
56606  * automatically set to position relative if it isn't already.
56607  * @param {Object} config A config object that sets properties on this grid.
56608  */
56609 Roo.grid.PropertyGrid = function(container, config){
56610     config = config || {};
56611     var store = new Roo.grid.PropertyStore(this);
56612     this.store = store;
56613     var cm = new Roo.grid.PropertyColumnModel(this, store);
56614     store.store.sort('name', 'ASC');
56615     Roo.grid.PropertyGrid.superclass.constructor.call(this, container, Roo.apply({
56616         ds: store.store,
56617         cm: cm,
56618         enableColLock:false,
56619         enableColumnMove:false,
56620         stripeRows:false,
56621         trackMouseOver: false,
56622         clicksToEdit:1
56623     }, config));
56624     this.getGridEl().addClass('x-props-grid');
56625     this.lastEditRow = null;
56626     this.on('columnresize', this.onColumnResize, this);
56627     this.addEvents({
56628          /**
56629              * @event beforepropertychange
56630              * Fires before a property changes (return false to stop?)
56631              * @param {Roo.grid.PropertyGrid} grid property grid? (check could be store)
56632              * @param {String} id Record Id
56633              * @param {String} newval New Value
56634          * @param {String} oldval Old Value
56635              */
56636         "beforepropertychange": true,
56637         /**
56638              * @event propertychange
56639              * Fires after a property changes
56640              * @param {Roo.grid.PropertyGrid} grid property grid? (check could be store)
56641              * @param {String} id Record Id
56642              * @param {String} newval New Value
56643          * @param {String} oldval Old Value
56644              */
56645         "propertychange": true
56646     });
56647     this.customEditors = this.customEditors || {};
56648 };
56649 Roo.extend(Roo.grid.PropertyGrid, Roo.grid.EditorGrid, {
56650     
56651      /**
56652      * @cfg {Object} customEditors map of colnames=> custom editors.
56653      * the custom editor can be one of the standard ones (date|string|number|int|boolean), or a
56654      * grid editor eg. Roo.grid.GridEditor(new Roo.form.TextArea({selectOnFocus:true})),
56655      * false disables editing of the field.
56656          */
56657     
56658       /**
56659      * @cfg {Object} propertyNames map of property Names to their displayed value
56660          */
56661     
56662     render : function(){
56663         Roo.grid.PropertyGrid.superclass.render.call(this);
56664         this.autoSize.defer(100, this);
56665     },
56666
56667     autoSize : function(){
56668         Roo.grid.PropertyGrid.superclass.autoSize.call(this);
56669         if(this.view){
56670             this.view.fitColumns();
56671         }
56672     },
56673
56674     onColumnResize : function(){
56675         this.colModel.setColumnWidth(1, this.container.getWidth(true)-this.colModel.getColumnWidth(0));
56676         this.autoSize();
56677     },
56678     /**
56679      * Sets the data for the Grid
56680      * accepts a Key => Value object of all the elements avaiable.
56681      * @param {Object} data  to appear in grid.
56682      */
56683     setSource : function(source){
56684         this.store.setSource(source);
56685         //this.autoSize();
56686     },
56687     /**
56688      * Gets all the data from the grid.
56689      * @return {Object} data  data stored in grid
56690      */
56691     getSource : function(){
56692         return this.store.getSource();
56693     }
56694 });/*
56695   
56696  * Licence LGPL
56697  
56698  */
56699  
56700 /**
56701  * @class Roo.grid.Calendar
56702  * @extends Roo.util.Grid
56703  * This class extends the Grid to provide a calendar widget
56704  * <br><br>Usage:<pre><code>
56705  var grid = new Roo.grid.Calendar("my-container-id", {
56706      ds: myDataStore,
56707      cm: myColModel,
56708      selModel: mySelectionModel,
56709      autoSizeColumns: true,
56710      monitorWindowResize: false,
56711      trackMouseOver: true
56712      eventstore : real data store..
56713  });
56714  // set any options
56715  grid.render();
56716   
56717   * @constructor
56718  * @param {String/HTMLElement/Roo.Element} container The element into which this grid will be rendered -
56719  * The container MUST have some type of size defined for the grid to fill. The container will be
56720  * automatically set to position relative if it isn't already.
56721  * @param {Object} config A config object that sets properties on this grid.
56722  */
56723 Roo.grid.Calendar = function(container, config){
56724         // initialize the container
56725         this.container = Roo.get(container);
56726         this.container.update("");
56727         this.container.setStyle("overflow", "hidden");
56728     this.container.addClass('x-grid-container');
56729
56730     this.id = this.container.id;
56731
56732     Roo.apply(this, config);
56733     // check and correct shorthanded configs
56734     
56735     var rows = [];
56736     var d =1;
56737     for (var r = 0;r < 6;r++) {
56738         
56739         rows[r]=[];
56740         for (var c =0;c < 7;c++) {
56741             rows[r][c]= '';
56742         }
56743     }
56744     if (this.eventStore) {
56745         this.eventStore= Roo.factory(this.eventStore, Roo.data);
56746         this.eventStore.on('load',this.onLoad, this);
56747         this.eventStore.on('beforeload',this.clearEvents, this);
56748          
56749     }
56750     
56751     this.dataSource = new Roo.data.Store({
56752             proxy: new Roo.data.MemoryProxy(rows),
56753             reader: new Roo.data.ArrayReader({}, [
56754                    'weekday0', 'weekday1', 'weekday2', 'weekday3', 'weekday4', 'weekday5', 'weekday6' ])
56755     });
56756
56757     this.dataSource.load();
56758     this.ds = this.dataSource;
56759     this.ds.xmodule = this.xmodule || false;
56760     
56761     
56762     var cellRender = function(v,x,r)
56763     {
56764         return String.format(
56765             '<div class="fc-day  fc-widget-content"><div>' +
56766                 '<div class="fc-event-container"></div>' +
56767                 '<div class="fc-day-number">{0}</div>'+
56768                 
56769                 '<div class="fc-day-content"><div style="position:relative"></div></div>' +
56770             '</div></div>', v);
56771     
56772     }
56773     
56774     
56775     this.colModel = new Roo.grid.ColumnModel( [
56776         {
56777             xtype: 'ColumnModel',
56778             xns: Roo.grid,
56779             dataIndex : 'weekday0',
56780             header : 'Sunday',
56781             renderer : cellRender
56782         },
56783         {
56784             xtype: 'ColumnModel',
56785             xns: Roo.grid,
56786             dataIndex : 'weekday1',
56787             header : 'Monday',
56788             renderer : cellRender
56789         },
56790         {
56791             xtype: 'ColumnModel',
56792             xns: Roo.grid,
56793             dataIndex : 'weekday2',
56794             header : 'Tuesday',
56795             renderer : cellRender
56796         },
56797         {
56798             xtype: 'ColumnModel',
56799             xns: Roo.grid,
56800             dataIndex : 'weekday3',
56801             header : 'Wednesday',
56802             renderer : cellRender
56803         },
56804         {
56805             xtype: 'ColumnModel',
56806             xns: Roo.grid,
56807             dataIndex : 'weekday4',
56808             header : 'Thursday',
56809             renderer : cellRender
56810         },
56811         {
56812             xtype: 'ColumnModel',
56813             xns: Roo.grid,
56814             dataIndex : 'weekday5',
56815             header : 'Friday',
56816             renderer : cellRender
56817         },
56818         {
56819             xtype: 'ColumnModel',
56820             xns: Roo.grid,
56821             dataIndex : 'weekday6',
56822             header : 'Saturday',
56823             renderer : cellRender
56824         }
56825     ]);
56826     this.cm = this.colModel;
56827     this.cm.xmodule = this.xmodule || false;
56828  
56829         
56830           
56831     //this.selModel = new Roo.grid.CellSelectionModel();
56832     //this.sm = this.selModel;
56833     //this.selModel.init(this);
56834     
56835     
56836     if(this.width){
56837         this.container.setWidth(this.width);
56838     }
56839
56840     if(this.height){
56841         this.container.setHeight(this.height);
56842     }
56843     /** @private */
56844         this.addEvents({
56845         // raw events
56846         /**
56847          * @event click
56848          * The raw click event for the entire grid.
56849          * @param {Roo.EventObject} e
56850          */
56851         "click" : true,
56852         /**
56853          * @event dblclick
56854          * The raw dblclick event for the entire grid.
56855          * @param {Roo.EventObject} e
56856          */
56857         "dblclick" : true,
56858         /**
56859          * @event contextmenu
56860          * The raw contextmenu event for the entire grid.
56861          * @param {Roo.EventObject} e
56862          */
56863         "contextmenu" : true,
56864         /**
56865          * @event mousedown
56866          * The raw mousedown event for the entire grid.
56867          * @param {Roo.EventObject} e
56868          */
56869         "mousedown" : true,
56870         /**
56871          * @event mouseup
56872          * The raw mouseup event for the entire grid.
56873          * @param {Roo.EventObject} e
56874          */
56875         "mouseup" : true,
56876         /**
56877          * @event mouseover
56878          * The raw mouseover event for the entire grid.
56879          * @param {Roo.EventObject} e
56880          */
56881         "mouseover" : true,
56882         /**
56883          * @event mouseout
56884          * The raw mouseout event for the entire grid.
56885          * @param {Roo.EventObject} e
56886          */
56887         "mouseout" : true,
56888         /**
56889          * @event keypress
56890          * The raw keypress event for the entire grid.
56891          * @param {Roo.EventObject} e
56892          */
56893         "keypress" : true,
56894         /**
56895          * @event keydown
56896          * The raw keydown event for the entire grid.
56897          * @param {Roo.EventObject} e
56898          */
56899         "keydown" : true,
56900
56901         // custom events
56902
56903         /**
56904          * @event cellclick
56905          * Fires when a cell is clicked
56906          * @param {Grid} this
56907          * @param {Number} rowIndex
56908          * @param {Number} columnIndex
56909          * @param {Roo.EventObject} e
56910          */
56911         "cellclick" : true,
56912         /**
56913          * @event celldblclick
56914          * Fires when a cell is double clicked
56915          * @param {Grid} this
56916          * @param {Number} rowIndex
56917          * @param {Number} columnIndex
56918          * @param {Roo.EventObject} e
56919          */
56920         "celldblclick" : true,
56921         /**
56922          * @event rowclick
56923          * Fires when a row is clicked
56924          * @param {Grid} this
56925          * @param {Number} rowIndex
56926          * @param {Roo.EventObject} e
56927          */
56928         "rowclick" : true,
56929         /**
56930          * @event rowdblclick
56931          * Fires when a row is double clicked
56932          * @param {Grid} this
56933          * @param {Number} rowIndex
56934          * @param {Roo.EventObject} e
56935          */
56936         "rowdblclick" : true,
56937         /**
56938          * @event headerclick
56939          * Fires when a header is clicked
56940          * @param {Grid} this
56941          * @param {Number} columnIndex
56942          * @param {Roo.EventObject} e
56943          */
56944         "headerclick" : true,
56945         /**
56946          * @event headerdblclick
56947          * Fires when a header cell is double clicked
56948          * @param {Grid} this
56949          * @param {Number} columnIndex
56950          * @param {Roo.EventObject} e
56951          */
56952         "headerdblclick" : true,
56953         /**
56954          * @event rowcontextmenu
56955          * Fires when a row is right clicked
56956          * @param {Grid} this
56957          * @param {Number} rowIndex
56958          * @param {Roo.EventObject} e
56959          */
56960         "rowcontextmenu" : true,
56961         /**
56962          * @event cellcontextmenu
56963          * Fires when a cell is right clicked
56964          * @param {Grid} this
56965          * @param {Number} rowIndex
56966          * @param {Number} cellIndex
56967          * @param {Roo.EventObject} e
56968          */
56969          "cellcontextmenu" : true,
56970         /**
56971          * @event headercontextmenu
56972          * Fires when a header is right clicked
56973          * @param {Grid} this
56974          * @param {Number} columnIndex
56975          * @param {Roo.EventObject} e
56976          */
56977         "headercontextmenu" : true,
56978         /**
56979          * @event bodyscroll
56980          * Fires when the body element is scrolled
56981          * @param {Number} scrollLeft
56982          * @param {Number} scrollTop
56983          */
56984         "bodyscroll" : true,
56985         /**
56986          * @event columnresize
56987          * Fires when the user resizes a column
56988          * @param {Number} columnIndex
56989          * @param {Number} newSize
56990          */
56991         "columnresize" : true,
56992         /**
56993          * @event columnmove
56994          * Fires when the user moves a column
56995          * @param {Number} oldIndex
56996          * @param {Number} newIndex
56997          */
56998         "columnmove" : true,
56999         /**
57000          * @event startdrag
57001          * Fires when row(s) start being dragged
57002          * @param {Grid} this
57003          * @param {Roo.GridDD} dd The drag drop object
57004          * @param {event} e The raw browser event
57005          */
57006         "startdrag" : true,
57007         /**
57008          * @event enddrag
57009          * Fires when a drag operation is complete
57010          * @param {Grid} this
57011          * @param {Roo.GridDD} dd The drag drop object
57012          * @param {event} e The raw browser event
57013          */
57014         "enddrag" : true,
57015         /**
57016          * @event dragdrop
57017          * Fires when dragged row(s) are dropped on a valid DD target
57018          * @param {Grid} this
57019          * @param {Roo.GridDD} dd The drag drop object
57020          * @param {String} targetId The target drag drop object
57021          * @param {event} e The raw browser event
57022          */
57023         "dragdrop" : true,
57024         /**
57025          * @event dragover
57026          * Fires while row(s) are being dragged. "targetId" is the id of the Yahoo.util.DD object the selected rows are being dragged over.
57027          * @param {Grid} this
57028          * @param {Roo.GridDD} dd The drag drop object
57029          * @param {String} targetId The target drag drop object
57030          * @param {event} e The raw browser event
57031          */
57032         "dragover" : true,
57033         /**
57034          * @event dragenter
57035          *  Fires when the dragged row(s) first cross another DD target while being dragged
57036          * @param {Grid} this
57037          * @param {Roo.GridDD} dd The drag drop object
57038          * @param {String} targetId The target drag drop object
57039          * @param {event} e The raw browser event
57040          */
57041         "dragenter" : true,
57042         /**
57043          * @event dragout
57044          * Fires when the dragged row(s) leave another DD target while being dragged
57045          * @param {Grid} this
57046          * @param {Roo.GridDD} dd The drag drop object
57047          * @param {String} targetId The target drag drop object
57048          * @param {event} e The raw browser event
57049          */
57050         "dragout" : true,
57051         /**
57052          * @event rowclass
57053          * Fires when a row is rendered, so you can change add a style to it.
57054          * @param {GridView} gridview   The grid view
57055          * @param {Object} rowcfg   contains record  rowIndex and rowClass - set rowClass to add a style.
57056          */
57057         'rowclass' : true,
57058
57059         /**
57060          * @event render
57061          * Fires when the grid is rendered
57062          * @param {Grid} grid
57063          */
57064         'render' : true,
57065             /**
57066              * @event select
57067              * Fires when a date is selected
57068              * @param {DatePicker} this
57069              * @param {Date} date The selected date
57070              */
57071         'select': true,
57072         /**
57073              * @event monthchange
57074              * Fires when the displayed month changes 
57075              * @param {DatePicker} this
57076              * @param {Date} date The selected month
57077              */
57078         'monthchange': true,
57079         /**
57080              * @event evententer
57081              * Fires when mouse over an event
57082              * @param {Calendar} this
57083              * @param {event} Event
57084              */
57085         'evententer': true,
57086         /**
57087              * @event eventleave
57088              * Fires when the mouse leaves an
57089              * @param {Calendar} this
57090              * @param {event}
57091              */
57092         'eventleave': true,
57093         /**
57094              * @event eventclick
57095              * Fires when the mouse click an
57096              * @param {Calendar} this
57097              * @param {event}
57098              */
57099         'eventclick': true,
57100         /**
57101              * @event eventrender
57102              * Fires before each cell is rendered, so you can modify the contents, like cls / title / qtip
57103              * @param {Calendar} this
57104              * @param {data} data to be modified
57105              */
57106         'eventrender': true
57107         
57108     });
57109
57110     Roo.grid.Grid.superclass.constructor.call(this);
57111     this.on('render', function() {
57112         this.view.el.addClass('x-grid-cal'); 
57113         
57114         (function() { this.setDate(new Date()); }).defer(100,this); //default today..
57115
57116     },this);
57117     
57118     if (!Roo.grid.Calendar.style) {
57119         Roo.grid.Calendar.style = Roo.util.CSS.createStyleSheet({
57120             
57121             
57122             '.x-grid-cal .x-grid-col' :  {
57123                 height: 'auto !important',
57124                 'vertical-align': 'top'
57125             },
57126             '.x-grid-cal  .fc-event-hori' : {
57127                 height: '14px'
57128             }
57129              
57130             
57131         }, Roo.id());
57132     }
57133
57134     
57135     
57136 };
57137 Roo.extend(Roo.grid.Calendar, Roo.grid.Grid, {
57138     /**
57139      * @cfg {Store} eventStore The store that loads events.
57140      */
57141     eventStore : 25,
57142
57143      
57144     activeDate : false,
57145     startDay : 0,
57146     autoWidth : true,
57147     monitorWindowResize : false,
57148
57149     
57150     resizeColumns : function() {
57151         var col = (this.view.el.getWidth() / 7) - 3;
57152         // loop through cols, and setWidth
57153         for(var i =0 ; i < 7 ; i++){
57154             this.cm.setColumnWidth(i, col);
57155         }
57156     },
57157      setDate :function(date) {
57158         
57159         Roo.log('setDate?');
57160         
57161         this.resizeColumns();
57162         var vd = this.activeDate;
57163         this.activeDate = date;
57164 //        if(vd && this.el){
57165 //            var t = date.getTime();
57166 //            if(vd.getMonth() == date.getMonth() && vd.getFullYear() == date.getFullYear()){
57167 //                Roo.log('using add remove');
57168 //                
57169 //                this.fireEvent('monthchange', this, date);
57170 //                
57171 //                this.cells.removeClass("fc-state-highlight");
57172 //                this.cells.each(function(c){
57173 //                   if(c.dateValue == t){
57174 //                       c.addClass("fc-state-highlight");
57175 //                       setTimeout(function(){
57176 //                            try{c.dom.firstChild.focus();}catch(e){}
57177 //                       }, 50);
57178 //                       return false;
57179 //                   }
57180 //                   return true;
57181 //                });
57182 //                return;
57183 //            }
57184 //        }
57185         
57186         var days = date.getDaysInMonth();
57187         
57188         var firstOfMonth = date.getFirstDateOfMonth();
57189         var startingPos = firstOfMonth.getDay()-this.startDay;
57190         
57191         if(startingPos < this.startDay){
57192             startingPos += 7;
57193         }
57194         
57195         var pm = date.add(Date.MONTH, -1);
57196         var prevStart = pm.getDaysInMonth()-startingPos;
57197 //        
57198         
57199         
57200         this.cells = this.view.el.select('.x-grid-row .x-grid-col',true);
57201         
57202         this.textNodes = this.view.el.query('.x-grid-row .x-grid-col .x-grid-cell-text');
57203         //this.cells.addClassOnOver('fc-state-hover');
57204         
57205         var cells = this.cells.elements;
57206         var textEls = this.textNodes;
57207         
57208         //Roo.each(cells, function(cell){
57209         //    cell.removeClass([ 'fc-past', 'fc-other-month', 'fc-future', 'fc-state-highlight', 'fc-state-disabled']);
57210         //});
57211         
57212         days += startingPos;
57213
57214         // convert everything to numbers so it's fast
57215         var day = 86400000;
57216         var d = (new Date(pm.getFullYear(), pm.getMonth(), prevStart)).clearTime();
57217         //Roo.log(d);
57218         //Roo.log(pm);
57219         //Roo.log(prevStart);
57220         
57221         var today = new Date().clearTime().getTime();
57222         var sel = date.clearTime().getTime();
57223         var min = this.minDate ? this.minDate.clearTime() : Number.NEGATIVE_INFINITY;
57224         var max = this.maxDate ? this.maxDate.clearTime() : Number.POSITIVE_INFINITY;
57225         var ddMatch = this.disabledDatesRE;
57226         var ddText = this.disabledDatesText;
57227         var ddays = this.disabledDays ? this.disabledDays.join("") : false;
57228         var ddaysText = this.disabledDaysText;
57229         var format = this.format;
57230         
57231         var setCellClass = function(cal, cell){
57232             
57233             //Roo.log('set Cell Class');
57234             cell.title = "";
57235             var t = d.getTime();
57236             
57237             //Roo.log(d);
57238             
57239             
57240             cell.dateValue = t;
57241             if(t == today){
57242                 cell.className += " fc-today";
57243                 cell.className += " fc-state-highlight";
57244                 cell.title = cal.todayText;
57245             }
57246             if(t == sel){
57247                 // disable highlight in other month..
57248                 cell.className += " fc-state-highlight";
57249                 
57250             }
57251             // disabling
57252             if(t < min) {
57253                 //cell.className = " fc-state-disabled";
57254                 cell.title = cal.minText;
57255                 return;
57256             }
57257             if(t > max) {
57258                 //cell.className = " fc-state-disabled";
57259                 cell.title = cal.maxText;
57260                 return;
57261             }
57262             if(ddays){
57263                 if(ddays.indexOf(d.getDay()) != -1){
57264                     // cell.title = ddaysText;
57265                    // cell.className = " fc-state-disabled";
57266                 }
57267             }
57268             if(ddMatch && format){
57269                 var fvalue = d.dateFormat(format);
57270                 if(ddMatch.test(fvalue)){
57271                     cell.title = ddText.replace("%0", fvalue);
57272                    cell.className = " fc-state-disabled";
57273                 }
57274             }
57275             
57276             if (!cell.initialClassName) {
57277                 cell.initialClassName = cell.dom.className;
57278             }
57279             
57280             cell.dom.className = cell.initialClassName  + ' ' +  cell.className;
57281         };
57282
57283         var i = 0;
57284         
57285         for(; i < startingPos; i++) {
57286             cells[i].dayName =  (++prevStart);
57287             Roo.log(textEls[i]);
57288             d.setDate(d.getDate()+1);
57289             
57290             //cells[i].className = "fc-past fc-other-month";
57291             setCellClass(this, cells[i]);
57292         }
57293         
57294         var intDay = 0;
57295         
57296         for(; i < days; i++){
57297             intDay = i - startingPos + 1;
57298             cells[i].dayName =  (intDay);
57299             d.setDate(d.getDate()+1);
57300             
57301             cells[i].className = ''; // "x-date-active";
57302             setCellClass(this, cells[i]);
57303         }
57304         var extraDays = 0;
57305         
57306         for(; i < 42; i++) {
57307             //textEls[i].innerHTML = (++extraDays);
57308             
57309             d.setDate(d.getDate()+1);
57310             cells[i].dayName = (++extraDays);
57311             cells[i].className = "fc-future fc-other-month";
57312             setCellClass(this, cells[i]);
57313         }
57314         
57315         //this.el.select('.fc-header-title h2',true).update(Date.monthNames[date.getMonth()] + " " + date.getFullYear());
57316         
57317         var totalRows = Math.ceil((date.getDaysInMonth() + date.getFirstDateOfMonth().getDay()) / 7);
57318         
57319         // this will cause all the cells to mis
57320         var rows= [];
57321         var i =0;
57322         for (var r = 0;r < 6;r++) {
57323             for (var c =0;c < 7;c++) {
57324                 this.ds.getAt(r).set('weekday' + c ,cells[i++].dayName );
57325             }    
57326         }
57327         
57328         this.cells = this.view.el.select('.x-grid-row .x-grid-col',true);
57329         for(i=0;i<cells.length;i++) {
57330             
57331             this.cells.elements[i].dayName = cells[i].dayName ;
57332             this.cells.elements[i].className = cells[i].className;
57333             this.cells.elements[i].initialClassName = cells[i].initialClassName ;
57334             this.cells.elements[i].title = cells[i].title ;
57335             this.cells.elements[i].dateValue = cells[i].dateValue ;
57336         }
57337         
57338         
57339         
57340         
57341         //this.el.select('tr.fc-week.fc-prev-last',true).removeClass('fc-last');
57342         //this.el.select('tr.fc-week.fc-next-last',true).addClass('fc-last').show();
57343         
57344         ////if(totalRows != 6){
57345             //this.el.select('tr.fc-week.fc-last',true).removeClass('fc-last').addClass('fc-next-last').hide();
57346            // this.el.select('tr.fc-week.fc-prev-last',true).addClass('fc-last');
57347        // }
57348         
57349         this.fireEvent('monthchange', this, date);
57350         
57351         
57352     },
57353  /**
57354      * Returns the grid's SelectionModel.
57355      * @return {SelectionModel}
57356      */
57357     getSelectionModel : function(){
57358         if(!this.selModel){
57359             this.selModel = new Roo.grid.CellSelectionModel();
57360         }
57361         return this.selModel;
57362     },
57363
57364     load: function() {
57365         this.eventStore.load()
57366         
57367         
57368         
57369     },
57370     
57371     findCell : function(dt) {
57372         dt = dt.clearTime().getTime();
57373         var ret = false;
57374         this.cells.each(function(c){
57375             //Roo.log("check " +c.dateValue + '?=' + dt);
57376             if(c.dateValue == dt){
57377                 ret = c;
57378                 return false;
57379             }
57380             return true;
57381         });
57382         
57383         return ret;
57384     },
57385     
57386     findCells : function(rec) {
57387         var s = rec.data.start_dt.clone().clearTime().getTime();
57388        // Roo.log(s);
57389         var e= rec.data.end_dt.clone().clearTime().getTime();
57390        // Roo.log(e);
57391         var ret = [];
57392         this.cells.each(function(c){
57393              ////Roo.log("check " +c.dateValue + '<' + e + ' > ' + s);
57394             
57395             if(c.dateValue > e){
57396                 return ;
57397             }
57398             if(c.dateValue < s){
57399                 return ;
57400             }
57401             ret.push(c);
57402         });
57403         
57404         return ret;    
57405     },
57406     
57407     findBestRow: function(cells)
57408     {
57409         var ret = 0;
57410         
57411         for (var i =0 ; i < cells.length;i++) {
57412             ret  = Math.max(cells[i].rows || 0,ret);
57413         }
57414         return ret;
57415         
57416     },
57417     
57418     
57419     addItem : function(rec)
57420     {
57421         // look for vertical location slot in
57422         var cells = this.findCells(rec);
57423         
57424         rec.row = this.findBestRow(cells);
57425         
57426         // work out the location.
57427         
57428         var crow = false;
57429         var rows = [];
57430         for(var i =0; i < cells.length; i++) {
57431             if (!crow) {
57432                 crow = {
57433                     start : cells[i],
57434                     end :  cells[i]
57435                 };
57436                 continue;
57437             }
57438             if (crow.start.getY() == cells[i].getY()) {
57439                 // on same row.
57440                 crow.end = cells[i];
57441                 continue;
57442             }
57443             // different row.
57444             rows.push(crow);
57445             crow = {
57446                 start: cells[i],
57447                 end : cells[i]
57448             };
57449             
57450         }
57451         
57452         rows.push(crow);
57453         rec.els = [];
57454         rec.rows = rows;
57455         rec.cells = cells;
57456         for (var i = 0; i < cells.length;i++) {
57457             cells[i].rows = Math.max(cells[i].rows || 0 , rec.row + 1 );
57458             
57459         }
57460         
57461         
57462     },
57463     
57464     clearEvents: function() {
57465         
57466         if (!this.eventStore.getCount()) {
57467             return;
57468         }
57469         // reset number of rows in cells.
57470         Roo.each(this.cells.elements, function(c){
57471             c.rows = 0;
57472         });
57473         
57474         this.eventStore.each(function(e) {
57475             this.clearEvent(e);
57476         },this);
57477         
57478     },
57479     
57480     clearEvent : function(ev)
57481     {
57482         if (ev.els) {
57483             Roo.each(ev.els, function(el) {
57484                 el.un('mouseenter' ,this.onEventEnter, this);
57485                 el.un('mouseleave' ,this.onEventLeave, this);
57486                 el.remove();
57487             },this);
57488             ev.els = [];
57489         }
57490     },
57491     
57492     
57493     renderEvent : function(ev,ctr) {
57494         if (!ctr) {
57495              ctr = this.view.el.select('.fc-event-container',true).first();
57496         }
57497         
57498          
57499         this.clearEvent(ev);
57500             //code
57501        
57502         
57503         
57504         ev.els = [];
57505         var cells = ev.cells;
57506         var rows = ev.rows;
57507         this.fireEvent('eventrender', this, ev);
57508         
57509         for(var i =0; i < rows.length; i++) {
57510             
57511             cls = '';
57512             if (i == 0) {
57513                 cls += ' fc-event-start';
57514             }
57515             if ((i+1) == rows.length) {
57516                 cls += ' fc-event-end';
57517             }
57518             
57519             //Roo.log(ev.data);
57520             // how many rows should it span..
57521             var cg = this.eventTmpl.append(ctr,Roo.apply({
57522                 fccls : cls
57523                 
57524             }, ev.data) , true);
57525             
57526             
57527             cg.on('mouseenter' ,this.onEventEnter, this, ev);
57528             cg.on('mouseleave' ,this.onEventLeave, this, ev);
57529             cg.on('click', this.onEventClick, this, ev);
57530             
57531             ev.els.push(cg);
57532             
57533             var sbox = rows[i].start.select('.fc-day-content',true).first().getBox();
57534             var ebox = rows[i].end.select('.fc-day-content',true).first().getBox();
57535             //Roo.log(cg);
57536              
57537             cg.setXY([sbox.x +2, sbox.y +(ev.row * 20)]);    
57538             cg.setWidth(ebox.right - sbox.x -2);
57539         }
57540     },
57541     
57542     renderEvents: function()
57543     {   
57544         // first make sure there is enough space..
57545         
57546         if (!this.eventTmpl) {
57547             this.eventTmpl = new Roo.Template(
57548                 '<div class="roo-dynamic fc-event fc-event-hori fc-event-draggable ui-draggable {fccls} {cls}"  style="position: absolute" unselectable="on">' +
57549                     '<div class="fc-event-inner">' +
57550                         '<span class="fc-event-time">{time}</span>' +
57551                         '<span class="fc-event-title" qtip="{qtip}">{title}</span>' +
57552                     '</div>' +
57553                     '<div class="ui-resizable-heandle ui-resizable-e">&nbsp;&nbsp;&nbsp;</div>' +
57554                 '</div>'
57555             );
57556                 
57557         }
57558                
57559         
57560         
57561         this.cells.each(function(c) {
57562             //Roo.log(c.select('.fc-day-content div',true).first());
57563             c.select('.fc-day-content div',true).first().setHeight(Math.max(34, (c.rows || 1) * 20));
57564         });
57565         
57566         var ctr = this.view.el.select('.fc-event-container',true).first();
57567         
57568         var cls;
57569         this.eventStore.each(function(ev){
57570             
57571             this.renderEvent(ev);
57572              
57573              
57574         }, this);
57575         this.view.layout();
57576         
57577     },
57578     
57579     onEventEnter: function (e, el,event,d) {
57580         this.fireEvent('evententer', this, el, event);
57581     },
57582     
57583     onEventLeave: function (e, el,event,d) {
57584         this.fireEvent('eventleave', this, el, event);
57585     },
57586     
57587     onEventClick: function (e, el,event,d) {
57588         this.fireEvent('eventclick', this, el, event);
57589     },
57590     
57591     onMonthChange: function () {
57592         this.store.load();
57593     },
57594     
57595     onLoad: function () {
57596         
57597         //Roo.log('calendar onload');
57598 //         
57599         if(this.eventStore.getCount() > 0){
57600             
57601            
57602             
57603             this.eventStore.each(function(d){
57604                 
57605                 
57606                 // FIXME..
57607                 var add =   d.data;
57608                 if (typeof(add.end_dt) == 'undefined')  {
57609                     Roo.log("Missing End time in calendar data: ");
57610                     Roo.log(d);
57611                     return;
57612                 }
57613                 if (typeof(add.start_dt) == 'undefined')  {
57614                     Roo.log("Missing Start time in calendar data: ");
57615                     Roo.log(d);
57616                     return;
57617                 }
57618                 add.start_dt = typeof(add.start_dt) == 'string' ? Date.parseDate(add.start_dt,'Y-m-d H:i:s') : add.start_dt,
57619                 add.end_dt = typeof(add.end_dt) == 'string' ? Date.parseDate(add.end_dt,'Y-m-d H:i:s') : add.end_dt,
57620                 add.id = add.id || d.id;
57621                 add.title = add.title || '??';
57622                 
57623                 this.addItem(d);
57624                 
57625              
57626             },this);
57627         }
57628         
57629         this.renderEvents();
57630     }
57631     
57632
57633 });
57634 /*
57635  grid : {
57636                 xtype: 'Grid',
57637                 xns: Roo.grid,
57638                 listeners : {
57639                     render : function ()
57640                     {
57641                         _this.grid = this;
57642                         
57643                         if (!this.view.el.hasClass('course-timesheet')) {
57644                             this.view.el.addClass('course-timesheet');
57645                         }
57646                         if (this.tsStyle) {
57647                             this.ds.load({});
57648                             return; 
57649                         }
57650                         Roo.log('width');
57651                         Roo.log(_this.grid.view.el.getWidth());
57652                         
57653                         
57654                         this.tsStyle =  Roo.util.CSS.createStyleSheet({
57655                             '.course-timesheet .x-grid-row' : {
57656                                 height: '80px'
57657                             },
57658                             '.x-grid-row td' : {
57659                                 'vertical-align' : 0
57660                             },
57661                             '.course-edit-link' : {
57662                                 'color' : 'blue',
57663                                 'text-overflow' : 'ellipsis',
57664                                 'overflow' : 'hidden',
57665                                 'white-space' : 'nowrap',
57666                                 'cursor' : 'pointer'
57667                             },
57668                             '.sub-link' : {
57669                                 'color' : 'green'
57670                             },
57671                             '.de-act-sup-link' : {
57672                                 'color' : 'purple',
57673                                 'text-decoration' : 'line-through'
57674                             },
57675                             '.de-act-link' : {
57676                                 'color' : 'red',
57677                                 'text-decoration' : 'line-through'
57678                             },
57679                             '.course-timesheet .course-highlight' : {
57680                                 'border-top-style': 'dashed !important',
57681                                 'border-bottom-bottom': 'dashed !important'
57682                             },
57683                             '.course-timesheet .course-item' : {
57684                                 'font-family'   : 'tahoma, arial, helvetica',
57685                                 'font-size'     : '11px',
57686                                 'overflow'      : 'hidden',
57687                                 'padding-left'  : '10px',
57688                                 'padding-right' : '10px',
57689                                 'padding-top' : '10px' 
57690                             }
57691                             
57692                         }, Roo.id());
57693                                 this.ds.load({});
57694                     }
57695                 },
57696                 autoWidth : true,
57697                 monitorWindowResize : false,
57698                 cellrenderer : function(v,x,r)
57699                 {
57700                     return v;
57701                 },
57702                 sm : {
57703                     xtype: 'CellSelectionModel',
57704                     xns: Roo.grid
57705                 },
57706                 dataSource : {
57707                     xtype: 'Store',
57708                     xns: Roo.data,
57709                     listeners : {
57710                         beforeload : function (_self, options)
57711                         {
57712                             options.params = options.params || {};
57713                             options.params._month = _this.monthField.getValue();
57714                             options.params.limit = 9999;
57715                             options.params['sort'] = 'when_dt';    
57716                             options.params['dir'] = 'ASC';    
57717                             this.proxy.loadResponse = this.loadResponse;
57718                             Roo.log("load?");
57719                             //this.addColumns();
57720                         },
57721                         load : function (_self, records, options)
57722                         {
57723                             _this.grid.view.el.select('.course-edit-link', true).on('click', function() {
57724                                 // if you click on the translation.. you can edit it...
57725                                 var el = Roo.get(this);
57726                                 var id = el.dom.getAttribute('data-id');
57727                                 var d = el.dom.getAttribute('data-date');
57728                                 var t = el.dom.getAttribute('data-time');
57729                                 //var id = this.child('span').dom.textContent;
57730                                 
57731                                 //Roo.log(this);
57732                                 Pman.Dialog.CourseCalendar.show({
57733                                     id : id,
57734                                     when_d : d,
57735                                     when_t : t,
57736                                     productitem_active : id ? 1 : 0
57737                                 }, function() {
57738                                     _this.grid.ds.load({});
57739                                 });
57740                            
57741                            });
57742                            
57743                            _this.panel.fireEvent('resize', [ '', '' ]);
57744                         }
57745                     },
57746                     loadResponse : function(o, success, response){
57747                             // this is overridden on before load..
57748                             
57749                             Roo.log("our code?");       
57750                             //Roo.log(success);
57751                             //Roo.log(response)
57752                             delete this.activeRequest;
57753                             if(!success){
57754                                 this.fireEvent("loadexception", this, o, response);
57755                                 o.request.callback.call(o.request.scope, null, o.request.arg, false);
57756                                 return;
57757                             }
57758                             var result;
57759                             try {
57760                                 result = o.reader.read(response);
57761                             }catch(e){
57762                                 Roo.log("load exception?");
57763                                 this.fireEvent("loadexception", this, o, response, e);
57764                                 o.request.callback.call(o.request.scope, null, o.request.arg, false);
57765                                 return;
57766                             }
57767                             Roo.log("ready...");        
57768                             // loop through result.records;
57769                             // and set this.tdate[date] = [] << array of records..
57770                             _this.tdata  = {};
57771                             Roo.each(result.records, function(r){
57772                                 //Roo.log(r.data);
57773                                 if(typeof(_this.tdata[r.data.when_dt.format('j')]) == 'undefined'){
57774                                     _this.tdata[r.data.when_dt.format('j')] = [];
57775                                 }
57776                                 _this.tdata[r.data.when_dt.format('j')].push(r.data);
57777                             });
57778                             
57779                             //Roo.log(_this.tdata);
57780                             
57781                             result.records = [];
57782                             result.totalRecords = 6;
57783                     
57784                             // let's generate some duumy records for the rows.
57785                             //var st = _this.dateField.getValue();
57786                             
57787                             // work out monday..
57788                             //st = st.add(Date.DAY, -1 * st.format('w'));
57789                             
57790                             var date = Date.parseDate(_this.monthField.getValue(), "Y-m-d");
57791                             
57792                             var firstOfMonth = date.getFirstDayOfMonth();
57793                             var days = date.getDaysInMonth();
57794                             var d = 1;
57795                             var firstAdded = false;
57796                             for (var i = 0; i < result.totalRecords ; i++) {
57797                                 //var d= st.add(Date.DAY, i);
57798                                 var row = {};
57799                                 var added = 0;
57800                                 for(var w = 0 ; w < 7 ; w++){
57801                                     if(!firstAdded && firstOfMonth != w){
57802                                         continue;
57803                                     }
57804                                     if(d > days){
57805                                         continue;
57806                                     }
57807                                     firstAdded = true;
57808                                     var dd = (d > 0 && d < 10) ? "0"+d : d;
57809                                     row['weekday'+w] = String.format(
57810                                                     '<span style="font-size: 16px;"><b>{0}</b></span>'+
57811                                                     '<span class="course-edit-link" style="color:blue;" data-id="0" data-date="{1}"> Add New</span>',
57812                                                     d,
57813                                                     date.format('Y-m-')+dd
57814                                                 );
57815                                     added++;
57816                                     if(typeof(_this.tdata[d]) != 'undefined'){
57817                                         Roo.each(_this.tdata[d], function(r){
57818                                             var is_sub = '';
57819                                             var deactive = '';
57820                                             var id = r.id;
57821                                             var desc = (r.productitem_id_descrip) ? r.productitem_id_descrip : '';
57822                                             if(r.parent_id*1>0){
57823                                                 is_sub = (r.productitem_id_visible*1 < 1) ? 'de-act-sup-link' :'sub-link';
57824                                                 id = r.parent_id;
57825                                             }
57826                                             if(r.productitem_id_visible*1 < 1 && r.parent_id*1 < 1){
57827                                                 deactive = 'de-act-link';
57828                                             }
57829                                             
57830                                             row['weekday'+w] += String.format(
57831                                                     '<br /><span class="course-edit-link {3} {4}" qtip="{5}" data-id="{0}">{2} - {1}</span>',
57832                                                     id, //0
57833                                                     r.product_id_name, //1
57834                                                     r.when_dt.format('h:ia'), //2
57835                                                     is_sub, //3
57836                                                     deactive, //4
57837                                                     desc // 5
57838                                             );
57839                                         });
57840                                     }
57841                                     d++;
57842                                 }
57843                                 
57844                                 // only do this if something added..
57845                                 if(added > 0){ 
57846                                     result.records.push(_this.grid.dataSource.reader.newRow(row));
57847                                 }
57848                                 
57849                                 
57850                                 // push it twice. (second one with an hour..
57851                                 
57852                             }
57853                             //Roo.log(result);
57854                             this.fireEvent("load", this, o, o.request.arg);
57855                             o.request.callback.call(o.request.scope, result, o.request.arg, true);
57856                         },
57857                     sortInfo : {field: 'when_dt', direction : 'ASC' },
57858                     proxy : {
57859                         xtype: 'HttpProxy',
57860                         xns: Roo.data,
57861                         method : 'GET',
57862                         url : baseURL + '/Roo/Shop_course.php'
57863                     },
57864                     reader : {
57865                         xtype: 'JsonReader',
57866                         xns: Roo.data,
57867                         id : 'id',
57868                         fields : [
57869                             {
57870                                 'name': 'id',
57871                                 'type': 'int'
57872                             },
57873                             {
57874                                 'name': 'when_dt',
57875                                 'type': 'string'
57876                             },
57877                             {
57878                                 'name': 'end_dt',
57879                                 'type': 'string'
57880                             },
57881                             {
57882                                 'name': 'parent_id',
57883                                 'type': 'int'
57884                             },
57885                             {
57886                                 'name': 'product_id',
57887                                 'type': 'int'
57888                             },
57889                             {
57890                                 'name': 'productitem_id',
57891                                 'type': 'int'
57892                             },
57893                             {
57894                                 'name': 'guid',
57895                                 'type': 'int'
57896                             }
57897                         ]
57898                     }
57899                 },
57900                 toolbar : {
57901                     xtype: 'Toolbar',
57902                     xns: Roo,
57903                     items : [
57904                         {
57905                             xtype: 'Button',
57906                             xns: Roo.Toolbar,
57907                             listeners : {
57908                                 click : function (_self, e)
57909                                 {
57910                                     var sd = Date.parseDate(_this.monthField.getValue(), "Y-m-d");
57911                                     sd.setMonth(sd.getMonth()-1);
57912                                     _this.monthField.setValue(sd.format('Y-m-d'));
57913                                     _this.grid.ds.load({});
57914                                 }
57915                             },
57916                             text : "Back"
57917                         },
57918                         {
57919                             xtype: 'Separator',
57920                             xns: Roo.Toolbar
57921                         },
57922                         {
57923                             xtype: 'MonthField',
57924                             xns: Roo.form,
57925                             listeners : {
57926                                 render : function (_self)
57927                                 {
57928                                     _this.monthField = _self;
57929                                    // _this.monthField.set  today
57930                                 },
57931                                 select : function (combo, date)
57932                                 {
57933                                     _this.grid.ds.load({});
57934                                 }
57935                             },
57936                             value : (function() { return new Date(); })()
57937                         },
57938                         {
57939                             xtype: 'Separator',
57940                             xns: Roo.Toolbar
57941                         },
57942                         {
57943                             xtype: 'TextItem',
57944                             xns: Roo.Toolbar,
57945                             text : "Blue: in-active, green: in-active sup-event, red: de-active, purple: de-active sup-event"
57946                         },
57947                         {
57948                             xtype: 'Fill',
57949                             xns: Roo.Toolbar
57950                         },
57951                         {
57952                             xtype: 'Button',
57953                             xns: Roo.Toolbar,
57954                             listeners : {
57955                                 click : function (_self, e)
57956                                 {
57957                                     var sd = Date.parseDate(_this.monthField.getValue(), "Y-m-d");
57958                                     sd.setMonth(sd.getMonth()+1);
57959                                     _this.monthField.setValue(sd.format('Y-m-d'));
57960                                     _this.grid.ds.load({});
57961                                 }
57962                             },
57963                             text : "Next"
57964                         }
57965                     ]
57966                 },
57967                  
57968             }
57969         };
57970         
57971         *//*
57972  * Based on:
57973  * Ext JS Library 1.1.1
57974  * Copyright(c) 2006-2007, Ext JS, LLC.
57975  *
57976  * Originally Released Under LGPL - original licence link has changed is not relivant.
57977  *
57978  * Fork - LGPL
57979  * <script type="text/javascript">
57980  */
57981  
57982 /**
57983  * @class Roo.LoadMask
57984  * A simple utility class for generically masking elements while loading data.  If the element being masked has
57985  * an underlying {@link Roo.data.Store}, the masking will be automatically synchronized with the store's loading
57986  * process and the mask element will be cached for reuse.  For all other elements, this mask will replace the
57987  * element's UpdateManager load indicator and will be destroyed after the initial load.
57988  * @constructor
57989  * Create a new LoadMask
57990  * @param {String/HTMLElement/Roo.Element} el The element or DOM node, or its id
57991  * @param {Object} config The config object
57992  */
57993 Roo.LoadMask = function(el, config){
57994     this.el = Roo.get(el);
57995     Roo.apply(this, config);
57996     if(this.store){
57997         this.store.on('beforeload', this.onBeforeLoad, this);
57998         this.store.on('load', this.onLoad, this);
57999         this.store.on('loadexception', this.onLoadException, this);
58000         this.removeMask = false;
58001     }else{
58002         var um = this.el.getUpdateManager();
58003         um.showLoadIndicator = false; // disable the default indicator
58004         um.on('beforeupdate', this.onBeforeLoad, this);
58005         um.on('update', this.onLoad, this);
58006         um.on('failure', this.onLoad, this);
58007         this.removeMask = true;
58008     }
58009 };
58010
58011 Roo.LoadMask.prototype = {
58012     /**
58013      * @cfg {Boolean} removeMask
58014      * True to create a single-use mask that is automatically destroyed after loading (useful for page loads),
58015      * False to persist the mask element reference for multiple uses (e.g., for paged data widgets).  Defaults to false.
58016      */
58017     /**
58018      * @cfg {String} msg
58019      * The text to display in a centered loading message box (defaults to 'Loading...')
58020      */
58021     msg : 'Loading...',
58022     /**
58023      * @cfg {String} msgCls
58024      * The CSS class to apply to the loading message element (defaults to "x-mask-loading")
58025      */
58026     msgCls : 'x-mask-loading',
58027
58028     /**
58029      * Read-only. True if the mask is currently disabled so that it will not be displayed (defaults to false)
58030      * @type Boolean
58031      */
58032     disabled: false,
58033
58034     /**
58035      * Disables the mask to prevent it from being displayed
58036      */
58037     disable : function(){
58038        this.disabled = true;
58039     },
58040
58041     /**
58042      * Enables the mask so that it can be displayed
58043      */
58044     enable : function(){
58045         this.disabled = false;
58046     },
58047     
58048     onLoadException : function()
58049     {
58050         Roo.log(arguments);
58051         
58052         if (typeof(arguments[3]) != 'undefined') {
58053             Roo.MessageBox.alert("Error loading",arguments[3]);
58054         } 
58055         /*
58056         try {
58057             if (this.store && typeof(this.store.reader.jsonData.errorMsg) != 'undefined') {
58058                 Roo.MessageBox.alert("Error loading",this.store.reader.jsonData.errorMsg);
58059             }   
58060         } catch(e) {
58061             
58062         }
58063         */
58064     
58065         
58066         
58067         this.el.unmask(this.removeMask);
58068     },
58069     // private
58070     onLoad : function()
58071     {
58072         this.el.unmask(this.removeMask);
58073     },
58074
58075     // private
58076     onBeforeLoad : function(){
58077         if(!this.disabled){
58078             this.el.mask(this.msg, this.msgCls);
58079         }
58080     },
58081
58082     // private
58083     destroy : function(){
58084         if(this.store){
58085             this.store.un('beforeload', this.onBeforeLoad, this);
58086             this.store.un('load', this.onLoad, this);
58087             this.store.un('loadexception', this.onLoadException, this);
58088         }else{
58089             var um = this.el.getUpdateManager();
58090             um.un('beforeupdate', this.onBeforeLoad, this);
58091             um.un('update', this.onLoad, this);
58092             um.un('failure', this.onLoad, this);
58093         }
58094     }
58095 };/*
58096  * Based on:
58097  * Ext JS Library 1.1.1
58098  * Copyright(c) 2006-2007, Ext JS, LLC.
58099  *
58100  * Originally Released Under LGPL - original licence link has changed is not relivant.
58101  *
58102  * Fork - LGPL
58103  * <script type="text/javascript">
58104  */
58105
58106
58107 /**
58108  * @class Roo.XTemplate
58109  * @extends Roo.Template
58110  * Provides a template that can have nested templates for loops or conditionals. The syntax is:
58111 <pre><code>
58112 var t = new Roo.XTemplate(
58113         '&lt;select name="{name}"&gt;',
58114                 '&lt;tpl for="options"&gt;&lt;option value="{value:trim}"&gt;{text:ellipsis(10)}&lt;/option&gt;&lt;/tpl&gt;',
58115         '&lt;/select&gt;'
58116 );
58117  
58118 // then append, applying the master template values
58119  </code></pre>
58120  *
58121  * Supported features:
58122  *
58123  *  Tags:
58124
58125 <pre><code>
58126       {a_variable} - output encoded.
58127       {a_variable.format:("Y-m-d")} - call a method on the variable
58128       {a_variable:raw} - unencoded output
58129       {a_variable:toFixed(1,2)} - Roo.util.Format."toFixed"
58130       {a_variable:this.method_on_template(...)} - call a method on the template object.
58131  
58132 </code></pre>
58133  *  The tpl tag:
58134 <pre><code>
58135         &lt;tpl for="a_variable or condition.."&gt;&lt;/tpl&gt;
58136         &lt;tpl if="a_variable or condition"&gt;&lt;/tpl&gt;
58137         &lt;tpl exec="some javascript"&gt;&lt;/tpl&gt;
58138         &lt;tpl name="named_template"&gt;&lt;/tpl&gt; (experimental)
58139   
58140         &lt;tpl for="."&gt;&lt;/tpl&gt; - just iterate the property..
58141         &lt;tpl for=".."&gt;&lt;/tpl&gt; - iterates with the parent (probably the template) 
58142 </code></pre>
58143  *      
58144  */
58145 Roo.XTemplate = function()
58146 {
58147     Roo.XTemplate.superclass.constructor.apply(this, arguments);
58148     if (this.html) {
58149         this.compile();
58150     }
58151 };
58152
58153
58154 Roo.extend(Roo.XTemplate, Roo.Template, {
58155
58156     /**
58157      * The various sub templates
58158      */
58159     tpls : false,
58160     /**
58161      *
58162      * basic tag replacing syntax
58163      * WORD:WORD()
58164      *
58165      * // you can fake an object call by doing this
58166      *  x.t:(test,tesT) 
58167      * 
58168      */
58169     re : /\{([\w-\.]+)(?:\:([\w\.]*)(?:\((.*?)?\))?)?\}/g,
58170
58171     /**
58172      * compile the template
58173      *
58174      * This is not recursive, so I'm not sure how nested templates are really going to be handled..
58175      *
58176      */
58177     compile: function()
58178     {
58179         var s = this.html;
58180      
58181         s = ['<tpl>', s, '</tpl>'].join('');
58182     
58183         var re     = /<tpl\b[^>]*>((?:(?=([^<]+))\2|<(?!tpl\b[^>]*>))*?)<\/tpl>/,
58184             nameRe = /^<tpl\b[^>]*?for="(.*?)"/,
58185             ifRe   = /^<tpl\b[^>]*?if="(.*?)"/,
58186             execRe = /^<tpl\b[^>]*?exec="(.*?)"/,
58187             namedRe = /^<tpl\b[^>]*?name="(\w+)"/,  // named templates..
58188             m,
58189             id     = 0,
58190             tpls   = [];
58191     
58192         while(true == !!(m = s.match(re))){
58193             var forMatch   = m[0].match(nameRe),
58194                 ifMatch   = m[0].match(ifRe),
58195                 execMatch   = m[0].match(execRe),
58196                 namedMatch   = m[0].match(namedRe),
58197                 
58198                 exp  = null, 
58199                 fn   = null,
58200                 exec = null,
58201                 name = forMatch && forMatch[1] ? forMatch[1] : '';
58202                 
58203             if (ifMatch) {
58204                 // if - puts fn into test..
58205                 exp = ifMatch && ifMatch[1] ? ifMatch[1] : null;
58206                 if(exp){
58207                    fn = new Function('values', 'parent', 'with(values){ return '+(Roo.util.Format.htmlDecode(exp))+'; }');
58208                 }
58209             }
58210             
58211             if (execMatch) {
58212                 // exec - calls a function... returns empty if true is  returned.
58213                 exp = execMatch && execMatch[1] ? execMatch[1] : null;
58214                 if(exp){
58215                    exec = new Function('values', 'parent', 'with(values){ '+(Roo.util.Format.htmlDecode(exp))+'; }');
58216                 }
58217             }
58218             
58219             
58220             if (name) {
58221                 // for = 
58222                 switch(name){
58223                     case '.':  name = new Function('values', 'parent', 'with(values){ return values; }'); break;
58224                     case '..': name = new Function('values', 'parent', 'with(values){ return parent; }'); break;
58225                     default:   name = new Function('values', 'parent', 'with(values){ return '+name+'; }');
58226                 }
58227             }
58228             var uid = namedMatch ? namedMatch[1] : id;
58229             
58230             
58231             tpls.push({
58232                 id:     namedMatch ? namedMatch[1] : id,
58233                 target: name,
58234                 exec:   exec,
58235                 test:   fn,
58236                 body:   m[1] || ''
58237             });
58238             if (namedMatch) {
58239                 s = s.replace(m[0], '');
58240             } else { 
58241                 s = s.replace(m[0], '{xtpl'+ id + '}');
58242             }
58243             ++id;
58244         }
58245         this.tpls = [];
58246         for(var i = tpls.length-1; i >= 0; --i){
58247             this.compileTpl(tpls[i]);
58248             this.tpls[tpls[i].id] = tpls[i];
58249         }
58250         this.master = tpls[tpls.length-1];
58251         return this;
58252     },
58253     /**
58254      * same as applyTemplate, except it's done to one of the subTemplates
58255      * when using named templates, you can do:
58256      *
58257      * var str = pl.applySubTemplate('your-name', values);
58258      *
58259      * 
58260      * @param {Number} id of the template
58261      * @param {Object} values to apply to template
58262      * @param {Object} parent (normaly the instance of this object)
58263      */
58264     applySubTemplate : function(id, values, parent)
58265     {
58266         
58267         
58268         var t = this.tpls[id];
58269         
58270         
58271         try { 
58272             if(t.test && !t.test.call(this, values, parent)){
58273                 return '';
58274             }
58275         } catch(e) {
58276             Roo.log("Xtemplate.applySubTemplate 'test': Exception thrown");
58277             Roo.log(e.toString());
58278             Roo.log(t.test);
58279             return ''
58280         }
58281         try { 
58282             
58283             if(t.exec && t.exec.call(this, values, parent)){
58284                 return '';
58285             }
58286         } catch(e) {
58287             Roo.log("Xtemplate.applySubTemplate 'exec': Exception thrown");
58288             Roo.log(e.toString());
58289             Roo.log(t.exec);
58290             return ''
58291         }
58292         try {
58293             var vs = t.target ? t.target.call(this, values, parent) : values;
58294             parent = t.target ? values : parent;
58295             if(t.target && vs instanceof Array){
58296                 var buf = [];
58297                 for(var i = 0, len = vs.length; i < len; i++){
58298                     buf[buf.length] = t.compiled.call(this, vs[i], parent);
58299                 }
58300                 return buf.join('');
58301             }
58302             return t.compiled.call(this, vs, parent);
58303         } catch (e) {
58304             Roo.log("Xtemplate.applySubTemplate : Exception thrown");
58305             Roo.log(e.toString());
58306             Roo.log(t.compiled);
58307             return '';
58308         }
58309     },
58310
58311     compileTpl : function(tpl)
58312     {
58313         var fm = Roo.util.Format;
58314         var useF = this.disableFormats !== true;
58315         var sep = Roo.isGecko ? "+" : ",";
58316         var undef = function(str) {
58317             Roo.log("Property not found :"  + str);
58318             return '';
58319         };
58320         
58321         var fn = function(m, name, format, args)
58322         {
58323             //Roo.log(arguments);
58324             args = args ? args.replace(/\\'/g,"'") : args;
58325             //["{TEST:(a,b,c)}", "TEST", "", "a,b,c", 0, "{TEST:(a,b,c)}"]
58326             if (typeof(format) == 'undefined') {
58327                 format= 'htmlEncode';
58328             }
58329             if (format == 'raw' ) {
58330                 format = false;
58331             }
58332             
58333             if(name.substr(0, 4) == 'xtpl'){
58334                 return "'"+ sep +'this.applySubTemplate('+name.substr(4)+', values, parent)'+sep+"'";
58335             }
58336             
58337             // build an array of options to determine if value is undefined..
58338             
58339             // basically get 'xxxx.yyyy' then do
58340             // (typeof(xxxx) == 'undefined' || typeof(xxx.yyyy) == 'undefined') ?
58341             //    (function () { Roo.log("Property not found"); return ''; })() :
58342             //    ......
58343             
58344             var udef_ar = [];
58345             var lookfor = '';
58346             Roo.each(name.split('.'), function(st) {
58347                 lookfor += (lookfor.length ? '.': '') + st;
58348                 udef_ar.push(  "(typeof(" + lookfor + ") == 'undefined')"  );
58349             });
58350             
58351             var udef_st = '((' + udef_ar.join(" || ") +") ? undef('" + name + "') : "; // .. needs )
58352             
58353             
58354             if(format && useF){
58355                 
58356                 args = args ? ',' + args : "";
58357                  
58358                 if(format.substr(0, 5) != "this."){
58359                     format = "fm." + format + '(';
58360                 }else{
58361                     format = 'this.call("'+ format.substr(5) + '", ';
58362                     args = ", values";
58363                 }
58364                 
58365                 return "'"+ sep +   udef_st   +    format + name + args + "))"+sep+"'";
58366             }
58367              
58368             if (args.length) {
58369                 // called with xxyx.yuu:(test,test)
58370                 // change to ()
58371                 return "'"+ sep + udef_st  + name + '(' +  args + "))"+sep+"'";
58372             }
58373             // raw.. - :raw modifier..
58374             return "'"+ sep + udef_st  + name + ")"+sep+"'";
58375             
58376         };
58377         var body;
58378         // branched to use + in gecko and [].join() in others
58379         if(Roo.isGecko){
58380             body = "tpl.compiled = function(values, parent){  with(values) { return '" +
58381                    tpl.body.replace(/(\r\n|\n)/g, '\\n').replace(/'/g, "\\'").replace(this.re, fn) +
58382                     "';};};";
58383         }else{
58384             body = ["tpl.compiled = function(values, parent){  with (values) { return ['"];
58385             body.push(tpl.body.replace(/(\r\n|\n)/g,
58386                             '\\n').replace(/'/g, "\\'").replace(this.re, fn));
58387             body.push("'].join('');};};");
58388             body = body.join('');
58389         }
58390         
58391         Roo.debug && Roo.log(body.replace(/\\n/,'\n'));
58392        
58393         /** eval:var:tpl eval:var:fm eval:var:useF eval:var:undef  */
58394         eval(body);
58395         
58396         return this;
58397     },
58398
58399     applyTemplate : function(values){
58400         return this.master.compiled.call(this, values, {});
58401         //var s = this.subs;
58402     },
58403
58404     apply : function(){
58405         return this.applyTemplate.apply(this, arguments);
58406     }
58407
58408  });
58409
58410 Roo.XTemplate.from = function(el){
58411     el = Roo.getDom(el);
58412     return new Roo.XTemplate(el.value || el.innerHTML);
58413 };