Roo/form/Checkbox.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         if (this.disabled) {
40891             return;
40892         }
40893         this.setChecked(!this.checked);
40894
40895         //if(this.el.dom.checked != this.checked){
40896         //    this.setValue(this.el.dom.checked);
40897        // }
40898     },
40899
40900     /**
40901      * Sets the checked state of the checkbox.
40902      * On is always based on a string comparison between inputValue and the param.
40903      * @param {Boolean/String} value - the value to set 
40904      * @param {Boolean/String} suppressEvent - whether to suppress the checkchange event.
40905      */
40906     setValue : function(v,suppressEvent){
40907         
40908         
40909         //this.checked = (v === true || v === 'true' || v == '1' || String(v).toLowerCase() == 'on');
40910         //if(this.el && this.el.dom){
40911         //    this.el.dom.checked = this.checked;
40912         //    this.el.dom.defaultChecked = this.checked;
40913         //}
40914         this.setChecked(String(v) === String(this.inputValue), suppressEvent);
40915         //this.fireEvent("check", this, this.checked);
40916     },
40917     // private..
40918     setChecked : function(state,suppressEvent)
40919     {
40920         if (this.inSetChecked) {
40921             this.checked = state;
40922             return;
40923         }
40924         
40925     
40926         if(this.wrap){
40927             this.wrap[state ? 'addClass' : 'removeClass']('x-menu-item-checked');
40928         }
40929         this.checked = state;
40930         if(suppressEvent !== true){
40931             this.fireEvent('check', this, state);
40932         }
40933         this.inSetChecked = true;
40934         this.el.dom.value = state ? this.inputValue : this.valueOff;
40935         this.inSetChecked = false;
40936         
40937     },
40938     // handle setting of hidden value by some other method!!?!?
40939     setFromHidden: function()
40940     {
40941         if(!this.el){
40942             return;
40943         }
40944         //console.log("SET FROM HIDDEN");
40945         //alert('setFrom hidden');
40946         this.setValue(this.el.dom.value);
40947     },
40948     
40949     onDestroy : function()
40950     {
40951         if(this.viewEl){
40952             Roo.get(this.viewEl).remove();
40953         }
40954          
40955         Roo.form.Checkbox.superclass.onDestroy.call(this);
40956     }
40957
40958 });/*
40959  * Based on:
40960  * Ext JS Library 1.1.1
40961  * Copyright(c) 2006-2007, Ext JS, LLC.
40962  *
40963  * Originally Released Under LGPL - original licence link has changed is not relivant.
40964  *
40965  * Fork - LGPL
40966  * <script type="text/javascript">
40967  */
40968  
40969 /**
40970  * @class Roo.form.Radio
40971  * @extends Roo.form.Checkbox
40972  * Single radio field.  Same as Checkbox, but provided as a convenience for automatically setting the input type.
40973  * Radio grouping is handled automatically by the browser if you give each radio in a group the same name.
40974  * @constructor
40975  * Creates a new Radio
40976  * @param {Object} config Configuration options
40977  */
40978 Roo.form.Radio = function(){
40979     Roo.form.Radio.superclass.constructor.apply(this, arguments);
40980 };
40981 Roo.extend(Roo.form.Radio, Roo.form.Checkbox, {
40982     inputType: 'radio',
40983
40984     /**
40985      * If this radio is part of a group, it will return the selected value
40986      * @return {String}
40987      */
40988     getGroupValue : function(){
40989         return this.el.up('form').child('input[name='+this.el.dom.name+']:checked', true).value;
40990     },
40991     
40992     
40993     onRender : function(ct, position){
40994         Roo.form.Checkbox.superclass.onRender.call(this, ct, position);
40995         
40996         if(this.inputValue !== undefined){
40997             this.el.dom.value = this.inputValue;
40998         }
40999          
41000         this.wrap = this.el.wrap({cls: "x-form-check-wrap"});
41001         //this.wrap = this.el.wrap({cls: 'x-menu-check-item '});
41002         //var viewEl = this.wrap.createChild({ 
41003         //    tag: 'img', cls: 'x-menu-item-icon', style: 'margin: 0px;' ,src : Roo.BLANK_IMAGE_URL });
41004         //this.viewEl = viewEl;   
41005         //this.wrap.on('click', this.onClick,  this); 
41006         
41007         //this.el.on('DOMAttrModified', this.setFromHidden,  this); //ff
41008         //this.el.on('propertychange', this.setFromHidden,  this);  //ie
41009         
41010         
41011         
41012         if(this.boxLabel){
41013             this.wrap.createChild({tag: 'label', htmlFor: this.el.id, cls: 'x-form-cb-label', html: this.boxLabel});
41014         //    viewEl.on('click', this.onClick,  this); 
41015         }
41016          if(this.checked){
41017             this.el.dom.checked =   'checked' ;
41018         }
41019          
41020     } 
41021     
41022     
41023 });//<script type="text/javascript">
41024
41025 /*
41026  * Based  Ext JS Library 1.1.1
41027  * Copyright(c) 2006-2007, Ext JS, LLC.
41028  * LGPL
41029  *
41030  */
41031  
41032 /**
41033  * @class Roo.HtmlEditorCore
41034  * @extends Roo.Component
41035  * Provides a the editing component for the HTML editors in Roo. (bootstrap and Roo.form)
41036  *
41037  * any element that has display set to 'none' can cause problems in Safari and Firefox.<br/><br/>
41038  */
41039
41040 Roo.HtmlEditorCore = function(config){
41041     
41042     
41043     Roo.HtmlEditorCore.superclass.constructor.call(this, config);
41044     
41045     
41046     this.addEvents({
41047         /**
41048          * @event initialize
41049          * Fires when the editor is fully initialized (including the iframe)
41050          * @param {Roo.HtmlEditorCore} this
41051          */
41052         initialize: true,
41053         /**
41054          * @event activate
41055          * Fires when the editor is first receives the focus. Any insertion must wait
41056          * until after this event.
41057          * @param {Roo.HtmlEditorCore} this
41058          */
41059         activate: true,
41060          /**
41061          * @event beforesync
41062          * Fires before the textarea is updated with content from the editor iframe. Return false
41063          * to cancel the sync.
41064          * @param {Roo.HtmlEditorCore} this
41065          * @param {String} html
41066          */
41067         beforesync: true,
41068          /**
41069          * @event beforepush
41070          * Fires before the iframe editor is updated with content from the textarea. Return false
41071          * to cancel the push.
41072          * @param {Roo.HtmlEditorCore} this
41073          * @param {String} html
41074          */
41075         beforepush: true,
41076          /**
41077          * @event sync
41078          * Fires when the textarea is updated with content from the editor iframe.
41079          * @param {Roo.HtmlEditorCore} this
41080          * @param {String} html
41081          */
41082         sync: true,
41083          /**
41084          * @event push
41085          * Fires when the iframe editor is updated with content from the textarea.
41086          * @param {Roo.HtmlEditorCore} this
41087          * @param {String} html
41088          */
41089         push: true,
41090         
41091         /**
41092          * @event editorevent
41093          * Fires when on any editor (mouse up/down cursor movement etc.) - used for toolbar hooks.
41094          * @param {Roo.HtmlEditorCore} this
41095          */
41096         editorevent: true
41097     });
41098     
41099     // at this point this.owner is set, so we can start working out the whitelisted / blacklisted elements
41100     
41101     // defaults : white / black...
41102     this.applyBlacklists();
41103     
41104     
41105     
41106 };
41107
41108
41109 Roo.extend(Roo.HtmlEditorCore, Roo.Component,  {
41110
41111
41112      /**
41113      * @cfg {Roo.form.HtmlEditor|Roo.bootstrap.HtmlEditor} the owner field 
41114      */
41115     
41116     owner : false,
41117     
41118      /**
41119      * @cfg {String} resizable  's' or 'se' or 'e' - wrapps the element in a
41120      *                        Roo.resizable.
41121      */
41122     resizable : false,
41123      /**
41124      * @cfg {Number} height (in pixels)
41125      */   
41126     height: 300,
41127    /**
41128      * @cfg {Number} width (in pixels)
41129      */   
41130     width: 500,
41131     
41132     /**
41133      * @cfg {Array} stylesheets url of stylesheets. set to [] to disable stylesheets.
41134      * 
41135      */
41136     stylesheets: false,
41137     
41138     // id of frame..
41139     frameId: false,
41140     
41141     // private properties
41142     validationEvent : false,
41143     deferHeight: true,
41144     initialized : false,
41145     activated : false,
41146     sourceEditMode : false,
41147     onFocus : Roo.emptyFn,
41148     iframePad:3,
41149     hideMode:'offsets',
41150     
41151     clearUp: true,
41152     
41153     // blacklist + whitelisted elements..
41154     black: false,
41155     white: false,
41156      
41157     
41158
41159     /**
41160      * Protected method that will not generally be called directly. It
41161      * is called when the editor initializes the iframe with HTML contents. Override this method if you
41162      * want to change the initialization markup of the iframe (e.g. to add stylesheets).
41163      */
41164     getDocMarkup : function(){
41165         // body styles..
41166         var st = '';
41167         Roo.log(this.stylesheets);
41168         
41169         // inherit styels from page...?? 
41170         if (this.stylesheets === false) {
41171             
41172             Roo.get(document.head).select('style').each(function(node) {
41173                 st += node.dom.outerHTML || new XMLSerializer().serializeToString(node.dom);
41174             });
41175             
41176             Roo.get(document.head).select('link').each(function(node) { 
41177                 st += node.dom.outerHTML || new XMLSerializer().serializeToString(node.dom);
41178             });
41179             
41180         } else if (!this.stylesheets.length) {
41181                 // simple..
41182                 st = '<style type="text/css">' +
41183                     'body{border:0;margin:0;padding:3px;height:98%;cursor:text;}' +
41184                    '</style>';
41185         } else {
41186             Roo.each(this.stylesheets, function(s) {
41187                 st += '<link rel="stylesheet" type="text/css" href="' + s +'" />'
41188             });
41189             
41190         }
41191         
41192         st +=  '<style type="text/css">' +
41193             'IMG { cursor: pointer } ' +
41194         '</style>';
41195
41196         
41197         return '<html><head>' + st  +
41198             //<style type="text/css">' +
41199             //'body{border:0;margin:0;padding:3px;height:98%;cursor:text;}' +
41200             //'</style>' +
41201             ' </head><body class="roo-htmleditor-body"></body></html>';
41202     },
41203
41204     // private
41205     onRender : function(ct, position)
41206     {
41207         var _t = this;
41208         //Roo.HtmlEditorCore.superclass.onRender.call(this, ct, position);
41209         this.el = this.owner.inputEl ? this.owner.inputEl() : this.owner.el;
41210         
41211         
41212         this.el.dom.style.border = '0 none';
41213         this.el.dom.setAttribute('tabIndex', -1);
41214         this.el.addClass('x-hidden hide');
41215         
41216         
41217         
41218         if(Roo.isIE){ // fix IE 1px bogus margin
41219             this.el.applyStyles('margin-top:-1px;margin-bottom:-1px;')
41220         }
41221        
41222         
41223         this.frameId = Roo.id();
41224         
41225          
41226         
41227         var iframe = this.owner.wrap.createChild({
41228             tag: 'iframe',
41229             cls: 'form-control', // bootstrap..
41230             id: this.frameId,
41231             name: this.frameId,
41232             frameBorder : 'no',
41233             'src' : Roo.SSL_SECURE_URL ? Roo.SSL_SECURE_URL  :  "javascript:false"
41234         }, this.el
41235         );
41236         
41237         
41238         this.iframe = iframe.dom;
41239
41240          this.assignDocWin();
41241         
41242         this.doc.designMode = 'on';
41243        
41244         this.doc.open();
41245         this.doc.write(this.getDocMarkup());
41246         this.doc.close();
41247
41248         
41249         var task = { // must defer to wait for browser to be ready
41250             run : function(){
41251                 //console.log("run task?" + this.doc.readyState);
41252                 this.assignDocWin();
41253                 if(this.doc.body || this.doc.readyState == 'complete'){
41254                     try {
41255                         this.doc.designMode="on";
41256                     } catch (e) {
41257                         return;
41258                     }
41259                     Roo.TaskMgr.stop(task);
41260                     this.initEditor.defer(10, this);
41261                 }
41262             },
41263             interval : 10,
41264             duration: 10000,
41265             scope: this
41266         };
41267         Roo.TaskMgr.start(task);
41268
41269         
41270          
41271     },
41272
41273     // private
41274     onResize : function(w, h)
41275     {
41276          Roo.log('resize: ' +w + ',' + h );
41277         //Roo.HtmlEditorCore.superclass.onResize.apply(this, arguments);
41278         if(!this.iframe){
41279             return;
41280         }
41281         if(typeof w == 'number'){
41282             
41283             this.iframe.style.width = w + 'px';
41284         }
41285         if(typeof h == 'number'){
41286             
41287             this.iframe.style.height = h + 'px';
41288             if(this.doc){
41289                 (this.doc.body || this.doc.documentElement).style.height = (h - (this.iframePad*2)) + 'px';
41290             }
41291         }
41292         
41293     },
41294
41295     /**
41296      * Toggles the editor between standard and source edit mode.
41297      * @param {Boolean} sourceEdit (optional) True for source edit, false for standard
41298      */
41299     toggleSourceEdit : function(sourceEditMode){
41300         
41301         this.sourceEditMode = sourceEditMode === true;
41302         
41303         if(this.sourceEditMode){
41304  
41305             Roo.get(this.iframe).addClass(['x-hidden','hide']);     //FIXME - what's the BS styles for these
41306             
41307         }else{
41308             Roo.get(this.iframe).removeClass(['x-hidden','hide']);
41309             //this.iframe.className = '';
41310             this.deferFocus();
41311         }
41312         //this.setSize(this.owner.wrap.getSize());
41313         //this.fireEvent('editmodechange', this, this.sourceEditMode);
41314     },
41315
41316     
41317   
41318
41319     /**
41320      * Protected method that will not generally be called directly. If you need/want
41321      * custom HTML cleanup, this is the method you should override.
41322      * @param {String} html The HTML to be cleaned
41323      * return {String} The cleaned HTML
41324      */
41325     cleanHtml : function(html){
41326         html = String(html);
41327         if(html.length > 5){
41328             if(Roo.isSafari){ // strip safari nonsense
41329                 html = html.replace(/\sclass="(?:Apple-style-span|khtml-block-placeholder)"/gi, '');
41330             }
41331         }
41332         if(html == '&nbsp;'){
41333             html = '';
41334         }
41335         return html;
41336     },
41337
41338     /**
41339      * HTML Editor -> Textarea
41340      * Protected method that will not generally be called directly. Syncs the contents
41341      * of the editor iframe with the textarea.
41342      */
41343     syncValue : function(){
41344         if(this.initialized){
41345             var bd = (this.doc.body || this.doc.documentElement);
41346             //this.cleanUpPaste(); -- this is done else where and causes havoc..
41347             var html = bd.innerHTML;
41348             if(Roo.isSafari){
41349                 var bs = bd.getAttribute('style'); // Safari puts text-align styles on the body element!
41350                 var m = bs ? bs.match(/text-align:(.*?);/i) : false;
41351                 if(m && m[1]){
41352                     html = '<div style="'+m[0]+'">' + html + '</div>';
41353                 }
41354             }
41355             html = this.cleanHtml(html);
41356             // fix up the special chars.. normaly like back quotes in word...
41357             // however we do not want to do this with chinese..
41358             html = html.replace(/([\x80-\uffff])/g, function (a, b) {
41359                 var cc = b.charCodeAt();
41360                 if (
41361                     (cc >= 0x4E00 && cc < 0xA000 ) ||
41362                     (cc >= 0x3400 && cc < 0x4E00 ) ||
41363                     (cc >= 0xf900 && cc < 0xfb00 )
41364                 ) {
41365                         return b;
41366                 }
41367                 return "&#"+cc+";" 
41368             });
41369             if(this.owner.fireEvent('beforesync', this, html) !== false){
41370                 this.el.dom.value = html;
41371                 this.owner.fireEvent('sync', this, html);
41372             }
41373         }
41374     },
41375
41376     /**
41377      * Protected method that will not generally be called directly. Pushes the value of the textarea
41378      * into the iframe editor.
41379      */
41380     pushValue : function(){
41381         if(this.initialized){
41382             var v = this.el.dom.value.trim();
41383             
41384 //            if(v.length < 1){
41385 //                v = '&#160;';
41386 //            }
41387             
41388             if(this.owner.fireEvent('beforepush', this, v) !== false){
41389                 var d = (this.doc.body || this.doc.documentElement);
41390                 d.innerHTML = v;
41391                 this.cleanUpPaste();
41392                 this.el.dom.value = d.innerHTML;
41393                 this.owner.fireEvent('push', this, v);
41394             }
41395         }
41396     },
41397
41398     // private
41399     deferFocus : function(){
41400         this.focus.defer(10, this);
41401     },
41402
41403     // doc'ed in Field
41404     focus : function(){
41405         if(this.win && !this.sourceEditMode){
41406             this.win.focus();
41407         }else{
41408             this.el.focus();
41409         }
41410     },
41411     
41412     assignDocWin: function()
41413     {
41414         var iframe = this.iframe;
41415         
41416          if(Roo.isIE){
41417             this.doc = iframe.contentWindow.document;
41418             this.win = iframe.contentWindow;
41419         } else {
41420 //            if (!Roo.get(this.frameId)) {
41421 //                return;
41422 //            }
41423 //            this.doc = (iframe.contentDocument || Roo.get(this.frameId).dom.document);
41424 //            this.win = Roo.get(this.frameId).dom.contentWindow;
41425             
41426             if (!Roo.get(this.frameId) && !iframe.contentDocument) {
41427                 return;
41428             }
41429             
41430             this.doc = (iframe.contentDocument || Roo.get(this.frameId).dom.document);
41431             this.win = (iframe.contentWindow || Roo.get(this.frameId).dom.contentWindow);
41432         }
41433     },
41434     
41435     // private
41436     initEditor : function(){
41437         //console.log("INIT EDITOR");
41438         this.assignDocWin();
41439         
41440         
41441         
41442         this.doc.designMode="on";
41443         this.doc.open();
41444         this.doc.write(this.getDocMarkup());
41445         this.doc.close();
41446         
41447         var dbody = (this.doc.body || this.doc.documentElement);
41448         //var ss = this.el.getStyles('font-size', 'font-family', 'background-image', 'background-repeat');
41449         // this copies styles from the containing element into thsi one..
41450         // not sure why we need all of this..
41451         //var ss = this.el.getStyles('font-size', 'background-image', 'background-repeat');
41452         
41453         //var ss = this.el.getStyles( 'background-image', 'background-repeat');
41454         //ss['background-attachment'] = 'fixed'; // w3c
41455         dbody.bgProperties = 'fixed'; // ie
41456         //Roo.DomHelper.applyStyles(dbody, ss);
41457         Roo.EventManager.on(this.doc, {
41458             //'mousedown': this.onEditorEvent,
41459             'mouseup': this.onEditorEvent,
41460             'dblclick': this.onEditorEvent,
41461             'click': this.onEditorEvent,
41462             'keyup': this.onEditorEvent,
41463             buffer:100,
41464             scope: this
41465         });
41466         if(Roo.isGecko){
41467             Roo.EventManager.on(this.doc, 'keypress', this.mozKeyPress, this);
41468         }
41469         if(Roo.isIE || Roo.isSafari || Roo.isOpera){
41470             Roo.EventManager.on(this.doc, 'keydown', this.fixKeys, this);
41471         }
41472         this.initialized = true;
41473
41474         this.owner.fireEvent('initialize', this);
41475         this.pushValue();
41476     },
41477
41478     // private
41479     onDestroy : function(){
41480         
41481         
41482         
41483         if(this.rendered){
41484             
41485             //for (var i =0; i < this.toolbars.length;i++) {
41486             //    // fixme - ask toolbars for heights?
41487             //    this.toolbars[i].onDestroy();
41488            // }
41489             
41490             //this.wrap.dom.innerHTML = '';
41491             //this.wrap.remove();
41492         }
41493     },
41494
41495     // private
41496     onFirstFocus : function(){
41497         
41498         this.assignDocWin();
41499         
41500         
41501         this.activated = true;
41502          
41503     
41504         if(Roo.isGecko){ // prevent silly gecko errors
41505             this.win.focus();
41506             var s = this.win.getSelection();
41507             if(!s.focusNode || s.focusNode.nodeType != 3){
41508                 var r = s.getRangeAt(0);
41509                 r.selectNodeContents((this.doc.body || this.doc.documentElement));
41510                 r.collapse(true);
41511                 this.deferFocus();
41512             }
41513             try{
41514                 this.execCmd('useCSS', true);
41515                 this.execCmd('styleWithCSS', false);
41516             }catch(e){}
41517         }
41518         this.owner.fireEvent('activate', this);
41519     },
41520
41521     // private
41522     adjustFont: function(btn){
41523         var adjust = btn.cmd == 'increasefontsize' ? 1 : -1;
41524         //if(Roo.isSafari){ // safari
41525         //    adjust *= 2;
41526        // }
41527         var v = parseInt(this.doc.queryCommandValue('FontSize')|| 3, 10);
41528         if(Roo.isSafari){ // safari
41529             var sm = { 10 : 1, 13: 2, 16:3, 18:4, 24: 5, 32:6, 48: 7 };
41530             v =  (v < 10) ? 10 : v;
41531             v =  (v > 48) ? 48 : v;
41532             v = typeof(sm[v]) == 'undefined' ? 1 : sm[v];
41533             
41534         }
41535         
41536         
41537         v = Math.max(1, v+adjust);
41538         
41539         this.execCmd('FontSize', v  );
41540     },
41541
41542     onEditorEvent : function(e){
41543         this.owner.fireEvent('editorevent', this, e);
41544       //  this.updateToolbar();
41545         this.syncValue(); //we can not sync so often.. sync cleans, so this breaks stuff
41546     },
41547
41548     insertTag : function(tg)
41549     {
41550         // could be a bit smarter... -> wrap the current selected tRoo..
41551         if (tg.toLowerCase() == 'span' || tg.toLowerCase() == 'code') {
41552             
41553             range = this.createRange(this.getSelection());
41554             var wrappingNode = this.doc.createElement(tg.toLowerCase());
41555             wrappingNode.appendChild(range.extractContents());
41556             range.insertNode(wrappingNode);
41557
41558             return;
41559             
41560             
41561             
41562         }
41563         this.execCmd("formatblock",   tg);
41564         
41565     },
41566     
41567     insertText : function(txt)
41568     {
41569         
41570         
41571         var range = this.createRange();
41572         range.deleteContents();
41573                //alert(Sender.getAttribute('label'));
41574                
41575         range.insertNode(this.doc.createTextNode(txt));
41576     } ,
41577     
41578      
41579
41580     /**
41581      * Executes a Midas editor command on the editor document and performs necessary focus and
41582      * toolbar updates. <b>This should only be called after the editor is initialized.</b>
41583      * @param {String} cmd The Midas command
41584      * @param {String/Boolean} value (optional) The value to pass to the command (defaults to null)
41585      */
41586     relayCmd : function(cmd, value){
41587         this.win.focus();
41588         this.execCmd(cmd, value);
41589         this.owner.fireEvent('editorevent', this);
41590         //this.updateToolbar();
41591         this.owner.deferFocus();
41592     },
41593
41594     /**
41595      * Executes a Midas editor command directly on the editor document.
41596      * For visual commands, you should use {@link #relayCmd} instead.
41597      * <b>This should only be called after the editor is initialized.</b>
41598      * @param {String} cmd The Midas command
41599      * @param {String/Boolean} value (optional) The value to pass to the command (defaults to null)
41600      */
41601     execCmd : function(cmd, value){
41602         this.doc.execCommand(cmd, false, value === undefined ? null : value);
41603         this.syncValue();
41604     },
41605  
41606  
41607    
41608     /**
41609      * Inserts the passed text at the current cursor position. Note: the editor must be initialized and activated
41610      * to insert tRoo.
41611      * @param {String} text | dom node.. 
41612      */
41613     insertAtCursor : function(text)
41614     {
41615         
41616         
41617         
41618         if(!this.activated){
41619             return;
41620         }
41621         /*
41622         if(Roo.isIE){
41623             this.win.focus();
41624             var r = this.doc.selection.createRange();
41625             if(r){
41626                 r.collapse(true);
41627                 r.pasteHTML(text);
41628                 this.syncValue();
41629                 this.deferFocus();
41630             
41631             }
41632             return;
41633         }
41634         */
41635         if(Roo.isGecko || Roo.isOpera || Roo.isSafari){
41636             this.win.focus();
41637             
41638             
41639             // from jquery ui (MIT licenced)
41640             var range, node;
41641             var win = this.win;
41642             
41643             if (win.getSelection && win.getSelection().getRangeAt) {
41644                 range = win.getSelection().getRangeAt(0);
41645                 node = typeof(text) == 'string' ? range.createContextualFragment(text) : text;
41646                 range.insertNode(node);
41647             } else if (win.document.selection && win.document.selection.createRange) {
41648                 // no firefox support
41649                 var txt = typeof(text) == 'string' ? text : text.outerHTML;
41650                 win.document.selection.createRange().pasteHTML(txt);
41651             } else {
41652                 // no firefox support
41653                 var txt = typeof(text) == 'string' ? text : text.outerHTML;
41654                 this.execCmd('InsertHTML', txt);
41655             } 
41656             
41657             this.syncValue();
41658             
41659             this.deferFocus();
41660         }
41661     },
41662  // private
41663     mozKeyPress : function(e){
41664         if(e.ctrlKey){
41665             var c = e.getCharCode(), cmd;
41666           
41667             if(c > 0){
41668                 c = String.fromCharCode(c).toLowerCase();
41669                 switch(c){
41670                     case 'b':
41671                         cmd = 'bold';
41672                         break;
41673                     case 'i':
41674                         cmd = 'italic';
41675                         break;
41676                     
41677                     case 'u':
41678                         cmd = 'underline';
41679                         break;
41680                     
41681                     case 'v':
41682                         this.cleanUpPaste.defer(100, this);
41683                         return;
41684                         
41685                 }
41686                 if(cmd){
41687                     this.win.focus();
41688                     this.execCmd(cmd);
41689                     this.deferFocus();
41690                     e.preventDefault();
41691                 }
41692                 
41693             }
41694         }
41695     },
41696
41697     // private
41698     fixKeys : function(){ // load time branching for fastest keydown performance
41699         if(Roo.isIE){
41700             return function(e){
41701                 var k = e.getKey(), r;
41702                 if(k == e.TAB){
41703                     e.stopEvent();
41704                     r = this.doc.selection.createRange();
41705                     if(r){
41706                         r.collapse(true);
41707                         r.pasteHTML('&#160;&#160;&#160;&#160;');
41708                         this.deferFocus();
41709                     }
41710                     return;
41711                 }
41712                 
41713                 if(k == e.ENTER){
41714                     r = this.doc.selection.createRange();
41715                     if(r){
41716                         var target = r.parentElement();
41717                         if(!target || target.tagName.toLowerCase() != 'li'){
41718                             e.stopEvent();
41719                             r.pasteHTML('<br />');
41720                             r.collapse(false);
41721                             r.select();
41722                         }
41723                     }
41724                 }
41725                 if (String.fromCharCode(k).toLowerCase() == 'v') { // paste
41726                     this.cleanUpPaste.defer(100, this);
41727                     return;
41728                 }
41729                 
41730                 
41731             };
41732         }else if(Roo.isOpera){
41733             return function(e){
41734                 var k = e.getKey();
41735                 if(k == e.TAB){
41736                     e.stopEvent();
41737                     this.win.focus();
41738                     this.execCmd('InsertHTML','&#160;&#160;&#160;&#160;');
41739                     this.deferFocus();
41740                 }
41741                 if (String.fromCharCode(k).toLowerCase() == 'v') { // paste
41742                     this.cleanUpPaste.defer(100, this);
41743                     return;
41744                 }
41745                 
41746             };
41747         }else if(Roo.isSafari){
41748             return function(e){
41749                 var k = e.getKey();
41750                 
41751                 if(k == e.TAB){
41752                     e.stopEvent();
41753                     this.execCmd('InsertText','\t');
41754                     this.deferFocus();
41755                     return;
41756                 }
41757                if (String.fromCharCode(k).toLowerCase() == 'v') { // paste
41758                     this.cleanUpPaste.defer(100, this);
41759                     return;
41760                 }
41761                 
41762              };
41763         }
41764     }(),
41765     
41766     getAllAncestors: function()
41767     {
41768         var p = this.getSelectedNode();
41769         var a = [];
41770         if (!p) {
41771             a.push(p); // push blank onto stack..
41772             p = this.getParentElement();
41773         }
41774         
41775         
41776         while (p && (p.nodeType == 1) && (p.tagName.toLowerCase() != 'body')) {
41777             a.push(p);
41778             p = p.parentNode;
41779         }
41780         a.push(this.doc.body);
41781         return a;
41782     },
41783     lastSel : false,
41784     lastSelNode : false,
41785     
41786     
41787     getSelection : function() 
41788     {
41789         this.assignDocWin();
41790         return Roo.isIE ? this.doc.selection : this.win.getSelection();
41791     },
41792     
41793     getSelectedNode: function() 
41794     {
41795         // this may only work on Gecko!!!
41796         
41797         // should we cache this!!!!
41798         
41799         
41800         
41801          
41802         var range = this.createRange(this.getSelection()).cloneRange();
41803         
41804         if (Roo.isIE) {
41805             var parent = range.parentElement();
41806             while (true) {
41807                 var testRange = range.duplicate();
41808                 testRange.moveToElementText(parent);
41809                 if (testRange.inRange(range)) {
41810                     break;
41811                 }
41812                 if ((parent.nodeType != 1) || (parent.tagName.toLowerCase() == 'body')) {
41813                     break;
41814                 }
41815                 parent = parent.parentElement;
41816             }
41817             return parent;
41818         }
41819         
41820         // is ancestor a text element.
41821         var ac =  range.commonAncestorContainer;
41822         if (ac.nodeType == 3) {
41823             ac = ac.parentNode;
41824         }
41825         
41826         var ar = ac.childNodes;
41827          
41828         var nodes = [];
41829         var other_nodes = [];
41830         var has_other_nodes = false;
41831         for (var i=0;i<ar.length;i++) {
41832             if ((ar[i].nodeType == 3) && (!ar[i].data.length)) { // empty text ? 
41833                 continue;
41834             }
41835             // fullly contained node.
41836             
41837             if (this.rangeIntersectsNode(range,ar[i]) && this.rangeCompareNode(range,ar[i]) == 3) {
41838                 nodes.push(ar[i]);
41839                 continue;
41840             }
41841             
41842             // probably selected..
41843             if ((ar[i].nodeType == 1) && this.rangeIntersectsNode(range,ar[i]) && (this.rangeCompareNode(range,ar[i]) > 0)) {
41844                 other_nodes.push(ar[i]);
41845                 continue;
41846             }
41847             // outer..
41848             if (!this.rangeIntersectsNode(range,ar[i])|| (this.rangeCompareNode(range,ar[i]) == 0))  {
41849                 continue;
41850             }
41851             
41852             
41853             has_other_nodes = true;
41854         }
41855         if (!nodes.length && other_nodes.length) {
41856             nodes= other_nodes;
41857         }
41858         if (has_other_nodes || !nodes.length || (nodes.length > 1)) {
41859             return false;
41860         }
41861         
41862         return nodes[0];
41863     },
41864     createRange: function(sel)
41865     {
41866         // this has strange effects when using with 
41867         // top toolbar - not sure if it's a great idea.
41868         //this.editor.contentWindow.focus();
41869         if (typeof sel != "undefined") {
41870             try {
41871                 return sel.getRangeAt ? sel.getRangeAt(0) : sel.createRange();
41872             } catch(e) {
41873                 return this.doc.createRange();
41874             }
41875         } else {
41876             return this.doc.createRange();
41877         }
41878     },
41879     getParentElement: function()
41880     {
41881         
41882         this.assignDocWin();
41883         var sel = Roo.isIE ? this.doc.selection : this.win.getSelection();
41884         
41885         var range = this.createRange(sel);
41886          
41887         try {
41888             var p = range.commonAncestorContainer;
41889             while (p.nodeType == 3) { // text node
41890                 p = p.parentNode;
41891             }
41892             return p;
41893         } catch (e) {
41894             return null;
41895         }
41896     
41897     },
41898     /***
41899      *
41900      * Range intersection.. the hard stuff...
41901      *  '-1' = before
41902      *  '0' = hits..
41903      *  '1' = after.
41904      *         [ -- selected range --- ]
41905      *   [fail]                        [fail]
41906      *
41907      *    basically..
41908      *      if end is before start or  hits it. fail.
41909      *      if start is after end or hits it fail.
41910      *
41911      *   if either hits (but other is outside. - then it's not 
41912      *   
41913      *    
41914      **/
41915     
41916     
41917     // @see http://www.thismuchiknow.co.uk/?p=64.
41918     rangeIntersectsNode : function(range, node)
41919     {
41920         var nodeRange = node.ownerDocument.createRange();
41921         try {
41922             nodeRange.selectNode(node);
41923         } catch (e) {
41924             nodeRange.selectNodeContents(node);
41925         }
41926     
41927         var rangeStartRange = range.cloneRange();
41928         rangeStartRange.collapse(true);
41929     
41930         var rangeEndRange = range.cloneRange();
41931         rangeEndRange.collapse(false);
41932     
41933         var nodeStartRange = nodeRange.cloneRange();
41934         nodeStartRange.collapse(true);
41935     
41936         var nodeEndRange = nodeRange.cloneRange();
41937         nodeEndRange.collapse(false);
41938     
41939         return rangeStartRange.compareBoundaryPoints(
41940                  Range.START_TO_START, nodeEndRange) == -1 &&
41941                rangeEndRange.compareBoundaryPoints(
41942                  Range.START_TO_START, nodeStartRange) == 1;
41943         
41944          
41945     },
41946     rangeCompareNode : function(range, node)
41947     {
41948         var nodeRange = node.ownerDocument.createRange();
41949         try {
41950             nodeRange.selectNode(node);
41951         } catch (e) {
41952             nodeRange.selectNodeContents(node);
41953         }
41954         
41955         
41956         range.collapse(true);
41957     
41958         nodeRange.collapse(true);
41959      
41960         var ss = range.compareBoundaryPoints( Range.START_TO_START, nodeRange);
41961         var ee = range.compareBoundaryPoints(  Range.END_TO_END, nodeRange);
41962          
41963         //Roo.log(node.tagName + ': ss='+ss +', ee='+ee)
41964         
41965         var nodeIsBefore   =  ss == 1;
41966         var nodeIsAfter    = ee == -1;
41967         
41968         if (nodeIsBefore && nodeIsAfter)
41969             return 0; // outer
41970         if (!nodeIsBefore && nodeIsAfter)
41971             return 1; //right trailed.
41972         
41973         if (nodeIsBefore && !nodeIsAfter)
41974             return 2;  // left trailed.
41975         // fully contined.
41976         return 3;
41977     },
41978
41979     // private? - in a new class?
41980     cleanUpPaste :  function()
41981     {
41982         // cleans up the whole document..
41983         Roo.log('cleanuppaste');
41984         
41985         this.cleanUpChildren(this.doc.body);
41986         var clean = this.cleanWordChars(this.doc.body.innerHTML);
41987         if (clean != this.doc.body.innerHTML) {
41988             this.doc.body.innerHTML = clean;
41989         }
41990         
41991     },
41992     
41993     cleanWordChars : function(input) {// change the chars to hex code
41994         var he = Roo.HtmlEditorCore;
41995         
41996         var output = input;
41997         Roo.each(he.swapCodes, function(sw) { 
41998             var swapper = new RegExp("\\u" + sw[0].toString(16), "g"); // hex codes
41999             
42000             output = output.replace(swapper, sw[1]);
42001         });
42002         
42003         return output;
42004     },
42005     
42006     
42007     cleanUpChildren : function (n)
42008     {
42009         if (!n.childNodes.length) {
42010             return;
42011         }
42012         for (var i = n.childNodes.length-1; i > -1 ; i--) {
42013            this.cleanUpChild(n.childNodes[i]);
42014         }
42015     },
42016     
42017     
42018         
42019     
42020     cleanUpChild : function (node)
42021     {
42022         var ed = this;
42023         //console.log(node);
42024         if (node.nodeName == "#text") {
42025             // clean up silly Windows -- stuff?
42026             return; 
42027         }
42028         if (node.nodeName == "#comment") {
42029             node.parentNode.removeChild(node);
42030             // clean up silly Windows -- stuff?
42031             return; 
42032         }
42033         var lcname = node.tagName.toLowerCase();
42034         // we ignore whitelists... ?? = not really the way to go, but we probably have not got a full
42035         // whitelist of tags..
42036         
42037         if (this.black.indexOf(lcname) > -1 && this.clearUp ) {
42038             // remove node.
42039             node.parentNode.removeChild(node);
42040             return;
42041             
42042         }
42043         
42044         var remove_keep_children= Roo.HtmlEditorCore.remove.indexOf(node.tagName.toLowerCase()) > -1;
42045         
42046         // remove <a name=....> as rendering on yahoo mailer is borked with this.
42047         // this will have to be flaged elsewhere - perhaps ablack=name... on the mailer..
42048         
42049         //if (node.tagName.toLowerCase() == 'a' && !node.hasAttribute('href')) {
42050         //    remove_keep_children = true;
42051         //}
42052         
42053         if (remove_keep_children) {
42054             this.cleanUpChildren(node);
42055             // inserts everything just before this node...
42056             while (node.childNodes.length) {
42057                 var cn = node.childNodes[0];
42058                 node.removeChild(cn);
42059                 node.parentNode.insertBefore(cn, node);
42060             }
42061             node.parentNode.removeChild(node);
42062             return;
42063         }
42064         
42065         if (!node.attributes || !node.attributes.length) {
42066             this.cleanUpChildren(node);
42067             return;
42068         }
42069         
42070         function cleanAttr(n,v)
42071         {
42072             
42073             if (v.match(/^\./) || v.match(/^\//)) {
42074                 return;
42075             }
42076             if (v.match(/^(http|https):\/\//) || v.match(/^mailto:/)) {
42077                 return;
42078             }
42079             if (v.match(/^#/)) {
42080                 return;
42081             }
42082 //            Roo.log("(REMOVE TAG)"+ node.tagName +'.' + n + '=' + v);
42083             node.removeAttribute(n);
42084             
42085         }
42086         
42087         var cwhite = this.cwhite;
42088         var cblack = this.cblack;
42089             
42090         function cleanStyle(n,v)
42091         {
42092             if (v.match(/expression/)) { //XSS?? should we even bother..
42093                 node.removeAttribute(n);
42094                 return;
42095             }
42096             
42097             var parts = v.split(/;/);
42098             var clean = [];
42099             
42100             Roo.each(parts, function(p) {
42101                 p = p.replace(/^\s+/g,'').replace(/\s+$/g,'');
42102                 if (!p.length) {
42103                     return true;
42104                 }
42105                 var l = p.split(':').shift().replace(/\s+/g,'');
42106                 l = l.replace(/^\s+/g,'').replace(/\s+$/g,'');
42107                 
42108                 if ( cwhite.length && cblack.indexOf(l) > -1) {
42109 //                    Roo.log('(REMOVE CSS)' + node.tagName +'.' + n + ':'+l + '=' + v);
42110                     //node.removeAttribute(n);
42111                     return true;
42112                 }
42113                 //Roo.log()
42114                 // only allow 'c whitelisted system attributes'
42115                 if ( cwhite.length &&  cwhite.indexOf(l) < 0) {
42116 //                    Roo.log('(REMOVE CSS)' + node.tagName +'.' + n + ':'+l + '=' + v);
42117                     //node.removeAttribute(n);
42118                     return true;
42119                 }
42120                 
42121                 
42122                  
42123                 
42124                 clean.push(p);
42125                 return true;
42126             });
42127             if (clean.length) { 
42128                 node.setAttribute(n, clean.join(';'));
42129             } else {
42130                 node.removeAttribute(n);
42131             }
42132             
42133         }
42134         
42135         
42136         for (var i = node.attributes.length-1; i > -1 ; i--) {
42137             var a = node.attributes[i];
42138             //console.log(a);
42139             
42140             if (a.name.toLowerCase().substr(0,2)=='on')  {
42141                 node.removeAttribute(a.name);
42142                 continue;
42143             }
42144             if (Roo.HtmlEditorCore.ablack.indexOf(a.name.toLowerCase()) > -1) {
42145                 node.removeAttribute(a.name);
42146                 continue;
42147             }
42148             if (Roo.HtmlEditorCore.aclean.indexOf(a.name.toLowerCase()) > -1) {
42149                 cleanAttr(a.name,a.value); // fixme..
42150                 continue;
42151             }
42152             if (a.name == 'style') {
42153                 cleanStyle(a.name,a.value);
42154                 continue;
42155             }
42156             /// clean up MS crap..
42157             // tecnically this should be a list of valid class'es..
42158             
42159             
42160             if (a.name == 'class') {
42161                 if (a.value.match(/^Mso/)) {
42162                     node.className = '';
42163                 }
42164                 
42165                 if (a.value.match(/body/)) {
42166                     node.className = '';
42167                 }
42168                 continue;
42169             }
42170             
42171             // style cleanup!?
42172             // class cleanup?
42173             
42174         }
42175         
42176         
42177         this.cleanUpChildren(node);
42178         
42179         
42180     },
42181     /**
42182      * Clean up MS wordisms...
42183      */
42184     cleanWord : function(node)
42185     {
42186         var _t = this;
42187         var cleanWordChildren = function()
42188         {
42189             if (!node.childNodes.length) {
42190                 return;
42191             }
42192             for (var i = node.childNodes.length-1; i > -1 ; i--) {
42193                _t.cleanWord(node.childNodes[i]);
42194             }
42195         }
42196         
42197         
42198         if (!node) {
42199             this.cleanWord(this.doc.body);
42200             return;
42201         }
42202         if (node.nodeName == "#text") {
42203             // clean up silly Windows -- stuff?
42204             return; 
42205         }
42206         if (node.nodeName == "#comment") {
42207             node.parentNode.removeChild(node);
42208             // clean up silly Windows -- stuff?
42209             return; 
42210         }
42211         
42212         if (node.tagName.toLowerCase().match(/^(style|script|applet|embed|noframes|noscript)$/)) {
42213             node.parentNode.removeChild(node);
42214             return;
42215         }
42216         
42217         // remove - but keep children..
42218         if (node.tagName.toLowerCase().match(/^(meta|link|\\?xml:|st1:|o:|font)/)) {
42219             while (node.childNodes.length) {
42220                 var cn = node.childNodes[0];
42221                 node.removeChild(cn);
42222                 node.parentNode.insertBefore(cn, node);
42223             }
42224             node.parentNode.removeChild(node);
42225             cleanWordChildren();
42226             return;
42227         }
42228         // clean styles
42229         if (node.className.length) {
42230             
42231             var cn = node.className.split(/\W+/);
42232             var cna = [];
42233             Roo.each(cn, function(cls) {
42234                 if (cls.match(/Mso[a-zA-Z]+/)) {
42235                     return;
42236                 }
42237                 cna.push(cls);
42238             });
42239             node.className = cna.length ? cna.join(' ') : '';
42240             if (!cna.length) {
42241                 node.removeAttribute("class");
42242             }
42243         }
42244         
42245         if (node.hasAttribute("lang")) {
42246             node.removeAttribute("lang");
42247         }
42248         
42249         if (node.hasAttribute("style")) {
42250             
42251             var styles = node.getAttribute("style").split(";");
42252             var nstyle = [];
42253             Roo.each(styles, function(s) {
42254                 if (!s.match(/:/)) {
42255                     return;
42256                 }
42257                 var kv = s.split(":");
42258                 if (kv[0].match(/^(mso-|line|font|background|margin|padding|color)/)) {
42259                     return;
42260                 }
42261                 // what ever is left... we allow.
42262                 nstyle.push(s);
42263             });
42264             node.setAttribute("style", nstyle.length ? nstyle.join(';') : '');
42265             if (!nstyle.length) {
42266                 node.removeAttribute('style');
42267             }
42268         }
42269         
42270         cleanWordChildren();
42271         
42272         
42273     },
42274     domToHTML : function(currentElement, depth, nopadtext) {
42275         
42276         depth = depth || 0;
42277         nopadtext = nopadtext || false;
42278     
42279         if (!currentElement) {
42280             return this.domToHTML(this.doc.body);
42281         }
42282         
42283         //Roo.log(currentElement);
42284         var j;
42285         var allText = false;
42286         var nodeName = currentElement.nodeName;
42287         var tagName = Roo.util.Format.htmlEncode(currentElement.tagName);
42288         
42289         if  (nodeName == '#text') {
42290             return currentElement.nodeValue;
42291         }
42292         
42293         
42294         var ret = '';
42295         if (nodeName != 'BODY') {
42296              
42297             var i = 0;
42298             // Prints the node tagName, such as <A>, <IMG>, etc
42299             if (tagName) {
42300                 var attr = [];
42301                 for(i = 0; i < currentElement.attributes.length;i++) {
42302                     // quoting?
42303                     var aname = currentElement.attributes.item(i).name;
42304                     if (!currentElement.attributes.item(i).value.length) {
42305                         continue;
42306                     }
42307                     attr.push(aname + '="' + Roo.util.Format.htmlEncode(currentElement.attributes.item(i).value) + '"' );
42308                 }
42309                 
42310                 ret = "<"+currentElement.tagName+ ( attr.length ? (' ' + attr.join(' ') ) : '') + ">";
42311             } 
42312             else {
42313                 
42314                 // eack
42315             }
42316         } else {
42317             tagName = false;
42318         }
42319         if (['IMG', 'BR', 'HR', 'INPUT'].indexOf(tagName) > -1) {
42320             return ret;
42321         }
42322         if (['PRE', 'TEXTAREA', 'TD', 'A', 'SPAN'].indexOf(tagName) > -1) { // or code?
42323             nopadtext = true;
42324         }
42325         
42326         
42327         // Traverse the tree
42328         i = 0;
42329         var currentElementChild = currentElement.childNodes.item(i);
42330         var allText = true;
42331         var innerHTML  = '';
42332         lastnode = '';
42333         while (currentElementChild) {
42334             // Formatting code (indent the tree so it looks nice on the screen)
42335             var nopad = nopadtext;
42336             if (lastnode == 'SPAN') {
42337                 nopad  = true;
42338             }
42339             // text
42340             if  (currentElementChild.nodeName == '#text') {
42341                 var toadd = Roo.util.Format.htmlEncode(currentElementChild.nodeValue);
42342                 if (!nopad && toadd.length > 80) {
42343                     innerHTML  += "\n" + (new Array( depth + 1 )).join( "  "  );
42344                 }
42345                 innerHTML  += toadd;
42346                 
42347                 i++;
42348                 currentElementChild = currentElement.childNodes.item(i);
42349                 lastNode = '';
42350                 continue;
42351             }
42352             allText = false;
42353             
42354             innerHTML  += nopad ? '' : "\n" + (new Array( depth + 1 )).join( "  "  );
42355                 
42356             // Recursively traverse the tree structure of the child node
42357             innerHTML   += this.domToHTML(currentElementChild, depth+1, nopadtext);
42358             lastnode = currentElementChild.nodeName;
42359             i++;
42360             currentElementChild=currentElement.childNodes.item(i);
42361         }
42362         
42363         ret += innerHTML;
42364         
42365         if (!allText) {
42366                 // The remaining code is mostly for formatting the tree
42367             ret+= nopadtext ? '' : "\n" + (new Array( depth  )).join( "  "  );
42368         }
42369         
42370         
42371         if (tagName) {
42372             ret+= "</"+tagName+">";
42373         }
42374         return ret;
42375         
42376     },
42377         
42378     applyBlacklists : function()
42379     {
42380         var w = typeof(this.owner.white) != 'undefined' && this.owner.white ? this.owner.white  : [];
42381         var b = typeof(this.owner.black) != 'undefined' && this.owner.black ? this.owner.black :  [];
42382         
42383         this.white = [];
42384         this.black = [];
42385         Roo.each(Roo.HtmlEditorCore.white, function(tag) {
42386             if (b.indexOf(tag) > -1) {
42387                 return;
42388             }
42389             this.white.push(tag);
42390             
42391         }, this);
42392         
42393         Roo.each(w, function(tag) {
42394             if (b.indexOf(tag) > -1) {
42395                 return;
42396             }
42397             if (this.white.indexOf(tag) > -1) {
42398                 return;
42399             }
42400             this.white.push(tag);
42401             
42402         }, this);
42403         
42404         
42405         Roo.each(Roo.HtmlEditorCore.black, function(tag) {
42406             if (w.indexOf(tag) > -1) {
42407                 return;
42408             }
42409             this.black.push(tag);
42410             
42411         }, this);
42412         
42413         Roo.each(b, function(tag) {
42414             if (w.indexOf(tag) > -1) {
42415                 return;
42416             }
42417             if (this.black.indexOf(tag) > -1) {
42418                 return;
42419             }
42420             this.black.push(tag);
42421             
42422         }, this);
42423         
42424         
42425         w = typeof(this.owner.cwhite) != 'undefined' && this.owner.cwhite ? this.owner.cwhite  : [];
42426         b = typeof(this.owner.cblack) != 'undefined' && this.owner.cblack ? this.owner.cblack :  [];
42427         
42428         this.cwhite = [];
42429         this.cblack = [];
42430         Roo.each(Roo.HtmlEditorCore.cwhite, function(tag) {
42431             if (b.indexOf(tag) > -1) {
42432                 return;
42433             }
42434             this.cwhite.push(tag);
42435             
42436         }, this);
42437         
42438         Roo.each(w, function(tag) {
42439             if (b.indexOf(tag) > -1) {
42440                 return;
42441             }
42442             if (this.cwhite.indexOf(tag) > -1) {
42443                 return;
42444             }
42445             this.cwhite.push(tag);
42446             
42447         }, this);
42448         
42449         
42450         Roo.each(Roo.HtmlEditorCore.cblack, function(tag) {
42451             if (w.indexOf(tag) > -1) {
42452                 return;
42453             }
42454             this.cblack.push(tag);
42455             
42456         }, this);
42457         
42458         Roo.each(b, function(tag) {
42459             if (w.indexOf(tag) > -1) {
42460                 return;
42461             }
42462             if (this.cblack.indexOf(tag) > -1) {
42463                 return;
42464             }
42465             this.cblack.push(tag);
42466             
42467         }, this);
42468     }
42469     
42470     // hide stuff that is not compatible
42471     /**
42472      * @event blur
42473      * @hide
42474      */
42475     /**
42476      * @event change
42477      * @hide
42478      */
42479     /**
42480      * @event focus
42481      * @hide
42482      */
42483     /**
42484      * @event specialkey
42485      * @hide
42486      */
42487     /**
42488      * @cfg {String} fieldClass @hide
42489      */
42490     /**
42491      * @cfg {String} focusClass @hide
42492      */
42493     /**
42494      * @cfg {String} autoCreate @hide
42495      */
42496     /**
42497      * @cfg {String} inputType @hide
42498      */
42499     /**
42500      * @cfg {String} invalidClass @hide
42501      */
42502     /**
42503      * @cfg {String} invalidText @hide
42504      */
42505     /**
42506      * @cfg {String} msgFx @hide
42507      */
42508     /**
42509      * @cfg {String} validateOnBlur @hide
42510      */
42511 });
42512
42513 Roo.HtmlEditorCore.white = [
42514         'area', 'br', 'img', 'input', 'hr', 'wbr',
42515         
42516        'address', 'blockquote', 'center', 'dd',      'dir',       'div', 
42517        'dl',      'dt',         'h1',     'h2',      'h3',        'h4', 
42518        'h5',      'h6',         'hr',     'isindex', 'listing',   'marquee', 
42519        'menu',    'multicol',   'ol',     'p',       'plaintext', 'pre', 
42520        'table',   'ul',         'xmp', 
42521        
42522        'caption', 'col', 'colgroup', 'tbody', 'td', 'tfoot', 'th', 
42523       'thead',   'tr', 
42524      
42525       'dir', 'menu', 'ol', 'ul', 'dl',
42526        
42527       'embed',  'object'
42528 ];
42529
42530
42531 Roo.HtmlEditorCore.black = [
42532     //    'embed',  'object', // enable - backend responsiblity to clean thiese
42533         'applet', // 
42534         'base',   'basefont', 'bgsound', 'blink',  'body', 
42535         'frame',  'frameset', 'head',    'html',   'ilayer', 
42536         'iframe', 'layer',  'link',     'meta',    'object',   
42537         'script', 'style' ,'title',  'xml' // clean later..
42538 ];
42539 Roo.HtmlEditorCore.clean = [
42540     'script', 'style', 'title', 'xml'
42541 ];
42542 Roo.HtmlEditorCore.remove = [
42543     'font'
42544 ];
42545 // attributes..
42546
42547 Roo.HtmlEditorCore.ablack = [
42548     'on'
42549 ];
42550     
42551 Roo.HtmlEditorCore.aclean = [ 
42552     'action', 'background', 'codebase', 'dynsrc', 'href', 'lowsrc' 
42553 ];
42554
42555 // protocols..
42556 Roo.HtmlEditorCore.pwhite= [
42557         'http',  'https',  'mailto'
42558 ];
42559
42560 // white listed style attributes.
42561 Roo.HtmlEditorCore.cwhite= [
42562       //  'text-align', /// default is to allow most things..
42563       
42564          
42565 //        'font-size'//??
42566 ];
42567
42568 // black listed style attributes.
42569 Roo.HtmlEditorCore.cblack= [
42570       //  'font-size' -- this can be set by the project 
42571 ];
42572
42573
42574 Roo.HtmlEditorCore.swapCodes   =[ 
42575     [    8211, "--" ], 
42576     [    8212, "--" ], 
42577     [    8216,  "'" ],  
42578     [    8217, "'" ],  
42579     [    8220, '"' ],  
42580     [    8221, '"' ],  
42581     [    8226, "*" ],  
42582     [    8230, "..." ]
42583 ]; 
42584
42585     //<script type="text/javascript">
42586
42587 /*
42588  * Ext JS Library 1.1.1
42589  * Copyright(c) 2006-2007, Ext JS, LLC.
42590  * Licence LGPL
42591  * 
42592  */
42593  
42594  
42595 Roo.form.HtmlEditor = function(config){
42596     
42597     
42598     
42599     Roo.form.HtmlEditor.superclass.constructor.call(this, config);
42600     
42601     if (!this.toolbars) {
42602         this.toolbars = [];
42603     }
42604     this.editorcore = new Roo.HtmlEditorCore(Roo.apply({ owner : this} , config));
42605     
42606     
42607 };
42608
42609 /**
42610  * @class Roo.form.HtmlEditor
42611  * @extends Roo.form.Field
42612  * Provides a lightweight HTML Editor component.
42613  *
42614  * This has been tested on Fireforx / Chrome.. IE may not be so great..
42615  * 
42616  * <br><br><b>Note: The focus/blur and validation marking functionality inherited from Ext.form.Field is NOT
42617  * supported by this editor.</b><br/><br/>
42618  * An Editor is a sensitive component that can't be used in all spots standard fields can be used. Putting an Editor within
42619  * any element that has display set to 'none' can cause problems in Safari and Firefox.<br/><br/>
42620  */
42621 Roo.extend(Roo.form.HtmlEditor, Roo.form.Field, {
42622     /**
42623      * @cfg {Boolean} clearUp
42624      */
42625     clearUp : true,
42626       /**
42627      * @cfg {Array} toolbars Array of toolbars. - defaults to just the Standard one
42628      */
42629     toolbars : false,
42630    
42631      /**
42632      * @cfg {String} resizable  's' or 'se' or 'e' - wrapps the element in a
42633      *                        Roo.resizable.
42634      */
42635     resizable : false,
42636      /**
42637      * @cfg {Number} height (in pixels)
42638      */   
42639     height: 300,
42640    /**
42641      * @cfg {Number} width (in pixels)
42642      */   
42643     width: 500,
42644     
42645     /**
42646      * @cfg {Array} stylesheets url of stylesheets. set to [] to disable stylesheets.
42647      * 
42648      */
42649     stylesheets: false,
42650     
42651     
42652      /**
42653      * @cfg {Array} blacklist of css styles style attributes (blacklist overrides whitelist)
42654      * 
42655      */
42656     cblack: false,
42657     /**
42658      * @cfg {Array} whitelist of css styles style attributes (blacklist overrides whitelist)
42659      * 
42660      */
42661     cwhite: false,
42662     
42663      /**
42664      * @cfg {Array} blacklist of html tags - in addition to standard blacklist.
42665      * 
42666      */
42667     black: false,
42668     /**
42669      * @cfg {Array} whitelist of html tags - in addition to statndard whitelist
42670      * 
42671      */
42672     white: false,
42673     
42674     // id of frame..
42675     frameId: false,
42676     
42677     // private properties
42678     validationEvent : false,
42679     deferHeight: true,
42680     initialized : false,
42681     activated : false,
42682     
42683     onFocus : Roo.emptyFn,
42684     iframePad:3,
42685     hideMode:'offsets',
42686     
42687     defaultAutoCreate : { // modified by initCompnoent..
42688         tag: "textarea",
42689         style:"width:500px;height:300px;",
42690         autocomplete: "off"
42691     },
42692
42693     // private
42694     initComponent : function(){
42695         this.addEvents({
42696             /**
42697              * @event initialize
42698              * Fires when the editor is fully initialized (including the iframe)
42699              * @param {HtmlEditor} this
42700              */
42701             initialize: true,
42702             /**
42703              * @event activate
42704              * Fires when the editor is first receives the focus. Any insertion must wait
42705              * until after this event.
42706              * @param {HtmlEditor} this
42707              */
42708             activate: true,
42709              /**
42710              * @event beforesync
42711              * Fires before the textarea is updated with content from the editor iframe. Return false
42712              * to cancel the sync.
42713              * @param {HtmlEditor} this
42714              * @param {String} html
42715              */
42716             beforesync: true,
42717              /**
42718              * @event beforepush
42719              * Fires before the iframe editor is updated with content from the textarea. Return false
42720              * to cancel the push.
42721              * @param {HtmlEditor} this
42722              * @param {String} html
42723              */
42724             beforepush: true,
42725              /**
42726              * @event sync
42727              * Fires when the textarea is updated with content from the editor iframe.
42728              * @param {HtmlEditor} this
42729              * @param {String} html
42730              */
42731             sync: true,
42732              /**
42733              * @event push
42734              * Fires when the iframe editor is updated with content from the textarea.
42735              * @param {HtmlEditor} this
42736              * @param {String} html
42737              */
42738             push: true,
42739              /**
42740              * @event editmodechange
42741              * Fires when the editor switches edit modes
42742              * @param {HtmlEditor} this
42743              * @param {Boolean} sourceEdit True if source edit, false if standard editing.
42744              */
42745             editmodechange: true,
42746             /**
42747              * @event editorevent
42748              * Fires when on any editor (mouse up/down cursor movement etc.) - used for toolbar hooks.
42749              * @param {HtmlEditor} this
42750              */
42751             editorevent: true,
42752             /**
42753              * @event firstfocus
42754              * Fires when on first focus - needed by toolbars..
42755              * @param {HtmlEditor} this
42756              */
42757             firstfocus: true,
42758             /**
42759              * @event autosave
42760              * Auto save the htmlEditor value as a file into Events
42761              * @param {HtmlEditor} this
42762              */
42763             autosave: true,
42764             /**
42765              * @event savedpreview
42766              * preview the saved version of htmlEditor
42767              * @param {HtmlEditor} this
42768              */
42769             savedpreview: true
42770         });
42771         this.defaultAutoCreate =  {
42772             tag: "textarea",
42773             style:'width: ' + this.width + 'px;height: ' + this.height + 'px;',
42774             autocomplete: "off"
42775         };
42776     },
42777
42778     /**
42779      * Protected method that will not generally be called directly. It
42780      * is called when the editor creates its toolbar. Override this method if you need to
42781      * add custom toolbar buttons.
42782      * @param {HtmlEditor} editor
42783      */
42784     createToolbar : function(editor){
42785         Roo.log("create toolbars");
42786         if (!editor.toolbars || !editor.toolbars.length) {
42787             editor.toolbars = [ new Roo.form.HtmlEditor.ToolbarStandard() ]; // can be empty?
42788         }
42789         
42790         for (var i =0 ; i < editor.toolbars.length;i++) {
42791             editor.toolbars[i] = Roo.factory(
42792                     typeof(editor.toolbars[i]) == 'string' ?
42793                         { xtype: editor.toolbars[i]} : editor.toolbars[i],
42794                 Roo.form.HtmlEditor);
42795             editor.toolbars[i].init(editor);
42796         }
42797          
42798         
42799     },
42800
42801      
42802     // private
42803     onRender : function(ct, position)
42804     {
42805         var _t = this;
42806         Roo.form.HtmlEditor.superclass.onRender.call(this, ct, position);
42807         
42808         this.wrap = this.el.wrap({
42809             cls:'x-html-editor-wrap', cn:{cls:'x-html-editor-tb'}
42810         });
42811         
42812         this.editorcore.onRender(ct, position);
42813          
42814         if (this.resizable) {
42815             this.resizeEl = new Roo.Resizable(this.wrap, {
42816                 pinned : true,
42817                 wrap: true,
42818                 dynamic : true,
42819                 minHeight : this.height,
42820                 height: this.height,
42821                 handles : this.resizable,
42822                 width: this.width,
42823                 listeners : {
42824                     resize : function(r, w, h) {
42825                         _t.onResize(w,h); // -something
42826                     }
42827                 }
42828             });
42829             
42830         }
42831         this.createToolbar(this);
42832        
42833         
42834         if(!this.width){
42835             this.setSize(this.wrap.getSize());
42836         }
42837         if (this.resizeEl) {
42838             this.resizeEl.resizeTo.defer(100, this.resizeEl,[ this.width,this.height ] );
42839             // should trigger onReize..
42840         }
42841         
42842 //        if(this.autosave && this.w){
42843 //            this.autoSaveFn = setInterval(this.autosave, 1000);
42844 //        }
42845     },
42846
42847     // private
42848     onResize : function(w, h)
42849     {
42850         //Roo.log('resize: ' +w + ',' + h );
42851         Roo.form.HtmlEditor.superclass.onResize.apply(this, arguments);
42852         var ew = false;
42853         var eh = false;
42854         
42855         if(this.el ){
42856             if(typeof w == 'number'){
42857                 var aw = w - this.wrap.getFrameWidth('lr');
42858                 this.el.setWidth(this.adjustWidth('textarea', aw));
42859                 ew = aw;
42860             }
42861             if(typeof h == 'number'){
42862                 var tbh = 0;
42863                 for (var i =0; i < this.toolbars.length;i++) {
42864                     // fixme - ask toolbars for heights?
42865                     tbh += this.toolbars[i].tb.el.getHeight();
42866                     if (this.toolbars[i].footer) {
42867                         tbh += this.toolbars[i].footer.el.getHeight();
42868                     }
42869                 }
42870                 
42871                 
42872                 
42873                 
42874                 var ah = h - this.wrap.getFrameWidth('tb') - tbh;// this.tb.el.getHeight();
42875                 ah -= 5; // knock a few pixes off for look..
42876                 this.el.setHeight(this.adjustWidth('textarea', ah));
42877                 var eh = ah;
42878             }
42879         }
42880         Roo.log('onResize:' + [w,h,ew,eh].join(',') );
42881         this.editorcore.onResize(ew,eh);
42882         
42883     },
42884
42885     /**
42886      * Toggles the editor between standard and source edit mode.
42887      * @param {Boolean} sourceEdit (optional) True for source edit, false for standard
42888      */
42889     toggleSourceEdit : function(sourceEditMode)
42890     {
42891         this.editorcore.toggleSourceEdit(sourceEditMode);
42892         
42893         if(this.editorcore.sourceEditMode){
42894             Roo.log('editor - showing textarea');
42895             
42896 //            Roo.log('in');
42897 //            Roo.log(this.syncValue());
42898             this.editorcore.syncValue();
42899             this.el.removeClass('x-hidden');
42900             this.el.dom.removeAttribute('tabIndex');
42901             this.el.focus();
42902         }else{
42903             Roo.log('editor - hiding textarea');
42904 //            Roo.log('out')
42905 //            Roo.log(this.pushValue()); 
42906             this.editorcore.pushValue();
42907             
42908             this.el.addClass('x-hidden');
42909             this.el.dom.setAttribute('tabIndex', -1);
42910             //this.deferFocus();
42911         }
42912          
42913         this.setSize(this.wrap.getSize());
42914         this.fireEvent('editmodechange', this, this.editorcore.sourceEditMode);
42915     },
42916  
42917     // private (for BoxComponent)
42918     adjustSize : Roo.BoxComponent.prototype.adjustSize,
42919
42920     // private (for BoxComponent)
42921     getResizeEl : function(){
42922         return this.wrap;
42923     },
42924
42925     // private (for BoxComponent)
42926     getPositionEl : function(){
42927         return this.wrap;
42928     },
42929
42930     // private
42931     initEvents : function(){
42932         this.originalValue = this.getValue();
42933     },
42934
42935     /**
42936      * Overridden and disabled. The editor element does not support standard valid/invalid marking. @hide
42937      * @method
42938      */
42939     markInvalid : Roo.emptyFn,
42940     /**
42941      * Overridden and disabled. The editor element does not support standard valid/invalid marking. @hide
42942      * @method
42943      */
42944     clearInvalid : Roo.emptyFn,
42945
42946     setValue : function(v){
42947         Roo.form.HtmlEditor.superclass.setValue.call(this, v);
42948         this.editorcore.pushValue();
42949     },
42950
42951      
42952     // private
42953     deferFocus : function(){
42954         this.focus.defer(10, this);
42955     },
42956
42957     // doc'ed in Field
42958     focus : function(){
42959         this.editorcore.focus();
42960         
42961     },
42962       
42963
42964     // private
42965     onDestroy : function(){
42966         
42967         
42968         
42969         if(this.rendered){
42970             
42971             for (var i =0; i < this.toolbars.length;i++) {
42972                 // fixme - ask toolbars for heights?
42973                 this.toolbars[i].onDestroy();
42974             }
42975             
42976             this.wrap.dom.innerHTML = '';
42977             this.wrap.remove();
42978         }
42979     },
42980
42981     // private
42982     onFirstFocus : function(){
42983         //Roo.log("onFirstFocus");
42984         this.editorcore.onFirstFocus();
42985          for (var i =0; i < this.toolbars.length;i++) {
42986             this.toolbars[i].onFirstFocus();
42987         }
42988         
42989     },
42990     
42991     // private
42992     syncValue : function()
42993     {
42994         this.editorcore.syncValue();
42995     },
42996     
42997     pushValue : function()
42998     {
42999         this.editorcore.pushValue();
43000     }
43001      
43002     
43003     // hide stuff that is not compatible
43004     /**
43005      * @event blur
43006      * @hide
43007      */
43008     /**
43009      * @event change
43010      * @hide
43011      */
43012     /**
43013      * @event focus
43014      * @hide
43015      */
43016     /**
43017      * @event specialkey
43018      * @hide
43019      */
43020     /**
43021      * @cfg {String} fieldClass @hide
43022      */
43023     /**
43024      * @cfg {String} focusClass @hide
43025      */
43026     /**
43027      * @cfg {String} autoCreate @hide
43028      */
43029     /**
43030      * @cfg {String} inputType @hide
43031      */
43032     /**
43033      * @cfg {String} invalidClass @hide
43034      */
43035     /**
43036      * @cfg {String} invalidText @hide
43037      */
43038     /**
43039      * @cfg {String} msgFx @hide
43040      */
43041     /**
43042      * @cfg {String} validateOnBlur @hide
43043      */
43044 });
43045  
43046     // <script type="text/javascript">
43047 /*
43048  * Based on
43049  * Ext JS Library 1.1.1
43050  * Copyright(c) 2006-2007, Ext JS, LLC.
43051  *  
43052  
43053  */
43054
43055 /**
43056  * @class Roo.form.HtmlEditorToolbar1
43057  * Basic Toolbar
43058  * 
43059  * Usage:
43060  *
43061  new Roo.form.HtmlEditor({
43062     ....
43063     toolbars : [
43064         new Roo.form.HtmlEditorToolbar1({
43065             disable : { fonts: 1 , format: 1, ..., ... , ...],
43066             btns : [ .... ]
43067         })
43068     }
43069      
43070  * 
43071  * @cfg {Object} disable List of elements to disable..
43072  * @cfg {Array} btns List of additional buttons.
43073  * 
43074  * 
43075  * NEEDS Extra CSS? 
43076  * .x-html-editor-tb .x-edit-none .x-btn-text { background: none; }
43077  */
43078  
43079 Roo.form.HtmlEditor.ToolbarStandard = function(config)
43080 {
43081     
43082     Roo.apply(this, config);
43083     
43084     // default disabled, based on 'good practice'..
43085     this.disable = this.disable || {};
43086     Roo.applyIf(this.disable, {
43087         fontSize : true,
43088         colors : true,
43089         specialElements : true
43090     });
43091     
43092     
43093     //Roo.form.HtmlEditorToolbar1.superclass.constructor.call(this, editor.wrap.dom.firstChild, [], config);
43094     // dont call parent... till later.
43095 }
43096
43097 Roo.apply(Roo.form.HtmlEditor.ToolbarStandard.prototype,  {
43098     
43099     tb: false,
43100     
43101     rendered: false,
43102     
43103     editor : false,
43104     editorcore : false,
43105     /**
43106      * @cfg {Object} disable  List of toolbar elements to disable
43107          
43108      */
43109     disable : false,
43110     
43111     
43112      /**
43113      * @cfg {String} createLinkText The default text for the create link prompt
43114      */
43115     createLinkText : 'Please enter the URL for the link:',
43116     /**
43117      * @cfg {String} defaultLinkValue The default value for the create link prompt (defaults to http:/ /)
43118      */
43119     defaultLinkValue : 'http:/'+'/',
43120    
43121     
43122       /**
43123      * @cfg {Array} fontFamilies An array of available font families
43124      */
43125     fontFamilies : [
43126         'Arial',
43127         'Courier New',
43128         'Tahoma',
43129         'Times New Roman',
43130         'Verdana'
43131     ],
43132     
43133     specialChars : [
43134            "&#169;",
43135           "&#174;",     
43136           "&#8482;",    
43137           "&#163;" ,    
43138          // "&#8212;",    
43139           "&#8230;",    
43140           "&#247;" ,    
43141         //  "&#225;" ,     ?? a acute?
43142            "&#8364;"    , //Euro
43143        //   "&#8220;"    ,
43144         //  "&#8221;"    ,
43145         //  "&#8226;"    ,
43146           "&#176;"  //   , // degrees
43147
43148          // "&#233;"     , // e ecute
43149          // "&#250;"     , // u ecute?
43150     ],
43151     
43152     specialElements : [
43153         {
43154             text: "Insert Table",
43155             xtype: 'MenuItem',
43156             xns : Roo.Menu,
43157             ihtml :  '<table><tr><td>Cell</td></tr></table>' 
43158                 
43159         },
43160         {    
43161             text: "Insert Image",
43162             xtype: 'MenuItem',
43163             xns : Roo.Menu,
43164             ihtml : '<img src="about:blank"/>'
43165             
43166         }
43167         
43168          
43169     ],
43170     
43171     
43172     inputElements : [ 
43173             "form", "input:text", "input:hidden", "input:checkbox", "input:radio", "input:password", 
43174             "input:submit", "input:button", "select", "textarea", "label" ],
43175     formats : [
43176         ["p"] ,  
43177         ["h1"],["h2"],["h3"],["h4"],["h5"],["h6"], 
43178         ["pre"],[ "code"], 
43179         ["abbr"],[ "acronym"],[ "address"],[ "cite"],[ "samp"],[ "var"],
43180         ['div'],['span']
43181     ],
43182     
43183     cleanStyles : [
43184         "font-size"
43185     ],
43186      /**
43187      * @cfg {String} defaultFont default font to use.
43188      */
43189     defaultFont: 'tahoma',
43190    
43191     fontSelect : false,
43192     
43193     
43194     formatCombo : false,
43195     
43196     init : function(editor)
43197     {
43198         this.editor = editor;
43199         this.editorcore = editor.editorcore ? editor.editorcore : editor;
43200         var editorcore = this.editorcore;
43201         
43202         var _t = this;
43203         
43204         var fid = editorcore.frameId;
43205         var etb = this;
43206         function btn(id, toggle, handler){
43207             var xid = fid + '-'+ id ;
43208             return {
43209                 id : xid,
43210                 cmd : id,
43211                 cls : 'x-btn-icon x-edit-'+id,
43212                 enableToggle:toggle !== false,
43213                 scope: _t, // was editor...
43214                 handler:handler||_t.relayBtnCmd,
43215                 clickEvent:'mousedown',
43216                 tooltip: etb.buttonTips[id] || undefined, ///tips ???
43217                 tabIndex:-1
43218             };
43219         }
43220         
43221         
43222         
43223         var tb = new Roo.Toolbar(editor.wrap.dom.firstChild);
43224         this.tb = tb;
43225          // stop form submits
43226         tb.el.on('click', function(e){
43227             e.preventDefault(); // what does this do?
43228         });
43229
43230         if(!this.disable.font) { // && !Roo.isSafari){
43231             /* why no safari for fonts 
43232             editor.fontSelect = tb.el.createChild({
43233                 tag:'select',
43234                 tabIndex: -1,
43235                 cls:'x-font-select',
43236                 html: this.createFontOptions()
43237             });
43238             
43239             editor.fontSelect.on('change', function(){
43240                 var font = editor.fontSelect.dom.value;
43241                 editor.relayCmd('fontname', font);
43242                 editor.deferFocus();
43243             }, editor);
43244             
43245             tb.add(
43246                 editor.fontSelect.dom,
43247                 '-'
43248             );
43249             */
43250             
43251         };
43252         if(!this.disable.formats){
43253             this.formatCombo = new Roo.form.ComboBox({
43254                 store: new Roo.data.SimpleStore({
43255                     id : 'tag',
43256                     fields: ['tag'],
43257                     data : this.formats // from states.js
43258                 }),
43259                 blockFocus : true,
43260                 name : '',
43261                 //autoCreate : {tag: "div",  size: "20"},
43262                 displayField:'tag',
43263                 typeAhead: false,
43264                 mode: 'local',
43265                 editable : false,
43266                 triggerAction: 'all',
43267                 emptyText:'Add tag',
43268                 selectOnFocus:true,
43269                 width:135,
43270                 listeners : {
43271                     'select': function(c, r, i) {
43272                         editorcore.insertTag(r.get('tag'));
43273                         editor.focus();
43274                     }
43275                 }
43276
43277             });
43278             tb.addField(this.formatCombo);
43279             
43280         }
43281         
43282         if(!this.disable.format){
43283             tb.add(
43284                 btn('bold'),
43285                 btn('italic'),
43286                 btn('underline')
43287             );
43288         };
43289         if(!this.disable.fontSize){
43290             tb.add(
43291                 '-',
43292                 
43293                 
43294                 btn('increasefontsize', false, editorcore.adjustFont),
43295                 btn('decreasefontsize', false, editorcore.adjustFont)
43296             );
43297         };
43298         
43299         
43300         if(!this.disable.colors){
43301             tb.add(
43302                 '-', {
43303                     id:editorcore.frameId +'-forecolor',
43304                     cls:'x-btn-icon x-edit-forecolor',
43305                     clickEvent:'mousedown',
43306                     tooltip: this.buttonTips['forecolor'] || undefined,
43307                     tabIndex:-1,
43308                     menu : new Roo.menu.ColorMenu({
43309                         allowReselect: true,
43310                         focus: Roo.emptyFn,
43311                         value:'000000',
43312                         plain:true,
43313                         selectHandler: function(cp, color){
43314                             editorcore.execCmd('forecolor', Roo.isSafari || Roo.isIE ? '#'+color : color);
43315                             editor.deferFocus();
43316                         },
43317                         scope: editorcore,
43318                         clickEvent:'mousedown'
43319                     })
43320                 }, {
43321                     id:editorcore.frameId +'backcolor',
43322                     cls:'x-btn-icon x-edit-backcolor',
43323                     clickEvent:'mousedown',
43324                     tooltip: this.buttonTips['backcolor'] || undefined,
43325                     tabIndex:-1,
43326                     menu : new Roo.menu.ColorMenu({
43327                         focus: Roo.emptyFn,
43328                         value:'FFFFFF',
43329                         plain:true,
43330                         allowReselect: true,
43331                         selectHandler: function(cp, color){
43332                             if(Roo.isGecko){
43333                                 editorcore.execCmd('useCSS', false);
43334                                 editorcore.execCmd('hilitecolor', color);
43335                                 editorcore.execCmd('useCSS', true);
43336                                 editor.deferFocus();
43337                             }else{
43338                                 editorcore.execCmd(Roo.isOpera ? 'hilitecolor' : 'backcolor', 
43339                                     Roo.isSafari || Roo.isIE ? '#'+color : color);
43340                                 editor.deferFocus();
43341                             }
43342                         },
43343                         scope:editorcore,
43344                         clickEvent:'mousedown'
43345                     })
43346                 }
43347             );
43348         };
43349         // now add all the items...
43350         
43351
43352         if(!this.disable.alignments){
43353             tb.add(
43354                 '-',
43355                 btn('justifyleft'),
43356                 btn('justifycenter'),
43357                 btn('justifyright')
43358             );
43359         };
43360
43361         //if(!Roo.isSafari){
43362             if(!this.disable.links){
43363                 tb.add(
43364                     '-',
43365                     btn('createlink', false, this.createLink)    /// MOVE TO HERE?!!?!?!?!
43366                 );
43367             };
43368
43369             if(!this.disable.lists){
43370                 tb.add(
43371                     '-',
43372                     btn('insertorderedlist'),
43373                     btn('insertunorderedlist')
43374                 );
43375             }
43376             if(!this.disable.sourceEdit){
43377                 tb.add(
43378                     '-',
43379                     btn('sourceedit', true, function(btn){
43380                         Roo.log(this);
43381                         this.toggleSourceEdit(btn.pressed);
43382                     })
43383                 );
43384             }
43385         //}
43386         
43387         var smenu = { };
43388         // special menu.. - needs to be tidied up..
43389         if (!this.disable.special) {
43390             smenu = {
43391                 text: "&#169;",
43392                 cls: 'x-edit-none',
43393                 
43394                 menu : {
43395                     items : []
43396                 }
43397             };
43398             for (var i =0; i < this.specialChars.length; i++) {
43399                 smenu.menu.items.push({
43400                     
43401                     html: this.specialChars[i],
43402                     handler: function(a,b) {
43403                         editorcore.insertAtCursor(String.fromCharCode(a.html.replace('&#','').replace(';', '')));
43404                         //editor.insertAtCursor(a.html);
43405                         
43406                     },
43407                     tabIndex:-1
43408                 });
43409             }
43410             
43411             
43412             tb.add(smenu);
43413             
43414             
43415         }
43416         
43417         var cmenu = { };
43418         if (!this.disable.cleanStyles) {
43419             cmenu = {
43420                 cls: 'x-btn-icon x-btn-clear',
43421                 
43422                 menu : {
43423                     items : []
43424                 }
43425             };
43426             for (var i =0; i < this.cleanStyles.length; i++) {
43427                 cmenu.menu.items.push({
43428                     actiontype : this.cleanStyles[i],
43429                     html: 'Remove ' + this.cleanStyles[i],
43430                     handler: function(a,b) {
43431                         Roo.log(a);
43432                         Roo.log(b);
43433                         var c = Roo.get(editorcore.doc.body);
43434                         c.select('[style]').each(function(s) {
43435                             s.dom.style.removeProperty(a.actiontype);
43436                         });
43437                         editorcore.syncValue();
43438                     },
43439                     tabIndex:-1
43440                 });
43441             }
43442             cmenu.menu.items.push({
43443                 actiontype : 'word',
43444                 html: 'Remove MS Word Formating',
43445                 handler: function(a,b) {
43446                     editorcore.cleanWord();
43447                     editorcore.syncValue();
43448                 },
43449                 tabIndex:-1
43450             });
43451             
43452             cmenu.menu.items.push({
43453                 actiontype : 'all',
43454                 html: 'Remove All Styles',
43455                 handler: function(a,b) {
43456                     
43457                     var c = Roo.get(editorcore.doc.body);
43458                     c.select('[style]').each(function(s) {
43459                         s.dom.removeAttribute('style');
43460                     });
43461                     editorcore.syncValue();
43462                 },
43463                 tabIndex:-1
43464             });
43465              cmenu.menu.items.push({
43466                 actiontype : 'word',
43467                 html: 'Tidy HTML Source',
43468                 handler: function(a,b) {
43469                     editorcore.doc.body.innerHTML = editorcore.domToHTML();
43470                     editorcore.syncValue();
43471                 },
43472                 tabIndex:-1
43473             });
43474             
43475             
43476             tb.add(cmenu);
43477         }
43478          
43479         if (!this.disable.specialElements) {
43480             var semenu = {
43481                 text: "Other;",
43482                 cls: 'x-edit-none',
43483                 menu : {
43484                     items : []
43485                 }
43486             };
43487             for (var i =0; i < this.specialElements.length; i++) {
43488                 semenu.menu.items.push(
43489                     Roo.apply({ 
43490                         handler: function(a,b) {
43491                             editor.insertAtCursor(this.ihtml);
43492                         }
43493                     }, this.specialElements[i])
43494                 );
43495                     
43496             }
43497             
43498             tb.add(semenu);
43499             
43500             
43501         }
43502          
43503         
43504         if (this.btns) {
43505             for(var i =0; i< this.btns.length;i++) {
43506                 var b = Roo.factory(this.btns[i],Roo.form);
43507                 b.cls =  'x-edit-none';
43508                 b.scope = editorcore;
43509                 tb.add(b);
43510             }
43511         
43512         }
43513         
43514         
43515         
43516         // disable everything...
43517         
43518         this.tb.items.each(function(item){
43519            if(item.id != editorcore.frameId+ '-sourceedit'){
43520                 item.disable();
43521             }
43522         });
43523         this.rendered = true;
43524         
43525         // the all the btns;
43526         editor.on('editorevent', this.updateToolbar, this);
43527         // other toolbars need to implement this..
43528         //editor.on('editmodechange', this.updateToolbar, this);
43529     },
43530     
43531     
43532     relayBtnCmd : function(btn) {
43533         this.editorcore.relayCmd(btn.cmd);
43534     },
43535     // private used internally
43536     createLink : function(){
43537         Roo.log("create link?");
43538         var url = prompt(this.createLinkText, this.defaultLinkValue);
43539         if(url && url != 'http:/'+'/'){
43540             this.editorcore.relayCmd('createlink', url);
43541         }
43542     },
43543
43544     
43545     /**
43546      * Protected method that will not generally be called directly. It triggers
43547      * a toolbar update by reading the markup state of the current selection in the editor.
43548      */
43549     updateToolbar: function(){
43550
43551         if(!this.editorcore.activated){
43552             this.editor.onFirstFocus();
43553             return;
43554         }
43555
43556         var btns = this.tb.items.map, 
43557             doc = this.editorcore.doc,
43558             frameId = this.editorcore.frameId;
43559
43560         if(!this.disable.font && !Roo.isSafari){
43561             /*
43562             var name = (doc.queryCommandValue('FontName')||this.editor.defaultFont).toLowerCase();
43563             if(name != this.fontSelect.dom.value){
43564                 this.fontSelect.dom.value = name;
43565             }
43566             */
43567         }
43568         if(!this.disable.format){
43569             btns[frameId + '-bold'].toggle(doc.queryCommandState('bold'));
43570             btns[frameId + '-italic'].toggle(doc.queryCommandState('italic'));
43571             btns[frameId + '-underline'].toggle(doc.queryCommandState('underline'));
43572         }
43573         if(!this.disable.alignments){
43574             btns[frameId + '-justifyleft'].toggle(doc.queryCommandState('justifyleft'));
43575             btns[frameId + '-justifycenter'].toggle(doc.queryCommandState('justifycenter'));
43576             btns[frameId + '-justifyright'].toggle(doc.queryCommandState('justifyright'));
43577         }
43578         if(!Roo.isSafari && !this.disable.lists){
43579             btns[frameId + '-insertorderedlist'].toggle(doc.queryCommandState('insertorderedlist'));
43580             btns[frameId + '-insertunorderedlist'].toggle(doc.queryCommandState('insertunorderedlist'));
43581         }
43582         
43583         var ans = this.editorcore.getAllAncestors();
43584         if (this.formatCombo) {
43585             
43586             
43587             var store = this.formatCombo.store;
43588             this.formatCombo.setValue("");
43589             for (var i =0; i < ans.length;i++) {
43590                 if (ans[i] && store.query('tag',ans[i].tagName.toLowerCase(), false).length) {
43591                     // select it..
43592                     this.formatCombo.setValue(ans[i].tagName.toLowerCase());
43593                     break;
43594                 }
43595             }
43596         }
43597         
43598         
43599         
43600         // hides menus... - so this cant be on a menu...
43601         Roo.menu.MenuMgr.hideAll();
43602
43603         //this.editorsyncValue();
43604     },
43605    
43606     
43607     createFontOptions : function(){
43608         var buf = [], fs = this.fontFamilies, ff, lc;
43609         
43610         
43611         
43612         for(var i = 0, len = fs.length; i< len; i++){
43613             ff = fs[i];
43614             lc = ff.toLowerCase();
43615             buf.push(
43616                 '<option value="',lc,'" style="font-family:',ff,';"',
43617                     (this.defaultFont == lc ? ' selected="true">' : '>'),
43618                     ff,
43619                 '</option>'
43620             );
43621         }
43622         return buf.join('');
43623     },
43624     
43625     toggleSourceEdit : function(sourceEditMode){
43626         
43627         Roo.log("toolbar toogle");
43628         if(sourceEditMode === undefined){
43629             sourceEditMode = !this.sourceEditMode;
43630         }
43631         this.sourceEditMode = sourceEditMode === true;
43632         var btn = this.tb.items.get(this.editorcore.frameId +'-sourceedit');
43633         // just toggle the button?
43634         if(btn.pressed !== this.sourceEditMode){
43635             btn.toggle(this.sourceEditMode);
43636             return;
43637         }
43638         
43639         if(sourceEditMode){
43640             Roo.log("disabling buttons");
43641             this.tb.items.each(function(item){
43642                 if(item.cmd != 'sourceedit'){
43643                     item.disable();
43644                 }
43645             });
43646           
43647         }else{
43648             Roo.log("enabling buttons");
43649             if(this.editorcore.initialized){
43650                 this.tb.items.each(function(item){
43651                     item.enable();
43652                 });
43653             }
43654             
43655         }
43656         Roo.log("calling toggole on editor");
43657         // tell the editor that it's been pressed..
43658         this.editor.toggleSourceEdit(sourceEditMode);
43659        
43660     },
43661      /**
43662      * Object collection of toolbar tooltips for the buttons in the editor. The key
43663      * is the command id associated with that button and the value is a valid QuickTips object.
43664      * For example:
43665 <pre><code>
43666 {
43667     bold : {
43668         title: 'Bold (Ctrl+B)',
43669         text: 'Make the selected text bold.',
43670         cls: 'x-html-editor-tip'
43671     },
43672     italic : {
43673         title: 'Italic (Ctrl+I)',
43674         text: 'Make the selected text italic.',
43675         cls: 'x-html-editor-tip'
43676     },
43677     ...
43678 </code></pre>
43679     * @type Object
43680      */
43681     buttonTips : {
43682         bold : {
43683             title: 'Bold (Ctrl+B)',
43684             text: 'Make the selected text bold.',
43685             cls: 'x-html-editor-tip'
43686         },
43687         italic : {
43688             title: 'Italic (Ctrl+I)',
43689             text: 'Make the selected text italic.',
43690             cls: 'x-html-editor-tip'
43691         },
43692         underline : {
43693             title: 'Underline (Ctrl+U)',
43694             text: 'Underline the selected text.',
43695             cls: 'x-html-editor-tip'
43696         },
43697         increasefontsize : {
43698             title: 'Grow Text',
43699             text: 'Increase the font size.',
43700             cls: 'x-html-editor-tip'
43701         },
43702         decreasefontsize : {
43703             title: 'Shrink Text',
43704             text: 'Decrease the font size.',
43705             cls: 'x-html-editor-tip'
43706         },
43707         backcolor : {
43708             title: 'Text Highlight Color',
43709             text: 'Change the background color of the selected text.',
43710             cls: 'x-html-editor-tip'
43711         },
43712         forecolor : {
43713             title: 'Font Color',
43714             text: 'Change the color of the selected text.',
43715             cls: 'x-html-editor-tip'
43716         },
43717         justifyleft : {
43718             title: 'Align Text Left',
43719             text: 'Align text to the left.',
43720             cls: 'x-html-editor-tip'
43721         },
43722         justifycenter : {
43723             title: 'Center Text',
43724             text: 'Center text in the editor.',
43725             cls: 'x-html-editor-tip'
43726         },
43727         justifyright : {
43728             title: 'Align Text Right',
43729             text: 'Align text to the right.',
43730             cls: 'x-html-editor-tip'
43731         },
43732         insertunorderedlist : {
43733             title: 'Bullet List',
43734             text: 'Start a bulleted list.',
43735             cls: 'x-html-editor-tip'
43736         },
43737         insertorderedlist : {
43738             title: 'Numbered List',
43739             text: 'Start a numbered list.',
43740             cls: 'x-html-editor-tip'
43741         },
43742         createlink : {
43743             title: 'Hyperlink',
43744             text: 'Make the selected text a hyperlink.',
43745             cls: 'x-html-editor-tip'
43746         },
43747         sourceedit : {
43748             title: 'Source Edit',
43749             text: 'Switch to source editing mode.',
43750             cls: 'x-html-editor-tip'
43751         }
43752     },
43753     // private
43754     onDestroy : function(){
43755         if(this.rendered){
43756             
43757             this.tb.items.each(function(item){
43758                 if(item.menu){
43759                     item.menu.removeAll();
43760                     if(item.menu.el){
43761                         item.menu.el.destroy();
43762                     }
43763                 }
43764                 item.destroy();
43765             });
43766              
43767         }
43768     },
43769     onFirstFocus: function() {
43770         this.tb.items.each(function(item){
43771            item.enable();
43772         });
43773     }
43774 });
43775
43776
43777
43778
43779 // <script type="text/javascript">
43780 /*
43781  * Based on
43782  * Ext JS Library 1.1.1
43783  * Copyright(c) 2006-2007, Ext JS, LLC.
43784  *  
43785  
43786  */
43787
43788  
43789 /**
43790  * @class Roo.form.HtmlEditor.ToolbarContext
43791  * Context Toolbar
43792  * 
43793  * Usage:
43794  *
43795  new Roo.form.HtmlEditor({
43796     ....
43797     toolbars : [
43798         { xtype: 'ToolbarStandard', styles : {} }
43799         { xtype: 'ToolbarContext', disable : {} }
43800     ]
43801 })
43802
43803      
43804  * 
43805  * @config : {Object} disable List of elements to disable.. (not done yet.)
43806  * @config : {Object} styles  Map of styles available.
43807  * 
43808  */
43809
43810 Roo.form.HtmlEditor.ToolbarContext = function(config)
43811 {
43812     
43813     Roo.apply(this, config);
43814     //Roo.form.HtmlEditorToolbar1.superclass.constructor.call(this, editor.wrap.dom.firstChild, [], config);
43815     // dont call parent... till later.
43816     this.styles = this.styles || {};
43817 }
43818
43819  
43820
43821 Roo.form.HtmlEditor.ToolbarContext.types = {
43822     'IMG' : {
43823         width : {
43824             title: "Width",
43825             width: 40
43826         },
43827         height:  {
43828             title: "Height",
43829             width: 40
43830         },
43831         align: {
43832             title: "Align",
43833             opts : [ [""],[ "left"],[ "right"],[ "center"],[ "top"]],
43834             width : 80
43835             
43836         },
43837         border: {
43838             title: "Border",
43839             width: 40
43840         },
43841         alt: {
43842             title: "Alt",
43843             width: 120
43844         },
43845         src : {
43846             title: "Src",
43847             width: 220
43848         }
43849         
43850     },
43851     'A' : {
43852         name : {
43853             title: "Name",
43854             width: 50
43855         },
43856         target:  {
43857             title: "Target",
43858             width: 120
43859         },
43860         href:  {
43861             title: "Href",
43862             width: 220
43863         } // border?
43864         
43865     },
43866     'TABLE' : {
43867         rows : {
43868             title: "Rows",
43869             width: 20
43870         },
43871         cols : {
43872             title: "Cols",
43873             width: 20
43874         },
43875         width : {
43876             title: "Width",
43877             width: 40
43878         },
43879         height : {
43880             title: "Height",
43881             width: 40
43882         },
43883         border : {
43884             title: "Border",
43885             width: 20
43886         }
43887     },
43888     'TD' : {
43889         width : {
43890             title: "Width",
43891             width: 40
43892         },
43893         height : {
43894             title: "Height",
43895             width: 40
43896         },   
43897         align: {
43898             title: "Align",
43899             opts : [[""],[ "left"],[ "center"],[ "right"],[ "justify"],[ "char"]],
43900             width: 80
43901         },
43902         valign: {
43903             title: "Valign",
43904             opts : [[""],[ "top"],[ "middle"],[ "bottom"],[ "baseline"]],
43905             width: 80
43906         },
43907         colspan: {
43908             title: "Colspan",
43909             width: 20
43910             
43911         },
43912          'font-family'  : {
43913             title : "Font",
43914             style : 'fontFamily',
43915             displayField: 'display',
43916             optname : 'font-family',
43917             width: 140
43918         }
43919     },
43920     'INPUT' : {
43921         name : {
43922             title: "name",
43923             width: 120
43924         },
43925         value : {
43926             title: "Value",
43927             width: 120
43928         },
43929         width : {
43930             title: "Width",
43931             width: 40
43932         }
43933     },
43934     'LABEL' : {
43935         'for' : {
43936             title: "For",
43937             width: 120
43938         }
43939     },
43940     'TEXTAREA' : {
43941           name : {
43942             title: "name",
43943             width: 120
43944         },
43945         rows : {
43946             title: "Rows",
43947             width: 20
43948         },
43949         cols : {
43950             title: "Cols",
43951             width: 20
43952         }
43953     },
43954     'SELECT' : {
43955         name : {
43956             title: "name",
43957             width: 120
43958         },
43959         selectoptions : {
43960             title: "Options",
43961             width: 200
43962         }
43963     },
43964     
43965     // should we really allow this??
43966     // should this just be 
43967     'BODY' : {
43968         title : {
43969             title: "Title",
43970             width: 200,
43971             disabled : true
43972         }
43973     },
43974     'SPAN' : {
43975         'font-family'  : {
43976             title : "Font",
43977             style : 'fontFamily',
43978             displayField: 'display',
43979             optname : 'font-family',
43980             width: 140
43981         }
43982     },
43983     'DIV' : {
43984         'font-family'  : {
43985             title : "Font",
43986             style : 'fontFamily',
43987             displayField: 'display',
43988             optname : 'font-family',
43989             width: 140
43990         }
43991     },
43992      'P' : {
43993         'font-family'  : {
43994             title : "Font",
43995             style : 'fontFamily',
43996             displayField: 'display',
43997             optname : 'font-family',
43998             width: 140
43999         }
44000     },
44001     
44002     '*' : {
44003         // empty..
44004     }
44005
44006 };
44007
44008 // this should be configurable.. - you can either set it up using stores, or modify options somehwere..
44009 Roo.form.HtmlEditor.ToolbarContext.stores = false;
44010
44011 Roo.form.HtmlEditor.ToolbarContext.options = {
44012         'font-family'  : [ 
44013                 [ 'Helvetica,Arial,sans-serif', 'Helvetica'],
44014                 [ 'Courier New', 'Courier New'],
44015                 [ 'Tahoma', 'Tahoma'],
44016                 [ 'Times New Roman,serif', 'Times'],
44017                 [ 'Verdana','Verdana' ]
44018         ]
44019 };
44020
44021 // fixme - these need to be configurable..
44022  
44023
44024 Roo.form.HtmlEditor.ToolbarContext.types
44025
44026
44027 Roo.apply(Roo.form.HtmlEditor.ToolbarContext.prototype,  {
44028     
44029     tb: false,
44030     
44031     rendered: false,
44032     
44033     editor : false,
44034     editorcore : false,
44035     /**
44036      * @cfg {Object} disable  List of toolbar elements to disable
44037          
44038      */
44039     disable : false,
44040     /**
44041      * @cfg {Object} styles List of styles 
44042      *    eg. { '*' : [ 'headline' ] , 'TD' : [ 'underline', 'double-underline' ] } 
44043      *
44044      * These must be defined in the page, so they get rendered correctly..
44045      * .headline { }
44046      * TD.underline { }
44047      * 
44048      */
44049     styles : false,
44050     
44051     options: false,
44052     
44053     toolbars : false,
44054     
44055     init : function(editor)
44056     {
44057         this.editor = editor;
44058         this.editorcore = editor.editorcore ? editor.editorcore : editor;
44059         var editorcore = this.editorcore;
44060         
44061         var fid = editorcore.frameId;
44062         var etb = this;
44063         function btn(id, toggle, handler){
44064             var xid = fid + '-'+ id ;
44065             return {
44066                 id : xid,
44067                 cmd : id,
44068                 cls : 'x-btn-icon x-edit-'+id,
44069                 enableToggle:toggle !== false,
44070                 scope: editorcore, // was editor...
44071                 handler:handler||editorcore.relayBtnCmd,
44072                 clickEvent:'mousedown',
44073                 tooltip: etb.buttonTips[id] || undefined, ///tips ???
44074                 tabIndex:-1
44075             };
44076         }
44077         // create a new element.
44078         var wdiv = editor.wrap.createChild({
44079                 tag: 'div'
44080             }, editor.wrap.dom.firstChild.nextSibling, true);
44081         
44082         // can we do this more than once??
44083         
44084          // stop form submits
44085       
44086  
44087         // disable everything...
44088         var ty= Roo.form.HtmlEditor.ToolbarContext.types;
44089         this.toolbars = {};
44090            
44091         for (var i in  ty) {
44092           
44093             this.toolbars[i] = this.buildToolbar(ty[i],i);
44094         }
44095         this.tb = this.toolbars.BODY;
44096         this.tb.el.show();
44097         this.buildFooter();
44098         this.footer.show();
44099         editor.on('hide', function( ) { this.footer.hide() }, this);
44100         editor.on('show', function( ) { this.footer.show() }, this);
44101         
44102          
44103         this.rendered = true;
44104         
44105         // the all the btns;
44106         editor.on('editorevent', this.updateToolbar, this);
44107         // other toolbars need to implement this..
44108         //editor.on('editmodechange', this.updateToolbar, this);
44109     },
44110     
44111     
44112     
44113     /**
44114      * Protected method that will not generally be called directly. It triggers
44115      * a toolbar update by reading the markup state of the current selection in the editor.
44116      */
44117     updateToolbar: function(editor,ev,sel){
44118
44119         //Roo.log(ev);
44120         // capture mouse up - this is handy for selecting images..
44121         // perhaps should go somewhere else...
44122         if(!this.editorcore.activated){
44123              this.editor.onFirstFocus();
44124             return;
44125         }
44126         
44127         // http://developer.yahoo.com/yui/docs/simple-editor.js.html
44128         // selectNode - might want to handle IE?
44129         if (ev &&
44130             (ev.type == 'mouseup' || ev.type == 'click' ) &&
44131             ev.target && ev.target.tagName == 'IMG') {
44132             // they have click on an image...
44133             // let's see if we can change the selection...
44134             sel = ev.target;
44135          
44136               var nodeRange = sel.ownerDocument.createRange();
44137             try {
44138                 nodeRange.selectNode(sel);
44139             } catch (e) {
44140                 nodeRange.selectNodeContents(sel);
44141             }
44142             //nodeRange.collapse(true);
44143             var s = this.editorcore.win.getSelection();
44144             s.removeAllRanges();
44145             s.addRange(nodeRange);
44146         }  
44147         
44148       
44149         var updateFooter = sel ? false : true;
44150         
44151         
44152         var ans = this.editorcore.getAllAncestors();
44153         
44154         // pick
44155         var ty= Roo.form.HtmlEditor.ToolbarContext.types;
44156         
44157         if (!sel) { 
44158             sel = ans.length ? (ans[0] ?  ans[0]  : ans[1]) : this.editorcore.doc.body;
44159             sel = sel ? sel : this.editorcore.doc.body;
44160             sel = sel.tagName.length ? sel : this.editorcore.doc.body;
44161             
44162         }
44163         // pick a menu that exists..
44164         var tn = sel.tagName.toUpperCase();
44165         //sel = typeof(ty[tn]) != 'undefined' ? sel : this.editor.doc.body;
44166         
44167         tn = sel.tagName.toUpperCase();
44168         
44169         var lastSel = this.tb.selectedNode
44170         
44171         this.tb.selectedNode = sel;
44172         
44173         // if current menu does not match..
44174         if ((this.tb.name != tn) || (lastSel != this.tb.selectedNode)) {
44175                 
44176             this.tb.el.hide();
44177             ///console.log("show: " + tn);
44178             this.tb =  typeof(ty[tn]) != 'undefined' ? this.toolbars[tn] : this.toolbars['*'];
44179             this.tb.el.show();
44180             // update name
44181             this.tb.items.first().el.innerHTML = tn + ':&nbsp;';
44182             
44183             
44184             // update attributes
44185             if (this.tb.fields) {
44186                 this.tb.fields.each(function(e) {
44187                     if (e.stylename) {
44188                         e.setValue(sel.style[e.stylename]);
44189                         return;
44190                     } 
44191                    e.setValue(sel.getAttribute(e.attrname));
44192                 });
44193             }
44194             
44195             var hasStyles = false;
44196             for(var i in this.styles) {
44197                 hasStyles = true;
44198                 break;
44199             }
44200             
44201             // update styles
44202             if (hasStyles) { 
44203                 var st = this.tb.fields.item(0);
44204                 
44205                 st.store.removeAll();
44206                
44207                 
44208                 var cn = sel.className.split(/\s+/);
44209                 
44210                 var avs = [];
44211                 if (this.styles['*']) {
44212                     
44213                     Roo.each(this.styles['*'], function(v) {
44214                         avs.push( [ v , cn.indexOf(v) > -1 ? 1 : 0 ] );         
44215                     });
44216                 }
44217                 if (this.styles[tn]) { 
44218                     Roo.each(this.styles[tn], function(v) {
44219                         avs.push( [ v , cn.indexOf(v) > -1 ? 1 : 0 ] );         
44220                     });
44221                 }
44222                 
44223                 st.store.loadData(avs);
44224                 st.collapse();
44225                 st.setValue(cn);
44226             }
44227             // flag our selected Node.
44228             this.tb.selectedNode = sel;
44229            
44230            
44231             Roo.menu.MenuMgr.hideAll();
44232
44233         }
44234         
44235         if (!updateFooter) {
44236             //this.footDisp.dom.innerHTML = ''; 
44237             return;
44238         }
44239         // update the footer
44240         //
44241         var html = '';
44242         
44243         this.footerEls = ans.reverse();
44244         Roo.each(this.footerEls, function(a,i) {
44245             if (!a) { return; }
44246             html += html.length ? ' &gt; '  :  '';
44247             
44248             html += '<span class="x-ed-loc-' + i + '">' + a.tagName + '</span>';
44249             
44250         });
44251        
44252         // 
44253         var sz = this.footDisp.up('td').getSize();
44254         this.footDisp.dom.style.width = (sz.width -10) + 'px';
44255         this.footDisp.dom.style.marginLeft = '5px';
44256         
44257         this.footDisp.dom.style.overflow = 'hidden';
44258         
44259         this.footDisp.dom.innerHTML = html;
44260             
44261         //this.editorsyncValue();
44262     },
44263      
44264     
44265    
44266        
44267     // private
44268     onDestroy : function(){
44269         if(this.rendered){
44270             
44271             this.tb.items.each(function(item){
44272                 if(item.menu){
44273                     item.menu.removeAll();
44274                     if(item.menu.el){
44275                         item.menu.el.destroy();
44276                     }
44277                 }
44278                 item.destroy();
44279             });
44280              
44281         }
44282     },
44283     onFirstFocus: function() {
44284         // need to do this for all the toolbars..
44285         this.tb.items.each(function(item){
44286            item.enable();
44287         });
44288     },
44289     buildToolbar: function(tlist, nm)
44290     {
44291         var editor = this.editor;
44292         var editorcore = this.editorcore;
44293          // create a new element.
44294         var wdiv = editor.wrap.createChild({
44295                 tag: 'div'
44296             }, editor.wrap.dom.firstChild.nextSibling, true);
44297         
44298        
44299         var tb = new Roo.Toolbar(wdiv);
44300         // add the name..
44301         
44302         tb.add(nm+ ":&nbsp;");
44303         
44304         var styles = [];
44305         for(var i in this.styles) {
44306             styles.push(i);
44307         }
44308         
44309         // styles...
44310         if (styles && styles.length) {
44311             
44312             // this needs a multi-select checkbox...
44313             tb.addField( new Roo.form.ComboBox({
44314                 store: new Roo.data.SimpleStore({
44315                     id : 'val',
44316                     fields: ['val', 'selected'],
44317                     data : [] 
44318                 }),
44319                 name : '-roo-edit-className',
44320                 attrname : 'className',
44321                 displayField: 'val',
44322                 typeAhead: false,
44323                 mode: 'local',
44324                 editable : false,
44325                 triggerAction: 'all',
44326                 emptyText:'Select Style',
44327                 selectOnFocus:true,
44328                 width: 130,
44329                 listeners : {
44330                     'select': function(c, r, i) {
44331                         // initial support only for on class per el..
44332                         tb.selectedNode.className =  r ? r.get('val') : '';
44333                         editorcore.syncValue();
44334                     }
44335                 }
44336     
44337             }));
44338         }
44339         
44340         var tbc = Roo.form.HtmlEditor.ToolbarContext;
44341         var tbops = tbc.options;
44342         
44343         for (var i in tlist) {
44344             
44345             var item = tlist[i];
44346             tb.add(item.title + ":&nbsp;");
44347             
44348             
44349             //optname == used so you can configure the options available..
44350             var opts = item.opts ? item.opts : false;
44351             if (item.optname) {
44352                 opts = tbops[item.optname];
44353            
44354             }
44355             
44356             if (opts) {
44357                 // opts == pulldown..
44358                 tb.addField( new Roo.form.ComboBox({
44359                     store:   typeof(tbc.stores[i]) != 'undefined' ?  Roo.factory(tbc.stores[i],Roo.data) : new Roo.data.SimpleStore({
44360                         id : 'val',
44361                         fields: ['val', 'display'],
44362                         data : opts  
44363                     }),
44364                     name : '-roo-edit-' + i,
44365                     attrname : i,
44366                     stylename : item.style ? item.style : false,
44367                     displayField: item.displayField ? item.displayField : 'val',
44368                     valueField :  'val',
44369                     typeAhead: false,
44370                     mode: typeof(tbc.stores[i]) != 'undefined'  ? 'remote' : 'local',
44371                     editable : false,
44372                     triggerAction: 'all',
44373                     emptyText:'Select',
44374                     selectOnFocus:true,
44375                     width: item.width ? item.width  : 130,
44376                     listeners : {
44377                         'select': function(c, r, i) {
44378                             if (c.stylename) {
44379                                 tb.selectedNode.style[c.stylename] =  r.get('val');
44380                                 return;
44381                             }
44382                             tb.selectedNode.setAttribute(c.attrname, r.get('val'));
44383                         }
44384                     }
44385
44386                 }));
44387                 continue;
44388                     
44389                  
44390                 
44391                 tb.addField( new Roo.form.TextField({
44392                     name: i,
44393                     width: 100,
44394                     //allowBlank:false,
44395                     value: ''
44396                 }));
44397                 continue;
44398             }
44399             tb.addField( new Roo.form.TextField({
44400                 name: '-roo-edit-' + i,
44401                 attrname : i,
44402                 
44403                 width: item.width,
44404                 //allowBlank:true,
44405                 value: '',
44406                 listeners: {
44407                     'change' : function(f, nv, ov) {
44408                         tb.selectedNode.setAttribute(f.attrname, nv);
44409                     }
44410                 }
44411             }));
44412              
44413         }
44414         tb.addFill();
44415         var _this = this;
44416         tb.addButton( {
44417             text: 'Remove Tag',
44418     
44419             listeners : {
44420                 click : function ()
44421                 {
44422                     // remove
44423                     // undo does not work.
44424                      
44425                     var sn = tb.selectedNode;
44426                     
44427                     var pn = sn.parentNode;
44428                     
44429                     var stn =  sn.childNodes[0];
44430                     var en = sn.childNodes[sn.childNodes.length - 1 ];
44431                     while (sn.childNodes.length) {
44432                         var node = sn.childNodes[0];
44433                         sn.removeChild(node);
44434                         //Roo.log(node);
44435                         pn.insertBefore(node, sn);
44436                         
44437                     }
44438                     pn.removeChild(sn);
44439                     var range = editorcore.createRange();
44440         
44441                     range.setStart(stn,0);
44442                     range.setEnd(en,0); //????
44443                     //range.selectNode(sel);
44444                     
44445                     
44446                     var selection = editorcore.getSelection();
44447                     selection.removeAllRanges();
44448                     selection.addRange(range);
44449                     
44450                     
44451                     
44452                     //_this.updateToolbar(null, null, pn);
44453                     _this.updateToolbar(null, null, null);
44454                     _this.footDisp.dom.innerHTML = ''; 
44455                 }
44456             }
44457             
44458                     
44459                 
44460             
44461         });
44462         
44463         
44464         tb.el.on('click', function(e){
44465             e.preventDefault(); // what does this do?
44466         });
44467         tb.el.setVisibilityMode( Roo.Element.DISPLAY);
44468         tb.el.hide();
44469         tb.name = nm;
44470         // dont need to disable them... as they will get hidden
44471         return tb;
44472          
44473         
44474     },
44475     buildFooter : function()
44476     {
44477         
44478         var fel = this.editor.wrap.createChild();
44479         this.footer = new Roo.Toolbar(fel);
44480         // toolbar has scrolly on left / right?
44481         var footDisp= new Roo.Toolbar.Fill();
44482         var _t = this;
44483         this.footer.add(
44484             {
44485                 text : '&lt;',
44486                 xtype: 'Button',
44487                 handler : function() {
44488                     _t.footDisp.scrollTo('left',0,true)
44489                 }
44490             }
44491         );
44492         this.footer.add( footDisp );
44493         this.footer.add( 
44494             {
44495                 text : '&gt;',
44496                 xtype: 'Button',
44497                 handler : function() {
44498                     // no animation..
44499                     _t.footDisp.select('span').last().scrollIntoView(_t.footDisp,true);
44500                 }
44501             }
44502         );
44503         var fel = Roo.get(footDisp.el);
44504         fel.addClass('x-editor-context');
44505         this.footDispWrap = fel; 
44506         this.footDispWrap.overflow  = 'hidden';
44507         
44508         this.footDisp = fel.createChild();
44509         this.footDispWrap.on('click', this.onContextClick, this)
44510         
44511         
44512     },
44513     onContextClick : function (ev,dom)
44514     {
44515         ev.preventDefault();
44516         var  cn = dom.className;
44517         //Roo.log(cn);
44518         if (!cn.match(/x-ed-loc-/)) {
44519             return;
44520         }
44521         var n = cn.split('-').pop();
44522         var ans = this.footerEls;
44523         var sel = ans[n];
44524         
44525          // pick
44526         var range = this.editorcore.createRange();
44527         
44528         range.selectNodeContents(sel);
44529         //range.selectNode(sel);
44530         
44531         
44532         var selection = this.editorcore.getSelection();
44533         selection.removeAllRanges();
44534         selection.addRange(range);
44535         
44536         
44537         
44538         this.updateToolbar(null, null, sel);
44539         
44540         
44541     }
44542     
44543     
44544     
44545     
44546     
44547 });
44548
44549
44550
44551
44552
44553 /*
44554  * Based on:
44555  * Ext JS Library 1.1.1
44556  * Copyright(c) 2006-2007, Ext JS, LLC.
44557  *
44558  * Originally Released Under LGPL - original licence link has changed is not relivant.
44559  *
44560  * Fork - LGPL
44561  * <script type="text/javascript">
44562  */
44563  
44564 /**
44565  * @class Roo.form.BasicForm
44566  * @extends Roo.util.Observable
44567  * Supplies the functionality to do "actions" on forms and initialize Roo.form.Field types on existing markup.
44568  * @constructor
44569  * @param {String/HTMLElement/Roo.Element} el The form element or its id
44570  * @param {Object} config Configuration options
44571  */
44572 Roo.form.BasicForm = function(el, config){
44573     this.allItems = [];
44574     this.childForms = [];
44575     Roo.apply(this, config);
44576     /*
44577      * The Roo.form.Field items in this form.
44578      * @type MixedCollection
44579      */
44580      
44581      
44582     this.items = new Roo.util.MixedCollection(false, function(o){
44583         return o.id || (o.id = Roo.id());
44584     });
44585     this.addEvents({
44586         /**
44587          * @event beforeaction
44588          * Fires before any action is performed. Return false to cancel the action.
44589          * @param {Form} this
44590          * @param {Action} action The action to be performed
44591          */
44592         beforeaction: true,
44593         /**
44594          * @event actionfailed
44595          * Fires when an action fails.
44596          * @param {Form} this
44597          * @param {Action} action The action that failed
44598          */
44599         actionfailed : true,
44600         /**
44601          * @event actioncomplete
44602          * Fires when an action is completed.
44603          * @param {Form} this
44604          * @param {Action} action The action that completed
44605          */
44606         actioncomplete : true
44607     });
44608     if(el){
44609         this.initEl(el);
44610     }
44611     Roo.form.BasicForm.superclass.constructor.call(this);
44612 };
44613
44614 Roo.extend(Roo.form.BasicForm, Roo.util.Observable, {
44615     /**
44616      * @cfg {String} method
44617      * The request method to use (GET or POST) for form actions if one isn't supplied in the action options.
44618      */
44619     /**
44620      * @cfg {DataReader} reader
44621      * An Roo.data.DataReader (e.g. {@link Roo.data.XmlReader}) to be used to read data when executing "load" actions.
44622      * This is optional as there is built-in support for processing JSON.
44623      */
44624     /**
44625      * @cfg {DataReader} errorReader
44626      * An Roo.data.DataReader (e.g. {@link Roo.data.XmlReader}) to be used to read data when reading validation errors on "submit" actions.
44627      * This is completely optional as there is built-in support for processing JSON.
44628      */
44629     /**
44630      * @cfg {String} url
44631      * The URL to use for form actions if one isn't supplied in the action options.
44632      */
44633     /**
44634      * @cfg {Boolean} fileUpload
44635      * Set to true if this form is a file upload.
44636      */
44637      
44638     /**
44639      * @cfg {Object} baseParams
44640      * Parameters to pass with all requests. e.g. baseParams: {id: '123', foo: 'bar'}.
44641      */
44642      /**
44643      
44644     /**
44645      * @cfg {Number} timeout Timeout for form actions in seconds (default is 30 seconds).
44646      */
44647     timeout: 30,
44648
44649     // private
44650     activeAction : null,
44651
44652     /**
44653      * @cfg {Boolean} trackResetOnLoad If set to true, form.reset() resets to the last loaded
44654      * or setValues() data instead of when the form was first created.
44655      */
44656     trackResetOnLoad : false,
44657     
44658     
44659     /**
44660      * childForms - used for multi-tab forms
44661      * @type {Array}
44662      */
44663     childForms : false,
44664     
44665     /**
44666      * allItems - full list of fields.
44667      * @type {Array}
44668      */
44669     allItems : false,
44670     
44671     /**
44672      * By default wait messages are displayed with Roo.MessageBox.wait. You can target a specific
44673      * element by passing it or its id or mask the form itself by passing in true.
44674      * @type Mixed
44675      */
44676     waitMsgTarget : false,
44677
44678     // private
44679     initEl : function(el){
44680         this.el = Roo.get(el);
44681         this.id = this.el.id || Roo.id();
44682         this.el.on('submit', this.onSubmit, this);
44683         this.el.addClass('x-form');
44684     },
44685
44686     // private
44687     onSubmit : function(e){
44688         e.stopEvent();
44689     },
44690
44691     /**
44692      * Returns true if client-side validation on the form is successful.
44693      * @return Boolean
44694      */
44695     isValid : function(){
44696         var valid = true;
44697         this.items.each(function(f){
44698            if(!f.validate()){
44699                valid = false;
44700            }
44701         });
44702         return valid;
44703     },
44704
44705     /**
44706      * Returns true if any fields in this form have changed since their original load.
44707      * @return Boolean
44708      */
44709     isDirty : function(){
44710         var dirty = false;
44711         this.items.each(function(f){
44712            if(f.isDirty()){
44713                dirty = true;
44714                return false;
44715            }
44716         });
44717         return dirty;
44718     },
44719
44720     /**
44721      * Performs a predefined action (submit or load) or custom actions you define on this form.
44722      * @param {String} actionName The name of the action type
44723      * @param {Object} options (optional) The options to pass to the action.  All of the config options listed
44724      * below are supported by both the submit and load actions unless otherwise noted (custom actions could also
44725      * accept other config options):
44726      * <pre>
44727 Property          Type             Description
44728 ----------------  ---------------  ----------------------------------------------------------------------------------
44729 url               String           The url for the action (defaults to the form's url)
44730 method            String           The form method to use (defaults to the form's method, or POST if not defined)
44731 params            String/Object    The params to pass (defaults to the form's baseParams, or none if not defined)
44732 clientValidation  Boolean          Applies to submit only.  Pass true to call form.isValid() prior to posting to
44733                                    validate the form on the client (defaults to false)
44734      * </pre>
44735      * @return {BasicForm} this
44736      */
44737     doAction : function(action, options){
44738         if(typeof action == 'string'){
44739             action = new Roo.form.Action.ACTION_TYPES[action](this, options);
44740         }
44741         if(this.fireEvent('beforeaction', this, action) !== false){
44742             this.beforeAction(action);
44743             action.run.defer(100, action);
44744         }
44745         return this;
44746     },
44747
44748     /**
44749      * Shortcut to do a submit action.
44750      * @param {Object} options The options to pass to the action (see {@link #doAction} for details)
44751      * @return {BasicForm} this
44752      */
44753     submit : function(options){
44754         this.doAction('submit', options);
44755         return this;
44756     },
44757
44758     /**
44759      * Shortcut to do a load action.
44760      * @param {Object} options The options to pass to the action (see {@link #doAction} for details)
44761      * @return {BasicForm} this
44762      */
44763     load : function(options){
44764         this.doAction('load', options);
44765         return this;
44766     },
44767
44768     /**
44769      * Persists the values in this form into the passed Roo.data.Record object in a beginEdit/endEdit block.
44770      * @param {Record} record The record to edit
44771      * @return {BasicForm} this
44772      */
44773     updateRecord : function(record){
44774         record.beginEdit();
44775         var fs = record.fields;
44776         fs.each(function(f){
44777             var field = this.findField(f.name);
44778             if(field){
44779                 record.set(f.name, field.getValue());
44780             }
44781         }, this);
44782         record.endEdit();
44783         return this;
44784     },
44785
44786     /**
44787      * Loads an Roo.data.Record into this form.
44788      * @param {Record} record The record to load
44789      * @return {BasicForm} this
44790      */
44791     loadRecord : function(record){
44792         this.setValues(record.data);
44793         return this;
44794     },
44795
44796     // private
44797     beforeAction : function(action){
44798         var o = action.options;
44799         
44800        
44801         if(this.waitMsgTarget === true){
44802             this.el.mask(o.waitMsg || "Sending", 'x-mask-loading');
44803         }else if(this.waitMsgTarget){
44804             this.waitMsgTarget = Roo.get(this.waitMsgTarget);
44805             this.waitMsgTarget.mask(o.waitMsg || "Sending", 'x-mask-loading');
44806         }else {
44807             Roo.MessageBox.wait(o.waitMsg || "Sending", o.waitTitle || this.waitTitle || 'Please Wait...');
44808         }
44809          
44810     },
44811
44812     // private
44813     afterAction : function(action, success){
44814         this.activeAction = null;
44815         var o = action.options;
44816         
44817         if(this.waitMsgTarget === true){
44818             this.el.unmask();
44819         }else if(this.waitMsgTarget){
44820             this.waitMsgTarget.unmask();
44821         }else{
44822             Roo.MessageBox.updateProgress(1);
44823             Roo.MessageBox.hide();
44824         }
44825          
44826         if(success){
44827             if(o.reset){
44828                 this.reset();
44829             }
44830             Roo.callback(o.success, o.scope, [this, action]);
44831             this.fireEvent('actioncomplete', this, action);
44832             
44833         }else{
44834             
44835             // failure condition..
44836             // we have a scenario where updates need confirming.
44837             // eg. if a locking scenario exists..
44838             // we look for { errors : { needs_confirm : true }} in the response.
44839             if (
44840                 (typeof(action.result) != 'undefined')  &&
44841                 (typeof(action.result.errors) != 'undefined')  &&
44842                 (typeof(action.result.errors.needs_confirm) != 'undefined')
44843            ){
44844                 var _t = this;
44845                 Roo.MessageBox.confirm(
44846                     "Change requires confirmation",
44847                     action.result.errorMsg,
44848                     function(r) {
44849                         if (r != 'yes') {
44850                             return;
44851                         }
44852                         _t.doAction('submit', { params :  { _submit_confirmed : 1 } }  );
44853                     }
44854                     
44855                 );
44856                 
44857                 
44858                 
44859                 return;
44860             }
44861             
44862             Roo.callback(o.failure, o.scope, [this, action]);
44863             // show an error message if no failed handler is set..
44864             if (!this.hasListener('actionfailed')) {
44865                 Roo.MessageBox.alert("Error",
44866                     (typeof(action.result) != 'undefined' && typeof(action.result.errorMsg) != 'undefined') ?
44867                         action.result.errorMsg :
44868                         "Saving Failed, please check your entries or try again"
44869                 );
44870             }
44871             
44872             this.fireEvent('actionfailed', this, action);
44873         }
44874         
44875     },
44876
44877     /**
44878      * Find a Roo.form.Field in this form by id, dataIndex, name or hiddenName
44879      * @param {String} id The value to search for
44880      * @return Field
44881      */
44882     findField : function(id){
44883         var field = this.items.get(id);
44884         if(!field){
44885             this.items.each(function(f){
44886                 if(f.isFormField && (f.dataIndex == id || f.id == id || f.getName() == id)){
44887                     field = f;
44888                     return false;
44889                 }
44890             });
44891         }
44892         return field || null;
44893     },
44894
44895     /**
44896      * Add a secondary form to this one, 
44897      * Used to provide tabbed forms. One form is primary, with hidden values 
44898      * which mirror the elements from the other forms.
44899      * 
44900      * @param {Roo.form.Form} form to add.
44901      * 
44902      */
44903     addForm : function(form)
44904     {
44905        
44906         if (this.childForms.indexOf(form) > -1) {
44907             // already added..
44908             return;
44909         }
44910         this.childForms.push(form);
44911         var n = '';
44912         Roo.each(form.allItems, function (fe) {
44913             
44914             n = typeof(fe.getName) == 'undefined' ? fe.name : fe.getName();
44915             if (this.findField(n)) { // already added..
44916                 return;
44917             }
44918             var add = new Roo.form.Hidden({
44919                 name : n
44920             });
44921             add.render(this.el);
44922             
44923             this.add( add );
44924         }, this);
44925         
44926     },
44927     /**
44928      * Mark fields in this form invalid in bulk.
44929      * @param {Array/Object} errors Either an array in the form [{id:'fieldId', msg:'The message'},...] or an object hash of {id: msg, id2: msg2}
44930      * @return {BasicForm} this
44931      */
44932     markInvalid : function(errors){
44933         if(errors instanceof Array){
44934             for(var i = 0, len = errors.length; i < len; i++){
44935                 var fieldError = errors[i];
44936                 var f = this.findField(fieldError.id);
44937                 if(f){
44938                     f.markInvalid(fieldError.msg);
44939                 }
44940             }
44941         }else{
44942             var field, id;
44943             for(id in errors){
44944                 if(typeof errors[id] != 'function' && (field = this.findField(id))){
44945                     field.markInvalid(errors[id]);
44946                 }
44947             }
44948         }
44949         Roo.each(this.childForms || [], function (f) {
44950             f.markInvalid(errors);
44951         });
44952         
44953         return this;
44954     },
44955
44956     /**
44957      * Set values for fields in this form in bulk.
44958      * @param {Array/Object} values Either an array in the form [{id:'fieldId', value:'foo'},...] or an object hash of {id: value, id2: value2}
44959      * @return {BasicForm} this
44960      */
44961     setValues : function(values){
44962         if(values instanceof Array){ // array of objects
44963             for(var i = 0, len = values.length; i < len; i++){
44964                 var v = values[i];
44965                 var f = this.findField(v.id);
44966                 if(f){
44967                     f.setValue(v.value);
44968                     if(this.trackResetOnLoad){
44969                         f.originalValue = f.getValue();
44970                     }
44971                 }
44972             }
44973         }else{ // object hash
44974             var field, id;
44975             for(id in values){
44976                 if(typeof values[id] != 'function' && (field = this.findField(id))){
44977                     
44978                     if (field.setFromData && 
44979                         field.valueField && 
44980                         field.displayField &&
44981                         // combos' with local stores can 
44982                         // be queried via setValue()
44983                         // to set their value..
44984                         (field.store && !field.store.isLocal)
44985                         ) {
44986                         // it's a combo
44987                         var sd = { };
44988                         sd[field.valueField] = typeof(values[field.hiddenName]) == 'undefined' ? '' : values[field.hiddenName];
44989                         sd[field.displayField] = typeof(values[field.name]) == 'undefined' ? '' : values[field.name];
44990                         field.setFromData(sd);
44991                         
44992                     } else {
44993                         field.setValue(values[id]);
44994                     }
44995                     
44996                     
44997                     if(this.trackResetOnLoad){
44998                         field.originalValue = field.getValue();
44999                     }
45000                 }
45001             }
45002         }
45003          
45004         Roo.each(this.childForms || [], function (f) {
45005             f.setValues(values);
45006         });
45007                 
45008         return this;
45009     },
45010
45011     /**
45012      * Returns the fields in this form as an object with key/value pairs. If multiple fields exist with the same name
45013      * they are returned as an array.
45014      * @param {Boolean} asString
45015      * @return {Object}
45016      */
45017     getValues : function(asString){
45018         if (this.childForms) {
45019             // copy values from the child forms
45020             Roo.each(this.childForms, function (f) {
45021                 this.setValues(f.getValues());
45022             }, this);
45023         }
45024         
45025         
45026         
45027         var fs = Roo.lib.Ajax.serializeForm(this.el.dom);
45028         if(asString === true){
45029             return fs;
45030         }
45031         return Roo.urlDecode(fs);
45032     },
45033     
45034     /**
45035      * Returns the fields in this form as an object with key/value pairs. 
45036      * This differs from getValues as it calls getValue on each child item, rather than using dom data.
45037      * @return {Object}
45038      */
45039     getFieldValues : function(with_hidden)
45040     {
45041         if (this.childForms) {
45042             // copy values from the child forms
45043             // should this call getFieldValues - probably not as we do not currently copy
45044             // hidden fields when we generate..
45045             Roo.each(this.childForms, function (f) {
45046                 this.setValues(f.getValues());
45047             }, this);
45048         }
45049         
45050         var ret = {};
45051         this.items.each(function(f){
45052             if (!f.getName()) {
45053                 return;
45054             }
45055             var v = f.getValue();
45056             if (f.inputType =='radio') {
45057                 if (typeof(ret[f.getName()]) == 'undefined') {
45058                     ret[f.getName()] = ''; // empty..
45059                 }
45060                 
45061                 if (!f.el.dom.checked) {
45062                     return;
45063                     
45064                 }
45065                 v = f.el.dom.value;
45066                 
45067             }
45068             
45069             // not sure if this supported any more..
45070             if ((typeof(v) == 'object') && f.getRawValue) {
45071                 v = f.getRawValue() ; // dates..
45072             }
45073             // combo boxes where name != hiddenName...
45074             if (f.name != f.getName()) {
45075                 ret[f.name] = f.getRawValue();
45076             }
45077             ret[f.getName()] = v;
45078         });
45079         
45080         return ret;
45081     },
45082
45083     /**
45084      * Clears all invalid messages in this form.
45085      * @return {BasicForm} this
45086      */
45087     clearInvalid : function(){
45088         this.items.each(function(f){
45089            f.clearInvalid();
45090         });
45091         
45092         Roo.each(this.childForms || [], function (f) {
45093             f.clearInvalid();
45094         });
45095         
45096         
45097         return this;
45098     },
45099
45100     /**
45101      * Resets this form.
45102      * @return {BasicForm} this
45103      */
45104     reset : function(){
45105         this.items.each(function(f){
45106             f.reset();
45107         });
45108         
45109         Roo.each(this.childForms || [], function (f) {
45110             f.reset();
45111         });
45112        
45113         
45114         return this;
45115     },
45116
45117     /**
45118      * Add Roo.form components to this form.
45119      * @param {Field} field1
45120      * @param {Field} field2 (optional)
45121      * @param {Field} etc (optional)
45122      * @return {BasicForm} this
45123      */
45124     add : function(){
45125         this.items.addAll(Array.prototype.slice.call(arguments, 0));
45126         return this;
45127     },
45128
45129
45130     /**
45131      * Removes a field from the items collection (does NOT remove its markup).
45132      * @param {Field} field
45133      * @return {BasicForm} this
45134      */
45135     remove : function(field){
45136         this.items.remove(field);
45137         return this;
45138     },
45139
45140     /**
45141      * Looks at the fields in this form, checks them for an id attribute,
45142      * and calls applyTo on the existing dom element with that id.
45143      * @return {BasicForm} this
45144      */
45145     render : function(){
45146         this.items.each(function(f){
45147             if(f.isFormField && !f.rendered && document.getElementById(f.id)){ // if the element exists
45148                 f.applyTo(f.id);
45149             }
45150         });
45151         return this;
45152     },
45153
45154     /**
45155      * Calls {@link Ext#apply} for all fields in this form with the passed object.
45156      * @param {Object} values
45157      * @return {BasicForm} this
45158      */
45159     applyToFields : function(o){
45160         this.items.each(function(f){
45161            Roo.apply(f, o);
45162         });
45163         return this;
45164     },
45165
45166     /**
45167      * Calls {@link Ext#applyIf} for all field in this form with the passed object.
45168      * @param {Object} values
45169      * @return {BasicForm} this
45170      */
45171     applyIfToFields : function(o){
45172         this.items.each(function(f){
45173            Roo.applyIf(f, o);
45174         });
45175         return this;
45176     }
45177 });
45178
45179 // back compat
45180 Roo.BasicForm = Roo.form.BasicForm;/*
45181  * Based on:
45182  * Ext JS Library 1.1.1
45183  * Copyright(c) 2006-2007, Ext JS, LLC.
45184  *
45185  * Originally Released Under LGPL - original licence link has changed is not relivant.
45186  *
45187  * Fork - LGPL
45188  * <script type="text/javascript">
45189  */
45190
45191 /**
45192  * @class Roo.form.Form
45193  * @extends Roo.form.BasicForm
45194  * Adds the ability to dynamically render forms with JavaScript to {@link Roo.form.BasicForm}.
45195  * @constructor
45196  * @param {Object} config Configuration options
45197  */
45198 Roo.form.Form = function(config){
45199     var xitems =  [];
45200     if (config.items) {
45201         xitems = config.items;
45202         delete config.items;
45203     }
45204    
45205     
45206     Roo.form.Form.superclass.constructor.call(this, null, config);
45207     this.url = this.url || this.action;
45208     if(!this.root){
45209         this.root = new Roo.form.Layout(Roo.applyIf({
45210             id: Roo.id()
45211         }, config));
45212     }
45213     this.active = this.root;
45214     /**
45215      * Array of all the buttons that have been added to this form via {@link addButton}
45216      * @type Array
45217      */
45218     this.buttons = [];
45219     this.allItems = [];
45220     this.addEvents({
45221         /**
45222          * @event clientvalidation
45223          * If the monitorValid config option is true, this event fires repetitively to notify of valid state
45224          * @param {Form} this
45225          * @param {Boolean} valid true if the form has passed client-side validation
45226          */
45227         clientvalidation: true,
45228         /**
45229          * @event rendered
45230          * Fires when the form is rendered
45231          * @param {Roo.form.Form} form
45232          */
45233         rendered : true
45234     });
45235     
45236     if (this.progressUrl) {
45237             // push a hidden field onto the list of fields..
45238             this.addxtype( {
45239                     xns: Roo.form, 
45240                     xtype : 'Hidden', 
45241                     name : 'UPLOAD_IDENTIFIER' 
45242             });
45243         }
45244         
45245     
45246     Roo.each(xitems, this.addxtype, this);
45247     
45248     
45249     
45250 };
45251
45252 Roo.extend(Roo.form.Form, Roo.form.BasicForm, {
45253     /**
45254      * @cfg {Number} labelWidth The width of labels. This property cascades to child containers.
45255      */
45256     /**
45257      * @cfg {String} itemCls A css class to apply to the x-form-item of fields. This property cascades to child containers.
45258      */
45259     /**
45260      * @cfg {String} buttonAlign Valid values are "left," "center" and "right" (defaults to "center")
45261      */
45262     buttonAlign:'center',
45263
45264     /**
45265      * @cfg {Number} minButtonWidth Minimum width of all buttons in pixels (defaults to 75)
45266      */
45267     minButtonWidth:75,
45268
45269     /**
45270      * @cfg {String} labelAlign Valid values are "left," "top" and "right" (defaults to "left").
45271      * This property cascades to child containers if not set.
45272      */
45273     labelAlign:'left',
45274
45275     /**
45276      * @cfg {Boolean} monitorValid If true the form monitors its valid state <b>client-side</b> and
45277      * fires a looping event with that state. This is required to bind buttons to the valid
45278      * state using the config value formBind:true on the button.
45279      */
45280     monitorValid : false,
45281
45282     /**
45283      * @cfg {Number} monitorPoll The milliseconds to poll valid state, ignored if monitorValid is not true (defaults to 200)
45284      */
45285     monitorPoll : 200,
45286     
45287     /**
45288      * @cfg {String} progressUrl - Url to return progress data 
45289      */
45290     
45291     progressUrl : false,
45292   
45293     /**
45294      * Opens a new {@link Roo.form.Column} container in the layout stack. If fields are passed after the config, the
45295      * fields are added and the column is closed. If no fields are passed the column remains open
45296      * until end() is called.
45297      * @param {Object} config The config to pass to the column
45298      * @param {Field} field1 (optional)
45299      * @param {Field} field2 (optional)
45300      * @param {Field} etc (optional)
45301      * @return Column The column container object
45302      */
45303     column : function(c){
45304         var col = new Roo.form.Column(c);
45305         this.start(col);
45306         if(arguments.length > 1){ // duplicate code required because of Opera
45307             this.add.apply(this, Array.prototype.slice.call(arguments, 1));
45308             this.end();
45309         }
45310         return col;
45311     },
45312
45313     /**
45314      * Opens a new {@link Roo.form.FieldSet} container in the layout stack. If fields are passed after the config, the
45315      * fields are added and the fieldset is closed. If no fields are passed the fieldset remains open
45316      * until end() is called.
45317      * @param {Object} config The config to pass to the fieldset
45318      * @param {Field} field1 (optional)
45319      * @param {Field} field2 (optional)
45320      * @param {Field} etc (optional)
45321      * @return FieldSet The fieldset container object
45322      */
45323     fieldset : function(c){
45324         var fs = new Roo.form.FieldSet(c);
45325         this.start(fs);
45326         if(arguments.length > 1){ // duplicate code required because of Opera
45327             this.add.apply(this, Array.prototype.slice.call(arguments, 1));
45328             this.end();
45329         }
45330         return fs;
45331     },
45332
45333     /**
45334      * Opens a new {@link Roo.form.Layout} container in the layout stack. If fields are passed after the config, the
45335      * fields are added and the container is closed. If no fields are passed the container remains open
45336      * until end() is called.
45337      * @param {Object} config The config to pass to the Layout
45338      * @param {Field} field1 (optional)
45339      * @param {Field} field2 (optional)
45340      * @param {Field} etc (optional)
45341      * @return Layout The container object
45342      */
45343     container : function(c){
45344         var l = new Roo.form.Layout(c);
45345         this.start(l);
45346         if(arguments.length > 1){ // duplicate code required because of Opera
45347             this.add.apply(this, Array.prototype.slice.call(arguments, 1));
45348             this.end();
45349         }
45350         return l;
45351     },
45352
45353     /**
45354      * Opens the passed container in the layout stack. The container can be any {@link Roo.form.Layout} or subclass.
45355      * @param {Object} container A Roo.form.Layout or subclass of Layout
45356      * @return {Form} this
45357      */
45358     start : function(c){
45359         // cascade label info
45360         Roo.applyIf(c, {'labelAlign': this.active.labelAlign, 'labelWidth': this.active.labelWidth, 'itemCls': this.active.itemCls});
45361         this.active.stack.push(c);
45362         c.ownerCt = this.active;
45363         this.active = c;
45364         return this;
45365     },
45366
45367     /**
45368      * Closes the current open container
45369      * @return {Form} this
45370      */
45371     end : function(){
45372         if(this.active == this.root){
45373             return this;
45374         }
45375         this.active = this.active.ownerCt;
45376         return this;
45377     },
45378
45379     /**
45380      * Add Roo.form components to the current open container (e.g. column, fieldset, etc.).  Fields added via this method
45381      * can also be passed with an additional property of fieldLabel, which if supplied, will provide the text to display
45382      * as the label of the field.
45383      * @param {Field} field1
45384      * @param {Field} field2 (optional)
45385      * @param {Field} etc. (optional)
45386      * @return {Form} this
45387      */
45388     add : function(){
45389         this.active.stack.push.apply(this.active.stack, arguments);
45390         this.allItems.push.apply(this.allItems,arguments);
45391         var r = [];
45392         for(var i = 0, a = arguments, len = a.length; i < len; i++) {
45393             if(a[i].isFormField){
45394                 r.push(a[i]);
45395             }
45396         }
45397         if(r.length > 0){
45398             Roo.form.Form.superclass.add.apply(this, r);
45399         }
45400         return this;
45401     },
45402     
45403
45404     
45405     
45406     
45407      /**
45408      * Find any element that has been added to a form, using it's ID or name
45409      * This can include framesets, columns etc. along with regular fields..
45410      * @param {String} id - id or name to find.
45411      
45412      * @return {Element} e - or false if nothing found.
45413      */
45414     findbyId : function(id)
45415     {
45416         var ret = false;
45417         if (!id) {
45418             return ret;
45419         }
45420         Roo.each(this.allItems, function(f){
45421             if (f.id == id || f.name == id ){
45422                 ret = f;
45423                 return false;
45424             }
45425         });
45426         return ret;
45427     },
45428
45429     
45430     
45431     /**
45432      * Render this form into the passed container. This should only be called once!
45433      * @param {String/HTMLElement/Element} container The element this component should be rendered into
45434      * @return {Form} this
45435      */
45436     render : function(ct)
45437     {
45438         
45439         
45440         
45441         ct = Roo.get(ct);
45442         var o = this.autoCreate || {
45443             tag: 'form',
45444             method : this.method || 'POST',
45445             id : this.id || Roo.id()
45446         };
45447         this.initEl(ct.createChild(o));
45448
45449         this.root.render(this.el);
45450         
45451        
45452              
45453         this.items.each(function(f){
45454             f.render('x-form-el-'+f.id);
45455         });
45456
45457         if(this.buttons.length > 0){
45458             // tables are required to maintain order and for correct IE layout
45459             var tb = this.el.createChild({cls:'x-form-btns-ct', cn: {
45460                 cls:"x-form-btns x-form-btns-"+this.buttonAlign,
45461                 html:'<table cellspacing="0"><tbody><tr></tr></tbody></table><div class="x-clear"></div>'
45462             }}, null, true);
45463             var tr = tb.getElementsByTagName('tr')[0];
45464             for(var i = 0, len = this.buttons.length; i < len; i++) {
45465                 var b = this.buttons[i];
45466                 var td = document.createElement('td');
45467                 td.className = 'x-form-btn-td';
45468                 b.render(tr.appendChild(td));
45469             }
45470         }
45471         if(this.monitorValid){ // initialize after render
45472             this.startMonitoring();
45473         }
45474         this.fireEvent('rendered', this);
45475         return this;
45476     },
45477
45478     /**
45479      * Adds a button to the footer of the form - this <b>must</b> be called before the form is rendered.
45480      * @param {String/Object} config A string becomes the button text, an object can either be a Button config
45481      * object or a valid Roo.DomHelper element config
45482      * @param {Function} handler The function called when the button is clicked
45483      * @param {Object} scope (optional) The scope of the handler function
45484      * @return {Roo.Button}
45485      */
45486     addButton : function(config, handler, scope){
45487         var bc = {
45488             handler: handler,
45489             scope: scope,
45490             minWidth: this.minButtonWidth,
45491             hideParent:true
45492         };
45493         if(typeof config == "string"){
45494             bc.text = config;
45495         }else{
45496             Roo.apply(bc, config);
45497         }
45498         var btn = new Roo.Button(null, bc);
45499         this.buttons.push(btn);
45500         return btn;
45501     },
45502
45503      /**
45504      * Adds a series of form elements (using the xtype property as the factory method.
45505      * Valid xtypes are:  TextField, TextArea .... Button, Layout, FieldSet, Column, (and 'end' to close a block)
45506      * @param {Object} config 
45507      */
45508     
45509     addxtype : function()
45510     {
45511         var ar = Array.prototype.slice.call(arguments, 0);
45512         var ret = false;
45513         for(var i = 0; i < ar.length; i++) {
45514             if (!ar[i]) {
45515                 continue; // skip -- if this happends something invalid got sent, we 
45516                 // should ignore it, as basically that interface element will not show up
45517                 // and that should be pretty obvious!!
45518             }
45519             
45520             if (Roo.form[ar[i].xtype]) {
45521                 ar[i].form = this;
45522                 var fe = Roo.factory(ar[i], Roo.form);
45523                 if (!ret) {
45524                     ret = fe;
45525                 }
45526                 fe.form = this;
45527                 if (fe.store) {
45528                     fe.store.form = this;
45529                 }
45530                 if (fe.isLayout) {  
45531                          
45532                     this.start(fe);
45533                     this.allItems.push(fe);
45534                     if (fe.items && fe.addxtype) {
45535                         fe.addxtype.apply(fe, fe.items);
45536                         delete fe.items;
45537                     }
45538                      this.end();
45539                     continue;
45540                 }
45541                 
45542                 
45543                  
45544                 this.add(fe);
45545               //  console.log('adding ' + ar[i].xtype);
45546             }
45547             if (ar[i].xtype == 'Button') {  
45548                 //console.log('adding button');
45549                 //console.log(ar[i]);
45550                 this.addButton(ar[i]);
45551                 this.allItems.push(fe);
45552                 continue;
45553             }
45554             
45555             if (ar[i].xtype == 'end') { // so we can add fieldsets... / layout etc.
45556                 alert('end is not supported on xtype any more, use items');
45557             //    this.end();
45558             //    //console.log('adding end');
45559             }
45560             
45561         }
45562         return ret;
45563     },
45564     
45565     /**
45566      * Starts monitoring of the valid state of this form. Usually this is done by passing the config
45567      * option "monitorValid"
45568      */
45569     startMonitoring : function(){
45570         if(!this.bound){
45571             this.bound = true;
45572             Roo.TaskMgr.start({
45573                 run : this.bindHandler,
45574                 interval : this.monitorPoll || 200,
45575                 scope: this
45576             });
45577         }
45578     },
45579
45580     /**
45581      * Stops monitoring of the valid state of this form
45582      */
45583     stopMonitoring : function(){
45584         this.bound = false;
45585     },
45586
45587     // private
45588     bindHandler : function(){
45589         if(!this.bound){
45590             return false; // stops binding
45591         }
45592         var valid = true;
45593         this.items.each(function(f){
45594             if(!f.isValid(true)){
45595                 valid = false;
45596                 return false;
45597             }
45598         });
45599         for(var i = 0, len = this.buttons.length; i < len; i++){
45600             var btn = this.buttons[i];
45601             if(btn.formBind === true && btn.disabled === valid){
45602                 btn.setDisabled(!valid);
45603             }
45604         }
45605         this.fireEvent('clientvalidation', this, valid);
45606     }
45607     
45608     
45609     
45610     
45611     
45612     
45613     
45614     
45615 });
45616
45617
45618 // back compat
45619 Roo.Form = Roo.form.Form;
45620 /*
45621  * Based on:
45622  * Ext JS Library 1.1.1
45623  * Copyright(c) 2006-2007, Ext JS, LLC.
45624  *
45625  * Originally Released Under LGPL - original licence link has changed is not relivant.
45626  *
45627  * Fork - LGPL
45628  * <script type="text/javascript">
45629  */
45630
45631 // as we use this in bootstrap.
45632 Roo.namespace('Roo.form');
45633  /**
45634  * @class Roo.form.Action
45635  * Internal Class used to handle form actions
45636  * @constructor
45637  * @param {Roo.form.BasicForm} el The form element or its id
45638  * @param {Object} config Configuration options
45639  */
45640
45641  
45642  
45643 // define the action interface
45644 Roo.form.Action = function(form, options){
45645     this.form = form;
45646     this.options = options || {};
45647 };
45648 /**
45649  * Client Validation Failed
45650  * @const 
45651  */
45652 Roo.form.Action.CLIENT_INVALID = 'client';
45653 /**
45654  * Server Validation Failed
45655  * @const 
45656  */
45657 Roo.form.Action.SERVER_INVALID = 'server';
45658  /**
45659  * Connect to Server Failed
45660  * @const 
45661  */
45662 Roo.form.Action.CONNECT_FAILURE = 'connect';
45663 /**
45664  * Reading Data from Server Failed
45665  * @const 
45666  */
45667 Roo.form.Action.LOAD_FAILURE = 'load';
45668
45669 Roo.form.Action.prototype = {
45670     type : 'default',
45671     failureType : undefined,
45672     response : undefined,
45673     result : undefined,
45674
45675     // interface method
45676     run : function(options){
45677
45678     },
45679
45680     // interface method
45681     success : function(response){
45682
45683     },
45684
45685     // interface method
45686     handleResponse : function(response){
45687
45688     },
45689
45690     // default connection failure
45691     failure : function(response){
45692         
45693         this.response = response;
45694         this.failureType = Roo.form.Action.CONNECT_FAILURE;
45695         this.form.afterAction(this, false);
45696     },
45697
45698     processResponse : function(response){
45699         this.response = response;
45700         if(!response.responseText){
45701             return true;
45702         }
45703         this.result = this.handleResponse(response);
45704         return this.result;
45705     },
45706
45707     // utility functions used internally
45708     getUrl : function(appendParams){
45709         var url = this.options.url || this.form.url || this.form.el.dom.action;
45710         if(appendParams){
45711             var p = this.getParams();
45712             if(p){
45713                 url += (url.indexOf('?') != -1 ? '&' : '?') + p;
45714             }
45715         }
45716         return url;
45717     },
45718
45719     getMethod : function(){
45720         return (this.options.method || this.form.method || this.form.el.dom.method || 'POST').toUpperCase();
45721     },
45722
45723     getParams : function(){
45724         var bp = this.form.baseParams;
45725         var p = this.options.params;
45726         if(p){
45727             if(typeof p == "object"){
45728                 p = Roo.urlEncode(Roo.applyIf(p, bp));
45729             }else if(typeof p == 'string' && bp){
45730                 p += '&' + Roo.urlEncode(bp);
45731             }
45732         }else if(bp){
45733             p = Roo.urlEncode(bp);
45734         }
45735         return p;
45736     },
45737
45738     createCallback : function(){
45739         return {
45740             success: this.success,
45741             failure: this.failure,
45742             scope: this,
45743             timeout: (this.form.timeout*1000),
45744             upload: this.form.fileUpload ? this.success : undefined
45745         };
45746     }
45747 };
45748
45749 Roo.form.Action.Submit = function(form, options){
45750     Roo.form.Action.Submit.superclass.constructor.call(this, form, options);
45751 };
45752
45753 Roo.extend(Roo.form.Action.Submit, Roo.form.Action, {
45754     type : 'submit',
45755
45756     haveProgress : false,
45757     uploadComplete : false,
45758     
45759     // uploadProgress indicator.
45760     uploadProgress : function()
45761     {
45762         if (!this.form.progressUrl) {
45763             return;
45764         }
45765         
45766         if (!this.haveProgress) {
45767             Roo.MessageBox.progress("Uploading", "Uploading");
45768         }
45769         if (this.uploadComplete) {
45770            Roo.MessageBox.hide();
45771            return;
45772         }
45773         
45774         this.haveProgress = true;
45775    
45776         var uid = this.form.findField('UPLOAD_IDENTIFIER').getValue();
45777         
45778         var c = new Roo.data.Connection();
45779         c.request({
45780             url : this.form.progressUrl,
45781             params: {
45782                 id : uid
45783             },
45784             method: 'GET',
45785             success : function(req){
45786                //console.log(data);
45787                 var rdata = false;
45788                 var edata;
45789                 try  {
45790                    rdata = Roo.decode(req.responseText)
45791                 } catch (e) {
45792                     Roo.log("Invalid data from server..");
45793                     Roo.log(edata);
45794                     return;
45795                 }
45796                 if (!rdata || !rdata.success) {
45797                     Roo.log(rdata);
45798                     Roo.MessageBox.alert(Roo.encode(rdata));
45799                     return;
45800                 }
45801                 var data = rdata.data;
45802                 
45803                 if (this.uploadComplete) {
45804                    Roo.MessageBox.hide();
45805                    return;
45806                 }
45807                    
45808                 if (data){
45809                     Roo.MessageBox.updateProgress(data.bytes_uploaded/data.bytes_total,
45810                        Math.floor((data.bytes_total - data.bytes_uploaded)/1000) + 'k remaining'
45811                     );
45812                 }
45813                 this.uploadProgress.defer(2000,this);
45814             },
45815        
45816             failure: function(data) {
45817                 Roo.log('progress url failed ');
45818                 Roo.log(data);
45819             },
45820             scope : this
45821         });
45822            
45823     },
45824     
45825     
45826     run : function()
45827     {
45828         // run get Values on the form, so it syncs any secondary forms.
45829         this.form.getValues();
45830         
45831         var o = this.options;
45832         var method = this.getMethod();
45833         var isPost = method == 'POST';
45834         if(o.clientValidation === false || this.form.isValid()){
45835             
45836             if (this.form.progressUrl) {
45837                 this.form.findField('UPLOAD_IDENTIFIER').setValue(
45838                     (new Date() * 1) + '' + Math.random());
45839                     
45840             } 
45841             
45842             
45843             Roo.Ajax.request(Roo.apply(this.createCallback(), {
45844                 form:this.form.el.dom,
45845                 url:this.getUrl(!isPost),
45846                 method: method,
45847                 params:isPost ? this.getParams() : null,
45848                 isUpload: this.form.fileUpload
45849             }));
45850             
45851             this.uploadProgress();
45852
45853         }else if (o.clientValidation !== false){ // client validation failed
45854             this.failureType = Roo.form.Action.CLIENT_INVALID;
45855             this.form.afterAction(this, false);
45856         }
45857     },
45858
45859     success : function(response)
45860     {
45861         this.uploadComplete= true;
45862         if (this.haveProgress) {
45863             Roo.MessageBox.hide();
45864         }
45865         
45866         
45867         var result = this.processResponse(response);
45868         if(result === true || result.success){
45869             this.form.afterAction(this, true);
45870             return;
45871         }
45872         if(result.errors){
45873             this.form.markInvalid(result.errors);
45874             this.failureType = Roo.form.Action.SERVER_INVALID;
45875         }
45876         this.form.afterAction(this, false);
45877     },
45878     failure : function(response)
45879     {
45880         this.uploadComplete= true;
45881         if (this.haveProgress) {
45882             Roo.MessageBox.hide();
45883         }
45884         
45885         this.response = response;
45886         this.failureType = Roo.form.Action.CONNECT_FAILURE;
45887         this.form.afterAction(this, false);
45888     },
45889     
45890     handleResponse : function(response){
45891         if(this.form.errorReader){
45892             var rs = this.form.errorReader.read(response);
45893             var errors = [];
45894             if(rs.records){
45895                 for(var i = 0, len = rs.records.length; i < len; i++) {
45896                     var r = rs.records[i];
45897                     errors[i] = r.data;
45898                 }
45899             }
45900             if(errors.length < 1){
45901                 errors = null;
45902             }
45903             return {
45904                 success : rs.success,
45905                 errors : errors
45906             };
45907         }
45908         var ret = false;
45909         try {
45910             ret = Roo.decode(response.responseText);
45911         } catch (e) {
45912             ret = {
45913                 success: false,
45914                 errorMsg: "Failed to read server message: " + (response ? response.responseText : ' - no message'),
45915                 errors : []
45916             };
45917         }
45918         return ret;
45919         
45920     }
45921 });
45922
45923
45924 Roo.form.Action.Load = function(form, options){
45925     Roo.form.Action.Load.superclass.constructor.call(this, form, options);
45926     this.reader = this.form.reader;
45927 };
45928
45929 Roo.extend(Roo.form.Action.Load, Roo.form.Action, {
45930     type : 'load',
45931
45932     run : function(){
45933         
45934         Roo.Ajax.request(Roo.apply(
45935                 this.createCallback(), {
45936                     method:this.getMethod(),
45937                     url:this.getUrl(false),
45938                     params:this.getParams()
45939         }));
45940     },
45941
45942     success : function(response){
45943         
45944         var result = this.processResponse(response);
45945         if(result === true || !result.success || !result.data){
45946             this.failureType = Roo.form.Action.LOAD_FAILURE;
45947             this.form.afterAction(this, false);
45948             return;
45949         }
45950         this.form.clearInvalid();
45951         this.form.setValues(result.data);
45952         this.form.afterAction(this, true);
45953     },
45954
45955     handleResponse : function(response){
45956         if(this.form.reader){
45957             var rs = this.form.reader.read(response);
45958             var data = rs.records && rs.records[0] ? rs.records[0].data : null;
45959             return {
45960                 success : rs.success,
45961                 data : data
45962             };
45963         }
45964         return Roo.decode(response.responseText);
45965     }
45966 });
45967
45968 Roo.form.Action.ACTION_TYPES = {
45969     'load' : Roo.form.Action.Load,
45970     'submit' : Roo.form.Action.Submit
45971 };/*
45972  * Based on:
45973  * Ext JS Library 1.1.1
45974  * Copyright(c) 2006-2007, Ext JS, LLC.
45975  *
45976  * Originally Released Under LGPL - original licence link has changed is not relivant.
45977  *
45978  * Fork - LGPL
45979  * <script type="text/javascript">
45980  */
45981  
45982 /**
45983  * @class Roo.form.Layout
45984  * @extends Roo.Component
45985  * Creates a container for layout and rendering of fields in an {@link Roo.form.Form}.
45986  * @constructor
45987  * @param {Object} config Configuration options
45988  */
45989 Roo.form.Layout = function(config){
45990     var xitems = [];
45991     if (config.items) {
45992         xitems = config.items;
45993         delete config.items;
45994     }
45995     Roo.form.Layout.superclass.constructor.call(this, config);
45996     this.stack = [];
45997     Roo.each(xitems, this.addxtype, this);
45998      
45999 };
46000
46001 Roo.extend(Roo.form.Layout, Roo.Component, {
46002     /**
46003      * @cfg {String/Object} autoCreate
46004      * A DomHelper element spec used to autocreate the layout (defaults to {tag: 'div', cls: 'x-form-ct'})
46005      */
46006     /**
46007      * @cfg {String/Object/Function} style
46008      * A style specification string, e.g. "width:100px", or object in the form {width:"100px"}, or
46009      * a function which returns such a specification.
46010      */
46011     /**
46012      * @cfg {String} labelAlign
46013      * Valid values are "left," "top" and "right" (defaults to "left")
46014      */
46015     /**
46016      * @cfg {Number} labelWidth
46017      * Fixed width in pixels of all field labels (defaults to undefined)
46018      */
46019     /**
46020      * @cfg {Boolean} clear
46021      * True to add a clearing element at the end of this layout, equivalent to CSS clear: both (defaults to true)
46022      */
46023     clear : true,
46024     /**
46025      * @cfg {String} labelSeparator
46026      * The separator to use after field labels (defaults to ':')
46027      */
46028     labelSeparator : ':',
46029     /**
46030      * @cfg {Boolean} hideLabels
46031      * True to suppress the display of field labels in this layout (defaults to false)
46032      */
46033     hideLabels : false,
46034
46035     // private
46036     defaultAutoCreate : {tag: 'div', cls: 'x-form-ct'},
46037     
46038     isLayout : true,
46039     
46040     // private
46041     onRender : function(ct, position){
46042         if(this.el){ // from markup
46043             this.el = Roo.get(this.el);
46044         }else {  // generate
46045             var cfg = this.getAutoCreate();
46046             this.el = ct.createChild(cfg, position);
46047         }
46048         if(this.style){
46049             this.el.applyStyles(this.style);
46050         }
46051         if(this.labelAlign){
46052             this.el.addClass('x-form-label-'+this.labelAlign);
46053         }
46054         if(this.hideLabels){
46055             this.labelStyle = "display:none";
46056             this.elementStyle = "padding-left:0;";
46057         }else{
46058             if(typeof this.labelWidth == 'number'){
46059                 this.labelStyle = "width:"+this.labelWidth+"px;";
46060                 this.elementStyle = "padding-left:"+((this.labelWidth+(typeof this.labelPad == 'number' ? this.labelPad : 5))+'px')+";";
46061             }
46062             if(this.labelAlign == 'top'){
46063                 this.labelStyle = "width:auto;";
46064                 this.elementStyle = "padding-left:0;";
46065             }
46066         }
46067         var stack = this.stack;
46068         var slen = stack.length;
46069         if(slen > 0){
46070             if(!this.fieldTpl){
46071                 var t = new Roo.Template(
46072                     '<div class="x-form-item {5}">',
46073                         '<label for="{0}" style="{2}">{1}{4}</label>',
46074                         '<div class="x-form-element" id="x-form-el-{0}" style="{3}">',
46075                         '</div>',
46076                     '</div><div class="x-form-clear-left"></div>'
46077                 );
46078                 t.disableFormats = true;
46079                 t.compile();
46080                 Roo.form.Layout.prototype.fieldTpl = t;
46081             }
46082             for(var i = 0; i < slen; i++) {
46083                 if(stack[i].isFormField){
46084                     this.renderField(stack[i]);
46085                 }else{
46086                     this.renderComponent(stack[i]);
46087                 }
46088             }
46089         }
46090         if(this.clear){
46091             this.el.createChild({cls:'x-form-clear'});
46092         }
46093     },
46094
46095     // private
46096     renderField : function(f){
46097         f.fieldEl = Roo.get(this.fieldTpl.append(this.el, [
46098                f.id, //0
46099                f.fieldLabel, //1
46100                f.labelStyle||this.labelStyle||'', //2
46101                this.elementStyle||'', //3
46102                typeof f.labelSeparator == 'undefined' ? this.labelSeparator : f.labelSeparator, //4
46103                f.itemCls||this.itemCls||''  //5
46104        ], true).getPrevSibling());
46105     },
46106
46107     // private
46108     renderComponent : function(c){
46109         c.render(c.isLayout ? this.el : this.el.createChild());    
46110     },
46111     /**
46112      * Adds a object form elements (using the xtype property as the factory method.)
46113      * Valid xtypes are:  TextField, TextArea .... Button, Layout, FieldSet, Column
46114      * @param {Object} config 
46115      */
46116     addxtype : function(o)
46117     {
46118         // create the lement.
46119         o.form = this.form;
46120         var fe = Roo.factory(o, Roo.form);
46121         this.form.allItems.push(fe);
46122         this.stack.push(fe);
46123         
46124         if (fe.isFormField) {
46125             this.form.items.add(fe);
46126         }
46127          
46128         return fe;
46129     }
46130 });
46131
46132 /**
46133  * @class Roo.form.Column
46134  * @extends Roo.form.Layout
46135  * Creates a column container for layout and rendering of fields in an {@link Roo.form.Form}.
46136  * @constructor
46137  * @param {Object} config Configuration options
46138  */
46139 Roo.form.Column = function(config){
46140     Roo.form.Column.superclass.constructor.call(this, config);
46141 };
46142
46143 Roo.extend(Roo.form.Column, Roo.form.Layout, {
46144     /**
46145      * @cfg {Number/String} width
46146      * The fixed width of the column in pixels or CSS value (defaults to "auto")
46147      */
46148     /**
46149      * @cfg {String/Object} autoCreate
46150      * A DomHelper element spec used to autocreate the column (defaults to {tag: 'div', cls: 'x-form-ct x-form-column'})
46151      */
46152
46153     // private
46154     defaultAutoCreate : {tag: 'div', cls: 'x-form-ct x-form-column'},
46155
46156     // private
46157     onRender : function(ct, position){
46158         Roo.form.Column.superclass.onRender.call(this, ct, position);
46159         if(this.width){
46160             this.el.setWidth(this.width);
46161         }
46162     }
46163 });
46164
46165
46166 /**
46167  * @class Roo.form.Row
46168  * @extends Roo.form.Layout
46169  * Creates a row container for layout and rendering of fields in an {@link Roo.form.Form}.
46170  * @constructor
46171  * @param {Object} config Configuration options
46172  */
46173
46174  
46175 Roo.form.Row = function(config){
46176     Roo.form.Row.superclass.constructor.call(this, config);
46177 };
46178  
46179 Roo.extend(Roo.form.Row, Roo.form.Layout, {
46180       /**
46181      * @cfg {Number/String} width
46182      * The fixed width of the column in pixels or CSS value (defaults to "auto")
46183      */
46184     /**
46185      * @cfg {Number/String} height
46186      * The fixed height of the column in pixels or CSS value (defaults to "auto")
46187      */
46188     defaultAutoCreate : {tag: 'div', cls: 'x-form-ct x-form-row'},
46189     
46190     padWidth : 20,
46191     // private
46192     onRender : function(ct, position){
46193         //console.log('row render');
46194         if(!this.rowTpl){
46195             var t = new Roo.Template(
46196                 '<div class="x-form-item {5}" style="float:left;width:{6}px">',
46197                     '<label for="{0}" style="{2}">{1}{4}</label>',
46198                     '<div class="x-form-element" id="x-form-el-{0}" style="{3}">',
46199                     '</div>',
46200                 '</div>'
46201             );
46202             t.disableFormats = true;
46203             t.compile();
46204             Roo.form.Layout.prototype.rowTpl = t;
46205         }
46206         this.fieldTpl = this.rowTpl;
46207         
46208         //console.log('lw' + this.labelWidth +', la:' + this.labelAlign);
46209         var labelWidth = 100;
46210         
46211         if ((this.labelAlign != 'top')) {
46212             if (typeof this.labelWidth == 'number') {
46213                 labelWidth = this.labelWidth
46214             }
46215             this.padWidth =  20 + labelWidth;
46216             
46217         }
46218         
46219         Roo.form.Column.superclass.onRender.call(this, ct, position);
46220         if(this.width){
46221             this.el.setWidth(this.width);
46222         }
46223         if(this.height){
46224             this.el.setHeight(this.height);
46225         }
46226     },
46227     
46228     // private
46229     renderField : function(f){
46230         f.fieldEl = this.fieldTpl.append(this.el, [
46231                f.id, f.fieldLabel,
46232                f.labelStyle||this.labelStyle||'',
46233                this.elementStyle||'',
46234                typeof f.labelSeparator == 'undefined' ? this.labelSeparator : f.labelSeparator,
46235                f.itemCls||this.itemCls||'',
46236                f.width ? f.width + this.padWidth : 160 + this.padWidth
46237        ],true);
46238     }
46239 });
46240  
46241
46242 /**
46243  * @class Roo.form.FieldSet
46244  * @extends Roo.form.Layout
46245  * Creates a fieldset container for layout and rendering of fields in an {@link Roo.form.Form}.
46246  * @constructor
46247  * @param {Object} config Configuration options
46248  */
46249 Roo.form.FieldSet = function(config){
46250     Roo.form.FieldSet.superclass.constructor.call(this, config);
46251 };
46252
46253 Roo.extend(Roo.form.FieldSet, Roo.form.Layout, {
46254     /**
46255      * @cfg {String} legend
46256      * The text to display as the legend for the FieldSet (defaults to '')
46257      */
46258     /**
46259      * @cfg {String/Object} autoCreate
46260      * A DomHelper element spec used to autocreate the fieldset (defaults to {tag: 'fieldset', cn: {tag:'legend'}})
46261      */
46262
46263     // private
46264     defaultAutoCreate : {tag: 'fieldset', cn: {tag:'legend'}},
46265
46266     // private
46267     onRender : function(ct, position){
46268         Roo.form.FieldSet.superclass.onRender.call(this, ct, position);
46269         if(this.legend){
46270             this.setLegend(this.legend);
46271         }
46272     },
46273
46274     // private
46275     setLegend : function(text){
46276         if(this.rendered){
46277             this.el.child('legend').update(text);
46278         }
46279     }
46280 });/*
46281  * Based on:
46282  * Ext JS Library 1.1.1
46283  * Copyright(c) 2006-2007, Ext JS, LLC.
46284  *
46285  * Originally Released Under LGPL - original licence link has changed is not relivant.
46286  *
46287  * Fork - LGPL
46288  * <script type="text/javascript">
46289  */
46290 /**
46291  * @class Roo.form.VTypes
46292  * Overridable validation definitions. The validations provided are basic and intended to be easily customizable and extended.
46293  * @singleton
46294  */
46295 Roo.form.VTypes = function(){
46296     // closure these in so they are only created once.
46297     var alpha = /^[a-zA-Z_]+$/;
46298     var alphanum = /^[a-zA-Z0-9_]+$/;
46299     var email = /^([\w]+)(.[\w]+)*@([\w-]+\.){1,5}([A-Za-z]){2,4}$/;
46300     var url = /(((https?)|(ftp)):\/\/([\-\w]+\.)+\w{2,3}(\/[%\-\w]+(\.\w{2,})?)*(([\w\-\.\?\\\/+@&#;`~=%!]*)(\.\w{2,})?)*\/?)/i;
46301
46302     // All these messages and functions are configurable
46303     return {
46304         /**
46305          * The function used to validate email addresses
46306          * @param {String} value The email address
46307          */
46308         'email' : function(v){
46309             return email.test(v);
46310         },
46311         /**
46312          * The error text to display when the email validation function returns false
46313          * @type String
46314          */
46315         'emailText' : 'This field should be an e-mail address in the format "user@domain.com"',
46316         /**
46317          * The keystroke filter mask to be applied on email input
46318          * @type RegExp
46319          */
46320         'emailMask' : /[a-z0-9_\.\-@]/i,
46321
46322         /**
46323          * The function used to validate URLs
46324          * @param {String} value The URL
46325          */
46326         'url' : function(v){
46327             return url.test(v);
46328         },
46329         /**
46330          * The error text to display when the url validation function returns false
46331          * @type String
46332          */
46333         'urlText' : 'This field should be a URL in the format "http:/'+'/www.domain.com"',
46334         
46335         /**
46336          * The function used to validate alpha values
46337          * @param {String} value The value
46338          */
46339         'alpha' : function(v){
46340             return alpha.test(v);
46341         },
46342         /**
46343          * The error text to display when the alpha validation function returns false
46344          * @type String
46345          */
46346         'alphaText' : 'This field should only contain letters and _',
46347         /**
46348          * The keystroke filter mask to be applied on alpha input
46349          * @type RegExp
46350          */
46351         'alphaMask' : /[a-z_]/i,
46352
46353         /**
46354          * The function used to validate alphanumeric values
46355          * @param {String} value The value
46356          */
46357         'alphanum' : function(v){
46358             return alphanum.test(v);
46359         },
46360         /**
46361          * The error text to display when the alphanumeric validation function returns false
46362          * @type String
46363          */
46364         'alphanumText' : 'This field should only contain letters, numbers and _',
46365         /**
46366          * The keystroke filter mask to be applied on alphanumeric input
46367          * @type RegExp
46368          */
46369         'alphanumMask' : /[a-z0-9_]/i
46370     };
46371 }();//<script type="text/javascript">
46372
46373 /**
46374  * @class Roo.form.FCKeditor
46375  * @extends Roo.form.TextArea
46376  * Wrapper around the FCKEditor http://www.fckeditor.net
46377  * @constructor
46378  * Creates a new FCKeditor
46379  * @param {Object} config Configuration options
46380  */
46381 Roo.form.FCKeditor = function(config){
46382     Roo.form.FCKeditor.superclass.constructor.call(this, config);
46383     this.addEvents({
46384          /**
46385          * @event editorinit
46386          * Fired when the editor is initialized - you can add extra handlers here..
46387          * @param {FCKeditor} this
46388          * @param {Object} the FCK object.
46389          */
46390         editorinit : true
46391     });
46392     
46393     
46394 };
46395 Roo.form.FCKeditor.editors = { };
46396 Roo.extend(Roo.form.FCKeditor, Roo.form.TextArea,
46397 {
46398     //defaultAutoCreate : {
46399     //    tag : "textarea",style   : "width:100px;height:60px;" ,autocomplete    : "off"
46400     //},
46401     // private
46402     /**
46403      * @cfg {Object} fck options - see fck manual for details.
46404      */
46405     fckconfig : false,
46406     
46407     /**
46408      * @cfg {Object} fck toolbar set (Basic or Default)
46409      */
46410     toolbarSet : 'Basic',
46411     /**
46412      * @cfg {Object} fck BasePath
46413      */ 
46414     basePath : '/fckeditor/',
46415     
46416     
46417     frame : false,
46418     
46419     value : '',
46420     
46421    
46422     onRender : function(ct, position)
46423     {
46424         if(!this.el){
46425             this.defaultAutoCreate = {
46426                 tag: "textarea",
46427                 style:"width:300px;height:60px;",
46428                 autocomplete: "off"
46429             };
46430         }
46431         Roo.form.FCKeditor.superclass.onRender.call(this, ct, position);
46432         /*
46433         if(this.grow){
46434             this.textSizeEl = Roo.DomHelper.append(document.body, {tag: "pre", cls: "x-form-grow-sizer"});
46435             if(this.preventScrollbars){
46436                 this.el.setStyle("overflow", "hidden");
46437             }
46438             this.el.setHeight(this.growMin);
46439         }
46440         */
46441         //console.log('onrender' + this.getId() );
46442         Roo.form.FCKeditor.editors[this.getId()] = this;
46443          
46444
46445         this.replaceTextarea() ;
46446         
46447     },
46448     
46449     getEditor : function() {
46450         return this.fckEditor;
46451     },
46452     /**
46453      * Sets a data value into the field and validates it.  To set the value directly without validation see {@link #setRawValue}.
46454      * @param {Mixed} value The value to set
46455      */
46456     
46457     
46458     setValue : function(value)
46459     {
46460         //console.log('setValue: ' + value);
46461         
46462         if(typeof(value) == 'undefined') { // not sure why this is happending...
46463             return;
46464         }
46465         Roo.form.FCKeditor.superclass.setValue.apply(this,[value]);
46466         
46467         //if(!this.el || !this.getEditor()) {
46468         //    this.value = value;
46469             //this.setValue.defer(100,this,[value]);    
46470         //    return;
46471         //} 
46472         
46473         if(!this.getEditor()) {
46474             return;
46475         }
46476         
46477         this.getEditor().SetData(value);
46478         
46479         //
46480
46481     },
46482
46483     /**
46484      * Returns the normalized data value (undefined or emptyText will be returned as '').  To return the raw value see {@link #getRawValue}.
46485      * @return {Mixed} value The field value
46486      */
46487     getValue : function()
46488     {
46489         
46490         if (this.frame && this.frame.dom.style.display == 'none') {
46491             return Roo.form.FCKeditor.superclass.getValue.call(this);
46492         }
46493         
46494         if(!this.el || !this.getEditor()) {
46495            
46496            // this.getValue.defer(100,this); 
46497             return this.value;
46498         }
46499        
46500         
46501         var value=this.getEditor().GetData();
46502         Roo.form.FCKeditor.superclass.setValue.apply(this,[value]);
46503         return Roo.form.FCKeditor.superclass.getValue.call(this);
46504         
46505
46506     },
46507
46508     /**
46509      * Returns the raw data value which may or may not be a valid, defined value.  To return a normalized value see {@link #getValue}.
46510      * @return {Mixed} value The field value
46511      */
46512     getRawValue : function()
46513     {
46514         if (this.frame && this.frame.dom.style.display == 'none') {
46515             return Roo.form.FCKeditor.superclass.getRawValue.call(this);
46516         }
46517         
46518         if(!this.el || !this.getEditor()) {
46519             //this.getRawValue.defer(100,this); 
46520             return this.value;
46521             return;
46522         }
46523         
46524         
46525         
46526         var value=this.getEditor().GetData();
46527         Roo.form.FCKeditor.superclass.setRawValue.apply(this,[value]);
46528         return Roo.form.FCKeditor.superclass.getRawValue.call(this);
46529          
46530     },
46531     
46532     setSize : function(w,h) {
46533         
46534         
46535         
46536         //if (this.frame && this.frame.dom.style.display == 'none') {
46537         //    Roo.form.FCKeditor.superclass.setSize.apply(this, [w, h]);
46538         //    return;
46539         //}
46540         //if(!this.el || !this.getEditor()) {
46541         //    this.setSize.defer(100,this, [w,h]); 
46542         //    return;
46543         //}
46544         
46545         
46546         
46547         Roo.form.FCKeditor.superclass.setSize.apply(this, [w, h]);
46548         
46549         this.frame.dom.setAttribute('width', w);
46550         this.frame.dom.setAttribute('height', h);
46551         this.frame.setSize(w,h);
46552         
46553     },
46554     
46555     toggleSourceEdit : function(value) {
46556         
46557       
46558          
46559         this.el.dom.style.display = value ? '' : 'none';
46560         this.frame.dom.style.display = value ?  'none' : '';
46561         
46562     },
46563     
46564     
46565     focus: function(tag)
46566     {
46567         if (this.frame.dom.style.display == 'none') {
46568             return Roo.form.FCKeditor.superclass.focus.call(this);
46569         }
46570         if(!this.el || !this.getEditor()) {
46571             this.focus.defer(100,this, [tag]); 
46572             return;
46573         }
46574         
46575         
46576         
46577         
46578         var tgs = this.getEditor().EditorDocument.getElementsByTagName(tag);
46579         this.getEditor().Focus();
46580         if (tgs.length) {
46581             if (!this.getEditor().Selection.GetSelection()) {
46582                 this.focus.defer(100,this, [tag]); 
46583                 return;
46584             }
46585             
46586             
46587             var r = this.getEditor().EditorDocument.createRange();
46588             r.setStart(tgs[0],0);
46589             r.setEnd(tgs[0],0);
46590             this.getEditor().Selection.GetSelection().removeAllRanges();
46591             this.getEditor().Selection.GetSelection().addRange(r);
46592             this.getEditor().Focus();
46593         }
46594         
46595     },
46596     
46597     
46598     
46599     replaceTextarea : function()
46600     {
46601         if ( document.getElementById( this.getId() + '___Frame' ) )
46602             return ;
46603         //if ( !this.checkBrowser || this._isCompatibleBrowser() )
46604         //{
46605             // We must check the elements firstly using the Id and then the name.
46606         var oTextarea = document.getElementById( this.getId() );
46607         
46608         var colElementsByName = document.getElementsByName( this.getId() ) ;
46609          
46610         oTextarea.style.display = 'none' ;
46611
46612         if ( oTextarea.tabIndex ) {            
46613             this.TabIndex = oTextarea.tabIndex ;
46614         }
46615         
46616         this._insertHtmlBefore( this._getConfigHtml(), oTextarea ) ;
46617         this._insertHtmlBefore( this._getIFrameHtml(), oTextarea ) ;
46618         this.frame = Roo.get(this.getId() + '___Frame')
46619     },
46620     
46621     _getConfigHtml : function()
46622     {
46623         var sConfig = '' ;
46624
46625         for ( var o in this.fckconfig ) {
46626             sConfig += sConfig.length > 0  ? '&amp;' : '';
46627             sConfig += encodeURIComponent( o ) + '=' + encodeURIComponent( this.fckconfig[o] ) ;
46628         }
46629
46630         return '<input type="hidden" id="' + this.getId() + '___Config" value="' + sConfig + '" style="display:none" />' ;
46631     },
46632     
46633     
46634     _getIFrameHtml : function()
46635     {
46636         var sFile = 'fckeditor.html' ;
46637         /* no idea what this is about..
46638         try
46639         {
46640             if ( (/fcksource=true/i).test( window.top.location.search ) )
46641                 sFile = 'fckeditor.original.html' ;
46642         }
46643         catch (e) { 
46644         */
46645
46646         var sLink = this.basePath + 'editor/' + sFile + '?InstanceName=' + encodeURIComponent( this.getId() ) ;
46647         sLink += this.toolbarSet ? ( '&amp;Toolbar=' + this.toolbarSet)  : '';
46648         
46649         
46650         var html = '<iframe id="' + this.getId() +
46651             '___Frame" src="' + sLink +
46652             '" width="' + this.width +
46653             '" height="' + this.height + '"' +
46654             (this.tabIndex ?  ' tabindex="' + this.tabIndex + '"' :'' ) +
46655             ' frameborder="0" scrolling="no"></iframe>' ;
46656
46657         return html ;
46658     },
46659     
46660     _insertHtmlBefore : function( html, element )
46661     {
46662         if ( element.insertAdjacentHTML )       {
46663             // IE
46664             element.insertAdjacentHTML( 'beforeBegin', html ) ;
46665         } else { // Gecko
46666             var oRange = document.createRange() ;
46667             oRange.setStartBefore( element ) ;
46668             var oFragment = oRange.createContextualFragment( html );
46669             element.parentNode.insertBefore( oFragment, element ) ;
46670         }
46671     }
46672     
46673     
46674   
46675     
46676     
46677     
46678     
46679
46680 });
46681
46682 //Roo.reg('fckeditor', Roo.form.FCKeditor);
46683
46684 function FCKeditor_OnComplete(editorInstance){
46685     var f = Roo.form.FCKeditor.editors[editorInstance.Name];
46686     f.fckEditor = editorInstance;
46687     //console.log("loaded");
46688     f.fireEvent('editorinit', f, editorInstance);
46689
46690   
46691
46692  
46693
46694
46695
46696
46697
46698
46699
46700
46701
46702
46703
46704
46705
46706
46707
46708 //<script type="text/javascript">
46709 /**
46710  * @class Roo.form.GridField
46711  * @extends Roo.form.Field
46712  * Embed a grid (or editable grid into a form)
46713  * STATUS ALPHA
46714  * 
46715  * This embeds a grid in a form, the value of the field should be the json encoded array of rows
46716  * it needs 
46717  * xgrid.store = Roo.data.Store
46718  * xgrid.store.proxy = Roo.data.MemoryProxy (data = [] )
46719  * xgrid.store.reader = Roo.data.JsonReader 
46720  * 
46721  * 
46722  * @constructor
46723  * Creates a new GridField
46724  * @param {Object} config Configuration options
46725  */
46726 Roo.form.GridField = function(config){
46727     Roo.form.GridField.superclass.constructor.call(this, config);
46728      
46729 };
46730
46731 Roo.extend(Roo.form.GridField, Roo.form.Field,  {
46732     /**
46733      * @cfg {Number} width  - used to restrict width of grid..
46734      */
46735     width : 100,
46736     /**
46737      * @cfg {Number} height - used to restrict height of grid..
46738      */
46739     height : 50,
46740      /**
46741      * @cfg {Object} xgrid (xtype'd description of grid) { xtype : 'Grid', dataSource: .... }
46742          * 
46743          *}
46744      */
46745     xgrid : false, 
46746     /**
46747      * @cfg {String/Object} autoCreate A DomHelper element spec, or true for a default element spec (defaults to
46748      * {tag: "input", type: "checkbox", autocomplete: "off"})
46749      */
46750    // defaultAutoCreate : { tag: 'div' },
46751     defaultAutoCreate : { tag: 'input', type: 'hidden', autocomplete: 'off'},
46752     /**
46753      * @cfg {String} addTitle Text to include for adding a title.
46754      */
46755     addTitle : false,
46756     //
46757     onResize : function(){
46758         Roo.form.Field.superclass.onResize.apply(this, arguments);
46759     },
46760
46761     initEvents : function(){
46762         // Roo.form.Checkbox.superclass.initEvents.call(this);
46763         // has no events...
46764        
46765     },
46766
46767
46768     getResizeEl : function(){
46769         return this.wrap;
46770     },
46771
46772     getPositionEl : function(){
46773         return this.wrap;
46774     },
46775
46776     // private
46777     onRender : function(ct, position){
46778         
46779         this.style = this.style || 'overflow: hidden; border:1px solid #c3daf9;';
46780         var style = this.style;
46781         delete this.style;
46782         
46783         Roo.form.GridField.superclass.onRender.call(this, ct, position);
46784         this.wrap = this.el.wrap({cls: ''}); // not sure why ive done thsi...
46785         this.viewEl = this.wrap.createChild({ tag: 'div' });
46786         if (style) {
46787             this.viewEl.applyStyles(style);
46788         }
46789         if (this.width) {
46790             this.viewEl.setWidth(this.width);
46791         }
46792         if (this.height) {
46793             this.viewEl.setHeight(this.height);
46794         }
46795         //if(this.inputValue !== undefined){
46796         //this.setValue(this.value);
46797         
46798         
46799         this.grid = new Roo.grid[this.xgrid.xtype](this.viewEl, this.xgrid);
46800         
46801         
46802         this.grid.render();
46803         this.grid.getDataSource().on('remove', this.refreshValue, this);
46804         this.grid.getDataSource().on('update', this.refreshValue, this);
46805         this.grid.on('afteredit', this.refreshValue, this);
46806  
46807     },
46808      
46809     
46810     /**
46811      * Sets the value of the item. 
46812      * @param {String} either an object  or a string..
46813      */
46814     setValue : function(v){
46815         //this.value = v;
46816         v = v || []; // empty set..
46817         // this does not seem smart - it really only affects memoryproxy grids..
46818         if (this.grid && this.grid.getDataSource() && typeof(v) != 'undefined') {
46819             var ds = this.grid.getDataSource();
46820             // assumes a json reader..
46821             var data = {}
46822             data[ds.reader.meta.root ] =  typeof(v) == 'string' ? Roo.decode(v) : v;
46823             ds.loadData( data);
46824         }
46825         // clear selection so it does not get stale.
46826         if (this.grid.sm) { 
46827             this.grid.sm.clearSelections();
46828         }
46829         
46830         Roo.form.GridField.superclass.setValue.call(this, v);
46831         this.refreshValue();
46832         // should load data in the grid really....
46833     },
46834     
46835     // private
46836     refreshValue: function() {
46837          var val = [];
46838         this.grid.getDataSource().each(function(r) {
46839             val.push(r.data);
46840         });
46841         this.el.dom.value = Roo.encode(val);
46842     }
46843     
46844      
46845     
46846     
46847 });/*
46848  * Based on:
46849  * Ext JS Library 1.1.1
46850  * Copyright(c) 2006-2007, Ext JS, LLC.
46851  *
46852  * Originally Released Under LGPL - original licence link has changed is not relivant.
46853  *
46854  * Fork - LGPL
46855  * <script type="text/javascript">
46856  */
46857 /**
46858  * @class Roo.form.DisplayField
46859  * @extends Roo.form.Field
46860  * A generic Field to display non-editable data.
46861  * @constructor
46862  * Creates a new Display Field item.
46863  * @param {Object} config Configuration options
46864  */
46865 Roo.form.DisplayField = function(config){
46866     Roo.form.DisplayField.superclass.constructor.call(this, config);
46867     
46868 };
46869
46870 Roo.extend(Roo.form.DisplayField, Roo.form.TextField,  {
46871     inputType:      'hidden',
46872     allowBlank:     true,
46873     readOnly:         true,
46874     
46875  
46876     /**
46877      * @cfg {String} focusClass The CSS class to use when the checkbox receives focus (defaults to undefined)
46878      */
46879     focusClass : undefined,
46880     /**
46881      * @cfg {String} fieldClass The default CSS class for the checkbox (defaults to "x-form-field")
46882      */
46883     fieldClass: 'x-form-field',
46884     
46885      /**
46886      * @cfg {Function} valueRenderer The renderer for the field (so you can reformat output). should return raw HTML
46887      */
46888     valueRenderer: undefined,
46889     
46890     width: 100,
46891     /**
46892      * @cfg {String/Object} autoCreate A DomHelper element spec, or true for a default element spec (defaults to
46893      * {tag: "input", type: "checkbox", autocomplete: "off"})
46894      */
46895      
46896  //   defaultAutoCreate : { tag: 'input', type: 'hidden', autocomplete: 'off'},
46897
46898     onResize : function(){
46899         Roo.form.DisplayField.superclass.onResize.apply(this, arguments);
46900         
46901     },
46902
46903     initEvents : function(){
46904         // Roo.form.Checkbox.superclass.initEvents.call(this);
46905         // has no events...
46906        
46907     },
46908
46909
46910     getResizeEl : function(){
46911         return this.wrap;
46912     },
46913
46914     getPositionEl : function(){
46915         return this.wrap;
46916     },
46917
46918     // private
46919     onRender : function(ct, position){
46920         
46921         Roo.form.DisplayField.superclass.onRender.call(this, ct, position);
46922         //if(this.inputValue !== undefined){
46923         this.wrap = this.el.wrap();
46924         
46925         this.viewEl = this.wrap.createChild({ tag: 'div', cls: 'x-form-displayfield'});
46926         
46927         if (this.bodyStyle) {
46928             this.viewEl.applyStyles(this.bodyStyle);
46929         }
46930         //this.viewEl.setStyle('padding', '2px');
46931         
46932         this.setValue(this.value);
46933         
46934     },
46935 /*
46936     // private
46937     initValue : Roo.emptyFn,
46938
46939   */
46940
46941         // private
46942     onClick : function(){
46943         
46944     },
46945
46946     /**
46947      * Sets the checked state of the checkbox.
46948      * @param {Boolean/String} checked True, 'true', '1', or 'on' to check the checkbox, any other value will uncheck it.
46949      */
46950     setValue : function(v){
46951         this.value = v;
46952         var html = this.valueRenderer ?  this.valueRenderer(v) : String.format('{0}', v);
46953         // this might be called before we have a dom element..
46954         if (!this.viewEl) {
46955             return;
46956         }
46957         this.viewEl.dom.innerHTML = html;
46958         Roo.form.DisplayField.superclass.setValue.call(this, v);
46959
46960     }
46961 });/*
46962  * 
46963  * Licence- LGPL
46964  * 
46965  */
46966
46967 /**
46968  * @class Roo.form.DayPicker
46969  * @extends Roo.form.Field
46970  * A Day picker show [M] [T] [W] ....
46971  * @constructor
46972  * Creates a new Day Picker
46973  * @param {Object} config Configuration options
46974  */
46975 Roo.form.DayPicker= function(config){
46976     Roo.form.DayPicker.superclass.constructor.call(this, config);
46977      
46978 };
46979
46980 Roo.extend(Roo.form.DayPicker, Roo.form.Field,  {
46981     /**
46982      * @cfg {String} focusClass The CSS class to use when the checkbox receives focus (defaults to undefined)
46983      */
46984     focusClass : undefined,
46985     /**
46986      * @cfg {String} fieldClass The default CSS class for the checkbox (defaults to "x-form-field")
46987      */
46988     fieldClass: "x-form-field",
46989    
46990     /**
46991      * @cfg {String/Object} autoCreate A DomHelper element spec, or true for a default element spec (defaults to
46992      * {tag: "input", type: "checkbox", autocomplete: "off"})
46993      */
46994     defaultAutoCreate : { tag: "input", type: 'hidden', autocomplete: "off"},
46995     
46996    
46997     actionMode : 'viewEl', 
46998     //
46999     // private
47000  
47001     inputType : 'hidden',
47002     
47003      
47004     inputElement: false, // real input element?
47005     basedOn: false, // ????
47006     
47007     isFormField: true, // not sure where this is needed!!!!
47008
47009     onResize : function(){
47010         Roo.form.Checkbox.superclass.onResize.apply(this, arguments);
47011         if(!this.boxLabel){
47012             this.el.alignTo(this.wrap, 'c-c');
47013         }
47014     },
47015
47016     initEvents : function(){
47017         Roo.form.Checkbox.superclass.initEvents.call(this);
47018         this.el.on("click", this.onClick,  this);
47019         this.el.on("change", this.onClick,  this);
47020     },
47021
47022
47023     getResizeEl : function(){
47024         return this.wrap;
47025     },
47026
47027     getPositionEl : function(){
47028         return this.wrap;
47029     },
47030
47031     
47032     // private
47033     onRender : function(ct, position){
47034         Roo.form.Checkbox.superclass.onRender.call(this, ct, position);
47035        
47036         this.wrap = this.el.wrap({cls: 'x-form-daypick-item '});
47037         
47038         var r1 = '<table><tr>';
47039         var r2 = '<tr class="x-form-daypick-icons">';
47040         for (var i=0; i < 7; i++) {
47041             r1+= '<td><div>' + Date.dayNames[i].substring(0,3) + '</div></td>';
47042             r2+= '<td><img class="x-menu-item-icon" src="' + Roo.BLANK_IMAGE_URL  +'"></td>';
47043         }
47044         
47045         var viewEl = this.wrap.createChild( r1 + '</tr>' + r2 + '</tr></table>');
47046         viewEl.select('img').on('click', this.onClick, this);
47047         this.viewEl = viewEl;   
47048         
47049         
47050         // this will not work on Chrome!!!
47051         this.el.on('DOMAttrModified', this.setFromHidden,  this); //ff
47052         this.el.on('propertychange', this.setFromHidden,  this);  //ie
47053         
47054         
47055           
47056
47057     },
47058
47059     // private
47060     initValue : Roo.emptyFn,
47061
47062     /**
47063      * Returns the checked state of the checkbox.
47064      * @return {Boolean} True if checked, else false
47065      */
47066     getValue : function(){
47067         return this.el.dom.value;
47068         
47069     },
47070
47071         // private
47072     onClick : function(e){ 
47073         //this.setChecked(!this.checked);
47074         Roo.get(e.target).toggleClass('x-menu-item-checked');
47075         this.refreshValue();
47076         //if(this.el.dom.checked != this.checked){
47077         //    this.setValue(this.el.dom.checked);
47078        // }
47079     },
47080     
47081     // private
47082     refreshValue : function()
47083     {
47084         var val = '';
47085         this.viewEl.select('img',true).each(function(e,i,n)  {
47086             val += e.is(".x-menu-item-checked") ? String(n) : '';
47087         });
47088         this.setValue(val, true);
47089     },
47090
47091     /**
47092      * Sets the checked state of the checkbox.
47093      * On is always based on a string comparison between inputValue and the param.
47094      * @param {Boolean/String} value - the value to set 
47095      * @param {Boolean/String} suppressEvent - whether to suppress the checkchange event.
47096      */
47097     setValue : function(v,suppressEvent){
47098         if (!this.el.dom) {
47099             return;
47100         }
47101         var old = this.el.dom.value ;
47102         this.el.dom.value = v;
47103         if (suppressEvent) {
47104             return ;
47105         }
47106          
47107         // update display..
47108         this.viewEl.select('img',true).each(function(e,i,n)  {
47109             
47110             var on = e.is(".x-menu-item-checked");
47111             var newv = v.indexOf(String(n)) > -1;
47112             if (on != newv) {
47113                 e.toggleClass('x-menu-item-checked');
47114             }
47115             
47116         });
47117         
47118         
47119         this.fireEvent('change', this, v, old);
47120         
47121         
47122     },
47123    
47124     // handle setting of hidden value by some other method!!?!?
47125     setFromHidden: function()
47126     {
47127         if(!this.el){
47128             return;
47129         }
47130         //console.log("SET FROM HIDDEN");
47131         //alert('setFrom hidden');
47132         this.setValue(this.el.dom.value);
47133     },
47134     
47135     onDestroy : function()
47136     {
47137         if(this.viewEl){
47138             Roo.get(this.viewEl).remove();
47139         }
47140          
47141         Roo.form.DayPicker.superclass.onDestroy.call(this);
47142     }
47143
47144 });/*
47145  * RooJS Library 1.1.1
47146  * Copyright(c) 2008-2011  Alan Knowles
47147  *
47148  * License - LGPL
47149  */
47150  
47151
47152 /**
47153  * @class Roo.form.ComboCheck
47154  * @extends Roo.form.ComboBox
47155  * A combobox for multiple select items.
47156  *
47157  * FIXME - could do with a reset button..
47158  * 
47159  * @constructor
47160  * Create a new ComboCheck
47161  * @param {Object} config Configuration options
47162  */
47163 Roo.form.ComboCheck = function(config){
47164     Roo.form.ComboCheck.superclass.constructor.call(this, config);
47165     // should verify some data...
47166     // like
47167     // hiddenName = required..
47168     // displayField = required
47169     // valudField == required
47170     var req= [ 'hiddenName', 'displayField', 'valueField' ];
47171     var _t = this;
47172     Roo.each(req, function(e) {
47173         if ((typeof(_t[e]) == 'undefined' ) || !_t[e].length) {
47174             throw "Roo.form.ComboCheck : missing value for: " + e;
47175         }
47176     });
47177     
47178     
47179 };
47180
47181 Roo.extend(Roo.form.ComboCheck, Roo.form.ComboBox, {
47182      
47183      
47184     editable : false,
47185      
47186     selectedClass: 'x-menu-item-checked', 
47187     
47188     // private
47189     onRender : function(ct, position){
47190         var _t = this;
47191         
47192         
47193         
47194         if(!this.tpl){
47195             var cls = 'x-combo-list';
47196
47197             
47198             this.tpl =  new Roo.Template({
47199                 html :  '<div class="'+cls+'-item x-menu-check-item">' +
47200                    '<img class="x-menu-item-icon" style="margin: 0px;" src="' + Roo.BLANK_IMAGE_URL + '">' + 
47201                    '<span>{' + this.displayField + '}</span>' +
47202                     '</div>' 
47203                 
47204             });
47205         }
47206  
47207         
47208         Roo.form.ComboCheck.superclass.onRender.call(this, ct, position);
47209         this.view.singleSelect = false;
47210         this.view.multiSelect = true;
47211         this.view.toggleSelect = true;
47212         this.pageTb.add(new Roo.Toolbar.Fill(), {
47213             
47214             text: 'Done',
47215             handler: function()
47216             {
47217                 _t.collapse();
47218             }
47219         });
47220     },
47221     
47222     onViewOver : function(e, t){
47223         // do nothing...
47224         return;
47225         
47226     },
47227     
47228     onViewClick : function(doFocus,index){
47229         return;
47230         
47231     },
47232     select: function () {
47233         //Roo.log("SELECT CALLED");
47234     },
47235      
47236     selectByValue : function(xv, scrollIntoView){
47237         var ar = this.getValueArray();
47238         var sels = [];
47239         
47240         Roo.each(ar, function(v) {
47241             if(v === undefined || v === null){
47242                 return;
47243             }
47244             var r = this.findRecord(this.valueField, v);
47245             if(r){
47246                 sels.push(this.store.indexOf(r))
47247                 
47248             }
47249         },this);
47250         this.view.select(sels);
47251         return false;
47252     },
47253     
47254     
47255     
47256     onSelect : function(record, index){
47257        // Roo.log("onselect Called");
47258        // this is only called by the clear button now..
47259         this.view.clearSelections();
47260         this.setValue('[]');
47261         if (this.value != this.valueBefore) {
47262             this.fireEvent('change', this, this.value, this.valueBefore);
47263             this.valueBefore = this.value;
47264         }
47265     },
47266     getValueArray : function()
47267     {
47268         var ar = [] ;
47269         
47270         try {
47271             //Roo.log(this.value);
47272             if (typeof(this.value) == 'undefined') {
47273                 return [];
47274             }
47275             var ar = Roo.decode(this.value);
47276             return  ar instanceof Array ? ar : []; //?? valid?
47277             
47278         } catch(e) {
47279             Roo.log(e + "\nRoo.form.ComboCheck:getValueArray  invalid data:" + this.getValue());
47280             return [];
47281         }
47282          
47283     },
47284     expand : function ()
47285     {
47286         
47287         Roo.form.ComboCheck.superclass.expand.call(this);
47288         this.valueBefore = typeof(this.value) == 'undefined' ? '' : this.value;
47289         //this.valueBefore = typeof(this.valueBefore) == 'undefined' ? '' : this.valueBefore;
47290         
47291
47292     },
47293     
47294     collapse : function(){
47295         Roo.form.ComboCheck.superclass.collapse.call(this);
47296         var sl = this.view.getSelectedIndexes();
47297         var st = this.store;
47298         var nv = [];
47299         var tv = [];
47300         var r;
47301         Roo.each(sl, function(i) {
47302             r = st.getAt(i);
47303             nv.push(r.get(this.valueField));
47304         },this);
47305         this.setValue(Roo.encode(nv));
47306         if (this.value != this.valueBefore) {
47307
47308             this.fireEvent('change', this, this.value, this.valueBefore);
47309             this.valueBefore = this.value;
47310         }
47311         
47312     },
47313     
47314     setValue : function(v){
47315         // Roo.log(v);
47316         this.value = v;
47317         
47318         var vals = this.getValueArray();
47319         var tv = [];
47320         Roo.each(vals, function(k) {
47321             var r = this.findRecord(this.valueField, k);
47322             if(r){
47323                 tv.push(r.data[this.displayField]);
47324             }else if(this.valueNotFoundText !== undefined){
47325                 tv.push( this.valueNotFoundText );
47326             }
47327         },this);
47328        // Roo.log(tv);
47329         
47330         Roo.form.ComboBox.superclass.setValue.call(this, tv.join(', '));
47331         this.hiddenField.value = v;
47332         this.value = v;
47333     }
47334     
47335 });/*
47336  * Based on:
47337  * Ext JS Library 1.1.1
47338  * Copyright(c) 2006-2007, Ext JS, LLC.
47339  *
47340  * Originally Released Under LGPL - original licence link has changed is not relivant.
47341  *
47342  * Fork - LGPL
47343  * <script type="text/javascript">
47344  */
47345  
47346 /**
47347  * @class Roo.form.Signature
47348  * @extends Roo.form.Field
47349  * Signature field.  
47350  * @constructor
47351  * 
47352  * @param {Object} config Configuration options
47353  */
47354
47355 Roo.form.Signature = function(config){
47356     Roo.form.Signature.superclass.constructor.call(this, config);
47357     
47358     this.addEvents({// not in used??
47359          /**
47360          * @event confirm
47361          * Fires when the 'confirm' icon is pressed (add a listener to enable add button)
47362              * @param {Roo.form.Signature} combo This combo box
47363              */
47364         'confirm' : true,
47365         /**
47366          * @event reset
47367          * Fires when the 'edit' icon is pressed (add a listener to enable add button)
47368              * @param {Roo.form.ComboBox} combo This combo box
47369              * @param {Roo.data.Record|false} record The data record returned from the underlying store (or false on nothing selected)
47370              */
47371         'reset' : true
47372     });
47373 };
47374
47375 Roo.extend(Roo.form.Signature, Roo.form.Field,  {
47376     /**
47377      * @cfg {Object} labels Label to use when rendering a form.
47378      * defaults to 
47379      * labels : { 
47380      *      clear : "Clear",
47381      *      confirm : "Confirm"
47382      *  }
47383      */
47384     labels : { 
47385         clear : "Clear",
47386         confirm : "Confirm"
47387     },
47388     /**
47389      * @cfg {Number} width The signature panel width (defaults to 300)
47390      */
47391     width: 300,
47392     /**
47393      * @cfg {Number} height The signature panel height (defaults to 100)
47394      */
47395     height : 100,
47396     /**
47397      * @cfg {Boolean} allowBlank False to validate that the value length > 0 (defaults to false)
47398      */
47399     allowBlank : false,
47400     
47401     //private
47402     // {Object} signPanel The signature SVG panel element (defaults to {})
47403     signPanel : {},
47404     // {Boolean} isMouseDown False to validate that the mouse down event (defaults to false)
47405     isMouseDown : false,
47406     // {Boolean} isConfirmed validate the signature is confirmed or not for submitting form (defaults to false)
47407     isConfirmed : false,
47408     // {String} signatureTmp SVG mapping string (defaults to empty string)
47409     signatureTmp : '',
47410     
47411     
47412     defaultAutoCreate : { // modified by initCompnoent..
47413         tag: "input",
47414         type:"hidden"
47415     },
47416
47417     // private
47418     onRender : function(ct, position){
47419         
47420         Roo.form.Signature.superclass.onRender.call(this, ct, position);
47421         
47422         this.wrap = this.el.wrap({
47423             cls:'x-form-signature-wrap', style : 'width: ' + this.width + 'px', cn:{cls:'x-form-signature'}
47424         });
47425         
47426         this.createToolbar(this);
47427         this.signPanel = this.wrap.createChild({
47428                 tag: 'div',
47429                 style: 'width: ' + this.width + 'px; height: ' + this.height + 'px; border: 0;'
47430             }, this.el
47431         );
47432             
47433         this.svgID = Roo.id();
47434         this.svgEl = this.signPanel.createChild({
47435               xmlns : 'http://www.w3.org/2000/svg',
47436               tag : 'svg',
47437               id : this.svgID + "-svg",
47438               width: this.width,
47439               height: this.height,
47440               viewBox: '0 0 '+this.width+' '+this.height,
47441               cn : [
47442                 {
47443                     tag: "rect",
47444                     id: this.svgID + "-svg-r",
47445                     width: this.width,
47446                     height: this.height,
47447                     fill: "#ffa"
47448                 },
47449                 {
47450                     tag: "line",
47451                     id: this.svgID + "-svg-l",
47452                     x1: "0", // start
47453                     y1: (this.height*0.8), // start set the line in 80% of height
47454                     x2: this.width, // end
47455                     y2: (this.height*0.8), // end set the line in 80% of height
47456                     'stroke': "#666",
47457                     'stroke-width': "1",
47458                     'stroke-dasharray': "3",
47459                     'shape-rendering': "crispEdges",
47460                     'pointer-events': "none"
47461                 },
47462                 {
47463                     tag: "path",
47464                     id: this.svgID + "-svg-p",
47465                     'stroke': "navy",
47466                     'stroke-width': "3",
47467                     'fill': "none",
47468                     'pointer-events': 'none'
47469                 }
47470               ]
47471         });
47472         this.createSVG();
47473         this.svgBox = this.svgEl.dom.getScreenCTM();
47474     },
47475     createSVG : function(){ 
47476         var svg = this.signPanel;
47477         var r = svg.select('#'+ this.svgID + '-svg-r', true).first().dom;
47478         var t = this;
47479
47480         r.addEventListener('mousedown', function(e) { return t.down(e); }, false);
47481         r.addEventListener('mousemove', function(e) { return t.move(e); }, false);
47482         r.addEventListener('mouseup', function(e) { return t.up(e); }, false);
47483         r.addEventListener('mouseout', function(e) { return t.up(e); }, false);
47484         r.addEventListener('touchstart', function(e) { return t.down(e); }, false);
47485         r.addEventListener('touchmove', function(e) { return t.move(e); }, false);
47486         r.addEventListener('touchend', function(e) { return t.up(e); }, false);
47487         
47488     },
47489     isTouchEvent : function(e){
47490         return e.type.match(/^touch/);
47491     },
47492     getCoords : function (e) {
47493         var pt    = this.svgEl.dom.createSVGPoint();
47494         pt.x = e.clientX; 
47495         pt.y = e.clientY;
47496         if (this.isTouchEvent(e)) {
47497             pt.x =  e.targetTouches[0].clientX 
47498             pt.y = e.targetTouches[0].clientY;
47499         }
47500         var a = this.svgEl.dom.getScreenCTM();
47501         var b = a.inverse();
47502         var mx = pt.matrixTransform(b);
47503         return mx.x + ',' + mx.y;
47504     },
47505     //mouse event headler 
47506     down : function (e) {
47507         this.signatureTmp += 'M' + this.getCoords(e) + ' ';
47508         this.signPanel.select('#'+ this.svgID + '-svg-p', true).first().attr('d', this.signatureTmp);
47509         
47510         this.isMouseDown = true;
47511         
47512         e.preventDefault();
47513     },
47514     move : function (e) {
47515         if (this.isMouseDown) {
47516             this.signatureTmp += 'L' + this.getCoords(e) + ' ';
47517             this.signPanel.select('#'+ this.svgID + '-svg-p', true).first().attr( 'd', this.signatureTmp);
47518         }
47519         
47520         e.preventDefault();
47521     },
47522     up : function (e) {
47523         this.isMouseDown = false;
47524         var sp = this.signatureTmp.split(' ');
47525         
47526         if(sp.length > 1){
47527             if(!sp[sp.length-2].match(/^L/)){
47528                 sp.pop();
47529                 sp.pop();
47530                 sp.push("");
47531                 this.signatureTmp = sp.join(" ");
47532             }
47533         }
47534         if(this.getValue() != this.signatureTmp){
47535             this.signPanel.select('#'+ this.svgID + '-svg-r', true).first().attr('fill', '#ffa');
47536             this.isConfirmed = false;
47537         }
47538         e.preventDefault();
47539     },
47540     
47541     /**
47542      * Protected method that will not generally be called directly. It
47543      * is called when the editor creates its toolbar. Override this method if you need to
47544      * add custom toolbar buttons.
47545      * @param {HtmlEditor} editor
47546      */
47547     createToolbar : function(editor){
47548          function btn(id, toggle, handler){
47549             var xid = fid + '-'+ id ;
47550             return {
47551                 id : xid,
47552                 cmd : id,
47553                 cls : 'x-btn-icon x-edit-'+id,
47554                 enableToggle:toggle !== false,
47555                 scope: editor, // was editor...
47556                 handler:handler||editor.relayBtnCmd,
47557                 clickEvent:'mousedown',
47558                 tooltip: etb.buttonTips[id] || undefined, ///tips ???
47559                 tabIndex:-1
47560             };
47561         }
47562         
47563         
47564         var tb = new Roo.Toolbar(editor.wrap.dom.firstChild);
47565         this.tb = tb;
47566         this.tb.add(
47567            {
47568                 cls : ' x-signature-btn x-signature-'+id,
47569                 scope: editor, // was editor...
47570                 handler: this.reset,
47571                 clickEvent:'mousedown',
47572                 text: this.labels.clear
47573             },
47574             {
47575                  xtype : 'Fill',
47576                  xns: Roo.Toolbar
47577             }, 
47578             {
47579                 cls : '  x-signature-btn x-signature-'+id,
47580                 scope: editor, // was editor...
47581                 handler: this.confirmHandler,
47582                 clickEvent:'mousedown',
47583                 text: this.labels.confirm
47584             }
47585         );
47586     
47587     },
47588     //public
47589     /**
47590      * when user is clicked confirm then show this image.....
47591      * 
47592      * @return {String} Image Data URI
47593      */
47594     getImageDataURI : function(){
47595         var svg = this.svgEl.dom.parentNode.innerHTML;
47596         var src = 'data:image/svg+xml;base64,'+window.btoa(svg);
47597         return src; 
47598     },
47599     /**
47600      * 
47601      * @return {Boolean} this.isConfirmed
47602      */
47603     getConfirmed : function(){
47604         return this.isConfirmed;
47605     },
47606     /**
47607      * 
47608      * @return {Number} this.width
47609      */
47610     getWidth : function(){
47611         return this.width;
47612     },
47613     /**
47614      * 
47615      * @return {Number} this.height
47616      */
47617     getHeight : function(){
47618         return this.height;
47619     },
47620     // private
47621     getSignature : function(){
47622         return this.signatureTmp;
47623     },
47624     // private
47625     reset : function(){
47626         this.signatureTmp = '';
47627         this.signPanel.select('#'+ this.svgID + '-svg-r', true).first().attr('fill', '#ffa');
47628         this.signPanel.select('#'+ this.svgID + '-svg-p', true).first().attr( 'd', '');
47629         this.isConfirmed = false;
47630         Roo.form.Signature.superclass.reset.call(this);
47631     },
47632     setSignature : function(s){
47633         this.signatureTmp = s;
47634         this.signPanel.select('#'+ this.svgID + '-svg-r', true).first().attr('fill', '#ffa');
47635         this.signPanel.select('#'+ this.svgID + '-svg-p', true).first().attr( 'd', s);
47636         this.setValue(s);
47637         this.isConfirmed = false;
47638         Roo.form.Signature.superclass.reset.call(this);
47639     }, 
47640     test : function(){
47641 //        Roo.log(this.signPanel.dom.contentWindow.up())
47642     },
47643     //private
47644     setConfirmed : function(){
47645         
47646         
47647         
47648 //        Roo.log(Roo.get(this.signPanel.dom.contentWindow.r).attr('fill', '#cfc'));
47649     },
47650     // private
47651     confirmHandler : function(){
47652         if(!this.getSignature()){
47653             return;
47654         }
47655         
47656         this.signPanel.select('#'+ this.svgID + '-svg-r', true).first().attr('fill', '#cfc');
47657         this.setValue(this.getSignature());
47658         this.isConfirmed = true;
47659         
47660         this.fireEvent('confirm', this);
47661     },
47662     // private
47663     // Subclasses should provide the validation implementation by overriding this
47664     validateValue : function(value){
47665         if(this.allowBlank){
47666             return true;
47667         }
47668         
47669         if(this.isConfirmed){
47670             return true;
47671         }
47672         return false;
47673     }
47674 });/*
47675  * Based on:
47676  * Ext JS Library 1.1.1
47677  * Copyright(c) 2006-2007, Ext JS, LLC.
47678  *
47679  * Originally Released Under LGPL - original licence link has changed is not relivant.
47680  *
47681  * Fork - LGPL
47682  * <script type="text/javascript">
47683  */
47684  
47685
47686 /**
47687  * @class Roo.form.ComboBox
47688  * @extends Roo.form.TriggerField
47689  * A combobox control with support for autocomplete, remote-loading, paging and many other features.
47690  * @constructor
47691  * Create a new ComboBox.
47692  * @param {Object} config Configuration options
47693  */
47694 Roo.form.Select = function(config){
47695     Roo.form.Select.superclass.constructor.call(this, config);
47696      
47697 };
47698
47699 Roo.extend(Roo.form.Select , Roo.form.ComboBox, {
47700     /**
47701      * @cfg {String/HTMLElement/Element} transform The id, DOM node or element of an existing select to convert to a ComboBox
47702      */
47703     /**
47704      * @cfg {Boolean} lazyRender True to prevent the ComboBox from rendering until requested (should always be used when
47705      * rendering into an Roo.Editor, defaults to false)
47706      */
47707     /**
47708      * @cfg {Boolean/Object} autoCreate A DomHelper element spec, or true for a default element spec (defaults to:
47709      * {tag: "input", type: "text", size: "24", autocomplete: "off"})
47710      */
47711     /**
47712      * @cfg {Roo.data.Store} store The data store to which this combo is bound (defaults to undefined)
47713      */
47714     /**
47715      * @cfg {String} title If supplied, a header element is created containing this text and added into the top of
47716      * the dropdown list (defaults to undefined, with no header element)
47717      */
47718
47719      /**
47720      * @cfg {String/Roo.Template} tpl The template to use to render the output
47721      */
47722      
47723     // private
47724     defaultAutoCreate : {tag: "select"  },
47725     /**
47726      * @cfg {Number} listWidth The width in pixels of the dropdown list (defaults to the width of the ComboBox field)
47727      */
47728     listWidth: undefined,
47729     /**
47730      * @cfg {String} displayField The underlying data field name to bind to this CombBox (defaults to undefined if
47731      * mode = 'remote' or 'text' if mode = 'local')
47732      */
47733     displayField: undefined,
47734     /**
47735      * @cfg {String} valueField The underlying data value name to bind to this CombBox (defaults to undefined if
47736      * mode = 'remote' or 'value' if mode = 'local'). 
47737      * Note: use of a valueField requires the user make a selection
47738      * in order for a value to be mapped.
47739      */
47740     valueField: undefined,
47741     
47742     
47743     /**
47744      * @cfg {String} hiddenName If specified, a hidden form field with this name is dynamically generated to store the
47745      * field's data value (defaults to the underlying DOM element's name)
47746      */
47747     hiddenName: undefined,
47748     /**
47749      * @cfg {String} listClass CSS class to apply to the dropdown list element (defaults to '')
47750      */
47751     listClass: '',
47752     /**
47753      * @cfg {String} selectedClass CSS class to apply to the selected item in the dropdown list (defaults to 'x-combo-selected')
47754      */
47755     selectedClass: 'x-combo-selected',
47756     /**
47757      * @cfg {String} triggerClass An additional CSS class used to style the trigger button.  The trigger will always get the
47758      * class 'x-form-trigger' and triggerClass will be <b>appended</b> if specified (defaults to 'x-form-arrow-trigger'
47759      * which displays a downward arrow icon).
47760      */
47761     triggerClass : 'x-form-arrow-trigger',
47762     /**
47763      * @cfg {Boolean/String} shadow True or "sides" for the default effect, "frame" for 4-way shadow, and "drop" for bottom-right
47764      */
47765     shadow:'sides',
47766     /**
47767      * @cfg {String} listAlign A valid anchor position value. See {@link Roo.Element#alignTo} for details on supported
47768      * anchor positions (defaults to 'tl-bl')
47769      */
47770     listAlign: 'tl-bl?',
47771     /**
47772      * @cfg {Number} maxHeight The maximum height in pixels of the dropdown list before scrollbars are shown (defaults to 300)
47773      */
47774     maxHeight: 300,
47775     /**
47776      * @cfg {String} triggerAction The action to execute when the trigger field is activated.  Use 'all' to run the
47777      * query specified by the allQuery config option (defaults to 'query')
47778      */
47779     triggerAction: 'query',
47780     /**
47781      * @cfg {Number} minChars The minimum number of characters the user must type before autocomplete and typeahead activate
47782      * (defaults to 4, does not apply if editable = false)
47783      */
47784     minChars : 4,
47785     /**
47786      * @cfg {Boolean} typeAhead True to populate and autoselect the remainder of the text being typed after a configurable
47787      * delay (typeAheadDelay) if it matches a known value (defaults to false)
47788      */
47789     typeAhead: false,
47790     /**
47791      * @cfg {Number} queryDelay The length of time in milliseconds to delay between the start of typing and sending the
47792      * query to filter the dropdown list (defaults to 500 if mode = 'remote' or 10 if mode = 'local')
47793      */
47794     queryDelay: 500,
47795     /**
47796      * @cfg {Number} pageSize If greater than 0, a paging toolbar is displayed in the footer of the dropdown list and the
47797      * filter queries will execute with page start and limit parameters.  Only applies when mode = 'remote' (defaults to 0)
47798      */
47799     pageSize: 0,
47800     /**
47801      * @cfg {Boolean} selectOnFocus True to select any existing text in the field immediately on focus.  Only applies
47802      * when editable = true (defaults to false)
47803      */
47804     selectOnFocus:false,
47805     /**
47806      * @cfg {String} queryParam Name of the query as it will be passed on the querystring (defaults to 'query')
47807      */
47808     queryParam: 'query',
47809     /**
47810      * @cfg {String} loadingText The text to display in the dropdown list while data is loading.  Only applies
47811      * when mode = 'remote' (defaults to 'Loading...')
47812      */
47813     loadingText: 'Loading...',
47814     /**
47815      * @cfg {Boolean} resizable True to add a resize handle to the bottom of the dropdown list (defaults to false)
47816      */
47817     resizable: false,
47818     /**
47819      * @cfg {Number} handleHeight The height in pixels of the dropdown list resize handle if resizable = true (defaults to 8)
47820      */
47821     handleHeight : 8,
47822     /**
47823      * @cfg {Boolean} editable False to prevent the user from typing text directly into the field, just like a
47824      * traditional select (defaults to true)
47825      */
47826     editable: true,
47827     /**
47828      * @cfg {String} allQuery The text query to send to the server to return all records for the list with no filtering (defaults to '')
47829      */
47830     allQuery: '',
47831     /**
47832      * @cfg {String} mode Set to 'local' if the ComboBox loads local data (defaults to 'remote' which loads from the server)
47833      */
47834     mode: 'remote',
47835     /**
47836      * @cfg {Number} minListWidth The minimum width of the dropdown list in pixels (defaults to 70, will be ignored if
47837      * listWidth has a higher value)
47838      */
47839     minListWidth : 70,
47840     /**
47841      * @cfg {Boolean} forceSelection True to restrict the selected value to one of the values in the list, false to
47842      * allow the user to set arbitrary text into the field (defaults to false)
47843      */
47844     forceSelection:false,
47845     /**
47846      * @cfg {Number} typeAheadDelay The length of time in milliseconds to wait until the typeahead text is displayed
47847      * if typeAhead = true (defaults to 250)
47848      */
47849     typeAheadDelay : 250,
47850     /**
47851      * @cfg {String} valueNotFoundText When using a name/value combo, if the value passed to setValue is not found in
47852      * the store, valueNotFoundText will be displayed as the field text if defined (defaults to undefined)
47853      */
47854     valueNotFoundText : undefined,
47855     
47856     /**
47857      * @cfg {String} defaultValue The value displayed after loading the store.
47858      */
47859     defaultValue: '',
47860     
47861     /**
47862      * @cfg {Boolean} blockFocus Prevents all focus calls, so it can work with things like HTML edtor bar
47863      */
47864     blockFocus : false,
47865     
47866     /**
47867      * @cfg {Boolean} disableClear Disable showing of clear button.
47868      */
47869     disableClear : false,
47870     /**
47871      * @cfg {Boolean} alwaysQuery  Disable caching of results, and always send query
47872      */
47873     alwaysQuery : false,
47874     
47875     //private
47876     addicon : false,
47877     editicon: false,
47878     
47879     // element that contains real text value.. (when hidden is used..)
47880      
47881     // private
47882     onRender : function(ct, position){
47883         Roo.form.Field.prototype.onRender.call(this, ct, position);
47884         
47885         if(this.store){
47886             this.store.on('beforeload', this.onBeforeLoad, this);
47887             this.store.on('load', this.onLoad, this);
47888             this.store.on('loadexception', this.onLoadException, this);
47889             this.store.load({});
47890         }
47891         
47892         
47893         
47894     },
47895
47896     // private
47897     initEvents : function(){
47898         //Roo.form.ComboBox.superclass.initEvents.call(this);
47899  
47900     },
47901
47902     onDestroy : function(){
47903        
47904         if(this.store){
47905             this.store.un('beforeload', this.onBeforeLoad, this);
47906             this.store.un('load', this.onLoad, this);
47907             this.store.un('loadexception', this.onLoadException, this);
47908         }
47909         //Roo.form.ComboBox.superclass.onDestroy.call(this);
47910     },
47911
47912     // private
47913     fireKey : function(e){
47914         if(e.isNavKeyPress() && !this.list.isVisible()){
47915             this.fireEvent("specialkey", this, e);
47916         }
47917     },
47918
47919     // private
47920     onResize: function(w, h){
47921         
47922         return; 
47923     
47924         
47925     },
47926
47927     /**
47928      * Allow or prevent the user from directly editing the field text.  If false is passed,
47929      * the user will only be able to select from the items defined in the dropdown list.  This method
47930      * is the runtime equivalent of setting the 'editable' config option at config time.
47931      * @param {Boolean} value True to allow the user to directly edit the field text
47932      */
47933     setEditable : function(value){
47934          
47935     },
47936
47937     // private
47938     onBeforeLoad : function(){
47939         
47940         Roo.log("Select before load");
47941         return;
47942     
47943         this.innerList.update(this.loadingText ?
47944                '<div class="loading-indicator">'+this.loadingText+'</div>' : '');
47945         //this.restrictHeight();
47946         this.selectedIndex = -1;
47947     },
47948
47949     // private
47950     onLoad : function(){
47951
47952     
47953         var dom = this.el.dom;
47954         dom.innerHTML = '';
47955          var od = dom.ownerDocument;
47956          
47957         if (this.emptyText) {
47958             var op = od.createElement('option');
47959             op.setAttribute('value', '');
47960             op.innerHTML = String.format('{0}', this.emptyText);
47961             dom.appendChild(op);
47962         }
47963         if(this.store.getCount() > 0){
47964            
47965             var vf = this.valueField;
47966             var df = this.displayField;
47967             this.store.data.each(function(r) {
47968                 // which colmsn to use... testing - cdoe / title..
47969                 var op = od.createElement('option');
47970                 op.setAttribute('value', r.data[vf]);
47971                 op.innerHTML = String.format('{0}', r.data[df]);
47972                 dom.appendChild(op);
47973             });
47974             if (typeof(this.defaultValue != 'undefined')) {
47975                 this.setValue(this.defaultValue);
47976             }
47977             
47978              
47979         }else{
47980             //this.onEmptyResults();
47981         }
47982         //this.el.focus();
47983     },
47984     // private
47985     onLoadException : function()
47986     {
47987         dom.innerHTML = '';
47988             
47989         Roo.log("Select on load exception");
47990         return;
47991     
47992         this.collapse();
47993         Roo.log(this.store.reader.jsonData);
47994         if (this.store && typeof(this.store.reader.jsonData.errorMsg) != 'undefined') {
47995             Roo.MessageBox.alert("Error loading",this.store.reader.jsonData.errorMsg);
47996         }
47997         
47998         
47999     },
48000     // private
48001     onTypeAhead : function(){
48002          
48003     },
48004
48005     // private
48006     onSelect : function(record, index){
48007         Roo.log('on select?');
48008         return;
48009         if(this.fireEvent('beforeselect', this, record, index) !== false){
48010             this.setFromData(index > -1 ? record.data : false);
48011             this.collapse();
48012             this.fireEvent('select', this, record, index);
48013         }
48014     },
48015
48016     /**
48017      * Returns the currently selected field value or empty string if no value is set.
48018      * @return {String} value The selected value
48019      */
48020     getValue : function(){
48021         var dom = this.el.dom;
48022         this.value = dom.options[dom.selectedIndex].value;
48023         return this.value;
48024         
48025     },
48026
48027     /**
48028      * Clears any text/value currently set in the field
48029      */
48030     clearValue : function(){
48031         this.value = '';
48032         this.el.dom.selectedIndex = this.emptyText ? 0 : -1;
48033         
48034     },
48035
48036     /**
48037      * Sets the specified value into the field.  If the value finds a match, the corresponding record text
48038      * will be displayed in the field.  If the value does not match the data value of an existing item,
48039      * and the valueNotFoundText config option is defined, it will be displayed as the default field text.
48040      * Otherwise the field will be blank (although the value will still be set).
48041      * @param {String} value The value to match
48042      */
48043     setValue : function(v){
48044         var d = this.el.dom;
48045         for (var i =0; i < d.options.length;i++) {
48046             if (v == d.options[i].value) {
48047                 d.selectedIndex = i;
48048                 this.value = v;
48049                 return;
48050             }
48051         }
48052         this.clearValue();
48053     },
48054     /**
48055      * @property {Object} the last set data for the element
48056      */
48057     
48058     lastData : false,
48059     /**
48060      * Sets the value of the field based on a object which is related to the record format for the store.
48061      * @param {Object} value the value to set as. or false on reset?
48062      */
48063     setFromData : function(o){
48064         Roo.log('setfrom data?');
48065          
48066         
48067         
48068     },
48069     // private
48070     reset : function(){
48071         this.clearValue();
48072     },
48073     // private
48074     findRecord : function(prop, value){
48075         
48076         return false;
48077     
48078         var record;
48079         if(this.store.getCount() > 0){
48080             this.store.each(function(r){
48081                 if(r.data[prop] == value){
48082                     record = r;
48083                     return false;
48084                 }
48085                 return true;
48086             });
48087         }
48088         return record;
48089     },
48090     
48091     getName: function()
48092     {
48093         // returns hidden if it's set..
48094         if (!this.rendered) {return ''};
48095         return !this.hiddenName && this.el.dom.name  ? this.el.dom.name : (this.hiddenName || '');
48096         
48097     },
48098      
48099
48100     
48101
48102     // private
48103     onEmptyResults : function(){
48104         Roo.log('empty results');
48105         //this.collapse();
48106     },
48107
48108     /**
48109      * Returns true if the dropdown list is expanded, else false.
48110      */
48111     isExpanded : function(){
48112         return false;
48113     },
48114
48115     /**
48116      * Select an item in the dropdown list by its data value. This function does NOT cause the select event to fire.
48117      * The store must be loaded and the list expanded for this function to work, otherwise use setValue.
48118      * @param {String} value The data value of the item to select
48119      * @param {Boolean} scrollIntoView False to prevent the dropdown list from autoscrolling to display the
48120      * selected item if it is not currently in view (defaults to true)
48121      * @return {Boolean} True if the value matched an item in the list, else false
48122      */
48123     selectByValue : function(v, scrollIntoView){
48124         Roo.log('select By Value');
48125         return false;
48126     
48127         if(v !== undefined && v !== null){
48128             var r = this.findRecord(this.valueField || this.displayField, v);
48129             if(r){
48130                 this.select(this.store.indexOf(r), scrollIntoView);
48131                 return true;
48132             }
48133         }
48134         return false;
48135     },
48136
48137     /**
48138      * Select an item in the dropdown list by its numeric index in the list. This function does NOT cause the select event to fire.
48139      * The store must be loaded and the list expanded for this function to work, otherwise use setValue.
48140      * @param {Number} index The zero-based index of the list item to select
48141      * @param {Boolean} scrollIntoView False to prevent the dropdown list from autoscrolling to display the
48142      * selected item if it is not currently in view (defaults to true)
48143      */
48144     select : function(index, scrollIntoView){
48145         Roo.log('select ');
48146         return  ;
48147         
48148         this.selectedIndex = index;
48149         this.view.select(index);
48150         if(scrollIntoView !== false){
48151             var el = this.view.getNode(index);
48152             if(el){
48153                 this.innerList.scrollChildIntoView(el, false);
48154             }
48155         }
48156     },
48157
48158       
48159
48160     // private
48161     validateBlur : function(){
48162         
48163         return;
48164         
48165     },
48166
48167     // private
48168     initQuery : function(){
48169         this.doQuery(this.getRawValue());
48170     },
48171
48172     // private
48173     doForce : function(){
48174         if(this.el.dom.value.length > 0){
48175             this.el.dom.value =
48176                 this.lastSelectionText === undefined ? '' : this.lastSelectionText;
48177              
48178         }
48179     },
48180
48181     /**
48182      * Execute a query to filter the dropdown list.  Fires the beforequery event prior to performing the
48183      * query allowing the query action to be canceled if needed.
48184      * @param {String} query The SQL query to execute
48185      * @param {Boolean} forceAll True to force the query to execute even if there are currently fewer characters
48186      * in the field than the minimum specified by the minChars config option.  It also clears any filter previously
48187      * saved in the current store (defaults to false)
48188      */
48189     doQuery : function(q, forceAll){
48190         
48191         Roo.log('doQuery?');
48192         if(q === undefined || q === null){
48193             q = '';
48194         }
48195         var qe = {
48196             query: q,
48197             forceAll: forceAll,
48198             combo: this,
48199             cancel:false
48200         };
48201         if(this.fireEvent('beforequery', qe)===false || qe.cancel){
48202             return false;
48203         }
48204         q = qe.query;
48205         forceAll = qe.forceAll;
48206         if(forceAll === true || (q.length >= this.minChars)){
48207             if(this.lastQuery != q || this.alwaysQuery){
48208                 this.lastQuery = q;
48209                 if(this.mode == 'local'){
48210                     this.selectedIndex = -1;
48211                     if(forceAll){
48212                         this.store.clearFilter();
48213                     }else{
48214                         this.store.filter(this.displayField, q);
48215                     }
48216                     this.onLoad();
48217                 }else{
48218                     this.store.baseParams[this.queryParam] = q;
48219                     this.store.load({
48220                         params: this.getParams(q)
48221                     });
48222                     this.expand();
48223                 }
48224             }else{
48225                 this.selectedIndex = -1;
48226                 this.onLoad();   
48227             }
48228         }
48229     },
48230
48231     // private
48232     getParams : function(q){
48233         var p = {};
48234         //p[this.queryParam] = q;
48235         if(this.pageSize){
48236             p.start = 0;
48237             p.limit = this.pageSize;
48238         }
48239         return p;
48240     },
48241
48242     /**
48243      * Hides the dropdown list if it is currently expanded. Fires the 'collapse' event on completion.
48244      */
48245     collapse : function(){
48246         
48247     },
48248
48249     // private
48250     collapseIf : function(e){
48251         
48252     },
48253
48254     /**
48255      * Expands the dropdown list if it is currently hidden. Fires the 'expand' event on completion.
48256      */
48257     expand : function(){
48258         
48259     } ,
48260
48261     // private
48262      
48263
48264     /** 
48265     * @cfg {Boolean} grow 
48266     * @hide 
48267     */
48268     /** 
48269     * @cfg {Number} growMin 
48270     * @hide 
48271     */
48272     /** 
48273     * @cfg {Number} growMax 
48274     * @hide 
48275     */
48276     /**
48277      * @hide
48278      * @method autoSize
48279      */
48280     
48281     setWidth : function()
48282     {
48283         
48284     },
48285     getResizeEl : function(){
48286         return this.el;
48287     }
48288 });//<script type="text/javasscript">
48289  
48290
48291 /**
48292  * @class Roo.DDView
48293  * A DnD enabled version of Roo.View.
48294  * @param {Element/String} container The Element in which to create the View.
48295  * @param {String} tpl The template string used to create the markup for each element of the View
48296  * @param {Object} config The configuration properties. These include all the config options of
48297  * {@link Roo.View} plus some specific to this class.<br>
48298  * <p>
48299  * Drag/drop is implemented by adding {@link Roo.data.Record}s to the target DDView. If copying is
48300  * not being performed, the original {@link Roo.data.Record} is removed from the source DDView.<br>
48301  * <p>
48302  * The following extra CSS rules are needed to provide insertion point highlighting:<pre><code>
48303 .x-view-drag-insert-above {
48304         border-top:1px dotted #3366cc;
48305 }
48306 .x-view-drag-insert-below {
48307         border-bottom:1px dotted #3366cc;
48308 }
48309 </code></pre>
48310  * 
48311  */
48312  
48313 Roo.DDView = function(container, tpl, config) {
48314     Roo.DDView.superclass.constructor.apply(this, arguments);
48315     this.getEl().setStyle("outline", "0px none");
48316     this.getEl().unselectable();
48317     if (this.dragGroup) {
48318                 this.setDraggable(this.dragGroup.split(","));
48319     }
48320     if (this.dropGroup) {
48321                 this.setDroppable(this.dropGroup.split(","));
48322     }
48323     if (this.deletable) {
48324         this.setDeletable();
48325     }
48326     this.isDirtyFlag = false;
48327         this.addEvents({
48328                 "drop" : true
48329         });
48330 };
48331
48332 Roo.extend(Roo.DDView, Roo.View, {
48333 /**     @cfg {String/Array} dragGroup The ddgroup name(s) for the View's DragZone. */
48334 /**     @cfg {String/Array} dropGroup The ddgroup name(s) for the View's DropZone. */
48335 /**     @cfg {Boolean} copy Causes drag operations to copy nodes rather than move. */
48336 /**     @cfg {Boolean} allowCopy Causes ctrl/drag operations to copy nodes rather than move. */
48337
48338         isFormField: true,
48339
48340         reset: Roo.emptyFn,
48341         
48342         clearInvalid: Roo.form.Field.prototype.clearInvalid,
48343
48344         validate: function() {
48345                 return true;
48346         },
48347         
48348         destroy: function() {
48349                 this.purgeListeners();
48350                 this.getEl.removeAllListeners();
48351                 this.getEl().remove();
48352                 if (this.dragZone) {
48353                         if (this.dragZone.destroy) {
48354                                 this.dragZone.destroy();
48355                         }
48356                 }
48357                 if (this.dropZone) {
48358                         if (this.dropZone.destroy) {
48359                                 this.dropZone.destroy();
48360                         }
48361                 }
48362         },
48363
48364 /**     Allows this class to be an Roo.form.Field so it can be found using {@link Roo.form.BasicForm#findField}. */
48365         getName: function() {
48366                 return this.name;
48367         },
48368
48369 /**     Loads the View from a JSON string representing the Records to put into the Store. */
48370         setValue: function(v) {
48371                 if (!this.store) {
48372                         throw "DDView.setValue(). DDView must be constructed with a valid Store";
48373                 }
48374                 var data = {};
48375                 data[this.store.reader.meta.root] = v ? [].concat(v) : [];
48376                 this.store.proxy = new Roo.data.MemoryProxy(data);
48377                 this.store.load();
48378         },
48379
48380 /**     @return {String} a parenthesised list of the ids of the Records in the View. */
48381         getValue: function() {
48382                 var result = '(';
48383                 this.store.each(function(rec) {
48384                         result += rec.id + ',';
48385                 });
48386                 return result.substr(0, result.length - 1) + ')';
48387         },
48388         
48389         getIds: function() {
48390                 var i = 0, result = new Array(this.store.getCount());
48391                 this.store.each(function(rec) {
48392                         result[i++] = rec.id;
48393                 });
48394                 return result;
48395         },
48396         
48397         isDirty: function() {
48398                 return this.isDirtyFlag;
48399         },
48400
48401 /**
48402  *      Part of the Roo.dd.DropZone interface. If no target node is found, the
48403  *      whole Element becomes the target, and this causes the drop gesture to append.
48404  */
48405     getTargetFromEvent : function(e) {
48406                 var target = e.getTarget();
48407                 while ((target !== null) && (target.parentNode != this.el.dom)) {
48408                 target = target.parentNode;
48409                 }
48410                 if (!target) {
48411                         target = this.el.dom.lastChild || this.el.dom;
48412                 }
48413                 return target;
48414     },
48415
48416 /**
48417  *      Create the drag data which consists of an object which has the property "ddel" as
48418  *      the drag proxy element. 
48419  */
48420     getDragData : function(e) {
48421         var target = this.findItemFromChild(e.getTarget());
48422                 if(target) {
48423                         this.handleSelection(e);
48424                         var selNodes = this.getSelectedNodes();
48425             var dragData = {
48426                 source: this,
48427                 copy: this.copy || (this.allowCopy && e.ctrlKey),
48428                 nodes: selNodes,
48429                 records: []
48430                         };
48431                         var selectedIndices = this.getSelectedIndexes();
48432                         for (var i = 0; i < selectedIndices.length; i++) {
48433                                 dragData.records.push(this.store.getAt(selectedIndices[i]));
48434                         }
48435                         if (selNodes.length == 1) {
48436                                 dragData.ddel = target.cloneNode(true); // the div element
48437                         } else {
48438                                 var div = document.createElement('div'); // create the multi element drag "ghost"
48439                                 div.className = 'multi-proxy';
48440                                 for (var i = 0, len = selNodes.length; i < len; i++) {
48441                                         div.appendChild(selNodes[i].cloneNode(true));
48442                                 }
48443                                 dragData.ddel = div;
48444                         }
48445             //console.log(dragData)
48446             //console.log(dragData.ddel.innerHTML)
48447                         return dragData;
48448                 }
48449         //console.log('nodragData')
48450                 return false;
48451     },
48452     
48453 /**     Specify to which ddGroup items in this DDView may be dragged. */
48454     setDraggable: function(ddGroup) {
48455         if (ddGroup instanceof Array) {
48456                 Roo.each(ddGroup, this.setDraggable, this);
48457                 return;
48458         }
48459         if (this.dragZone) {
48460                 this.dragZone.addToGroup(ddGroup);
48461         } else {
48462                         this.dragZone = new Roo.dd.DragZone(this.getEl(), {
48463                                 containerScroll: true,
48464                                 ddGroup: ddGroup 
48465
48466                         });
48467 //                      Draggability implies selection. DragZone's mousedown selects the element.
48468                         if (!this.multiSelect) { this.singleSelect = true; }
48469
48470 //                      Wire the DragZone's handlers up to methods in *this*
48471                         this.dragZone.getDragData = this.getDragData.createDelegate(this);
48472                 }
48473     },
48474
48475 /**     Specify from which ddGroup this DDView accepts drops. */
48476     setDroppable: function(ddGroup) {
48477         if (ddGroup instanceof Array) {
48478                 Roo.each(ddGroup, this.setDroppable, this);
48479                 return;
48480         }
48481         if (this.dropZone) {
48482                 this.dropZone.addToGroup(ddGroup);
48483         } else {
48484                         this.dropZone = new Roo.dd.DropZone(this.getEl(), {
48485                                 containerScroll: true,
48486                                 ddGroup: ddGroup
48487                         });
48488
48489 //                      Wire the DropZone's handlers up to methods in *this*
48490                         this.dropZone.getTargetFromEvent = this.getTargetFromEvent.createDelegate(this);
48491                         this.dropZone.onNodeEnter = this.onNodeEnter.createDelegate(this);
48492                         this.dropZone.onNodeOver = this.onNodeOver.createDelegate(this);
48493                         this.dropZone.onNodeOut = this.onNodeOut.createDelegate(this);
48494                         this.dropZone.onNodeDrop = this.onNodeDrop.createDelegate(this);
48495                 }
48496     },
48497
48498 /**     Decide whether to drop above or below a View node. */
48499     getDropPoint : function(e, n, dd){
48500         if (n == this.el.dom) { return "above"; }
48501                 var t = Roo.lib.Dom.getY(n), b = t + n.offsetHeight;
48502                 var c = t + (b - t) / 2;
48503                 var y = Roo.lib.Event.getPageY(e);
48504                 if(y <= c) {
48505                         return "above";
48506                 }else{
48507                         return "below";
48508                 }
48509     },
48510
48511     onNodeEnter : function(n, dd, e, data){
48512                 return false;
48513     },
48514     
48515     onNodeOver : function(n, dd, e, data){
48516                 var pt = this.getDropPoint(e, n, dd);
48517                 // set the insert point style on the target node
48518                 var dragElClass = this.dropNotAllowed;
48519                 if (pt) {
48520                         var targetElClass;
48521                         if (pt == "above"){
48522                                 dragElClass = n.previousSibling ? "x-tree-drop-ok-between" : "x-tree-drop-ok-above";
48523                                 targetElClass = "x-view-drag-insert-above";
48524                         } else {
48525                                 dragElClass = n.nextSibling ? "x-tree-drop-ok-between" : "x-tree-drop-ok-below";
48526                                 targetElClass = "x-view-drag-insert-below";
48527                         }
48528                         if (this.lastInsertClass != targetElClass){
48529                                 Roo.fly(n).replaceClass(this.lastInsertClass, targetElClass);
48530                                 this.lastInsertClass = targetElClass;
48531                         }
48532                 }
48533                 return dragElClass;
48534         },
48535
48536     onNodeOut : function(n, dd, e, data){
48537                 this.removeDropIndicators(n);
48538     },
48539
48540     onNodeDrop : function(n, dd, e, data){
48541         if (this.fireEvent("drop", this, n, dd, e, data) === false) {
48542                 return false;
48543         }
48544         var pt = this.getDropPoint(e, n, dd);
48545                 var insertAt = (n == this.el.dom) ? this.nodes.length : n.nodeIndex;
48546                 if (pt == "below") { insertAt++; }
48547                 for (var i = 0; i < data.records.length; i++) {
48548                         var r = data.records[i];
48549                         var dup = this.store.getById(r.id);
48550                         if (dup && (dd != this.dragZone)) {
48551                                 Roo.fly(this.getNode(this.store.indexOf(dup))).frame("red", 1);
48552                         } else {
48553                                 if (data.copy) {
48554                                         this.store.insert(insertAt++, r.copy());
48555                                 } else {
48556                                         data.source.isDirtyFlag = true;
48557                                         r.store.remove(r);
48558                                         this.store.insert(insertAt++, r);
48559                                 }
48560                                 this.isDirtyFlag = true;
48561                         }
48562                 }
48563                 this.dragZone.cachedTarget = null;
48564                 return true;
48565     },
48566
48567     removeDropIndicators : function(n){
48568                 if(n){
48569                         Roo.fly(n).removeClass([
48570                                 "x-view-drag-insert-above",
48571                                 "x-view-drag-insert-below"]);
48572                         this.lastInsertClass = "_noclass";
48573                 }
48574     },
48575
48576 /**
48577  *      Utility method. Add a delete option to the DDView's context menu.
48578  *      @param {String} imageUrl The URL of the "delete" icon image.
48579  */
48580         setDeletable: function(imageUrl) {
48581                 if (!this.singleSelect && !this.multiSelect) {
48582                         this.singleSelect = true;
48583                 }
48584                 var c = this.getContextMenu();
48585                 this.contextMenu.on("itemclick", function(item) {
48586                         switch (item.id) {
48587                                 case "delete":
48588                                         this.remove(this.getSelectedIndexes());
48589                                         break;
48590                         }
48591                 }, this);
48592                 this.contextMenu.add({
48593                         icon: imageUrl,
48594                         id: "delete",
48595                         text: 'Delete'
48596                 });
48597         },
48598         
48599 /**     Return the context menu for this DDView. */
48600         getContextMenu: function() {
48601                 if (!this.contextMenu) {
48602 //                      Create the View's context menu
48603                         this.contextMenu = new Roo.menu.Menu({
48604                                 id: this.id + "-contextmenu"
48605                         });
48606                         this.el.on("contextmenu", this.showContextMenu, this);
48607                 }
48608                 return this.contextMenu;
48609         },
48610         
48611         disableContextMenu: function() {
48612                 if (this.contextMenu) {
48613                         this.el.un("contextmenu", this.showContextMenu, this);
48614                 }
48615         },
48616
48617         showContextMenu: function(e, item) {
48618         item = this.findItemFromChild(e.getTarget());
48619                 if (item) {
48620                         e.stopEvent();
48621                         this.select(this.getNode(item), this.multiSelect && e.ctrlKey, true);
48622                         this.contextMenu.showAt(e.getXY());
48623             }
48624     },
48625
48626 /**
48627  *      Remove {@link Roo.data.Record}s at the specified indices.
48628  *      @param {Array/Number} selectedIndices The index (or Array of indices) of Records to remove.
48629  */
48630     remove: function(selectedIndices) {
48631                 selectedIndices = [].concat(selectedIndices);
48632                 for (var i = 0; i < selectedIndices.length; i++) {
48633                         var rec = this.store.getAt(selectedIndices[i]);
48634                         this.store.remove(rec);
48635                 }
48636     },
48637
48638 /**
48639  *      Double click fires the event, but also, if this is draggable, and there is only one other
48640  *      related DropZone, it transfers the selected node.
48641  */
48642     onDblClick : function(e){
48643         var item = this.findItemFromChild(e.getTarget());
48644         if(item){
48645             if (this.fireEvent("dblclick", this, this.indexOf(item), item, e) === false) {
48646                 return false;
48647             }
48648             if (this.dragGroup) {
48649                     var targets = Roo.dd.DragDropMgr.getRelated(this.dragZone, true);
48650                     while (targets.indexOf(this.dropZone) > -1) {
48651                             targets.remove(this.dropZone);
48652                                 }
48653                     if (targets.length == 1) {
48654                                         this.dragZone.cachedTarget = null;
48655                         var el = Roo.get(targets[0].getEl());
48656                         var box = el.getBox(true);
48657                         targets[0].onNodeDrop(el.dom, {
48658                                 target: el.dom,
48659                                 xy: [box.x, box.y + box.height - 1]
48660                         }, null, this.getDragData(e));
48661                     }
48662                 }
48663         }
48664     },
48665     
48666     handleSelection: function(e) {
48667                 this.dragZone.cachedTarget = null;
48668         var item = this.findItemFromChild(e.getTarget());
48669         if (!item) {
48670                 this.clearSelections(true);
48671                 return;
48672         }
48673                 if (item && (this.multiSelect || this.singleSelect)){
48674                         if(this.multiSelect && e.shiftKey && (!e.ctrlKey) && this.lastSelection){
48675                                 this.select(this.getNodes(this.indexOf(this.lastSelection), item.nodeIndex), false);
48676                         }else if (this.isSelected(this.getNode(item)) && e.ctrlKey){
48677                                 this.unselect(item);
48678                         } else {
48679                                 this.select(item, this.multiSelect && e.ctrlKey);
48680                                 this.lastSelection = item;
48681                         }
48682                 }
48683     },
48684
48685     onItemClick : function(item, index, e){
48686                 if(this.fireEvent("beforeclick", this, index, item, e) === false){
48687                         return false;
48688                 }
48689                 return true;
48690     },
48691
48692     unselect : function(nodeInfo, suppressEvent){
48693                 var node = this.getNode(nodeInfo);
48694                 if(node && this.isSelected(node)){
48695                         if(this.fireEvent("beforeselect", this, node, this.selections) !== false){
48696                                 Roo.fly(node).removeClass(this.selectedClass);
48697                                 this.selections.remove(node);
48698                                 if(!suppressEvent){
48699                                         this.fireEvent("selectionchange", this, this.selections);
48700                                 }
48701                         }
48702                 }
48703     }
48704 });
48705 /*
48706  * Based on:
48707  * Ext JS Library 1.1.1
48708  * Copyright(c) 2006-2007, Ext JS, LLC.
48709  *
48710  * Originally Released Under LGPL - original licence link has changed is not relivant.
48711  *
48712  * Fork - LGPL
48713  * <script type="text/javascript">
48714  */
48715  
48716 /**
48717  * @class Roo.LayoutManager
48718  * @extends Roo.util.Observable
48719  * Base class for layout managers.
48720  */
48721 Roo.LayoutManager = function(container, config){
48722     Roo.LayoutManager.superclass.constructor.call(this);
48723     this.el = Roo.get(container);
48724     // ie scrollbar fix
48725     if(this.el.dom == document.body && Roo.isIE && !config.allowScroll){
48726         document.body.scroll = "no";
48727     }else if(this.el.dom != document.body && this.el.getStyle('position') == 'static'){
48728         this.el.position('relative');
48729     }
48730     this.id = this.el.id;
48731     this.el.addClass("x-layout-container");
48732     /** false to disable window resize monitoring @type Boolean */
48733     this.monitorWindowResize = true;
48734     this.regions = {};
48735     this.addEvents({
48736         /**
48737          * @event layout
48738          * Fires when a layout is performed. 
48739          * @param {Roo.LayoutManager} this
48740          */
48741         "layout" : true,
48742         /**
48743          * @event regionresized
48744          * Fires when the user resizes a region. 
48745          * @param {Roo.LayoutRegion} region The resized region
48746          * @param {Number} newSize The new size (width for east/west, height for north/south)
48747          */
48748         "regionresized" : true,
48749         /**
48750          * @event regioncollapsed
48751          * Fires when a region is collapsed. 
48752          * @param {Roo.LayoutRegion} region The collapsed region
48753          */
48754         "regioncollapsed" : true,
48755         /**
48756          * @event regionexpanded
48757          * Fires when a region is expanded.  
48758          * @param {Roo.LayoutRegion} region The expanded region
48759          */
48760         "regionexpanded" : true
48761     });
48762     this.updating = false;
48763     Roo.EventManager.onWindowResize(this.onWindowResize, this, true);
48764 };
48765
48766 Roo.extend(Roo.LayoutManager, Roo.util.Observable, {
48767     /**
48768      * Returns true if this layout is currently being updated
48769      * @return {Boolean}
48770      */
48771     isUpdating : function(){
48772         return this.updating; 
48773     },
48774     
48775     /**
48776      * Suspend the LayoutManager from doing auto-layouts while
48777      * making multiple add or remove calls
48778      */
48779     beginUpdate : function(){
48780         this.updating = true;    
48781     },
48782     
48783     /**
48784      * Restore auto-layouts and optionally disable the manager from performing a layout
48785      * @param {Boolean} noLayout true to disable a layout update 
48786      */
48787     endUpdate : function(noLayout){
48788         this.updating = false;
48789         if(!noLayout){
48790             this.layout();
48791         }    
48792     },
48793     
48794     layout: function(){
48795         
48796     },
48797     
48798     onRegionResized : function(region, newSize){
48799         this.fireEvent("regionresized", region, newSize);
48800         this.layout();
48801     },
48802     
48803     onRegionCollapsed : function(region){
48804         this.fireEvent("regioncollapsed", region);
48805     },
48806     
48807     onRegionExpanded : function(region){
48808         this.fireEvent("regionexpanded", region);
48809     },
48810         
48811     /**
48812      * Returns the size of the current view. This method normalizes document.body and element embedded layouts and
48813      * performs box-model adjustments.
48814      * @return {Object} The size as an object {width: (the width), height: (the height)}
48815      */
48816     getViewSize : function(){
48817         var size;
48818         if(this.el.dom != document.body){
48819             size = this.el.getSize();
48820         }else{
48821             size = {width: Roo.lib.Dom.getViewWidth(), height: Roo.lib.Dom.getViewHeight()};
48822         }
48823         size.width -= this.el.getBorderWidth("lr")-this.el.getPadding("lr");
48824         size.height -= this.el.getBorderWidth("tb")-this.el.getPadding("tb");
48825         return size;
48826     },
48827     
48828     /**
48829      * Returns the Element this layout is bound to.
48830      * @return {Roo.Element}
48831      */
48832     getEl : function(){
48833         return this.el;
48834     },
48835     
48836     /**
48837      * Returns the specified region.
48838      * @param {String} target The region key ('center', 'north', 'south', 'east' or 'west')
48839      * @return {Roo.LayoutRegion}
48840      */
48841     getRegion : function(target){
48842         return this.regions[target.toLowerCase()];
48843     },
48844     
48845     onWindowResize : function(){
48846         if(this.monitorWindowResize){
48847             this.layout();
48848         }
48849     }
48850 });/*
48851  * Based on:
48852  * Ext JS Library 1.1.1
48853  * Copyright(c) 2006-2007, Ext JS, LLC.
48854  *
48855  * Originally Released Under LGPL - original licence link has changed is not relivant.
48856  *
48857  * Fork - LGPL
48858  * <script type="text/javascript">
48859  */
48860 /**
48861  * @class Roo.BorderLayout
48862  * @extends Roo.LayoutManager
48863  * This class represents a common layout manager used in desktop applications. For screenshots and more details,
48864  * please see: <br><br>
48865  * <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>
48866  * <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>
48867  * Example:
48868  <pre><code>
48869  var layout = new Roo.BorderLayout(document.body, {
48870     north: {
48871         initialSize: 25,
48872         titlebar: false
48873     },
48874     west: {
48875         split:true,
48876         initialSize: 200,
48877         minSize: 175,
48878         maxSize: 400,
48879         titlebar: true,
48880         collapsible: true
48881     },
48882     east: {
48883         split:true,
48884         initialSize: 202,
48885         minSize: 175,
48886         maxSize: 400,
48887         titlebar: true,
48888         collapsible: true
48889     },
48890     south: {
48891         split:true,
48892         initialSize: 100,
48893         minSize: 100,
48894         maxSize: 200,
48895         titlebar: true,
48896         collapsible: true
48897     },
48898     center: {
48899         titlebar: true,
48900         autoScroll:true,
48901         resizeTabs: true,
48902         minTabWidth: 50,
48903         preferredTabWidth: 150
48904     }
48905 });
48906
48907 // shorthand
48908 var CP = Roo.ContentPanel;
48909
48910 layout.beginUpdate();
48911 layout.add("north", new CP("north", "North"));
48912 layout.add("south", new CP("south", {title: "South", closable: true}));
48913 layout.add("west", new CP("west", {title: "West"}));
48914 layout.add("east", new CP("autoTabs", {title: "Auto Tabs", closable: true}));
48915 layout.add("center", new CP("center1", {title: "Close Me", closable: true}));
48916 layout.add("center", new CP("center2", {title: "Center Panel", closable: false}));
48917 layout.getRegion("center").showPanel("center1");
48918 layout.endUpdate();
48919 </code></pre>
48920
48921 <b>The container the layout is rendered into can be either the body element or any other element.
48922 If it is not the body element, the container needs to either be an absolute positioned element,
48923 or you will need to add "position:relative" to the css of the container.  You will also need to specify
48924 the container size if it is not the body element.</b>
48925
48926 * @constructor
48927 * Create a new BorderLayout
48928 * @param {String/HTMLElement/Element} container The container this layout is bound to
48929 * @param {Object} config Configuration options
48930  */
48931 Roo.BorderLayout = function(container, config){
48932     config = config || {};
48933     Roo.BorderLayout.superclass.constructor.call(this, container, config);
48934     this.factory = config.factory || Roo.BorderLayout.RegionFactory;
48935     for(var i = 0, len = this.factory.validRegions.length; i < len; i++) {
48936         var target = this.factory.validRegions[i];
48937         if(config[target]){
48938             this.addRegion(target, config[target]);
48939         }
48940     }
48941 };
48942
48943 Roo.extend(Roo.BorderLayout, Roo.LayoutManager, {
48944     /**
48945      * Creates and adds a new region if it doesn't already exist.
48946      * @param {String} target The target region key (north, south, east, west or center).
48947      * @param {Object} config The regions config object
48948      * @return {BorderLayoutRegion} The new region
48949      */
48950     addRegion : function(target, config){
48951         if(!this.regions[target]){
48952             var r = this.factory.create(target, this, config);
48953             this.bindRegion(target, r);
48954         }
48955         return this.regions[target];
48956     },
48957
48958     // private (kinda)
48959     bindRegion : function(name, r){
48960         this.regions[name] = r;
48961         r.on("visibilitychange", this.layout, this);
48962         r.on("paneladded", this.layout, this);
48963         r.on("panelremoved", this.layout, this);
48964         r.on("invalidated", this.layout, this);
48965         r.on("resized", this.onRegionResized, this);
48966         r.on("collapsed", this.onRegionCollapsed, this);
48967         r.on("expanded", this.onRegionExpanded, this);
48968     },
48969
48970     /**
48971      * Performs a layout update.
48972      */
48973     layout : function(){
48974         if(this.updating) return;
48975         var size = this.getViewSize();
48976         var w = size.width;
48977         var h = size.height;
48978         var centerW = w;
48979         var centerH = h;
48980         var centerY = 0;
48981         var centerX = 0;
48982         //var x = 0, y = 0;
48983
48984         var rs = this.regions;
48985         var north = rs["north"];
48986         var south = rs["south"]; 
48987         var west = rs["west"];
48988         var east = rs["east"];
48989         var center = rs["center"];
48990         //if(this.hideOnLayout){ // not supported anymore
48991             //c.el.setStyle("display", "none");
48992         //}
48993         if(north && north.isVisible()){
48994             var b = north.getBox();
48995             var m = north.getMargins();
48996             b.width = w - (m.left+m.right);
48997             b.x = m.left;
48998             b.y = m.top;
48999             centerY = b.height + b.y + m.bottom;
49000             centerH -= centerY;
49001             north.updateBox(this.safeBox(b));
49002         }
49003         if(south && south.isVisible()){
49004             var b = south.getBox();
49005             var m = south.getMargins();
49006             b.width = w - (m.left+m.right);
49007             b.x = m.left;
49008             var totalHeight = (b.height + m.top + m.bottom);
49009             b.y = h - totalHeight + m.top;
49010             centerH -= totalHeight;
49011             south.updateBox(this.safeBox(b));
49012         }
49013         if(west && west.isVisible()){
49014             var b = west.getBox();
49015             var m = west.getMargins();
49016             b.height = centerH - (m.top+m.bottom);
49017             b.x = m.left;
49018             b.y = centerY + m.top;
49019             var totalWidth = (b.width + m.left + m.right);
49020             centerX += totalWidth;
49021             centerW -= totalWidth;
49022             west.updateBox(this.safeBox(b));
49023         }
49024         if(east && east.isVisible()){
49025             var b = east.getBox();
49026             var m = east.getMargins();
49027             b.height = centerH - (m.top+m.bottom);
49028             var totalWidth = (b.width + m.left + m.right);
49029             b.x = w - totalWidth + m.left;
49030             b.y = centerY + m.top;
49031             centerW -= totalWidth;
49032             east.updateBox(this.safeBox(b));
49033         }
49034         if(center){
49035             var m = center.getMargins();
49036             var centerBox = {
49037                 x: centerX + m.left,
49038                 y: centerY + m.top,
49039                 width: centerW - (m.left+m.right),
49040                 height: centerH - (m.top+m.bottom)
49041             };
49042             //if(this.hideOnLayout){
49043                 //center.el.setStyle("display", "block");
49044             //}
49045             center.updateBox(this.safeBox(centerBox));
49046         }
49047         this.el.repaint();
49048         this.fireEvent("layout", this);
49049     },
49050
49051     // private
49052     safeBox : function(box){
49053         box.width = Math.max(0, box.width);
49054         box.height = Math.max(0, box.height);
49055         return box;
49056     },
49057
49058     /**
49059      * Adds a ContentPanel (or subclass) to this layout.
49060      * @param {String} target The target region key (north, south, east, west or center).
49061      * @param {Roo.ContentPanel} panel The panel to add
49062      * @return {Roo.ContentPanel} The added panel
49063      */
49064     add : function(target, panel){
49065          
49066         target = target.toLowerCase();
49067         return this.regions[target].add(panel);
49068     },
49069
49070     /**
49071      * Remove a ContentPanel (or subclass) to this layout.
49072      * @param {String} target The target region key (north, south, east, west or center).
49073      * @param {Number/String/Roo.ContentPanel} panel The index, id or panel to remove
49074      * @return {Roo.ContentPanel} The removed panel
49075      */
49076     remove : function(target, panel){
49077         target = target.toLowerCase();
49078         return this.regions[target].remove(panel);
49079     },
49080
49081     /**
49082      * Searches all regions for a panel with the specified id
49083      * @param {String} panelId
49084      * @return {Roo.ContentPanel} The panel or null if it wasn't found
49085      */
49086     findPanel : function(panelId){
49087         var rs = this.regions;
49088         for(var target in rs){
49089             if(typeof rs[target] != "function"){
49090                 var p = rs[target].getPanel(panelId);
49091                 if(p){
49092                     return p;
49093                 }
49094             }
49095         }
49096         return null;
49097     },
49098
49099     /**
49100      * Searches all regions for a panel with the specified id and activates (shows) it.
49101      * @param {String/ContentPanel} panelId The panels id or the panel itself
49102      * @return {Roo.ContentPanel} The shown panel or null
49103      */
49104     showPanel : function(panelId) {
49105       var rs = this.regions;
49106       for(var target in rs){
49107          var r = rs[target];
49108          if(typeof r != "function"){
49109             if(r.hasPanel(panelId)){
49110                return r.showPanel(panelId);
49111             }
49112          }
49113       }
49114       return null;
49115    },
49116
49117    /**
49118      * Restores this layout's state using Roo.state.Manager or the state provided by the passed provider.
49119      * @param {Roo.state.Provider} provider (optional) An alternate state provider
49120      */
49121     restoreState : function(provider){
49122         if(!provider){
49123             provider = Roo.state.Manager;
49124         }
49125         var sm = new Roo.LayoutStateManager();
49126         sm.init(this, provider);
49127     },
49128
49129     /**
49130      * Adds a batch of multiple ContentPanels dynamically by passing a special regions config object.  This config
49131      * object should contain properties for each region to add ContentPanels to, and each property's value should be
49132      * a valid ContentPanel config object.  Example:
49133      * <pre><code>
49134 // Create the main layout
49135 var layout = new Roo.BorderLayout('main-ct', {
49136     west: {
49137         split:true,
49138         minSize: 175,
49139         titlebar: true
49140     },
49141     center: {
49142         title:'Components'
49143     }
49144 }, 'main-ct');
49145
49146 // Create and add multiple ContentPanels at once via configs
49147 layout.batchAdd({
49148    west: {
49149        id: 'source-files',
49150        autoCreate:true,
49151        title:'Ext Source Files',
49152        autoScroll:true,
49153        fitToFrame:true
49154    },
49155    center : {
49156        el: cview,
49157        autoScroll:true,
49158        fitToFrame:true,
49159        toolbar: tb,
49160        resizeEl:'cbody'
49161    }
49162 });
49163 </code></pre>
49164      * @param {Object} regions An object containing ContentPanel configs by region name
49165      */
49166     batchAdd : function(regions){
49167         this.beginUpdate();
49168         for(var rname in regions){
49169             var lr = this.regions[rname];
49170             if(lr){
49171                 this.addTypedPanels(lr, regions[rname]);
49172             }
49173         }
49174         this.endUpdate();
49175     },
49176
49177     // private
49178     addTypedPanels : function(lr, ps){
49179         if(typeof ps == 'string'){
49180             lr.add(new Roo.ContentPanel(ps));
49181         }
49182         else if(ps instanceof Array){
49183             for(var i =0, len = ps.length; i < len; i++){
49184                 this.addTypedPanels(lr, ps[i]);
49185             }
49186         }
49187         else if(!ps.events){ // raw config?
49188             var el = ps.el;
49189             delete ps.el; // prevent conflict
49190             lr.add(new Roo.ContentPanel(el || Roo.id(), ps));
49191         }
49192         else {  // panel object assumed!
49193             lr.add(ps);
49194         }
49195     },
49196     /**
49197      * Adds a xtype elements to the layout.
49198      * <pre><code>
49199
49200 layout.addxtype({
49201        xtype : 'ContentPanel',
49202        region: 'west',
49203        items: [ .... ]
49204    }
49205 );
49206
49207 layout.addxtype({
49208         xtype : 'NestedLayoutPanel',
49209         region: 'west',
49210         layout: {
49211            center: { },
49212            west: { }   
49213         },
49214         items : [ ... list of content panels or nested layout panels.. ]
49215    }
49216 );
49217 </code></pre>
49218      * @param {Object} cfg Xtype definition of item to add.
49219      */
49220     addxtype : function(cfg)
49221     {
49222         // basically accepts a pannel...
49223         // can accept a layout region..!?!?
49224         //Roo.log('Roo.BorderLayout add ' + cfg.xtype)
49225         
49226         if (!cfg.xtype.match(/Panel$/)) {
49227             return false;
49228         }
49229         var ret = false;
49230         
49231         if (typeof(cfg.region) == 'undefined') {
49232             Roo.log("Failed to add Panel, region was not set");
49233             Roo.log(cfg);
49234             return false;
49235         }
49236         var region = cfg.region;
49237         delete cfg.region;
49238         
49239           
49240         var xitems = [];
49241         if (cfg.items) {
49242             xitems = cfg.items;
49243             delete cfg.items;
49244         }
49245         var nb = false;
49246         
49247         switch(cfg.xtype) 
49248         {
49249             case 'ContentPanel':  // ContentPanel (el, cfg)
49250             case 'ScrollPanel':  // ContentPanel (el, cfg)
49251             case 'ViewPanel': 
49252                 if(cfg.autoCreate) {
49253                     ret = new Roo[cfg.xtype](cfg); // new panel!!!!!
49254                 } else {
49255                     var el = this.el.createChild();
49256                     ret = new Roo[cfg.xtype](el, cfg); // new panel!!!!!
49257                 }
49258                 
49259                 this.add(region, ret);
49260                 break;
49261             
49262             
49263             case 'TreePanel': // our new panel!
49264                 cfg.el = this.el.createChild();
49265                 ret = new Roo[cfg.xtype](cfg); // new panel!!!!!
49266                 this.add(region, ret);
49267                 break;
49268             
49269             case 'NestedLayoutPanel': 
49270                 // create a new Layout (which is  a Border Layout...
49271                 var el = this.el.createChild();
49272                 var clayout = cfg.layout;
49273                 delete cfg.layout;
49274                 clayout.items   = clayout.items  || [];
49275                 // replace this exitems with the clayout ones..
49276                 xitems = clayout.items;
49277                  
49278                 
49279                 if (region == 'center' && this.active && this.getRegion('center').panels.length < 1) {
49280                     cfg.background = false;
49281                 }
49282                 var layout = new Roo.BorderLayout(el, clayout);
49283                 
49284                 ret = new Roo[cfg.xtype](layout, cfg); // new panel!!!!!
49285                 //console.log('adding nested layout panel '  + cfg.toSource());
49286                 this.add(region, ret);
49287                 nb = {}; /// find first...
49288                 break;
49289                 
49290             case 'GridPanel': 
49291             
49292                 // needs grid and region
49293                 
49294                 //var el = this.getRegion(region).el.createChild();
49295                 var el = this.el.createChild();
49296                 // create the grid first...
49297                 
49298                 var grid = new Roo.grid[cfg.grid.xtype](el, cfg.grid);
49299                 delete cfg.grid;
49300                 if (region == 'center' && this.active ) {
49301                     cfg.background = false;
49302                 }
49303                 ret = new Roo[cfg.xtype](grid, cfg); // new panel!!!!!
49304                 
49305                 this.add(region, ret);
49306                 if (cfg.background) {
49307                     ret.on('activate', function(gp) {
49308                         if (!gp.grid.rendered) {
49309                             gp.grid.render();
49310                         }
49311                     });
49312                 } else {
49313                     grid.render();
49314                 }
49315                 break;
49316            
49317            
49318            
49319                 
49320                 
49321                 
49322             default:
49323                 if (typeof(Roo[cfg.xtype]) != 'undefined') {
49324                     
49325                     ret = new Roo[cfg.xtype](cfg); // new panel!!!!!
49326                     this.add(region, ret);
49327                 } else {
49328                 
49329                     alert("Can not add '" + cfg.xtype + "' to BorderLayout");
49330                     return null;
49331                 }
49332                 
49333              // GridPanel (grid, cfg)
49334             
49335         }
49336         this.beginUpdate();
49337         // add children..
49338         var region = '';
49339         var abn = {};
49340         Roo.each(xitems, function(i)  {
49341             region = nb && i.region ? i.region : false;
49342             
49343             var add = ret.addxtype(i);
49344            
49345             if (region) {
49346                 nb[region] = nb[region] == undefined ? 0 : nb[region]+1;
49347                 if (!i.background) {
49348                     abn[region] = nb[region] ;
49349                 }
49350             }
49351             
49352         });
49353         this.endUpdate();
49354
49355         // make the last non-background panel active..
49356         //if (nb) { Roo.log(abn); }
49357         if (nb) {
49358             
49359             for(var r in abn) {
49360                 region = this.getRegion(r);
49361                 if (region) {
49362                     // tried using nb[r], but it does not work..
49363                      
49364                     region.showPanel(abn[r]);
49365                    
49366                 }
49367             }
49368         }
49369         return ret;
49370         
49371     }
49372 });
49373
49374 /**
49375  * Shortcut for creating a new BorderLayout object and adding one or more ContentPanels to it in a single step, handling
49376  * the beginUpdate and endUpdate calls internally.  The key to this method is the <b>panels</b> property that can be
49377  * provided with each region config, which allows you to add ContentPanel configs in addition to the region configs
49378  * during creation.  The following code is equivalent to the constructor-based example at the beginning of this class:
49379  * <pre><code>
49380 // shorthand
49381 var CP = Roo.ContentPanel;
49382
49383 var layout = Roo.BorderLayout.create({
49384     north: {
49385         initialSize: 25,
49386         titlebar: false,
49387         panels: [new CP("north", "North")]
49388     },
49389     west: {
49390         split:true,
49391         initialSize: 200,
49392         minSize: 175,
49393         maxSize: 400,
49394         titlebar: true,
49395         collapsible: true,
49396         panels: [new CP("west", {title: "West"})]
49397     },
49398     east: {
49399         split:true,
49400         initialSize: 202,
49401         minSize: 175,
49402         maxSize: 400,
49403         titlebar: true,
49404         collapsible: true,
49405         panels: [new CP("autoTabs", {title: "Auto Tabs", closable: true})]
49406     },
49407     south: {
49408         split:true,
49409         initialSize: 100,
49410         minSize: 100,
49411         maxSize: 200,
49412         titlebar: true,
49413         collapsible: true,
49414         panels: [new CP("south", {title: "South", closable: true})]
49415     },
49416     center: {
49417         titlebar: true,
49418         autoScroll:true,
49419         resizeTabs: true,
49420         minTabWidth: 50,
49421         preferredTabWidth: 150,
49422         panels: [
49423             new CP("center1", {title: "Close Me", closable: true}),
49424             new CP("center2", {title: "Center Panel", closable: false})
49425         ]
49426     }
49427 }, document.body);
49428
49429 layout.getRegion("center").showPanel("center1");
49430 </code></pre>
49431  * @param config
49432  * @param targetEl
49433  */
49434 Roo.BorderLayout.create = function(config, targetEl){
49435     var layout = new Roo.BorderLayout(targetEl || document.body, config);
49436     layout.beginUpdate();
49437     var regions = Roo.BorderLayout.RegionFactory.validRegions;
49438     for(var j = 0, jlen = regions.length; j < jlen; j++){
49439         var lr = regions[j];
49440         if(layout.regions[lr] && config[lr].panels){
49441             var r = layout.regions[lr];
49442             var ps = config[lr].panels;
49443             layout.addTypedPanels(r, ps);
49444         }
49445     }
49446     layout.endUpdate();
49447     return layout;
49448 };
49449
49450 // private
49451 Roo.BorderLayout.RegionFactory = {
49452     // private
49453     validRegions : ["north","south","east","west","center"],
49454
49455     // private
49456     create : function(target, mgr, config){
49457         target = target.toLowerCase();
49458         if(config.lightweight || config.basic){
49459             return new Roo.BasicLayoutRegion(mgr, config, target);
49460         }
49461         switch(target){
49462             case "north":
49463                 return new Roo.NorthLayoutRegion(mgr, config);
49464             case "south":
49465                 return new Roo.SouthLayoutRegion(mgr, config);
49466             case "east":
49467                 return new Roo.EastLayoutRegion(mgr, config);
49468             case "west":
49469                 return new Roo.WestLayoutRegion(mgr, config);
49470             case "center":
49471                 return new Roo.CenterLayoutRegion(mgr, config);
49472         }
49473         throw 'Layout region "'+target+'" not supported.';
49474     }
49475 };/*
49476  * Based on:
49477  * Ext JS Library 1.1.1
49478  * Copyright(c) 2006-2007, Ext JS, LLC.
49479  *
49480  * Originally Released Under LGPL - original licence link has changed is not relivant.
49481  *
49482  * Fork - LGPL
49483  * <script type="text/javascript">
49484  */
49485  
49486 /**
49487  * @class Roo.BasicLayoutRegion
49488  * @extends Roo.util.Observable
49489  * This class represents a lightweight region in a layout manager. This region does not move dom nodes
49490  * and does not have a titlebar, tabs or any other features. All it does is size and position 
49491  * panels. To create a BasicLayoutRegion, add lightweight:true or basic:true to your regions config.
49492  */
49493 Roo.BasicLayoutRegion = function(mgr, config, pos, skipConfig){
49494     this.mgr = mgr;
49495     this.position  = pos;
49496     this.events = {
49497         /**
49498          * @scope Roo.BasicLayoutRegion
49499          */
49500         
49501         /**
49502          * @event beforeremove
49503          * Fires before a panel is removed (or closed). To cancel the removal set "e.cancel = true" on the event argument.
49504          * @param {Roo.LayoutRegion} this
49505          * @param {Roo.ContentPanel} panel The panel
49506          * @param {Object} e The cancel event object
49507          */
49508         "beforeremove" : true,
49509         /**
49510          * @event invalidated
49511          * Fires when the layout for this region is changed.
49512          * @param {Roo.LayoutRegion} this
49513          */
49514         "invalidated" : true,
49515         /**
49516          * @event visibilitychange
49517          * Fires when this region is shown or hidden 
49518          * @param {Roo.LayoutRegion} this
49519          * @param {Boolean} visibility true or false
49520          */
49521         "visibilitychange" : true,
49522         /**
49523          * @event paneladded
49524          * Fires when a panel is added. 
49525          * @param {Roo.LayoutRegion} this
49526          * @param {Roo.ContentPanel} panel The panel
49527          */
49528         "paneladded" : true,
49529         /**
49530          * @event panelremoved
49531          * Fires when a panel is removed. 
49532          * @param {Roo.LayoutRegion} this
49533          * @param {Roo.ContentPanel} panel The panel
49534          */
49535         "panelremoved" : true,
49536         /**
49537          * @event collapsed
49538          * Fires when this region is collapsed.
49539          * @param {Roo.LayoutRegion} this
49540          */
49541         "collapsed" : true,
49542         /**
49543          * @event expanded
49544          * Fires when this region is expanded.
49545          * @param {Roo.LayoutRegion} this
49546          */
49547         "expanded" : true,
49548         /**
49549          * @event slideshow
49550          * Fires when this region is slid into view.
49551          * @param {Roo.LayoutRegion} this
49552          */
49553         "slideshow" : true,
49554         /**
49555          * @event slidehide
49556          * Fires when this region slides out of view. 
49557          * @param {Roo.LayoutRegion} this
49558          */
49559         "slidehide" : true,
49560         /**
49561          * @event panelactivated
49562          * Fires when a panel is activated. 
49563          * @param {Roo.LayoutRegion} this
49564          * @param {Roo.ContentPanel} panel The activated panel
49565          */
49566         "panelactivated" : true,
49567         /**
49568          * @event resized
49569          * Fires when the user resizes this region. 
49570          * @param {Roo.LayoutRegion} this
49571          * @param {Number} newSize The new size (width for east/west, height for north/south)
49572          */
49573         "resized" : true
49574     };
49575     /** A collection of panels in this region. @type Roo.util.MixedCollection */
49576     this.panels = new Roo.util.MixedCollection();
49577     this.panels.getKey = this.getPanelId.createDelegate(this);
49578     this.box = null;
49579     this.activePanel = null;
49580     // ensure listeners are added...
49581     
49582     if (config.listeners || config.events) {
49583         Roo.BasicLayoutRegion.superclass.constructor.call(this, {
49584             listeners : config.listeners || {},
49585             events : config.events || {}
49586         });
49587     }
49588     
49589     if(skipConfig !== true){
49590         this.applyConfig(config);
49591     }
49592 };
49593
49594 Roo.extend(Roo.BasicLayoutRegion, Roo.util.Observable, {
49595     getPanelId : function(p){
49596         return p.getId();
49597     },
49598     
49599     applyConfig : function(config){
49600         this.margins = config.margins || this.margins || {top: 0, left: 0, right:0, bottom: 0};
49601         this.config = config;
49602         
49603     },
49604     
49605     /**
49606      * Resizes the region to the specified size. For vertical regions (west, east) this adjusts 
49607      * the width, for horizontal (north, south) the height.
49608      * @param {Number} newSize The new width or height
49609      */
49610     resizeTo : function(newSize){
49611         var el = this.el ? this.el :
49612                  (this.activePanel ? this.activePanel.getEl() : null);
49613         if(el){
49614             switch(this.position){
49615                 case "east":
49616                 case "west":
49617                     el.setWidth(newSize);
49618                     this.fireEvent("resized", this, newSize);
49619                 break;
49620                 case "north":
49621                 case "south":
49622                     el.setHeight(newSize);
49623                     this.fireEvent("resized", this, newSize);
49624                 break;                
49625             }
49626         }
49627     },
49628     
49629     getBox : function(){
49630         return this.activePanel ? this.activePanel.getEl().getBox(false, true) : null;
49631     },
49632     
49633     getMargins : function(){
49634         return this.margins;
49635     },
49636     
49637     updateBox : function(box){
49638         this.box = box;
49639         var el = this.activePanel.getEl();
49640         el.dom.style.left = box.x + "px";
49641         el.dom.style.top = box.y + "px";
49642         this.activePanel.setSize(box.width, box.height);
49643     },
49644     
49645     /**
49646      * Returns the container element for this region.
49647      * @return {Roo.Element}
49648      */
49649     getEl : function(){
49650         return this.activePanel;
49651     },
49652     
49653     /**
49654      * Returns true if this region is currently visible.
49655      * @return {Boolean}
49656      */
49657     isVisible : function(){
49658         return this.activePanel ? true : false;
49659     },
49660     
49661     setActivePanel : function(panel){
49662         panel = this.getPanel(panel);
49663         if(this.activePanel && this.activePanel != panel){
49664             this.activePanel.setActiveState(false);
49665             this.activePanel.getEl().setLeftTop(-10000,-10000);
49666         }
49667         this.activePanel = panel;
49668         panel.setActiveState(true);
49669         if(this.box){
49670             panel.setSize(this.box.width, this.box.height);
49671         }
49672         this.fireEvent("panelactivated", this, panel);
49673         this.fireEvent("invalidated");
49674     },
49675     
49676     /**
49677      * Show the specified panel.
49678      * @param {Number/String/ContentPanel} panelId The panels index, id or the panel itself
49679      * @return {Roo.ContentPanel} The shown panel or null
49680      */
49681     showPanel : function(panel){
49682         if(panel = this.getPanel(panel)){
49683             this.setActivePanel(panel);
49684         }
49685         return panel;
49686     },
49687     
49688     /**
49689      * Get the active panel for this region.
49690      * @return {Roo.ContentPanel} The active panel or null
49691      */
49692     getActivePanel : function(){
49693         return this.activePanel;
49694     },
49695     
49696     /**
49697      * Add the passed ContentPanel(s)
49698      * @param {ContentPanel...} panel The ContentPanel(s) to add (you can pass more than one)
49699      * @return {Roo.ContentPanel} The panel added (if only one was added)
49700      */
49701     add : function(panel){
49702         if(arguments.length > 1){
49703             for(var i = 0, len = arguments.length; i < len; i++) {
49704                 this.add(arguments[i]);
49705             }
49706             return null;
49707         }
49708         if(this.hasPanel(panel)){
49709             this.showPanel(panel);
49710             return panel;
49711         }
49712         var el = panel.getEl();
49713         if(el.dom.parentNode != this.mgr.el.dom){
49714             this.mgr.el.dom.appendChild(el.dom);
49715         }
49716         if(panel.setRegion){
49717             panel.setRegion(this);
49718         }
49719         this.panels.add(panel);
49720         el.setStyle("position", "absolute");
49721         if(!panel.background){
49722             this.setActivePanel(panel);
49723             if(this.config.initialSize && this.panels.getCount()==1){
49724                 this.resizeTo(this.config.initialSize);
49725             }
49726         }
49727         this.fireEvent("paneladded", this, panel);
49728         return panel;
49729     },
49730     
49731     /**
49732      * Returns true if the panel is in this region.
49733      * @param {Number/String/ContentPanel} panel The panels index, id or the panel itself
49734      * @return {Boolean}
49735      */
49736     hasPanel : function(panel){
49737         if(typeof panel == "object"){ // must be panel obj
49738             panel = panel.getId();
49739         }
49740         return this.getPanel(panel) ? true : false;
49741     },
49742     
49743     /**
49744      * Removes the specified panel. If preservePanel is not true (either here or in the config), the panel is destroyed.
49745      * @param {Number/String/ContentPanel} panel The panels index, id or the panel itself
49746      * @param {Boolean} preservePanel Overrides the config preservePanel option
49747      * @return {Roo.ContentPanel} The panel that was removed
49748      */
49749     remove : function(panel, preservePanel){
49750         panel = this.getPanel(panel);
49751         if(!panel){
49752             return null;
49753         }
49754         var e = {};
49755         this.fireEvent("beforeremove", this, panel, e);
49756         if(e.cancel === true){
49757             return null;
49758         }
49759         var panelId = panel.getId();
49760         this.panels.removeKey(panelId);
49761         return panel;
49762     },
49763     
49764     /**
49765      * Returns the panel specified or null if it's not in this region.
49766      * @param {Number/String/ContentPanel} panel The panels index, id or the panel itself
49767      * @return {Roo.ContentPanel}
49768      */
49769     getPanel : function(id){
49770         if(typeof id == "object"){ // must be panel obj
49771             return id;
49772         }
49773         return this.panels.get(id);
49774     },
49775     
49776     /**
49777      * Returns this regions position (north/south/east/west/center).
49778      * @return {String} 
49779      */
49780     getPosition: function(){
49781         return this.position;    
49782     }
49783 });/*
49784  * Based on:
49785  * Ext JS Library 1.1.1
49786  * Copyright(c) 2006-2007, Ext JS, LLC.
49787  *
49788  * Originally Released Under LGPL - original licence link has changed is not relivant.
49789  *
49790  * Fork - LGPL
49791  * <script type="text/javascript">
49792  */
49793  
49794 /**
49795  * @class Roo.LayoutRegion
49796  * @extends Roo.BasicLayoutRegion
49797  * This class represents a region in a layout manager.
49798  * @cfg {Boolean}   collapsible     False to disable collapsing (defaults to true)
49799  * @cfg {Boolean}   collapsed       True to set the initial display to collapsed (defaults to false)
49800  * @cfg {Boolean}   floatable       False to disable floating (defaults to true)
49801  * @cfg {Object}    margins         Margins for the element (defaults to {top: 0, left: 0, right:0, bottom: 0})
49802  * @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})
49803  * @cfg {String}    tabPosition     "top" or "bottom" (defaults to "bottom")
49804  * @cfg {String}    collapsedTitle  Optional string message to display in the collapsed block of a north or south region
49805  * @cfg {Boolean}   alwaysShowTabs  True to always display tabs even when there is only 1 panel (defaults to false)
49806  * @cfg {Boolean}   autoScroll      True to enable overflow scrolling (defaults to false)
49807  * @cfg {Boolean}   titlebar        True to display a title bar (defaults to true)
49808  * @cfg {String}    title           The title for the region (overrides panel titles)
49809  * @cfg {Boolean}   animate         True to animate expand/collapse (defaults to false)
49810  * @cfg {Boolean}   autoHide        False to disable auto hiding when the mouse leaves the "floated" region (defaults to true)
49811  * @cfg {Boolean}   preservePanels  True to preserve removed panels so they can be readded later (defaults to false)
49812  * @cfg {Boolean}   closeOnTab      True to place the close icon on the tabs instead of the region titlebar (defaults to false)
49813  * @cfg {Boolean}   hideTabs        True to hide the tab strip (defaults to false)
49814  * @cfg {Boolean}   resizeTabs      True to enable automatic tab resizing. This will resize the tabs so they are all the same size and fit within
49815  *                      the space available, similar to FireFox 1.5 tabs (defaults to false)
49816  * @cfg {Number}    minTabWidth     The minimum tab width (defaults to 40)
49817  * @cfg {Number}    preferredTabWidth The preferred tab width (defaults to 150)
49818  * @cfg {Boolean}   showPin         True to show a pin button
49819  * @cfg {Boolean}   hidden          True to start the region hidden (defaults to false)
49820  * @cfg {Boolean}   hideWhenEmpty   True to hide the region when it has no panels
49821  * @cfg {Boolean}   disableTabTips  True to disable tab tooltips
49822  * @cfg {Number}    width           For East/West panels
49823  * @cfg {Number}    height          For North/South panels
49824  * @cfg {Boolean}   split           To show the splitter
49825  * @cfg {Boolean}   toolbar         xtype configuration for a toolbar - shows on right of tabbar
49826  */
49827 Roo.LayoutRegion = function(mgr, config, pos){
49828     Roo.LayoutRegion.superclass.constructor.call(this, mgr, config, pos, true);
49829     var dh = Roo.DomHelper;
49830     /** This region's container element 
49831     * @type Roo.Element */
49832     this.el = dh.append(mgr.el.dom, {tag: "div", cls: "x-layout-panel x-layout-panel-" + this.position}, true);
49833     /** This region's title element 
49834     * @type Roo.Element */
49835
49836     this.titleEl = dh.append(this.el.dom, {tag: "div", unselectable: "on", cls: "x-unselectable x-layout-panel-hd x-layout-title-"+this.position, children:[
49837         {tag: "span", cls: "x-unselectable x-layout-panel-hd-text", unselectable: "on", html: "&#160;"},
49838         {tag: "div", cls: "x-unselectable x-layout-panel-hd-tools", unselectable: "on"}
49839     ]}, true);
49840     this.titleEl.enableDisplayMode();
49841     /** This region's title text element 
49842     * @type HTMLElement */
49843     this.titleTextEl = this.titleEl.dom.firstChild;
49844     this.tools = Roo.get(this.titleEl.dom.childNodes[1], true);
49845     this.closeBtn = this.createTool(this.tools.dom, "x-layout-close");
49846     this.closeBtn.enableDisplayMode();
49847     this.closeBtn.on("click", this.closeClicked, this);
49848     this.closeBtn.hide();
49849
49850     this.createBody(config);
49851     this.visible = true;
49852     this.collapsed = false;
49853
49854     if(config.hideWhenEmpty){
49855         this.hide();
49856         this.on("paneladded", this.validateVisibility, this);
49857         this.on("panelremoved", this.validateVisibility, this);
49858     }
49859     this.applyConfig(config);
49860 };
49861
49862 Roo.extend(Roo.LayoutRegion, Roo.BasicLayoutRegion, {
49863
49864     createBody : function(){
49865         /** This region's body element 
49866         * @type Roo.Element */
49867         this.bodyEl = this.el.createChild({tag: "div", cls: "x-layout-panel-body"});
49868     },
49869
49870     applyConfig : function(c){
49871         if(c.collapsible && this.position != "center" && !this.collapsedEl){
49872             var dh = Roo.DomHelper;
49873             if(c.titlebar !== false){
49874                 this.collapseBtn = this.createTool(this.tools.dom, "x-layout-collapse-"+this.position);
49875                 this.collapseBtn.on("click", this.collapse, this);
49876                 this.collapseBtn.enableDisplayMode();
49877
49878                 if(c.showPin === true || this.showPin){
49879                     this.stickBtn = this.createTool(this.tools.dom, "x-layout-stick");
49880                     this.stickBtn.enableDisplayMode();
49881                     this.stickBtn.on("click", this.expand, this);
49882                     this.stickBtn.hide();
49883                 }
49884             }
49885             /** This region's collapsed element
49886             * @type Roo.Element */
49887             this.collapsedEl = dh.append(this.mgr.el.dom, {cls: "x-layout-collapsed x-layout-collapsed-"+this.position, children:[
49888                 {cls: "x-layout-collapsed-tools", children:[{cls: "x-layout-ctools-inner"}]}
49889             ]}, true);
49890             if(c.floatable !== false){
49891                this.collapsedEl.addClassOnOver("x-layout-collapsed-over");
49892                this.collapsedEl.on("click", this.collapseClick, this);
49893             }
49894
49895             if(c.collapsedTitle && (this.position == "north" || this.position== "south")) {
49896                 this.collapsedTitleTextEl = dh.append(this.collapsedEl.dom, {tag: "div", cls: "x-unselectable x-layout-panel-hd-text",
49897                    id: "message", unselectable: "on", style:{"float":"left"}});
49898                this.collapsedTitleTextEl.innerHTML = c.collapsedTitle;
49899              }
49900             this.expandBtn = this.createTool(this.collapsedEl.dom.firstChild.firstChild, "x-layout-expand-"+this.position);
49901             this.expandBtn.on("click", this.expand, this);
49902         }
49903         if(this.collapseBtn){
49904             this.collapseBtn.setVisible(c.collapsible == true);
49905         }
49906         this.cmargins = c.cmargins || this.cmargins ||
49907                          (this.position == "west" || this.position == "east" ?
49908                              {top: 0, left: 2, right:2, bottom: 0} :
49909                              {top: 2, left: 0, right:0, bottom: 2});
49910         this.margins = c.margins || this.margins || {top: 0, left: 0, right:0, bottom: 0};
49911         this.bottomTabs = c.tabPosition != "top";
49912         this.autoScroll = c.autoScroll || false;
49913         if(this.autoScroll){
49914             this.bodyEl.setStyle("overflow", "auto");
49915         }else{
49916             this.bodyEl.setStyle("overflow", "hidden");
49917         }
49918         //if(c.titlebar !== false){
49919             if((!c.titlebar && !c.title) || c.titlebar === false){
49920                 this.titleEl.hide();
49921             }else{
49922                 this.titleEl.show();
49923                 if(c.title){
49924                     this.titleTextEl.innerHTML = c.title;
49925                 }
49926             }
49927         //}
49928         this.duration = c.duration || .30;
49929         this.slideDuration = c.slideDuration || .45;
49930         this.config = c;
49931         if(c.collapsed){
49932             this.collapse(true);
49933         }
49934         if(c.hidden){
49935             this.hide();
49936         }
49937     },
49938     /**
49939      * Returns true if this region is currently visible.
49940      * @return {Boolean}
49941      */
49942     isVisible : function(){
49943         return this.visible;
49944     },
49945
49946     /**
49947      * Updates the title for collapsed north/south regions (used with {@link #collapsedTitle} config option)
49948      * @param {String} title (optional) The title text (accepts HTML markup, defaults to the numeric character reference for a non-breaking space, "&amp;#160;")
49949      */
49950     setCollapsedTitle : function(title){
49951         title = title || "&#160;";
49952         if(this.collapsedTitleTextEl){
49953             this.collapsedTitleTextEl.innerHTML = title;
49954         }
49955     },
49956
49957     getBox : function(){
49958         var b;
49959         if(!this.collapsed){
49960             b = this.el.getBox(false, true);
49961         }else{
49962             b = this.collapsedEl.getBox(false, true);
49963         }
49964         return b;
49965     },
49966
49967     getMargins : function(){
49968         return this.collapsed ? this.cmargins : this.margins;
49969     },
49970
49971     highlight : function(){
49972         this.el.addClass("x-layout-panel-dragover");
49973     },
49974
49975     unhighlight : function(){
49976         this.el.removeClass("x-layout-panel-dragover");
49977     },
49978
49979     updateBox : function(box){
49980         this.box = box;
49981         if(!this.collapsed){
49982             this.el.dom.style.left = box.x + "px";
49983             this.el.dom.style.top = box.y + "px";
49984             this.updateBody(box.width, box.height);
49985         }else{
49986             this.collapsedEl.dom.style.left = box.x + "px";
49987             this.collapsedEl.dom.style.top = box.y + "px";
49988             this.collapsedEl.setSize(box.width, box.height);
49989         }
49990         if(this.tabs){
49991             this.tabs.autoSizeTabs();
49992         }
49993     },
49994
49995     updateBody : function(w, h){
49996         if(w !== null){
49997             this.el.setWidth(w);
49998             w -= this.el.getBorderWidth("rl");
49999             if(this.config.adjustments){
50000                 w += this.config.adjustments[0];
50001             }
50002         }
50003         if(h !== null){
50004             this.el.setHeight(h);
50005             h = this.titleEl && this.titleEl.isDisplayed() ? h - (this.titleEl.getHeight()||0) : h;
50006             h -= this.el.getBorderWidth("tb");
50007             if(this.config.adjustments){
50008                 h += this.config.adjustments[1];
50009             }
50010             this.bodyEl.setHeight(h);
50011             if(this.tabs){
50012                 h = this.tabs.syncHeight(h);
50013             }
50014         }
50015         if(this.panelSize){
50016             w = w !== null ? w : this.panelSize.width;
50017             h = h !== null ? h : this.panelSize.height;
50018         }
50019         if(this.activePanel){
50020             var el = this.activePanel.getEl();
50021             w = w !== null ? w : el.getWidth();
50022             h = h !== null ? h : el.getHeight();
50023             this.panelSize = {width: w, height: h};
50024             this.activePanel.setSize(w, h);
50025         }
50026         if(Roo.isIE && this.tabs){
50027             this.tabs.el.repaint();
50028         }
50029     },
50030
50031     /**
50032      * Returns the container element for this region.
50033      * @return {Roo.Element}
50034      */
50035     getEl : function(){
50036         return this.el;
50037     },
50038
50039     /**
50040      * Hides this region.
50041      */
50042     hide : function(){
50043         if(!this.collapsed){
50044             this.el.dom.style.left = "-2000px";
50045             this.el.hide();
50046         }else{
50047             this.collapsedEl.dom.style.left = "-2000px";
50048             this.collapsedEl.hide();
50049         }
50050         this.visible = false;
50051         this.fireEvent("visibilitychange", this, false);
50052     },
50053
50054     /**
50055      * Shows this region if it was previously hidden.
50056      */
50057     show : function(){
50058         if(!this.collapsed){
50059             this.el.show();
50060         }else{
50061             this.collapsedEl.show();
50062         }
50063         this.visible = true;
50064         this.fireEvent("visibilitychange", this, true);
50065     },
50066
50067     closeClicked : function(){
50068         if(this.activePanel){
50069             this.remove(this.activePanel);
50070         }
50071     },
50072
50073     collapseClick : function(e){
50074         if(this.isSlid){
50075            e.stopPropagation();
50076            this.slideIn();
50077         }else{
50078            e.stopPropagation();
50079            this.slideOut();
50080         }
50081     },
50082
50083     /**
50084      * Collapses this region.
50085      * @param {Boolean} skipAnim (optional) true to collapse the element without animation (if animate is true)
50086      */
50087     collapse : function(skipAnim){
50088         if(this.collapsed) return;
50089         this.collapsed = true;
50090         if(this.split){
50091             this.split.el.hide();
50092         }
50093         if(this.config.animate && skipAnim !== true){
50094             this.fireEvent("invalidated", this);
50095             this.animateCollapse();
50096         }else{
50097             this.el.setLocation(-20000,-20000);
50098             this.el.hide();
50099             this.collapsedEl.show();
50100             this.fireEvent("collapsed", this);
50101             this.fireEvent("invalidated", this);
50102         }
50103     },
50104
50105     animateCollapse : function(){
50106         // overridden
50107     },
50108
50109     /**
50110      * Expands this region if it was previously collapsed.
50111      * @param {Roo.EventObject} e The event that triggered the expand (or null if calling manually)
50112      * @param {Boolean} skipAnim (optional) true to expand the element without animation (if animate is true)
50113      */
50114     expand : function(e, skipAnim){
50115         if(e) e.stopPropagation();
50116         if(!this.collapsed || this.el.hasActiveFx()) return;
50117         if(this.isSlid){
50118             this.afterSlideIn();
50119             skipAnim = true;
50120         }
50121         this.collapsed = false;
50122         if(this.config.animate && skipAnim !== true){
50123             this.animateExpand();
50124         }else{
50125             this.el.show();
50126             if(this.split){
50127                 this.split.el.show();
50128             }
50129             this.collapsedEl.setLocation(-2000,-2000);
50130             this.collapsedEl.hide();
50131             this.fireEvent("invalidated", this);
50132             this.fireEvent("expanded", this);
50133         }
50134     },
50135
50136     animateExpand : function(){
50137         // overridden
50138     },
50139
50140     initTabs : function()
50141     {
50142         this.bodyEl.setStyle("overflow", "hidden");
50143         var ts = new Roo.TabPanel(
50144                 this.bodyEl.dom,
50145                 {
50146                     tabPosition: this.bottomTabs ? 'bottom' : 'top',
50147                     disableTooltips: this.config.disableTabTips,
50148                     toolbar : this.config.toolbar
50149                 }
50150         );
50151         if(this.config.hideTabs){
50152             ts.stripWrap.setDisplayed(false);
50153         }
50154         this.tabs = ts;
50155         ts.resizeTabs = this.config.resizeTabs === true;
50156         ts.minTabWidth = this.config.minTabWidth || 40;
50157         ts.maxTabWidth = this.config.maxTabWidth || 250;
50158         ts.preferredTabWidth = this.config.preferredTabWidth || 150;
50159         ts.monitorResize = false;
50160         ts.bodyEl.setStyle("overflow", this.config.autoScroll ? "auto" : "hidden");
50161         ts.bodyEl.addClass('x-layout-tabs-body');
50162         this.panels.each(this.initPanelAsTab, this);
50163     },
50164
50165     initPanelAsTab : function(panel){
50166         var ti = this.tabs.addTab(panel.getEl().id, panel.getTitle(), null,
50167                     this.config.closeOnTab && panel.isClosable());
50168         if(panel.tabTip !== undefined){
50169             ti.setTooltip(panel.tabTip);
50170         }
50171         ti.on("activate", function(){
50172               this.setActivePanel(panel);
50173         }, this);
50174         if(this.config.closeOnTab){
50175             ti.on("beforeclose", function(t, e){
50176                 e.cancel = true;
50177                 this.remove(panel);
50178             }, this);
50179         }
50180         return ti;
50181     },
50182
50183     updatePanelTitle : function(panel, title){
50184         if(this.activePanel == panel){
50185             this.updateTitle(title);
50186         }
50187         if(this.tabs){
50188             var ti = this.tabs.getTab(panel.getEl().id);
50189             ti.setText(title);
50190             if(panel.tabTip !== undefined){
50191                 ti.setTooltip(panel.tabTip);
50192             }
50193         }
50194     },
50195
50196     updateTitle : function(title){
50197         if(this.titleTextEl && !this.config.title){
50198             this.titleTextEl.innerHTML = (typeof title != "undefined" && title.length > 0 ? title : "&#160;");
50199         }
50200     },
50201
50202     setActivePanel : function(panel){
50203         panel = this.getPanel(panel);
50204         if(this.activePanel && this.activePanel != panel){
50205             this.activePanel.setActiveState(false);
50206         }
50207         this.activePanel = panel;
50208         panel.setActiveState(true);
50209         if(this.panelSize){
50210             panel.setSize(this.panelSize.width, this.panelSize.height);
50211         }
50212         if(this.closeBtn){
50213             this.closeBtn.setVisible(!this.config.closeOnTab && !this.isSlid && panel.isClosable());
50214         }
50215         this.updateTitle(panel.getTitle());
50216         if(this.tabs){
50217             this.fireEvent("invalidated", this);
50218         }
50219         this.fireEvent("panelactivated", this, panel);
50220     },
50221
50222     /**
50223      * Shows the specified panel.
50224      * @param {Number/String/ContentPanel} panelId The panel's index, id or the panel itself
50225      * @return {Roo.ContentPanel} The shown panel, or null if a panel could not be found from panelId
50226      */
50227     showPanel : function(panel){
50228         if(panel = this.getPanel(panel)){
50229             if(this.tabs){
50230                 var tab = this.tabs.getTab(panel.getEl().id);
50231                 if(tab.isHidden()){
50232                     this.tabs.unhideTab(tab.id);
50233                 }
50234                 tab.activate();
50235             }else{
50236                 this.setActivePanel(panel);
50237             }
50238         }
50239         return panel;
50240     },
50241
50242     /**
50243      * Get the active panel for this region.
50244      * @return {Roo.ContentPanel} The active panel or null
50245      */
50246     getActivePanel : function(){
50247         return this.activePanel;
50248     },
50249
50250     validateVisibility : function(){
50251         if(this.panels.getCount() < 1){
50252             this.updateTitle("&#160;");
50253             this.closeBtn.hide();
50254             this.hide();
50255         }else{
50256             if(!this.isVisible()){
50257                 this.show();
50258             }
50259         }
50260     },
50261
50262     /**
50263      * Adds the passed ContentPanel(s) to this region.
50264      * @param {ContentPanel...} panel The ContentPanel(s) to add (you can pass more than one)
50265      * @return {Roo.ContentPanel} The panel added (if only one was added; null otherwise)
50266      */
50267     add : function(panel){
50268         if(arguments.length > 1){
50269             for(var i = 0, len = arguments.length; i < len; i++) {
50270                 this.add(arguments[i]);
50271             }
50272             return null;
50273         }
50274         if(this.hasPanel(panel)){
50275             this.showPanel(panel);
50276             return panel;
50277         }
50278         panel.setRegion(this);
50279         this.panels.add(panel);
50280         if(this.panels.getCount() == 1 && !this.config.alwaysShowTabs){
50281             this.bodyEl.dom.appendChild(panel.getEl().dom);
50282             if(panel.background !== true){
50283                 this.setActivePanel(panel);
50284             }
50285             this.fireEvent("paneladded", this, panel);
50286             return panel;
50287         }
50288         if(!this.tabs){
50289             this.initTabs();
50290         }else{
50291             this.initPanelAsTab(panel);
50292         }
50293         if(panel.background !== true){
50294             this.tabs.activate(panel.getEl().id);
50295         }
50296         this.fireEvent("paneladded", this, panel);
50297         return panel;
50298     },
50299
50300     /**
50301      * Hides the tab for the specified panel.
50302      * @param {Number/String/ContentPanel} panel The panel's index, id or the panel itself
50303      */
50304     hidePanel : function(panel){
50305         if(this.tabs && (panel = this.getPanel(panel))){
50306             this.tabs.hideTab(panel.getEl().id);
50307         }
50308     },
50309
50310     /**
50311      * Unhides the tab for a previously hidden panel.
50312      * @param {Number/String/ContentPanel} panel The panel's index, id or the panel itself
50313      */
50314     unhidePanel : function(panel){
50315         if(this.tabs && (panel = this.getPanel(panel))){
50316             this.tabs.unhideTab(panel.getEl().id);
50317         }
50318     },
50319
50320     clearPanels : function(){
50321         while(this.panels.getCount() > 0){
50322              this.remove(this.panels.first());
50323         }
50324     },
50325
50326     /**
50327      * Removes the specified panel. If preservePanel is not true (either here or in the config), the panel is destroyed.
50328      * @param {Number/String/ContentPanel} panel The panel's index, id or the panel itself
50329      * @param {Boolean} preservePanel Overrides the config preservePanel option
50330      * @return {Roo.ContentPanel} The panel that was removed
50331      */
50332     remove : function(panel, preservePanel){
50333         panel = this.getPanel(panel);
50334         if(!panel){
50335             return null;
50336         }
50337         var e = {};
50338         this.fireEvent("beforeremove", this, panel, e);
50339         if(e.cancel === true){
50340             return null;
50341         }
50342         preservePanel = (typeof preservePanel != "undefined" ? preservePanel : (this.config.preservePanels === true || panel.preserve === true));
50343         var panelId = panel.getId();
50344         this.panels.removeKey(panelId);
50345         if(preservePanel){
50346             document.body.appendChild(panel.getEl().dom);
50347         }
50348         if(this.tabs){
50349             this.tabs.removeTab(panel.getEl().id);
50350         }else if (!preservePanel){
50351             this.bodyEl.dom.removeChild(panel.getEl().dom);
50352         }
50353         if(this.panels.getCount() == 1 && this.tabs && !this.config.alwaysShowTabs){
50354             var p = this.panels.first();
50355             var tempEl = document.createElement("div"); // temp holder to keep IE from deleting the node
50356             tempEl.appendChild(p.getEl().dom);
50357             this.bodyEl.update("");
50358             this.bodyEl.dom.appendChild(p.getEl().dom);
50359             tempEl = null;
50360             this.updateTitle(p.getTitle());
50361             this.tabs = null;
50362             this.bodyEl.setStyle("overflow", this.config.autoScroll ? "auto" : "hidden");
50363             this.setActivePanel(p);
50364         }
50365         panel.setRegion(null);
50366         if(this.activePanel == panel){
50367             this.activePanel = null;
50368         }
50369         if(this.config.autoDestroy !== false && preservePanel !== true){
50370             try{panel.destroy();}catch(e){}
50371         }
50372         this.fireEvent("panelremoved", this, panel);
50373         return panel;
50374     },
50375
50376     /**
50377      * Returns the TabPanel component used by this region
50378      * @return {Roo.TabPanel}
50379      */
50380     getTabs : function(){
50381         return this.tabs;
50382     },
50383
50384     createTool : function(parentEl, className){
50385         var btn = Roo.DomHelper.append(parentEl, {tag: "div", cls: "x-layout-tools-button",
50386             children: [{tag: "div", cls: "x-layout-tools-button-inner " + className, html: "&#160;"}]}, true);
50387         btn.addClassOnOver("x-layout-tools-button-over");
50388         return btn;
50389     }
50390 });/*
50391  * Based on:
50392  * Ext JS Library 1.1.1
50393  * Copyright(c) 2006-2007, Ext JS, LLC.
50394  *
50395  * Originally Released Under LGPL - original licence link has changed is not relivant.
50396  *
50397  * Fork - LGPL
50398  * <script type="text/javascript">
50399  */
50400  
50401
50402
50403 /**
50404  * @class Roo.SplitLayoutRegion
50405  * @extends Roo.LayoutRegion
50406  * Adds a splitbar and other (private) useful functionality to a {@link Roo.LayoutRegion}.
50407  */
50408 Roo.SplitLayoutRegion = function(mgr, config, pos, cursor){
50409     this.cursor = cursor;
50410     Roo.SplitLayoutRegion.superclass.constructor.call(this, mgr, config, pos);
50411 };
50412
50413 Roo.extend(Roo.SplitLayoutRegion, Roo.LayoutRegion, {
50414     splitTip : "Drag to resize.",
50415     collapsibleSplitTip : "Drag to resize. Double click to hide.",
50416     useSplitTips : false,
50417
50418     applyConfig : function(config){
50419         Roo.SplitLayoutRegion.superclass.applyConfig.call(this, config);
50420         if(config.split){
50421             if(!this.split){
50422                 var splitEl = Roo.DomHelper.append(this.mgr.el.dom, 
50423                         {tag: "div", id: this.el.id + "-split", cls: "x-layout-split x-layout-split-"+this.position, html: "&#160;"});
50424                 /** The SplitBar for this region 
50425                 * @type Roo.SplitBar */
50426                 this.split = new Roo.SplitBar(splitEl, this.el, this.orientation);
50427                 this.split.on("moved", this.onSplitMove, this);
50428                 this.split.useShim = config.useShim === true;
50429                 this.split.getMaximumSize = this[this.position == 'north' || this.position == 'south' ? 'getVMaxSize' : 'getHMaxSize'].createDelegate(this);
50430                 if(this.useSplitTips){
50431                     this.split.el.dom.title = config.collapsible ? this.collapsibleSplitTip : this.splitTip;
50432                 }
50433                 if(config.collapsible){
50434                     this.split.el.on("dblclick", this.collapse,  this);
50435                 }
50436             }
50437             if(typeof config.minSize != "undefined"){
50438                 this.split.minSize = config.minSize;
50439             }
50440             if(typeof config.maxSize != "undefined"){
50441                 this.split.maxSize = config.maxSize;
50442             }
50443             if(config.hideWhenEmpty || config.hidden || config.collapsed){
50444                 this.hideSplitter();
50445             }
50446         }
50447     },
50448
50449     getHMaxSize : function(){
50450          var cmax = this.config.maxSize || 10000;
50451          var center = this.mgr.getRegion("center");
50452          return Math.min(cmax, (this.el.getWidth()+center.getEl().getWidth())-center.getMinWidth());
50453     },
50454
50455     getVMaxSize : function(){
50456          var cmax = this.config.maxSize || 10000;
50457          var center = this.mgr.getRegion("center");
50458          return Math.min(cmax, (this.el.getHeight()+center.getEl().getHeight())-center.getMinHeight());
50459     },
50460
50461     onSplitMove : function(split, newSize){
50462         this.fireEvent("resized", this, newSize);
50463     },
50464     
50465     /** 
50466      * Returns the {@link Roo.SplitBar} for this region.
50467      * @return {Roo.SplitBar}
50468      */
50469     getSplitBar : function(){
50470         return this.split;
50471     },
50472     
50473     hide : function(){
50474         this.hideSplitter();
50475         Roo.SplitLayoutRegion.superclass.hide.call(this);
50476     },
50477
50478     hideSplitter : function(){
50479         if(this.split){
50480             this.split.el.setLocation(-2000,-2000);
50481             this.split.el.hide();
50482         }
50483     },
50484
50485     show : function(){
50486         if(this.split){
50487             this.split.el.show();
50488         }
50489         Roo.SplitLayoutRegion.superclass.show.call(this);
50490     },
50491     
50492     beforeSlide: function(){
50493         if(Roo.isGecko){// firefox overflow auto bug workaround
50494             this.bodyEl.clip();
50495             if(this.tabs) this.tabs.bodyEl.clip();
50496             if(this.activePanel){
50497                 this.activePanel.getEl().clip();
50498                 
50499                 if(this.activePanel.beforeSlide){
50500                     this.activePanel.beforeSlide();
50501                 }
50502             }
50503         }
50504     },
50505     
50506     afterSlide : function(){
50507         if(Roo.isGecko){// firefox overflow auto bug workaround
50508             this.bodyEl.unclip();
50509             if(this.tabs) this.tabs.bodyEl.unclip();
50510             if(this.activePanel){
50511                 this.activePanel.getEl().unclip();
50512                 if(this.activePanel.afterSlide){
50513                     this.activePanel.afterSlide();
50514                 }
50515             }
50516         }
50517     },
50518
50519     initAutoHide : function(){
50520         if(this.autoHide !== false){
50521             if(!this.autoHideHd){
50522                 var st = new Roo.util.DelayedTask(this.slideIn, this);
50523                 this.autoHideHd = {
50524                     "mouseout": function(e){
50525                         if(!e.within(this.el, true)){
50526                             st.delay(500);
50527                         }
50528                     },
50529                     "mouseover" : function(e){
50530                         st.cancel();
50531                     },
50532                     scope : this
50533                 };
50534             }
50535             this.el.on(this.autoHideHd);
50536         }
50537     },
50538
50539     clearAutoHide : function(){
50540         if(this.autoHide !== false){
50541             this.el.un("mouseout", this.autoHideHd.mouseout);
50542             this.el.un("mouseover", this.autoHideHd.mouseover);
50543         }
50544     },
50545
50546     clearMonitor : function(){
50547         Roo.get(document).un("click", this.slideInIf, this);
50548     },
50549
50550     // these names are backwards but not changed for compat
50551     slideOut : function(){
50552         if(this.isSlid || this.el.hasActiveFx()){
50553             return;
50554         }
50555         this.isSlid = true;
50556         if(this.collapseBtn){
50557             this.collapseBtn.hide();
50558         }
50559         this.closeBtnState = this.closeBtn.getStyle('display');
50560         this.closeBtn.hide();
50561         if(this.stickBtn){
50562             this.stickBtn.show();
50563         }
50564         this.el.show();
50565         this.el.alignTo(this.collapsedEl, this.getCollapseAnchor());
50566         this.beforeSlide();
50567         this.el.setStyle("z-index", 10001);
50568         this.el.slideIn(this.getSlideAnchor(), {
50569             callback: function(){
50570                 this.afterSlide();
50571                 this.initAutoHide();
50572                 Roo.get(document).on("click", this.slideInIf, this);
50573                 this.fireEvent("slideshow", this);
50574             },
50575             scope: this,
50576             block: true
50577         });
50578     },
50579
50580     afterSlideIn : function(){
50581         this.clearAutoHide();
50582         this.isSlid = false;
50583         this.clearMonitor();
50584         this.el.setStyle("z-index", "");
50585         if(this.collapseBtn){
50586             this.collapseBtn.show();
50587         }
50588         this.closeBtn.setStyle('display', this.closeBtnState);
50589         if(this.stickBtn){
50590             this.stickBtn.hide();
50591         }
50592         this.fireEvent("slidehide", this);
50593     },
50594
50595     slideIn : function(cb){
50596         if(!this.isSlid || this.el.hasActiveFx()){
50597             Roo.callback(cb);
50598             return;
50599         }
50600         this.isSlid = false;
50601         this.beforeSlide();
50602         this.el.slideOut(this.getSlideAnchor(), {
50603             callback: function(){
50604                 this.el.setLeftTop(-10000, -10000);
50605                 this.afterSlide();
50606                 this.afterSlideIn();
50607                 Roo.callback(cb);
50608             },
50609             scope: this,
50610             block: true
50611         });
50612     },
50613     
50614     slideInIf : function(e){
50615         if(!e.within(this.el)){
50616             this.slideIn();
50617         }
50618     },
50619
50620     animateCollapse : function(){
50621         this.beforeSlide();
50622         this.el.setStyle("z-index", 20000);
50623         var anchor = this.getSlideAnchor();
50624         this.el.slideOut(anchor, {
50625             callback : function(){
50626                 this.el.setStyle("z-index", "");
50627                 this.collapsedEl.slideIn(anchor, {duration:.3});
50628                 this.afterSlide();
50629                 this.el.setLocation(-10000,-10000);
50630                 this.el.hide();
50631                 this.fireEvent("collapsed", this);
50632             },
50633             scope: this,
50634             block: true
50635         });
50636     },
50637
50638     animateExpand : function(){
50639         this.beforeSlide();
50640         this.el.alignTo(this.collapsedEl, this.getCollapseAnchor(), this.getExpandAdj());
50641         this.el.setStyle("z-index", 20000);
50642         this.collapsedEl.hide({
50643             duration:.1
50644         });
50645         this.el.slideIn(this.getSlideAnchor(), {
50646             callback : function(){
50647                 this.el.setStyle("z-index", "");
50648                 this.afterSlide();
50649                 if(this.split){
50650                     this.split.el.show();
50651                 }
50652                 this.fireEvent("invalidated", this);
50653                 this.fireEvent("expanded", this);
50654             },
50655             scope: this,
50656             block: true
50657         });
50658     },
50659
50660     anchors : {
50661         "west" : "left",
50662         "east" : "right",
50663         "north" : "top",
50664         "south" : "bottom"
50665     },
50666
50667     sanchors : {
50668         "west" : "l",
50669         "east" : "r",
50670         "north" : "t",
50671         "south" : "b"
50672     },
50673
50674     canchors : {
50675         "west" : "tl-tr",
50676         "east" : "tr-tl",
50677         "north" : "tl-bl",
50678         "south" : "bl-tl"
50679     },
50680
50681     getAnchor : function(){
50682         return this.anchors[this.position];
50683     },
50684
50685     getCollapseAnchor : function(){
50686         return this.canchors[this.position];
50687     },
50688
50689     getSlideAnchor : function(){
50690         return this.sanchors[this.position];
50691     },
50692
50693     getAlignAdj : function(){
50694         var cm = this.cmargins;
50695         switch(this.position){
50696             case "west":
50697                 return [0, 0];
50698             break;
50699             case "east":
50700                 return [0, 0];
50701             break;
50702             case "north":
50703                 return [0, 0];
50704             break;
50705             case "south":
50706                 return [0, 0];
50707             break;
50708         }
50709     },
50710
50711     getExpandAdj : function(){
50712         var c = this.collapsedEl, cm = this.cmargins;
50713         switch(this.position){
50714             case "west":
50715                 return [-(cm.right+c.getWidth()+cm.left), 0];
50716             break;
50717             case "east":
50718                 return [cm.right+c.getWidth()+cm.left, 0];
50719             break;
50720             case "north":
50721                 return [0, -(cm.top+cm.bottom+c.getHeight())];
50722             break;
50723             case "south":
50724                 return [0, cm.top+cm.bottom+c.getHeight()];
50725             break;
50726         }
50727     }
50728 });/*
50729  * Based on:
50730  * Ext JS Library 1.1.1
50731  * Copyright(c) 2006-2007, Ext JS, LLC.
50732  *
50733  * Originally Released Under LGPL - original licence link has changed is not relivant.
50734  *
50735  * Fork - LGPL
50736  * <script type="text/javascript">
50737  */
50738 /*
50739  * These classes are private internal classes
50740  */
50741 Roo.CenterLayoutRegion = function(mgr, config){
50742     Roo.LayoutRegion.call(this, mgr, config, "center");
50743     this.visible = true;
50744     this.minWidth = config.minWidth || 20;
50745     this.minHeight = config.minHeight || 20;
50746 };
50747
50748 Roo.extend(Roo.CenterLayoutRegion, Roo.LayoutRegion, {
50749     hide : function(){
50750         // center panel can't be hidden
50751     },
50752     
50753     show : function(){
50754         // center panel can't be hidden
50755     },
50756     
50757     getMinWidth: function(){
50758         return this.minWidth;
50759     },
50760     
50761     getMinHeight: function(){
50762         return this.minHeight;
50763     }
50764 });
50765
50766
50767 Roo.NorthLayoutRegion = function(mgr, config){
50768     Roo.LayoutRegion.call(this, mgr, config, "north", "n-resize");
50769     if(this.split){
50770         this.split.placement = Roo.SplitBar.TOP;
50771         this.split.orientation = Roo.SplitBar.VERTICAL;
50772         this.split.el.addClass("x-layout-split-v");
50773     }
50774     var size = config.initialSize || config.height;
50775     if(typeof size != "undefined"){
50776         this.el.setHeight(size);
50777     }
50778 };
50779 Roo.extend(Roo.NorthLayoutRegion, Roo.SplitLayoutRegion, {
50780     orientation: Roo.SplitBar.VERTICAL,
50781     getBox : function(){
50782         if(this.collapsed){
50783             return this.collapsedEl.getBox();
50784         }
50785         var box = this.el.getBox();
50786         if(this.split){
50787             box.height += this.split.el.getHeight();
50788         }
50789         return box;
50790     },
50791     
50792     updateBox : function(box){
50793         if(this.split && !this.collapsed){
50794             box.height -= this.split.el.getHeight();
50795             this.split.el.setLeft(box.x);
50796             this.split.el.setTop(box.y+box.height);
50797             this.split.el.setWidth(box.width);
50798         }
50799         if(this.collapsed){
50800             this.updateBody(box.width, null);
50801         }
50802         Roo.LayoutRegion.prototype.updateBox.call(this, box);
50803     }
50804 });
50805
50806 Roo.SouthLayoutRegion = function(mgr, config){
50807     Roo.SplitLayoutRegion.call(this, mgr, config, "south", "s-resize");
50808     if(this.split){
50809         this.split.placement = Roo.SplitBar.BOTTOM;
50810         this.split.orientation = Roo.SplitBar.VERTICAL;
50811         this.split.el.addClass("x-layout-split-v");
50812     }
50813     var size = config.initialSize || config.height;
50814     if(typeof size != "undefined"){
50815         this.el.setHeight(size);
50816     }
50817 };
50818 Roo.extend(Roo.SouthLayoutRegion, Roo.SplitLayoutRegion, {
50819     orientation: Roo.SplitBar.VERTICAL,
50820     getBox : function(){
50821         if(this.collapsed){
50822             return this.collapsedEl.getBox();
50823         }
50824         var box = this.el.getBox();
50825         if(this.split){
50826             var sh = this.split.el.getHeight();
50827             box.height += sh;
50828             box.y -= sh;
50829         }
50830         return box;
50831     },
50832     
50833     updateBox : function(box){
50834         if(this.split && !this.collapsed){
50835             var sh = this.split.el.getHeight();
50836             box.height -= sh;
50837             box.y += sh;
50838             this.split.el.setLeft(box.x);
50839             this.split.el.setTop(box.y-sh);
50840             this.split.el.setWidth(box.width);
50841         }
50842         if(this.collapsed){
50843             this.updateBody(box.width, null);
50844         }
50845         Roo.LayoutRegion.prototype.updateBox.call(this, box);
50846     }
50847 });
50848
50849 Roo.EastLayoutRegion = function(mgr, config){
50850     Roo.SplitLayoutRegion.call(this, mgr, config, "east", "e-resize");
50851     if(this.split){
50852         this.split.placement = Roo.SplitBar.RIGHT;
50853         this.split.orientation = Roo.SplitBar.HORIZONTAL;
50854         this.split.el.addClass("x-layout-split-h");
50855     }
50856     var size = config.initialSize || config.width;
50857     if(typeof size != "undefined"){
50858         this.el.setWidth(size);
50859     }
50860 };
50861 Roo.extend(Roo.EastLayoutRegion, Roo.SplitLayoutRegion, {
50862     orientation: Roo.SplitBar.HORIZONTAL,
50863     getBox : function(){
50864         if(this.collapsed){
50865             return this.collapsedEl.getBox();
50866         }
50867         var box = this.el.getBox();
50868         if(this.split){
50869             var sw = this.split.el.getWidth();
50870             box.width += sw;
50871             box.x -= sw;
50872         }
50873         return box;
50874     },
50875
50876     updateBox : function(box){
50877         if(this.split && !this.collapsed){
50878             var sw = this.split.el.getWidth();
50879             box.width -= sw;
50880             this.split.el.setLeft(box.x);
50881             this.split.el.setTop(box.y);
50882             this.split.el.setHeight(box.height);
50883             box.x += sw;
50884         }
50885         if(this.collapsed){
50886             this.updateBody(null, box.height);
50887         }
50888         Roo.LayoutRegion.prototype.updateBox.call(this, box);
50889     }
50890 });
50891
50892 Roo.WestLayoutRegion = function(mgr, config){
50893     Roo.SplitLayoutRegion.call(this, mgr, config, "west", "w-resize");
50894     if(this.split){
50895         this.split.placement = Roo.SplitBar.LEFT;
50896         this.split.orientation = Roo.SplitBar.HORIZONTAL;
50897         this.split.el.addClass("x-layout-split-h");
50898     }
50899     var size = config.initialSize || config.width;
50900     if(typeof size != "undefined"){
50901         this.el.setWidth(size);
50902     }
50903 };
50904 Roo.extend(Roo.WestLayoutRegion, Roo.SplitLayoutRegion, {
50905     orientation: Roo.SplitBar.HORIZONTAL,
50906     getBox : function(){
50907         if(this.collapsed){
50908             return this.collapsedEl.getBox();
50909         }
50910         var box = this.el.getBox();
50911         if(this.split){
50912             box.width += this.split.el.getWidth();
50913         }
50914         return box;
50915     },
50916     
50917     updateBox : function(box){
50918         if(this.split && !this.collapsed){
50919             var sw = this.split.el.getWidth();
50920             box.width -= sw;
50921             this.split.el.setLeft(box.x+box.width);
50922             this.split.el.setTop(box.y);
50923             this.split.el.setHeight(box.height);
50924         }
50925         if(this.collapsed){
50926             this.updateBody(null, box.height);
50927         }
50928         Roo.LayoutRegion.prototype.updateBox.call(this, box);
50929     }
50930 });
50931 /*
50932  * Based on:
50933  * Ext JS Library 1.1.1
50934  * Copyright(c) 2006-2007, Ext JS, LLC.
50935  *
50936  * Originally Released Under LGPL - original licence link has changed is not relivant.
50937  *
50938  * Fork - LGPL
50939  * <script type="text/javascript">
50940  */
50941  
50942  
50943 /*
50944  * Private internal class for reading and applying state
50945  */
50946 Roo.LayoutStateManager = function(layout){
50947      // default empty state
50948      this.state = {
50949         north: {},
50950         south: {},
50951         east: {},
50952         west: {}       
50953     };
50954 };
50955
50956 Roo.LayoutStateManager.prototype = {
50957     init : function(layout, provider){
50958         this.provider = provider;
50959         var state = provider.get(layout.id+"-layout-state");
50960         if(state){
50961             var wasUpdating = layout.isUpdating();
50962             if(!wasUpdating){
50963                 layout.beginUpdate();
50964             }
50965             for(var key in state){
50966                 if(typeof state[key] != "function"){
50967                     var rstate = state[key];
50968                     var r = layout.getRegion(key);
50969                     if(r && rstate){
50970                         if(rstate.size){
50971                             r.resizeTo(rstate.size);
50972                         }
50973                         if(rstate.collapsed == true){
50974                             r.collapse(true);
50975                         }else{
50976                             r.expand(null, true);
50977                         }
50978                     }
50979                 }
50980             }
50981             if(!wasUpdating){
50982                 layout.endUpdate();
50983             }
50984             this.state = state; 
50985         }
50986         this.layout = layout;
50987         layout.on("regionresized", this.onRegionResized, this);
50988         layout.on("regioncollapsed", this.onRegionCollapsed, this);
50989         layout.on("regionexpanded", this.onRegionExpanded, this);
50990     },
50991     
50992     storeState : function(){
50993         this.provider.set(this.layout.id+"-layout-state", this.state);
50994     },
50995     
50996     onRegionResized : function(region, newSize){
50997         this.state[region.getPosition()].size = newSize;
50998         this.storeState();
50999     },
51000     
51001     onRegionCollapsed : function(region){
51002         this.state[region.getPosition()].collapsed = true;
51003         this.storeState();
51004     },
51005     
51006     onRegionExpanded : function(region){
51007         this.state[region.getPosition()].collapsed = false;
51008         this.storeState();
51009     }
51010 };/*
51011  * Based on:
51012  * Ext JS Library 1.1.1
51013  * Copyright(c) 2006-2007, Ext JS, LLC.
51014  *
51015  * Originally Released Under LGPL - original licence link has changed is not relivant.
51016  *
51017  * Fork - LGPL
51018  * <script type="text/javascript">
51019  */
51020 /**
51021  * @class Roo.ContentPanel
51022  * @extends Roo.util.Observable
51023  * A basic ContentPanel element.
51024  * @cfg {Boolean}   fitToFrame    True for this panel to adjust its size to fit when the region resizes  (defaults to false)
51025  * @cfg {Boolean}   fitContainer   When using {@link #fitToFrame} and {@link #resizeEl}, you can also fit the parent container  (defaults to false)
51026  * @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
51027  * @cfg {Boolean}   closable      True if the panel can be closed/removed
51028  * @cfg {Boolean}   background    True if the panel should not be activated when it is added (defaults to false)
51029  * @cfg {String/HTMLElement/Element} resizeEl An element to resize if {@link #fitToFrame} is true (instead of this panel's element)
51030  * @cfg {Toolbar}   toolbar       A toolbar for this panel
51031  * @cfg {Boolean} autoScroll    True to scroll overflow in this panel (use with {@link #fitToFrame})
51032  * @cfg {String} title          The title for this panel
51033  * @cfg {Array} adjustments     Values to <b>add</b> to the width/height when doing a {@link #fitToFrame} (default is [0, 0])
51034  * @cfg {String} url            Calls {@link #setUrl} with this value
51035  * @cfg {String} region         (center|north|south|east|west) which region to put this panel on (when used with xtype constructors)
51036  * @cfg {String/Object} params  When used with {@link #url}, calls {@link #setUrl} with this value
51037  * @cfg {Boolean} loadOnce      When used with {@link #url}, calls {@link #setUrl} with this value
51038  * @cfg {String}    content        Raw content to fill content panel with (uses setContent on construction.)
51039
51040  * @constructor
51041  * Create a new ContentPanel.
51042  * @param {String/HTMLElement/Roo.Element} el The container element for this panel
51043  * @param {String/Object} config A string to set only the title or a config object
51044  * @param {String} content (optional) Set the HTML content for this panel
51045  * @param {String} region (optional) Used by xtype constructors to add to regions. (values center,east,west,south,north)
51046  */
51047 Roo.ContentPanel = function(el, config, content){
51048     
51049      
51050     /*
51051     if(el.autoCreate || el.xtype){ // xtype is available if this is called from factory
51052         config = el;
51053         el = Roo.id();
51054     }
51055     if (config && config.parentLayout) { 
51056         el = config.parentLayout.el.createChild(); 
51057     }
51058     */
51059     if(el.autoCreate){ // xtype is available if this is called from factory
51060         config = el;
51061         el = Roo.id();
51062     }
51063     this.el = Roo.get(el);
51064     if(!this.el && config && config.autoCreate){
51065         if(typeof config.autoCreate == "object"){
51066             if(!config.autoCreate.id){
51067                 config.autoCreate.id = config.id||el;
51068             }
51069             this.el = Roo.DomHelper.append(document.body,
51070                         config.autoCreate, true);
51071         }else{
51072             this.el = Roo.DomHelper.append(document.body,
51073                         {tag: "div", cls: "x-layout-inactive-content", id: config.id||el}, true);
51074         }
51075     }
51076     this.closable = false;
51077     this.loaded = false;
51078     this.active = false;
51079     if(typeof config == "string"){
51080         this.title = config;
51081     }else{
51082         Roo.apply(this, config);
51083     }
51084     
51085     if (this.toolbar && !this.toolbar.el && this.toolbar.xtype) {
51086         this.wrapEl = this.el.wrap();
51087         this.toolbar.container = this.el.insertSibling(false, 'before');
51088         this.toolbar = new Roo.Toolbar(this.toolbar);
51089     }
51090     
51091     // xtype created footer. - not sure if will work as we normally have to render first..
51092     if (this.footer && !this.footer.el && this.footer.xtype) {
51093         if (!this.wrapEl) {
51094             this.wrapEl = this.el.wrap();
51095         }
51096     
51097         this.footer.container = this.wrapEl.createChild();
51098          
51099         this.footer = Roo.factory(this.footer, Roo);
51100         
51101     }
51102     
51103     if(this.resizeEl){
51104         this.resizeEl = Roo.get(this.resizeEl, true);
51105     }else{
51106         this.resizeEl = this.el;
51107     }
51108     // handle view.xtype
51109     
51110  
51111     
51112     
51113     this.addEvents({
51114         /**
51115          * @event activate
51116          * Fires when this panel is activated. 
51117          * @param {Roo.ContentPanel} this
51118          */
51119         "activate" : true,
51120         /**
51121          * @event deactivate
51122          * Fires when this panel is activated. 
51123          * @param {Roo.ContentPanel} this
51124          */
51125         "deactivate" : true,
51126
51127         /**
51128          * @event resize
51129          * Fires when this panel is resized if fitToFrame is true.
51130          * @param {Roo.ContentPanel} this
51131          * @param {Number} width The width after any component adjustments
51132          * @param {Number} height The height after any component adjustments
51133          */
51134         "resize" : true,
51135         
51136          /**
51137          * @event render
51138          * Fires when this tab is created
51139          * @param {Roo.ContentPanel} this
51140          */
51141         "render" : true
51142         
51143         
51144         
51145     });
51146     
51147
51148     
51149     
51150     if(this.autoScroll){
51151         this.resizeEl.setStyle("overflow", "auto");
51152     } else {
51153         // fix randome scrolling
51154         this.el.on('scroll', function() {
51155             Roo.log('fix random scolling');
51156             this.scrollTo('top',0); 
51157         });
51158     }
51159     content = content || this.content;
51160     if(content){
51161         this.setContent(content);
51162     }
51163     if(config && config.url){
51164         this.setUrl(this.url, this.params, this.loadOnce);
51165     }
51166     
51167     
51168     
51169     Roo.ContentPanel.superclass.constructor.call(this);
51170     
51171     if (this.view && typeof(this.view.xtype) != 'undefined') {
51172         this.view.el = this.el.appendChild(document.createElement("div"));
51173         this.view = Roo.factory(this.view); 
51174         this.view.render  &&  this.view.render(false, '');  
51175     }
51176     
51177     
51178     this.fireEvent('render', this);
51179 };
51180
51181 Roo.extend(Roo.ContentPanel, Roo.util.Observable, {
51182     tabTip:'',
51183     setRegion : function(region){
51184         this.region = region;
51185         if(region){
51186            this.el.replaceClass("x-layout-inactive-content", "x-layout-active-content");
51187         }else{
51188            this.el.replaceClass("x-layout-active-content", "x-layout-inactive-content");
51189         } 
51190     },
51191     
51192     /**
51193      * Returns the toolbar for this Panel if one was configured. 
51194      * @return {Roo.Toolbar} 
51195      */
51196     getToolbar : function(){
51197         return this.toolbar;
51198     },
51199     
51200     setActiveState : function(active){
51201         this.active = active;
51202         if(!active){
51203             this.fireEvent("deactivate", this);
51204         }else{
51205             this.fireEvent("activate", this);
51206         }
51207     },
51208     /**
51209      * Updates this panel's element
51210      * @param {String} content The new content
51211      * @param {Boolean} loadScripts (optional) true to look for and process scripts
51212     */
51213     setContent : function(content, loadScripts){
51214         this.el.update(content, loadScripts);
51215     },
51216
51217     ignoreResize : function(w, h){
51218         if(this.lastSize && this.lastSize.width == w && this.lastSize.height == h){
51219             return true;
51220         }else{
51221             this.lastSize = {width: w, height: h};
51222             return false;
51223         }
51224     },
51225     /**
51226      * Get the {@link Roo.UpdateManager} for this panel. Enables you to perform Ajax updates.
51227      * @return {Roo.UpdateManager} The UpdateManager
51228      */
51229     getUpdateManager : function(){
51230         return this.el.getUpdateManager();
51231     },
51232      /**
51233      * Loads this content panel immediately with content from XHR. Note: to delay loading until the panel is activated, use {@link #setUrl}.
51234      * @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:
51235 <pre><code>
51236 panel.load({
51237     url: "your-url.php",
51238     params: {param1: "foo", param2: "bar"}, // or a URL encoded string
51239     callback: yourFunction,
51240     scope: yourObject, //(optional scope)
51241     discardUrl: false,
51242     nocache: false,
51243     text: "Loading...",
51244     timeout: 30,
51245     scripts: false
51246 });
51247 </code></pre>
51248      * The only required property is <i>url</i>. The optional properties <i>nocache</i>, <i>text</i> and <i>scripts</i>
51249      * 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.
51250      * @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}
51251      * @param {Function} callback (optional) Callback when transaction is complete -- called with signature (oElement, bSuccess, oResponse)
51252      * @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.
51253      * @return {Roo.ContentPanel} this
51254      */
51255     load : function(){
51256         var um = this.el.getUpdateManager();
51257         um.update.apply(um, arguments);
51258         return this;
51259     },
51260
51261
51262     /**
51263      * 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.
51264      * @param {String/Function} url The URL to load the content from or a function to call to get the URL
51265      * @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)
51266      * @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)
51267      * @return {Roo.UpdateManager} The UpdateManager
51268      */
51269     setUrl : function(url, params, loadOnce){
51270         if(this.refreshDelegate){
51271             this.removeListener("activate", this.refreshDelegate);
51272         }
51273         this.refreshDelegate = this._handleRefresh.createDelegate(this, [url, params, loadOnce]);
51274         this.on("activate", this.refreshDelegate);
51275         return this.el.getUpdateManager();
51276     },
51277     
51278     _handleRefresh : function(url, params, loadOnce){
51279         if(!loadOnce || !this.loaded){
51280             var updater = this.el.getUpdateManager();
51281             updater.update(url, params, this._setLoaded.createDelegate(this));
51282         }
51283     },
51284     
51285     _setLoaded : function(){
51286         this.loaded = true;
51287     }, 
51288     
51289     /**
51290      * Returns this panel's id
51291      * @return {String} 
51292      */
51293     getId : function(){
51294         return this.el.id;
51295     },
51296     
51297     /** 
51298      * Returns this panel's element - used by regiosn to add.
51299      * @return {Roo.Element} 
51300      */
51301     getEl : function(){
51302         return this.wrapEl || this.el;
51303     },
51304     
51305     adjustForComponents : function(width, height)
51306     {
51307         //Roo.log('adjustForComponents ');
51308         if(this.resizeEl != this.el){
51309             width -= this.el.getFrameWidth('lr');
51310             height -= this.el.getFrameWidth('tb');
51311         }
51312         if(this.toolbar){
51313             var te = this.toolbar.getEl();
51314             height -= te.getHeight();
51315             te.setWidth(width);
51316         }
51317         if(this.footer){
51318             var te = this.footer.getEl();
51319             Roo.log("footer:" + te.getHeight());
51320             
51321             height -= te.getHeight();
51322             te.setWidth(width);
51323         }
51324         
51325         
51326         if(this.adjustments){
51327             width += this.adjustments[0];
51328             height += this.adjustments[1];
51329         }
51330         return {"width": width, "height": height};
51331     },
51332     
51333     setSize : function(width, height){
51334         if(this.fitToFrame && !this.ignoreResize(width, height)){
51335             if(this.fitContainer && this.resizeEl != this.el){
51336                 this.el.setSize(width, height);
51337             }
51338             var size = this.adjustForComponents(width, height);
51339             this.resizeEl.setSize(this.autoWidth ? "auto" : size.width, this.autoHeight ? "auto" : size.height);
51340             this.fireEvent('resize', this, size.width, size.height);
51341         }
51342     },
51343     
51344     /**
51345      * Returns this panel's title
51346      * @return {String} 
51347      */
51348     getTitle : function(){
51349         return this.title;
51350     },
51351     
51352     /**
51353      * Set this panel's title
51354      * @param {String} title
51355      */
51356     setTitle : function(title){
51357         this.title = title;
51358         if(this.region){
51359             this.region.updatePanelTitle(this, title);
51360         }
51361     },
51362     
51363     /**
51364      * Returns true is this panel was configured to be closable
51365      * @return {Boolean} 
51366      */
51367     isClosable : function(){
51368         return this.closable;
51369     },
51370     
51371     beforeSlide : function(){
51372         this.el.clip();
51373         this.resizeEl.clip();
51374     },
51375     
51376     afterSlide : function(){
51377         this.el.unclip();
51378         this.resizeEl.unclip();
51379     },
51380     
51381     /**
51382      *   Force a content refresh from the URL specified in the {@link #setUrl} method.
51383      *   Will fail silently if the {@link #setUrl} method has not been called.
51384      *   This does not activate the panel, just updates its content.
51385      */
51386     refresh : function(){
51387         if(this.refreshDelegate){
51388            this.loaded = false;
51389            this.refreshDelegate();
51390         }
51391     },
51392     
51393     /**
51394      * Destroys this panel
51395      */
51396     destroy : function(){
51397         this.el.removeAllListeners();
51398         var tempEl = document.createElement("span");
51399         tempEl.appendChild(this.el.dom);
51400         tempEl.innerHTML = "";
51401         this.el.remove();
51402         this.el = null;
51403     },
51404     
51405     /**
51406      * form - if the content panel contains a form - this is a reference to it.
51407      * @type {Roo.form.Form}
51408      */
51409     form : false,
51410     /**
51411      * view - if the content panel contains a view (Roo.DatePicker / Roo.View / Roo.JsonView)
51412      *    This contains a reference to it.
51413      * @type {Roo.View}
51414      */
51415     view : false,
51416     
51417       /**
51418      * Adds a xtype elements to the panel - currently only supports Forms, View, JsonView.
51419      * <pre><code>
51420
51421 layout.addxtype({
51422        xtype : 'Form',
51423        items: [ .... ]
51424    }
51425 );
51426
51427 </code></pre>
51428      * @param {Object} cfg Xtype definition of item to add.
51429      */
51430     
51431     addxtype : function(cfg) {
51432         // add form..
51433         if (cfg.xtype.match(/^Form$/)) {
51434             
51435             var el;
51436             //if (this.footer) {
51437             //    el = this.footer.container.insertSibling(false, 'before');
51438             //} else {
51439                 el = this.el.createChild();
51440             //}
51441
51442             this.form = new  Roo.form.Form(cfg);
51443             
51444             
51445             if ( this.form.allItems.length) this.form.render(el.dom);
51446             return this.form;
51447         }
51448         // should only have one of theses..
51449         if ([ 'View', 'JsonView', 'DatePicker'].indexOf(cfg.xtype) > -1) {
51450             // views.. should not be just added - used named prop 'view''
51451             
51452             cfg.el = this.el.appendChild(document.createElement("div"));
51453             // factory?
51454             
51455             var ret = new Roo.factory(cfg);
51456              
51457              ret.render && ret.render(false, ''); // render blank..
51458             this.view = ret;
51459             return ret;
51460         }
51461         return false;
51462     }
51463 });
51464
51465 /**
51466  * @class Roo.GridPanel
51467  * @extends Roo.ContentPanel
51468  * @constructor
51469  * Create a new GridPanel.
51470  * @param {Roo.grid.Grid} grid The grid for this panel
51471  * @param {String/Object} config A string to set only the panel's title, or a config object
51472  */
51473 Roo.GridPanel = function(grid, config){
51474     
51475   
51476     this.wrapper = Roo.DomHelper.append(document.body, // wrapper for IE7 strict & safari scroll issue
51477         {tag: "div", cls: "x-layout-grid-wrapper x-layout-inactive-content"}, true);
51478         
51479     this.wrapper.dom.appendChild(grid.getGridEl().dom);
51480     
51481     Roo.GridPanel.superclass.constructor.call(this, this.wrapper, config);
51482     
51483     if(this.toolbar){
51484         this.toolbar.el.insertBefore(this.wrapper.dom.firstChild);
51485     }
51486     // xtype created footer. - not sure if will work as we normally have to render first..
51487     if (this.footer && !this.footer.el && this.footer.xtype) {
51488         
51489         this.footer.container = this.grid.getView().getFooterPanel(true);
51490         this.footer.dataSource = this.grid.dataSource;
51491         this.footer = Roo.factory(this.footer, Roo);
51492         
51493     }
51494     
51495     grid.monitorWindowResize = false; // turn off autosizing
51496     grid.autoHeight = false;
51497     grid.autoWidth = false;
51498     this.grid = grid;
51499     this.grid.getGridEl().replaceClass("x-layout-inactive-content", "x-layout-component-panel");
51500 };
51501
51502 Roo.extend(Roo.GridPanel, Roo.ContentPanel, {
51503     getId : function(){
51504         return this.grid.id;
51505     },
51506     
51507     /**
51508      * Returns the grid for this panel
51509      * @return {Roo.grid.Grid} 
51510      */
51511     getGrid : function(){
51512         return this.grid;    
51513     },
51514     
51515     setSize : function(width, height){
51516         if(!this.ignoreResize(width, height)){
51517             var grid = this.grid;
51518             var size = this.adjustForComponents(width, height);
51519             grid.getGridEl().setSize(size.width, size.height);
51520             grid.autoSize();
51521         }
51522     },
51523     
51524     beforeSlide : function(){
51525         this.grid.getView().scroller.clip();
51526     },
51527     
51528     afterSlide : function(){
51529         this.grid.getView().scroller.unclip();
51530     },
51531     
51532     destroy : function(){
51533         this.grid.destroy();
51534         delete this.grid;
51535         Roo.GridPanel.superclass.destroy.call(this); 
51536     }
51537 });
51538
51539
51540 /**
51541  * @class Roo.NestedLayoutPanel
51542  * @extends Roo.ContentPanel
51543  * @constructor
51544  * Create a new NestedLayoutPanel.
51545  * 
51546  * 
51547  * @param {Roo.BorderLayout} layout The layout for this panel
51548  * @param {String/Object} config A string to set only the title or a config object
51549  */
51550 Roo.NestedLayoutPanel = function(layout, config)
51551 {
51552     // construct with only one argument..
51553     /* FIXME - implement nicer consturctors
51554     if (layout.layout) {
51555         config = layout;
51556         layout = config.layout;
51557         delete config.layout;
51558     }
51559     if (layout.xtype && !layout.getEl) {
51560         // then layout needs constructing..
51561         layout = Roo.factory(layout, Roo);
51562     }
51563     */
51564     
51565     
51566     Roo.NestedLayoutPanel.superclass.constructor.call(this, layout.getEl(), config);
51567     
51568     layout.monitorWindowResize = false; // turn off autosizing
51569     this.layout = layout;
51570     this.layout.getEl().addClass("x-layout-nested-layout");
51571     
51572     
51573     
51574     
51575 };
51576
51577 Roo.extend(Roo.NestedLayoutPanel, Roo.ContentPanel, {
51578
51579     setSize : function(width, height){
51580         if(!this.ignoreResize(width, height)){
51581             var size = this.adjustForComponents(width, height);
51582             var el = this.layout.getEl();
51583             el.setSize(size.width, size.height);
51584             var touch = el.dom.offsetWidth;
51585             this.layout.layout();
51586             // ie requires a double layout on the first pass
51587             if(Roo.isIE && !this.initialized){
51588                 this.initialized = true;
51589                 this.layout.layout();
51590             }
51591         }
51592     },
51593     
51594     // activate all subpanels if not currently active..
51595     
51596     setActiveState : function(active){
51597         this.active = active;
51598         if(!active){
51599             this.fireEvent("deactivate", this);
51600             return;
51601         }
51602         
51603         this.fireEvent("activate", this);
51604         // not sure if this should happen before or after..
51605         if (!this.layout) {
51606             return; // should not happen..
51607         }
51608         var reg = false;
51609         for (var r in this.layout.regions) {
51610             reg = this.layout.getRegion(r);
51611             if (reg.getActivePanel()) {
51612                 //reg.showPanel(reg.getActivePanel()); // force it to activate.. 
51613                 reg.setActivePanel(reg.getActivePanel());
51614                 continue;
51615             }
51616             if (!reg.panels.length) {
51617                 continue;
51618             }
51619             reg.showPanel(reg.getPanel(0));
51620         }
51621         
51622         
51623         
51624         
51625     },
51626     
51627     /**
51628      * Returns the nested BorderLayout for this panel
51629      * @return {Roo.BorderLayout} 
51630      */
51631     getLayout : function(){
51632         return this.layout;
51633     },
51634     
51635      /**
51636      * Adds a xtype elements to the layout of the nested panel
51637      * <pre><code>
51638
51639 panel.addxtype({
51640        xtype : 'ContentPanel',
51641        region: 'west',
51642        items: [ .... ]
51643    }
51644 );
51645
51646 panel.addxtype({
51647         xtype : 'NestedLayoutPanel',
51648         region: 'west',
51649         layout: {
51650            center: { },
51651            west: { }   
51652         },
51653         items : [ ... list of content panels or nested layout panels.. ]
51654    }
51655 );
51656 </code></pre>
51657      * @param {Object} cfg Xtype definition of item to add.
51658      */
51659     addxtype : function(cfg) {
51660         return this.layout.addxtype(cfg);
51661     
51662     }
51663 });
51664
51665 Roo.ScrollPanel = function(el, config, content){
51666     config = config || {};
51667     config.fitToFrame = true;
51668     Roo.ScrollPanel.superclass.constructor.call(this, el, config, content);
51669     
51670     this.el.dom.style.overflow = "hidden";
51671     var wrap = this.el.wrap({cls: "x-scroller x-layout-inactive-content"});
51672     this.el.removeClass("x-layout-inactive-content");
51673     this.el.on("mousewheel", this.onWheel, this);
51674
51675     var up = wrap.createChild({cls: "x-scroller-up", html: "&#160;"}, this.el.dom);
51676     var down = wrap.createChild({cls: "x-scroller-down", html: "&#160;"});
51677     up.unselectable(); down.unselectable();
51678     up.on("click", this.scrollUp, this);
51679     down.on("click", this.scrollDown, this);
51680     up.addClassOnOver("x-scroller-btn-over");
51681     down.addClassOnOver("x-scroller-btn-over");
51682     up.addClassOnClick("x-scroller-btn-click");
51683     down.addClassOnClick("x-scroller-btn-click");
51684     this.adjustments = [0, -(up.getHeight() + down.getHeight())];
51685
51686     this.resizeEl = this.el;
51687     this.el = wrap; this.up = up; this.down = down;
51688 };
51689
51690 Roo.extend(Roo.ScrollPanel, Roo.ContentPanel, {
51691     increment : 100,
51692     wheelIncrement : 5,
51693     scrollUp : function(){
51694         this.resizeEl.scroll("up", this.increment, {callback: this.afterScroll, scope: this});
51695     },
51696
51697     scrollDown : function(){
51698         this.resizeEl.scroll("down", this.increment, {callback: this.afterScroll, scope: this});
51699     },
51700
51701     afterScroll : function(){
51702         var el = this.resizeEl;
51703         var t = el.dom.scrollTop, h = el.dom.scrollHeight, ch = el.dom.clientHeight;
51704         this.up[t == 0 ? "addClass" : "removeClass"]("x-scroller-btn-disabled");
51705         this.down[h - t <= ch ? "addClass" : "removeClass"]("x-scroller-btn-disabled");
51706     },
51707
51708     setSize : function(){
51709         Roo.ScrollPanel.superclass.setSize.apply(this, arguments);
51710         this.afterScroll();
51711     },
51712
51713     onWheel : function(e){
51714         var d = e.getWheelDelta();
51715         this.resizeEl.dom.scrollTop -= (d*this.wheelIncrement);
51716         this.afterScroll();
51717         e.stopEvent();
51718     },
51719
51720     setContent : function(content, loadScripts){
51721         this.resizeEl.update(content, loadScripts);
51722     }
51723
51724 });
51725
51726
51727
51728
51729
51730
51731
51732
51733
51734 /**
51735  * @class Roo.TreePanel
51736  * @extends Roo.ContentPanel
51737  * @constructor
51738  * Create a new TreePanel. - defaults to fit/scoll contents.
51739  * @param {String/Object} config A string to set only the panel's title, or a config object
51740  * @cfg {Roo.tree.TreePanel} tree The tree TreePanel, with config etc.
51741  */
51742 Roo.TreePanel = function(config){
51743     var el = config.el;
51744     var tree = config.tree;
51745     delete config.tree; 
51746     delete config.el; // hopefull!
51747     
51748     // wrapper for IE7 strict & safari scroll issue
51749     
51750     var treeEl = el.createChild();
51751     config.resizeEl = treeEl;
51752     
51753     
51754     
51755     Roo.TreePanel.superclass.constructor.call(this, el, config);
51756  
51757  
51758     this.tree = new Roo.tree.TreePanel(treeEl , tree);
51759     //console.log(tree);
51760     this.on('activate', function()
51761     {
51762         if (this.tree.rendered) {
51763             return;
51764         }
51765         //console.log('render tree');
51766         this.tree.render();
51767     });
51768     // this should not be needed.. - it's actually the 'el' that resizes?
51769     // actuall it breaks the containerScroll - dragging nodes auto scroll at top
51770     
51771     //this.on('resize',  function (cp, w, h) {
51772     //        this.tree.innerCt.setWidth(w);
51773     //        this.tree.innerCt.setHeight(h);
51774     //        //this.tree.innerCt.setStyle('overflow-y', 'auto');
51775     //});
51776
51777         
51778     
51779 };
51780
51781 Roo.extend(Roo.TreePanel, Roo.ContentPanel, {   
51782     fitToFrame : true,
51783     autoScroll : true
51784 });
51785
51786
51787
51788
51789
51790
51791
51792
51793
51794
51795
51796 /*
51797  * Based on:
51798  * Ext JS Library 1.1.1
51799  * Copyright(c) 2006-2007, Ext JS, LLC.
51800  *
51801  * Originally Released Under LGPL - original licence link has changed is not relivant.
51802  *
51803  * Fork - LGPL
51804  * <script type="text/javascript">
51805  */
51806  
51807
51808 /**
51809  * @class Roo.ReaderLayout
51810  * @extends Roo.BorderLayout
51811  * This is a pre-built layout that represents a classic, 5-pane application.  It consists of a header, a primary
51812  * center region containing two nested regions (a top one for a list view and one for item preview below),
51813  * and regions on either side that can be used for navigation, application commands, informational displays, etc.
51814  * The setup and configuration work exactly the same as it does for a {@link Roo.BorderLayout} - this class simply
51815  * expedites the setup of the overall layout and regions for this common application style.
51816  * Example:
51817  <pre><code>
51818 var reader = new Roo.ReaderLayout();
51819 var CP = Roo.ContentPanel;  // shortcut for adding
51820
51821 reader.beginUpdate();
51822 reader.add("north", new CP("north", "North"));
51823 reader.add("west", new CP("west", {title: "West"}));
51824 reader.add("east", new CP("east", {title: "East"}));
51825
51826 reader.regions.listView.add(new CP("listView", "List"));
51827 reader.regions.preview.add(new CP("preview", "Preview"));
51828 reader.endUpdate();
51829 </code></pre>
51830 * @constructor
51831 * Create a new ReaderLayout
51832 * @param {Object} config Configuration options
51833 * @param {String/HTMLElement/Element} container (optional) The container this layout is bound to (defaults to
51834 * document.body if omitted)
51835 */
51836 Roo.ReaderLayout = function(config, renderTo){
51837     var c = config || {size:{}};
51838     Roo.ReaderLayout.superclass.constructor.call(this, renderTo || document.body, {
51839         north: c.north !== false ? Roo.apply({
51840             split:false,
51841             initialSize: 32,
51842             titlebar: false
51843         }, c.north) : false,
51844         west: c.west !== false ? Roo.apply({
51845             split:true,
51846             initialSize: 200,
51847             minSize: 175,
51848             maxSize: 400,
51849             titlebar: true,
51850             collapsible: true,
51851             animate: true,
51852             margins:{left:5,right:0,bottom:5,top:5},
51853             cmargins:{left:5,right:5,bottom:5,top:5}
51854         }, c.west) : false,
51855         east: c.east !== false ? Roo.apply({
51856             split:true,
51857             initialSize: 200,
51858             minSize: 175,
51859             maxSize: 400,
51860             titlebar: true,
51861             collapsible: true,
51862             animate: true,
51863             margins:{left:0,right:5,bottom:5,top:5},
51864             cmargins:{left:5,right:5,bottom:5,top:5}
51865         }, c.east) : false,
51866         center: Roo.apply({
51867             tabPosition: 'top',
51868             autoScroll:false,
51869             closeOnTab: true,
51870             titlebar:false,
51871             margins:{left:c.west!==false ? 0 : 5,right:c.east!==false ? 0 : 5,bottom:5,top:2}
51872         }, c.center)
51873     });
51874
51875     this.el.addClass('x-reader');
51876
51877     this.beginUpdate();
51878
51879     var inner = new Roo.BorderLayout(Roo.get(document.body).createChild(), {
51880         south: c.preview !== false ? Roo.apply({
51881             split:true,
51882             initialSize: 200,
51883             minSize: 100,
51884             autoScroll:true,
51885             collapsible:true,
51886             titlebar: true,
51887             cmargins:{top:5,left:0, right:0, bottom:0}
51888         }, c.preview) : false,
51889         center: Roo.apply({
51890             autoScroll:false,
51891             titlebar:false,
51892             minHeight:200
51893         }, c.listView)
51894     });
51895     this.add('center', new Roo.NestedLayoutPanel(inner,
51896             Roo.apply({title: c.mainTitle || '',tabTip:''},c.innerPanelCfg)));
51897
51898     this.endUpdate();
51899
51900     this.regions.preview = inner.getRegion('south');
51901     this.regions.listView = inner.getRegion('center');
51902 };
51903
51904 Roo.extend(Roo.ReaderLayout, Roo.BorderLayout);/*
51905  * Based on:
51906  * Ext JS Library 1.1.1
51907  * Copyright(c) 2006-2007, Ext JS, LLC.
51908  *
51909  * Originally Released Under LGPL - original licence link has changed is not relivant.
51910  *
51911  * Fork - LGPL
51912  * <script type="text/javascript">
51913  */
51914  
51915 /**
51916  * @class Roo.grid.Grid
51917  * @extends Roo.util.Observable
51918  * This class represents the primary interface of a component based grid control.
51919  * <br><br>Usage:<pre><code>
51920  var grid = new Roo.grid.Grid("my-container-id", {
51921      ds: myDataStore,
51922      cm: myColModel,
51923      selModel: mySelectionModel,
51924      autoSizeColumns: true,
51925      monitorWindowResize: false,
51926      trackMouseOver: true
51927  });
51928  // set any options
51929  grid.render();
51930  * </code></pre>
51931  * <b>Common Problems:</b><br/>
51932  * - Grid does not resize properly when going smaller: Setting overflow hidden on the container
51933  * element will correct this<br/>
51934  * - If you get el.style[camel]= NaNpx or -2px or something related, be certain you have given your container element
51935  * dimensions. The grid adapts to your container's size, if your container has no size defined then the results
51936  * are unpredictable.<br/>
51937  * - Do not render the grid into an element with display:none. Try using visibility:hidden. Otherwise there is no way for the
51938  * grid to calculate dimensions/offsets.<br/>
51939   * @constructor
51940  * @param {String/HTMLElement/Roo.Element} container The element into which this grid will be rendered -
51941  * The container MUST have some type of size defined for the grid to fill. The container will be
51942  * automatically set to position relative if it isn't already.
51943  * @param {Object} config A config object that sets properties on this grid.
51944  */
51945 Roo.grid.Grid = function(container, config){
51946         // initialize the container
51947         this.container = Roo.get(container);
51948         this.container.update("");
51949         this.container.setStyle("overflow", "hidden");
51950     this.container.addClass('x-grid-container');
51951
51952     this.id = this.container.id;
51953
51954     Roo.apply(this, config);
51955     // check and correct shorthanded configs
51956     if(this.ds){
51957         this.dataSource = this.ds;
51958         delete this.ds;
51959     }
51960     if(this.cm){
51961         this.colModel = this.cm;
51962         delete this.cm;
51963     }
51964     if(this.sm){
51965         this.selModel = this.sm;
51966         delete this.sm;
51967     }
51968
51969     if (this.selModel) {
51970         this.selModel = Roo.factory(this.selModel, Roo.grid);
51971         this.sm = this.selModel;
51972         this.sm.xmodule = this.xmodule || false;
51973     }
51974     if (typeof(this.colModel.config) == 'undefined') {
51975         this.colModel = new Roo.grid.ColumnModel(this.colModel);
51976         this.cm = this.colModel;
51977         this.cm.xmodule = this.xmodule || false;
51978     }
51979     if (this.dataSource) {
51980         this.dataSource= Roo.factory(this.dataSource, Roo.data);
51981         this.ds = this.dataSource;
51982         this.ds.xmodule = this.xmodule || false;
51983          
51984     }
51985     
51986     
51987     
51988     if(this.width){
51989         this.container.setWidth(this.width);
51990     }
51991
51992     if(this.height){
51993         this.container.setHeight(this.height);
51994     }
51995     /** @private */
51996         this.addEvents({
51997         // raw events
51998         /**
51999          * @event click
52000          * The raw click event for the entire grid.
52001          * @param {Roo.EventObject} e
52002          */
52003         "click" : true,
52004         /**
52005          * @event dblclick
52006          * The raw dblclick event for the entire grid.
52007          * @param {Roo.EventObject} e
52008          */
52009         "dblclick" : true,
52010         /**
52011          * @event contextmenu
52012          * The raw contextmenu event for the entire grid.
52013          * @param {Roo.EventObject} e
52014          */
52015         "contextmenu" : true,
52016         /**
52017          * @event mousedown
52018          * The raw mousedown event for the entire grid.
52019          * @param {Roo.EventObject} e
52020          */
52021         "mousedown" : true,
52022         /**
52023          * @event mouseup
52024          * The raw mouseup event for the entire grid.
52025          * @param {Roo.EventObject} e
52026          */
52027         "mouseup" : true,
52028         /**
52029          * @event mouseover
52030          * The raw mouseover event for the entire grid.
52031          * @param {Roo.EventObject} e
52032          */
52033         "mouseover" : true,
52034         /**
52035          * @event mouseout
52036          * The raw mouseout event for the entire grid.
52037          * @param {Roo.EventObject} e
52038          */
52039         "mouseout" : true,
52040         /**
52041          * @event keypress
52042          * The raw keypress event for the entire grid.
52043          * @param {Roo.EventObject} e
52044          */
52045         "keypress" : true,
52046         /**
52047          * @event keydown
52048          * The raw keydown event for the entire grid.
52049          * @param {Roo.EventObject} e
52050          */
52051         "keydown" : true,
52052
52053         // custom events
52054
52055         /**
52056          * @event cellclick
52057          * Fires when a cell is clicked
52058          * @param {Grid} this
52059          * @param {Number} rowIndex
52060          * @param {Number} columnIndex
52061          * @param {Roo.EventObject} e
52062          */
52063         "cellclick" : true,
52064         /**
52065          * @event celldblclick
52066          * Fires when a cell is double clicked
52067          * @param {Grid} this
52068          * @param {Number} rowIndex
52069          * @param {Number} columnIndex
52070          * @param {Roo.EventObject} e
52071          */
52072         "celldblclick" : true,
52073         /**
52074          * @event rowclick
52075          * Fires when a row is clicked
52076          * @param {Grid} this
52077          * @param {Number} rowIndex
52078          * @param {Roo.EventObject} e
52079          */
52080         "rowclick" : true,
52081         /**
52082          * @event rowdblclick
52083          * Fires when a row is double clicked
52084          * @param {Grid} this
52085          * @param {Number} rowIndex
52086          * @param {Roo.EventObject} e
52087          */
52088         "rowdblclick" : true,
52089         /**
52090          * @event headerclick
52091          * Fires when a header is clicked
52092          * @param {Grid} this
52093          * @param {Number} columnIndex
52094          * @param {Roo.EventObject} e
52095          */
52096         "headerclick" : true,
52097         /**
52098          * @event headerdblclick
52099          * Fires when a header cell is double clicked
52100          * @param {Grid} this
52101          * @param {Number} columnIndex
52102          * @param {Roo.EventObject} e
52103          */
52104         "headerdblclick" : true,
52105         /**
52106          * @event rowcontextmenu
52107          * Fires when a row is right clicked
52108          * @param {Grid} this
52109          * @param {Number} rowIndex
52110          * @param {Roo.EventObject} e
52111          */
52112         "rowcontextmenu" : true,
52113         /**
52114          * @event cellcontextmenu
52115          * Fires when a cell is right clicked
52116          * @param {Grid} this
52117          * @param {Number} rowIndex
52118          * @param {Number} cellIndex
52119          * @param {Roo.EventObject} e
52120          */
52121          "cellcontextmenu" : true,
52122         /**
52123          * @event headercontextmenu
52124          * Fires when a header is right clicked
52125          * @param {Grid} this
52126          * @param {Number} columnIndex
52127          * @param {Roo.EventObject} e
52128          */
52129         "headercontextmenu" : true,
52130         /**
52131          * @event bodyscroll
52132          * Fires when the body element is scrolled
52133          * @param {Number} scrollLeft
52134          * @param {Number} scrollTop
52135          */
52136         "bodyscroll" : true,
52137         /**
52138          * @event columnresize
52139          * Fires when the user resizes a column
52140          * @param {Number} columnIndex
52141          * @param {Number} newSize
52142          */
52143         "columnresize" : true,
52144         /**
52145          * @event columnmove
52146          * Fires when the user moves a column
52147          * @param {Number} oldIndex
52148          * @param {Number} newIndex
52149          */
52150         "columnmove" : true,
52151         /**
52152          * @event startdrag
52153          * Fires when row(s) start being dragged
52154          * @param {Grid} this
52155          * @param {Roo.GridDD} dd The drag drop object
52156          * @param {event} e The raw browser event
52157          */
52158         "startdrag" : true,
52159         /**
52160          * @event enddrag
52161          * Fires when a drag operation is complete
52162          * @param {Grid} this
52163          * @param {Roo.GridDD} dd The drag drop object
52164          * @param {event} e The raw browser event
52165          */
52166         "enddrag" : true,
52167         /**
52168          * @event dragdrop
52169          * Fires when dragged row(s) are dropped on a valid DD target
52170          * @param {Grid} this
52171          * @param {Roo.GridDD} dd The drag drop object
52172          * @param {String} targetId The target drag drop object
52173          * @param {event} e The raw browser event
52174          */
52175         "dragdrop" : true,
52176         /**
52177          * @event dragover
52178          * Fires while row(s) are being dragged. "targetId" is the id of the Yahoo.util.DD object the selected rows are being dragged over.
52179          * @param {Grid} this
52180          * @param {Roo.GridDD} dd The drag drop object
52181          * @param {String} targetId The target drag drop object
52182          * @param {event} e The raw browser event
52183          */
52184         "dragover" : true,
52185         /**
52186          * @event dragenter
52187          *  Fires when the dragged row(s) first cross another DD target while being dragged
52188          * @param {Grid} this
52189          * @param {Roo.GridDD} dd The drag drop object
52190          * @param {String} targetId The target drag drop object
52191          * @param {event} e The raw browser event
52192          */
52193         "dragenter" : true,
52194         /**
52195          * @event dragout
52196          * Fires when the dragged row(s) leave another DD target while being dragged
52197          * @param {Grid} this
52198          * @param {Roo.GridDD} dd The drag drop object
52199          * @param {String} targetId The target drag drop object
52200          * @param {event} e The raw browser event
52201          */
52202         "dragout" : true,
52203         /**
52204          * @event rowclass
52205          * Fires when a row is rendered, so you can change add a style to it.
52206          * @param {GridView} gridview   The grid view
52207          * @param {Object} rowcfg   contains record  rowIndex and rowClass - set rowClass to add a style.
52208          */
52209         'rowclass' : true,
52210
52211         /**
52212          * @event render
52213          * Fires when the grid is rendered
52214          * @param {Grid} grid
52215          */
52216         'render' : true
52217     });
52218
52219     Roo.grid.Grid.superclass.constructor.call(this);
52220 };
52221 Roo.extend(Roo.grid.Grid, Roo.util.Observable, {
52222     
52223     /**
52224      * @cfg {String} ddGroup - drag drop group.
52225      */
52226
52227     /**
52228      * @cfg {Number} minColumnWidth The minimum width a column can be resized to. Default is 25.
52229      */
52230     minColumnWidth : 25,
52231
52232     /**
52233      * @cfg {Boolean} autoSizeColumns True to automatically resize the columns to fit their content
52234      * <b>on initial render.</b> It is more efficient to explicitly size the columns
52235      * through the ColumnModel's {@link Roo.grid.ColumnModel#width} config option.  Default is false.
52236      */
52237     autoSizeColumns : false,
52238
52239     /**
52240      * @cfg {Boolean} autoSizeHeaders True to measure headers with column data when auto sizing columns. Default is true.
52241      */
52242     autoSizeHeaders : true,
52243
52244     /**
52245      * @cfg {Boolean} monitorWindowResize True to autoSize the grid when the window resizes. Default is true.
52246      */
52247     monitorWindowResize : true,
52248
52249     /**
52250      * @cfg {Boolean} maxRowsToMeasure If autoSizeColumns is on, maxRowsToMeasure can be used to limit the number of
52251      * rows measured to get a columns size. Default is 0 (all rows).
52252      */
52253     maxRowsToMeasure : 0,
52254
52255     /**
52256      * @cfg {Boolean} trackMouseOver True to highlight rows when the mouse is over. Default is true.
52257      */
52258     trackMouseOver : true,
52259
52260     /**
52261     * @cfg {Boolean} enableDrag  True to enable drag of rows. Default is false. (double check if this is needed?)
52262     */
52263     
52264     /**
52265     * @cfg {Boolean} enableDragDrop True to enable drag and drop of rows. Default is false.
52266     */
52267     enableDragDrop : false,
52268     
52269     /**
52270     * @cfg {Boolean} enableColumnMove True to enable drag and drop reorder of columns. Default is true.
52271     */
52272     enableColumnMove : true,
52273     
52274     /**
52275     * @cfg {Boolean} enableColumnHide True to enable hiding of columns with the header context menu. Default is true.
52276     */
52277     enableColumnHide : true,
52278     
52279     /**
52280     * @cfg {Boolean} enableRowHeightSync True to manually sync row heights across locked and not locked rows. Default is false.
52281     */
52282     enableRowHeightSync : false,
52283     
52284     /**
52285     * @cfg {Boolean} stripeRows True to stripe the rows.  Default is true.
52286     */
52287     stripeRows : true,
52288     
52289     /**
52290     * @cfg {Boolean} autoHeight True to fit the height of the grid container to the height of the data. Default is false.
52291     */
52292     autoHeight : false,
52293
52294     /**
52295      * @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.
52296      */
52297     autoExpandColumn : false,
52298
52299     /**
52300     * @cfg {Number} autoExpandMin The minimum width the autoExpandColumn can have (if enabled).
52301     * Default is 50.
52302     */
52303     autoExpandMin : 50,
52304
52305     /**
52306     * @cfg {Number} autoExpandMax The maximum width the autoExpandColumn can have (if enabled). Default is 1000.
52307     */
52308     autoExpandMax : 1000,
52309
52310     /**
52311     * @cfg {Object} view The {@link Roo.grid.GridView} used by the grid. This can be set before a call to render().
52312     */
52313     view : null,
52314
52315     /**
52316     * @cfg {Object} loadMask An {@link Roo.LoadMask} config or true to mask the grid while loading. Default is false.
52317     */
52318     loadMask : false,
52319     /**
52320     * @cfg {Roo.dd.DropTarget} dropTarget An {@link Roo.dd.DropTarget} config
52321     */
52322     dropTarget: false,
52323     
52324    
52325     
52326     // private
52327     rendered : false,
52328
52329     /**
52330     * @cfg {Boolean} autoWidth True to set the grid's width to the default total width of the grid's columns instead
52331     * of a fixed width. Default is false.
52332     */
52333     /**
52334     * @cfg {Number} maxHeight Sets the maximum height of the grid - ignored if autoHeight is not on.
52335     */
52336     /**
52337      * Called once after all setup has been completed and the grid is ready to be rendered.
52338      * @return {Roo.grid.Grid} this
52339      */
52340     render : function()
52341     {
52342         var c = this.container;
52343         // try to detect autoHeight/width mode
52344         if((!c.dom.offsetHeight || c.dom.offsetHeight < 20) || c.getStyle("height") == "auto"){
52345             this.autoHeight = true;
52346         }
52347         var view = this.getView();
52348         view.init(this);
52349
52350         c.on("click", this.onClick, this);
52351         c.on("dblclick", this.onDblClick, this);
52352         c.on("contextmenu", this.onContextMenu, this);
52353         c.on("keydown", this.onKeyDown, this);
52354         if (Roo.isTouch) {
52355             c.on("touchstart", this.onTouchStart, this);
52356         }
52357
52358         this.relayEvents(c, ["mousedown","mouseup","mouseover","mouseout","keypress"]);
52359
52360         this.getSelectionModel().init(this);
52361
52362         view.render();
52363
52364         if(this.loadMask){
52365             this.loadMask = new Roo.LoadMask(this.container,
52366                     Roo.apply({store:this.dataSource}, this.loadMask));
52367         }
52368         
52369         
52370         if (this.toolbar && this.toolbar.xtype) {
52371             this.toolbar.container = this.getView().getHeaderPanel(true);
52372             this.toolbar = new Roo.Toolbar(this.toolbar);
52373         }
52374         if (this.footer && this.footer.xtype) {
52375             this.footer.dataSource = this.getDataSource();
52376             this.footer.container = this.getView().getFooterPanel(true);
52377             this.footer = Roo.factory(this.footer, Roo);
52378         }
52379         if (this.dropTarget && this.dropTarget.xtype) {
52380             delete this.dropTarget.xtype;
52381             this.dropTarget =  new Roo.dd.DropTarget(this.getView().mainBody, this.dropTarget);
52382         }
52383         
52384         
52385         this.rendered = true;
52386         this.fireEvent('render', this);
52387         return this;
52388     },
52389
52390         /**
52391          * Reconfigures the grid to use a different Store and Column Model.
52392          * The View will be bound to the new objects and refreshed.
52393          * @param {Roo.data.Store} dataSource The new {@link Roo.data.Store} object
52394          * @param {Roo.grid.ColumnModel} The new {@link Roo.grid.ColumnModel} object
52395          */
52396     reconfigure : function(dataSource, colModel){
52397         if(this.loadMask){
52398             this.loadMask.destroy();
52399             this.loadMask = new Roo.LoadMask(this.container,
52400                     Roo.apply({store:dataSource}, this.loadMask));
52401         }
52402         this.view.bind(dataSource, colModel);
52403         this.dataSource = dataSource;
52404         this.colModel = colModel;
52405         this.view.refresh(true);
52406     },
52407
52408     // private
52409     onKeyDown : function(e){
52410         this.fireEvent("keydown", e);
52411     },
52412
52413     /**
52414      * Destroy this grid.
52415      * @param {Boolean} removeEl True to remove the element
52416      */
52417     destroy : function(removeEl, keepListeners){
52418         if(this.loadMask){
52419             this.loadMask.destroy();
52420         }
52421         var c = this.container;
52422         c.removeAllListeners();
52423         this.view.destroy();
52424         this.colModel.purgeListeners();
52425         if(!keepListeners){
52426             this.purgeListeners();
52427         }
52428         c.update("");
52429         if(removeEl === true){
52430             c.remove();
52431         }
52432     },
52433
52434     // private
52435     processEvent : function(name, e){
52436         // does this fire select???
52437         Roo.log('grid:processEvent '  + name);
52438         
52439         if (name != 'touchstart' ) {
52440             this.fireEvent(name, e);    
52441         }
52442         
52443         var t = e.getTarget();
52444         var v = this.view;
52445         var header = v.findHeaderIndex(t);
52446         if(header !== false){
52447             var ename = name == 'touchstart' ? 'click' : name;
52448              
52449             this.fireEvent("header" + ename, this, header, e);
52450         }else{
52451             var row = v.findRowIndex(t);
52452             var cell = v.findCellIndex(t);
52453             if (name == 'touchstart') {
52454                 // first touch is always a click.
52455                 // hopefull this happens after selection is updated.?
52456                 name = false;
52457                 
52458                 if (typeof(this.selModel.getSelectedCell) != 'undefined') {
52459                     var cs = this.selModel.getSelectedCell();
52460                     if (row == cs[0] && cell == cs[1]){
52461                         name = 'dblclick';
52462                     }
52463                 }
52464                 if (typeof(this.selModel.getSelections) != 'undefined') {
52465                     var cs = this.selModel.getSelections();
52466                     var ds = this.dataSource;
52467                     if (cs.length == 1 && ds.getAt(row) == cs[0]){
52468                         name = 'dblclick';
52469                     }
52470                 }
52471                 if (!name) {
52472                     return;
52473                 }
52474             }
52475             
52476             
52477             if(row !== false){
52478                 this.fireEvent("row" + name, this, row, e);
52479                 if(cell !== false){
52480                     this.fireEvent("cell" + name, this, row, cell, e);
52481                 }
52482             }
52483         }
52484     },
52485
52486     // private
52487     onClick : function(e){
52488         this.processEvent("click", e);
52489     },
52490    // private
52491     onTouchStart : function(e){
52492         this.processEvent("touchstart", e);
52493     },
52494
52495     // private
52496     onContextMenu : function(e, t){
52497         this.processEvent("contextmenu", e);
52498     },
52499
52500     // private
52501     onDblClick : function(e){
52502         this.processEvent("dblclick", e);
52503     },
52504
52505     // private
52506     walkCells : function(row, col, step, fn, scope){
52507         var cm = this.colModel, clen = cm.getColumnCount();
52508         var ds = this.dataSource, rlen = ds.getCount(), first = true;
52509         if(step < 0){
52510             if(col < 0){
52511                 row--;
52512                 first = false;
52513             }
52514             while(row >= 0){
52515                 if(!first){
52516                     col = clen-1;
52517                 }
52518                 first = false;
52519                 while(col >= 0){
52520                     if(fn.call(scope || this, row, col, cm) === true){
52521                         return [row, col];
52522                     }
52523                     col--;
52524                 }
52525                 row--;
52526             }
52527         } else {
52528             if(col >= clen){
52529                 row++;
52530                 first = false;
52531             }
52532             while(row < rlen){
52533                 if(!first){
52534                     col = 0;
52535                 }
52536                 first = false;
52537                 while(col < clen){
52538                     if(fn.call(scope || this, row, col, cm) === true){
52539                         return [row, col];
52540                     }
52541                     col++;
52542                 }
52543                 row++;
52544             }
52545         }
52546         return null;
52547     },
52548
52549     // private
52550     getSelections : function(){
52551         return this.selModel.getSelections();
52552     },
52553
52554     /**
52555      * Causes the grid to manually recalculate its dimensions. Generally this is done automatically,
52556      * but if manual update is required this method will initiate it.
52557      */
52558     autoSize : function(){
52559         if(this.rendered){
52560             this.view.layout();
52561             if(this.view.adjustForScroll){
52562                 this.view.adjustForScroll();
52563             }
52564         }
52565     },
52566
52567     /**
52568      * Returns the grid's underlying element.
52569      * @return {Element} The element
52570      */
52571     getGridEl : function(){
52572         return this.container;
52573     },
52574
52575     // private for compatibility, overridden by editor grid
52576     stopEditing : function(){},
52577
52578     /**
52579      * Returns the grid's SelectionModel.
52580      * @return {SelectionModel}
52581      */
52582     getSelectionModel : function(){
52583         if(!this.selModel){
52584             this.selModel = new Roo.grid.RowSelectionModel();
52585         }
52586         return this.selModel;
52587     },
52588
52589     /**
52590      * Returns the grid's DataSource.
52591      * @return {DataSource}
52592      */
52593     getDataSource : function(){
52594         return this.dataSource;
52595     },
52596
52597     /**
52598      * Returns the grid's ColumnModel.
52599      * @return {ColumnModel}
52600      */
52601     getColumnModel : function(){
52602         return this.colModel;
52603     },
52604
52605     /**
52606      * Returns the grid's GridView object.
52607      * @return {GridView}
52608      */
52609     getView : function(){
52610         if(!this.view){
52611             this.view = new Roo.grid.GridView(this.viewConfig);
52612         }
52613         return this.view;
52614     },
52615     /**
52616      * Called to get grid's drag proxy text, by default returns this.ddText.
52617      * @return {String}
52618      */
52619     getDragDropText : function(){
52620         var count = this.selModel.getCount();
52621         return String.format(this.ddText, count, count == 1 ? '' : 's');
52622     }
52623 });
52624 /**
52625  * Configures the text is the drag proxy (defaults to "%0 selected row(s)").
52626  * %0 is replaced with the number of selected rows.
52627  * @type String
52628  */
52629 Roo.grid.Grid.prototype.ddText = "{0} selected row{1}";/*
52630  * Based on:
52631  * Ext JS Library 1.1.1
52632  * Copyright(c) 2006-2007, Ext JS, LLC.
52633  *
52634  * Originally Released Under LGPL - original licence link has changed is not relivant.
52635  *
52636  * Fork - LGPL
52637  * <script type="text/javascript">
52638  */
52639  
52640 Roo.grid.AbstractGridView = function(){
52641         this.grid = null;
52642         
52643         this.events = {
52644             "beforerowremoved" : true,
52645             "beforerowsinserted" : true,
52646             "beforerefresh" : true,
52647             "rowremoved" : true,
52648             "rowsinserted" : true,
52649             "rowupdated" : true,
52650             "refresh" : true
52651         };
52652     Roo.grid.AbstractGridView.superclass.constructor.call(this);
52653 };
52654
52655 Roo.extend(Roo.grid.AbstractGridView, Roo.util.Observable, {
52656     rowClass : "x-grid-row",
52657     cellClass : "x-grid-cell",
52658     tdClass : "x-grid-td",
52659     hdClass : "x-grid-hd",
52660     splitClass : "x-grid-hd-split",
52661     
52662     init: function(grid){
52663         this.grid = grid;
52664                 var cid = this.grid.getGridEl().id;
52665         this.colSelector = "#" + cid + " ." + this.cellClass + "-";
52666         this.tdSelector = "#" + cid + " ." + this.tdClass + "-";
52667         this.hdSelector = "#" + cid + " ." + this.hdClass + "-";
52668         this.splitSelector = "#" + cid + " ." + this.splitClass + "-";
52669         },
52670         
52671     getColumnRenderers : function(){
52672         var renderers = [];
52673         var cm = this.grid.colModel;
52674         var colCount = cm.getColumnCount();
52675         for(var i = 0; i < colCount; i++){
52676             renderers[i] = cm.getRenderer(i);
52677         }
52678         return renderers;
52679     },
52680     
52681     getColumnIds : function(){
52682         var ids = [];
52683         var cm = this.grid.colModel;
52684         var colCount = cm.getColumnCount();
52685         for(var i = 0; i < colCount; i++){
52686             ids[i] = cm.getColumnId(i);
52687         }
52688         return ids;
52689     },
52690     
52691     getDataIndexes : function(){
52692         if(!this.indexMap){
52693             this.indexMap = this.buildIndexMap();
52694         }
52695         return this.indexMap.colToData;
52696     },
52697     
52698     getColumnIndexByDataIndex : function(dataIndex){
52699         if(!this.indexMap){
52700             this.indexMap = this.buildIndexMap();
52701         }
52702         return this.indexMap.dataToCol[dataIndex];
52703     },
52704     
52705     /**
52706      * Set a css style for a column dynamically. 
52707      * @param {Number} colIndex The index of the column
52708      * @param {String} name The css property name
52709      * @param {String} value The css value
52710      */
52711     setCSSStyle : function(colIndex, name, value){
52712         var selector = "#" + this.grid.id + " .x-grid-col-" + colIndex;
52713         Roo.util.CSS.updateRule(selector, name, value);
52714     },
52715     
52716     generateRules : function(cm){
52717         var ruleBuf = [], rulesId = this.grid.id + '-cssrules';
52718         Roo.util.CSS.removeStyleSheet(rulesId);
52719         for(var i = 0, len = cm.getColumnCount(); i < len; i++){
52720             var cid = cm.getColumnId(i);
52721             ruleBuf.push(this.colSelector, cid, " {\n", cm.config[i].css, "}\n",
52722                          this.tdSelector, cid, " {\n}\n",
52723                          this.hdSelector, cid, " {\n}\n",
52724                          this.splitSelector, cid, " {\n}\n");
52725         }
52726         return Roo.util.CSS.createStyleSheet(ruleBuf.join(""), rulesId);
52727     }
52728 });/*
52729  * Based on:
52730  * Ext JS Library 1.1.1
52731  * Copyright(c) 2006-2007, Ext JS, LLC.
52732  *
52733  * Originally Released Under LGPL - original licence link has changed is not relivant.
52734  *
52735  * Fork - LGPL
52736  * <script type="text/javascript">
52737  */
52738
52739 // private
52740 // This is a support class used internally by the Grid components
52741 Roo.grid.HeaderDragZone = function(grid, hd, hd2){
52742     this.grid = grid;
52743     this.view = grid.getView();
52744     this.ddGroup = "gridHeader" + this.grid.getGridEl().id;
52745     Roo.grid.HeaderDragZone.superclass.constructor.call(this, hd);
52746     if(hd2){
52747         this.setHandleElId(Roo.id(hd));
52748         this.setOuterHandleElId(Roo.id(hd2));
52749     }
52750     this.scroll = false;
52751 };
52752 Roo.extend(Roo.grid.HeaderDragZone, Roo.dd.DragZone, {
52753     maxDragWidth: 120,
52754     getDragData : function(e){
52755         var t = Roo.lib.Event.getTarget(e);
52756         var h = this.view.findHeaderCell(t);
52757         if(h){
52758             return {ddel: h.firstChild, header:h};
52759         }
52760         return false;
52761     },
52762
52763     onInitDrag : function(e){
52764         this.view.headersDisabled = true;
52765         var clone = this.dragData.ddel.cloneNode(true);
52766         clone.id = Roo.id();
52767         clone.style.width = Math.min(this.dragData.header.offsetWidth,this.maxDragWidth) + "px";
52768         this.proxy.update(clone);
52769         return true;
52770     },
52771
52772     afterValidDrop : function(){
52773         var v = this.view;
52774         setTimeout(function(){
52775             v.headersDisabled = false;
52776         }, 50);
52777     },
52778
52779     afterInvalidDrop : function(){
52780         var v = this.view;
52781         setTimeout(function(){
52782             v.headersDisabled = false;
52783         }, 50);
52784     }
52785 });
52786 /*
52787  * Based on:
52788  * Ext JS Library 1.1.1
52789  * Copyright(c) 2006-2007, Ext JS, LLC.
52790  *
52791  * Originally Released Under LGPL - original licence link has changed is not relivant.
52792  *
52793  * Fork - LGPL
52794  * <script type="text/javascript">
52795  */
52796 // private
52797 // This is a support class used internally by the Grid components
52798 Roo.grid.HeaderDropZone = function(grid, hd, hd2){
52799     this.grid = grid;
52800     this.view = grid.getView();
52801     // split the proxies so they don't interfere with mouse events
52802     this.proxyTop = Roo.DomHelper.append(document.body, {
52803         cls:"col-move-top", html:"&#160;"
52804     }, true);
52805     this.proxyBottom = Roo.DomHelper.append(document.body, {
52806         cls:"col-move-bottom", html:"&#160;"
52807     }, true);
52808     this.proxyTop.hide = this.proxyBottom.hide = function(){
52809         this.setLeftTop(-100,-100);
52810         this.setStyle("visibility", "hidden");
52811     };
52812     this.ddGroup = "gridHeader" + this.grid.getGridEl().id;
52813     // temporarily disabled
52814     //Roo.dd.ScrollManager.register(this.view.scroller.dom);
52815     Roo.grid.HeaderDropZone.superclass.constructor.call(this, grid.getGridEl().dom);
52816 };
52817 Roo.extend(Roo.grid.HeaderDropZone, Roo.dd.DropZone, {
52818     proxyOffsets : [-4, -9],
52819     fly: Roo.Element.fly,
52820
52821     getTargetFromEvent : function(e){
52822         var t = Roo.lib.Event.getTarget(e);
52823         var cindex = this.view.findCellIndex(t);
52824         if(cindex !== false){
52825             return this.view.getHeaderCell(cindex);
52826         }
52827         return null;
52828     },
52829
52830     nextVisible : function(h){
52831         var v = this.view, cm = this.grid.colModel;
52832         h = h.nextSibling;
52833         while(h){
52834             if(!cm.isHidden(v.getCellIndex(h))){
52835                 return h;
52836             }
52837             h = h.nextSibling;
52838         }
52839         return null;
52840     },
52841
52842     prevVisible : function(h){
52843         var v = this.view, cm = this.grid.colModel;
52844         h = h.prevSibling;
52845         while(h){
52846             if(!cm.isHidden(v.getCellIndex(h))){
52847                 return h;
52848             }
52849             h = h.prevSibling;
52850         }
52851         return null;
52852     },
52853
52854     positionIndicator : function(h, n, e){
52855         var x = Roo.lib.Event.getPageX(e);
52856         var r = Roo.lib.Dom.getRegion(n.firstChild);
52857         var px, pt, py = r.top + this.proxyOffsets[1];
52858         if((r.right - x) <= (r.right-r.left)/2){
52859             px = r.right+this.view.borderWidth;
52860             pt = "after";
52861         }else{
52862             px = r.left;
52863             pt = "before";
52864         }
52865         var oldIndex = this.view.getCellIndex(h);
52866         var newIndex = this.view.getCellIndex(n);
52867
52868         if(this.grid.colModel.isFixed(newIndex)){
52869             return false;
52870         }
52871
52872         var locked = this.grid.colModel.isLocked(newIndex);
52873
52874         if(pt == "after"){
52875             newIndex++;
52876         }
52877         if(oldIndex < newIndex){
52878             newIndex--;
52879         }
52880         if(oldIndex == newIndex && (locked == this.grid.colModel.isLocked(oldIndex))){
52881             return false;
52882         }
52883         px +=  this.proxyOffsets[0];
52884         this.proxyTop.setLeftTop(px, py);
52885         this.proxyTop.show();
52886         if(!this.bottomOffset){
52887             this.bottomOffset = this.view.mainHd.getHeight();
52888         }
52889         this.proxyBottom.setLeftTop(px, py+this.proxyTop.dom.offsetHeight+this.bottomOffset);
52890         this.proxyBottom.show();
52891         return pt;
52892     },
52893
52894     onNodeEnter : function(n, dd, e, data){
52895         if(data.header != n){
52896             this.positionIndicator(data.header, n, e);
52897         }
52898     },
52899
52900     onNodeOver : function(n, dd, e, data){
52901         var result = false;
52902         if(data.header != n){
52903             result = this.positionIndicator(data.header, n, e);
52904         }
52905         if(!result){
52906             this.proxyTop.hide();
52907             this.proxyBottom.hide();
52908         }
52909         return result ? this.dropAllowed : this.dropNotAllowed;
52910     },
52911
52912     onNodeOut : function(n, dd, e, data){
52913         this.proxyTop.hide();
52914         this.proxyBottom.hide();
52915     },
52916
52917     onNodeDrop : function(n, dd, e, data){
52918         var h = data.header;
52919         if(h != n){
52920             var cm = this.grid.colModel;
52921             var x = Roo.lib.Event.getPageX(e);
52922             var r = Roo.lib.Dom.getRegion(n.firstChild);
52923             var pt = (r.right - x) <= ((r.right-r.left)/2) ? "after" : "before";
52924             var oldIndex = this.view.getCellIndex(h);
52925             var newIndex = this.view.getCellIndex(n);
52926             var locked = cm.isLocked(newIndex);
52927             if(pt == "after"){
52928                 newIndex++;
52929             }
52930             if(oldIndex < newIndex){
52931                 newIndex--;
52932             }
52933             if(oldIndex == newIndex && (locked == cm.isLocked(oldIndex))){
52934                 return false;
52935             }
52936             cm.setLocked(oldIndex, locked, true);
52937             cm.moveColumn(oldIndex, newIndex);
52938             this.grid.fireEvent("columnmove", oldIndex, newIndex);
52939             return true;
52940         }
52941         return false;
52942     }
52943 });
52944 /*
52945  * Based on:
52946  * Ext JS Library 1.1.1
52947  * Copyright(c) 2006-2007, Ext JS, LLC.
52948  *
52949  * Originally Released Under LGPL - original licence link has changed is not relivant.
52950  *
52951  * Fork - LGPL
52952  * <script type="text/javascript">
52953  */
52954   
52955 /**
52956  * @class Roo.grid.GridView
52957  * @extends Roo.util.Observable
52958  *
52959  * @constructor
52960  * @param {Object} config
52961  */
52962 Roo.grid.GridView = function(config){
52963     Roo.grid.GridView.superclass.constructor.call(this);
52964     this.el = null;
52965
52966     Roo.apply(this, config);
52967 };
52968
52969 Roo.extend(Roo.grid.GridView, Roo.grid.AbstractGridView, {
52970
52971     unselectable :  'unselectable="on"',
52972     unselectableCls :  'x-unselectable',
52973     
52974     
52975     rowClass : "x-grid-row",
52976
52977     cellClass : "x-grid-col",
52978
52979     tdClass : "x-grid-td",
52980
52981     hdClass : "x-grid-hd",
52982
52983     splitClass : "x-grid-split",
52984
52985     sortClasses : ["sort-asc", "sort-desc"],
52986
52987     enableMoveAnim : false,
52988
52989     hlColor: "C3DAF9",
52990
52991     dh : Roo.DomHelper,
52992
52993     fly : Roo.Element.fly,
52994
52995     css : Roo.util.CSS,
52996
52997     borderWidth: 1,
52998
52999     splitOffset: 3,
53000
53001     scrollIncrement : 22,
53002
53003     cellRE: /(?:.*?)x-grid-(?:hd|cell|csplit)-(?:[\d]+)-([\d]+)(?:.*?)/,
53004
53005     findRE: /\s?(?:x-grid-hd|x-grid-col|x-grid-csplit)\s/,
53006
53007     bind : function(ds, cm){
53008         if(this.ds){
53009             this.ds.un("load", this.onLoad, this);
53010             this.ds.un("datachanged", this.onDataChange, this);
53011             this.ds.un("add", this.onAdd, this);
53012             this.ds.un("remove", this.onRemove, this);
53013             this.ds.un("update", this.onUpdate, this);
53014             this.ds.un("clear", this.onClear, this);
53015         }
53016         if(ds){
53017             ds.on("load", this.onLoad, this);
53018             ds.on("datachanged", this.onDataChange, this);
53019             ds.on("add", this.onAdd, this);
53020             ds.on("remove", this.onRemove, this);
53021             ds.on("update", this.onUpdate, this);
53022             ds.on("clear", this.onClear, this);
53023         }
53024         this.ds = ds;
53025
53026         if(this.cm){
53027             this.cm.un("widthchange", this.onColWidthChange, this);
53028             this.cm.un("headerchange", this.onHeaderChange, this);
53029             this.cm.un("hiddenchange", this.onHiddenChange, this);
53030             this.cm.un("columnmoved", this.onColumnMove, this);
53031             this.cm.un("columnlockchange", this.onColumnLock, this);
53032         }
53033         if(cm){
53034             this.generateRules(cm);
53035             cm.on("widthchange", this.onColWidthChange, this);
53036             cm.on("headerchange", this.onHeaderChange, this);
53037             cm.on("hiddenchange", this.onHiddenChange, this);
53038             cm.on("columnmoved", this.onColumnMove, this);
53039             cm.on("columnlockchange", this.onColumnLock, this);
53040         }
53041         this.cm = cm;
53042     },
53043
53044     init: function(grid){
53045         Roo.grid.GridView.superclass.init.call(this, grid);
53046
53047         this.bind(grid.dataSource, grid.colModel);
53048
53049         grid.on("headerclick", this.handleHeaderClick, this);
53050
53051         if(grid.trackMouseOver){
53052             grid.on("mouseover", this.onRowOver, this);
53053             grid.on("mouseout", this.onRowOut, this);
53054         }
53055         grid.cancelTextSelection = function(){};
53056         this.gridId = grid.id;
53057
53058         var tpls = this.templates || {};
53059
53060         if(!tpls.master){
53061             tpls.master = new Roo.Template(
53062                '<div class="x-grid" hidefocus="true">',
53063                 '<a href="#" class="x-grid-focus" tabIndex="-1"></a>',
53064                   '<div class="x-grid-topbar"></div>',
53065                   '<div class="x-grid-scroller"><div></div></div>',
53066                   '<div class="x-grid-locked">',
53067                       '<div class="x-grid-header">{lockedHeader}</div>',
53068                       '<div class="x-grid-body">{lockedBody}</div>',
53069                   "</div>",
53070                   '<div class="x-grid-viewport">',
53071                       '<div class="x-grid-header">{header}</div>',
53072                       '<div class="x-grid-body">{body}</div>',
53073                   "</div>",
53074                   '<div class="x-grid-bottombar"></div>',
53075                  
53076                   '<div class="x-grid-resize-proxy">&#160;</div>',
53077                "</div>"
53078             );
53079             tpls.master.disableformats = true;
53080         }
53081
53082         if(!tpls.header){
53083             tpls.header = new Roo.Template(
53084                '<table border="0" cellspacing="0" cellpadding="0">',
53085                '<tbody><tr class="x-grid-hd-row">{cells}</tr></tbody>',
53086                "</table>{splits}"
53087             );
53088             tpls.header.disableformats = true;
53089         }
53090         tpls.header.compile();
53091
53092         if(!tpls.hcell){
53093             tpls.hcell = new Roo.Template(
53094                 '<td class="x-grid-hd x-grid-td-{id} {cellId}"><div title="{title}" class="x-grid-hd-inner x-grid-hd-{id}">',
53095                 '<div class="x-grid-hd-text ' + this.unselectableCls +  '" ' + this.unselectable +'>{value}<img class="x-grid-sort-icon" src="', Roo.BLANK_IMAGE_URL, '" /></div>',
53096                 "</div></td>"
53097              );
53098              tpls.hcell.disableFormats = true;
53099         }
53100         tpls.hcell.compile();
53101
53102         if(!tpls.hsplit){
53103             tpls.hsplit = new Roo.Template('<div class="x-grid-split {splitId} x-grid-split-{id}" style="{style} ' +
53104                                             this.unselectableCls +  '" ' + this.unselectable +'>&#160;</div>');
53105             tpls.hsplit.disableFormats = true;
53106         }
53107         tpls.hsplit.compile();
53108
53109         if(!tpls.body){
53110             tpls.body = new Roo.Template(
53111                '<table border="0" cellspacing="0" cellpadding="0">',
53112                "<tbody>{rows}</tbody>",
53113                "</table>"
53114             );
53115             tpls.body.disableFormats = true;
53116         }
53117         tpls.body.compile();
53118
53119         if(!tpls.row){
53120             tpls.row = new Roo.Template('<tr class="x-grid-row {alt}">{cells}</tr>');
53121             tpls.row.disableFormats = true;
53122         }
53123         tpls.row.compile();
53124
53125         if(!tpls.cell){
53126             tpls.cell = new Roo.Template(
53127                 '<td class="x-grid-col x-grid-td-{id} {cellId} {css}" tabIndex="0">',
53128                 '<div class="x-grid-col-{id} x-grid-cell-inner"><div class="x-grid-cell-text ' +
53129                     this.unselectableCls +  '" ' + this.unselectable +'" {attr}>{value}</div></div>',
53130                 "</td>"
53131             );
53132             tpls.cell.disableFormats = true;
53133         }
53134         tpls.cell.compile();
53135
53136         this.templates = tpls;
53137     },
53138
53139     // remap these for backwards compat
53140     onColWidthChange : function(){
53141         this.updateColumns.apply(this, arguments);
53142     },
53143     onHeaderChange : function(){
53144         this.updateHeaders.apply(this, arguments);
53145     }, 
53146     onHiddenChange : function(){
53147         this.handleHiddenChange.apply(this, arguments);
53148     },
53149     onColumnMove : function(){
53150         this.handleColumnMove.apply(this, arguments);
53151     },
53152     onColumnLock : function(){
53153         this.handleLockChange.apply(this, arguments);
53154     },
53155
53156     onDataChange : function(){
53157         this.refresh();
53158         this.updateHeaderSortState();
53159     },
53160
53161     onClear : function(){
53162         this.refresh();
53163     },
53164
53165     onUpdate : function(ds, record){
53166         this.refreshRow(record);
53167     },
53168
53169     refreshRow : function(record){
53170         var ds = this.ds, index;
53171         if(typeof record == 'number'){
53172             index = record;
53173             record = ds.getAt(index);
53174         }else{
53175             index = ds.indexOf(record);
53176         }
53177         this.insertRows(ds, index, index, true);
53178         this.onRemove(ds, record, index+1, true);
53179         this.syncRowHeights(index, index);
53180         this.layout();
53181         this.fireEvent("rowupdated", this, index, record);
53182     },
53183
53184     onAdd : function(ds, records, index){
53185         this.insertRows(ds, index, index + (records.length-1));
53186     },
53187
53188     onRemove : function(ds, record, index, isUpdate){
53189         if(isUpdate !== true){
53190             this.fireEvent("beforerowremoved", this, index, record);
53191         }
53192         var bt = this.getBodyTable(), lt = this.getLockedTable();
53193         if(bt.rows[index]){
53194             bt.firstChild.removeChild(bt.rows[index]);
53195         }
53196         if(lt.rows[index]){
53197             lt.firstChild.removeChild(lt.rows[index]);
53198         }
53199         if(isUpdate !== true){
53200             this.stripeRows(index);
53201             this.syncRowHeights(index, index);
53202             this.layout();
53203             this.fireEvent("rowremoved", this, index, record);
53204         }
53205     },
53206
53207     onLoad : function(){
53208         this.scrollToTop();
53209     },
53210
53211     /**
53212      * Scrolls the grid to the top
53213      */
53214     scrollToTop : function(){
53215         if(this.scroller){
53216             this.scroller.dom.scrollTop = 0;
53217             this.syncScroll();
53218         }
53219     },
53220
53221     /**
53222      * Gets a panel in the header of the grid that can be used for toolbars etc.
53223      * After modifying the contents of this panel a call to grid.autoSize() may be
53224      * required to register any changes in size.
53225      * @param {Boolean} doShow By default the header is hidden. Pass true to show the panel
53226      * @return Roo.Element
53227      */
53228     getHeaderPanel : function(doShow){
53229         if(doShow){
53230             this.headerPanel.show();
53231         }
53232         return this.headerPanel;
53233     },
53234
53235     /**
53236      * Gets a panel in the footer of the grid that can be used for toolbars etc.
53237      * After modifying the contents of this panel a call to grid.autoSize() may be
53238      * required to register any changes in size.
53239      * @param {Boolean} doShow By default the footer is hidden. Pass true to show the panel
53240      * @return Roo.Element
53241      */
53242     getFooterPanel : function(doShow){
53243         if(doShow){
53244             this.footerPanel.show();
53245         }
53246         return this.footerPanel;
53247     },
53248
53249     initElements : function(){
53250         var E = Roo.Element;
53251         var el = this.grid.getGridEl().dom.firstChild;
53252         var cs = el.childNodes;
53253
53254         this.el = new E(el);
53255         
53256          this.focusEl = new E(el.firstChild);
53257         this.focusEl.swallowEvent("click", true);
53258         
53259         this.headerPanel = new E(cs[1]);
53260         this.headerPanel.enableDisplayMode("block");
53261
53262         this.scroller = new E(cs[2]);
53263         this.scrollSizer = new E(this.scroller.dom.firstChild);
53264
53265         this.lockedWrap = new E(cs[3]);
53266         this.lockedHd = new E(this.lockedWrap.dom.firstChild);
53267         this.lockedBody = new E(this.lockedWrap.dom.childNodes[1]);
53268
53269         this.mainWrap = new E(cs[4]);
53270         this.mainHd = new E(this.mainWrap.dom.firstChild);
53271         this.mainBody = new E(this.mainWrap.dom.childNodes[1]);
53272
53273         this.footerPanel = new E(cs[5]);
53274         this.footerPanel.enableDisplayMode("block");
53275
53276         this.resizeProxy = new E(cs[6]);
53277
53278         this.headerSelector = String.format(
53279            '#{0} td.x-grid-hd, #{1} td.x-grid-hd',
53280            this.lockedHd.id, this.mainHd.id
53281         );
53282
53283         this.splitterSelector = String.format(
53284            '#{0} div.x-grid-split, #{1} div.x-grid-split',
53285            this.idToCssName(this.lockedHd.id), this.idToCssName(this.mainHd.id)
53286         );
53287     },
53288     idToCssName : function(s)
53289     {
53290         return s.replace(/[^a-z0-9]+/ig, '-');
53291     },
53292
53293     getHeaderCell : function(index){
53294         return Roo.DomQuery.select(this.headerSelector)[index];
53295     },
53296
53297     getHeaderCellMeasure : function(index){
53298         return this.getHeaderCell(index).firstChild;
53299     },
53300
53301     getHeaderCellText : function(index){
53302         return this.getHeaderCell(index).firstChild.firstChild;
53303     },
53304
53305     getLockedTable : function(){
53306         return this.lockedBody.dom.firstChild;
53307     },
53308
53309     getBodyTable : function(){
53310         return this.mainBody.dom.firstChild;
53311     },
53312
53313     getLockedRow : function(index){
53314         return this.getLockedTable().rows[index];
53315     },
53316
53317     getRow : function(index){
53318         return this.getBodyTable().rows[index];
53319     },
53320
53321     getRowComposite : function(index){
53322         if(!this.rowEl){
53323             this.rowEl = new Roo.CompositeElementLite();
53324         }
53325         var els = [], lrow, mrow;
53326         if(lrow = this.getLockedRow(index)){
53327             els.push(lrow);
53328         }
53329         if(mrow = this.getRow(index)){
53330             els.push(mrow);
53331         }
53332         this.rowEl.elements = els;
53333         return this.rowEl;
53334     },
53335     /**
53336      * Gets the 'td' of the cell
53337      * 
53338      * @param {Integer} rowIndex row to select
53339      * @param {Integer} colIndex column to select
53340      * 
53341      * @return {Object} 
53342      */
53343     getCell : function(rowIndex, colIndex){
53344         var locked = this.cm.getLockedCount();
53345         var source;
53346         if(colIndex < locked){
53347             source = this.lockedBody.dom.firstChild;
53348         }else{
53349             source = this.mainBody.dom.firstChild;
53350             colIndex -= locked;
53351         }
53352         return source.rows[rowIndex].childNodes[colIndex];
53353     },
53354
53355     getCellText : function(rowIndex, colIndex){
53356         return this.getCell(rowIndex, colIndex).firstChild.firstChild;
53357     },
53358
53359     getCellBox : function(cell){
53360         var b = this.fly(cell).getBox();
53361         if(Roo.isOpera){ // opera fails to report the Y
53362             b.y = cell.offsetTop + this.mainBody.getY();
53363         }
53364         return b;
53365     },
53366
53367     getCellIndex : function(cell){
53368         var id = String(cell.className).match(this.cellRE);
53369         if(id){
53370             return parseInt(id[1], 10);
53371         }
53372         return 0;
53373     },
53374
53375     findHeaderIndex : function(n){
53376         var r = Roo.fly(n).findParent("td." + this.hdClass, 6);
53377         return r ? this.getCellIndex(r) : false;
53378     },
53379
53380     findHeaderCell : function(n){
53381         var r = Roo.fly(n).findParent("td." + this.hdClass, 6);
53382         return r ? r : false;
53383     },
53384
53385     findRowIndex : function(n){
53386         if(!n){
53387             return false;
53388         }
53389         var r = Roo.fly(n).findParent("tr." + this.rowClass, 6);
53390         return r ? r.rowIndex : false;
53391     },
53392
53393     findCellIndex : function(node){
53394         var stop = this.el.dom;
53395         while(node && node != stop){
53396             if(this.findRE.test(node.className)){
53397                 return this.getCellIndex(node);
53398             }
53399             node = node.parentNode;
53400         }
53401         return false;
53402     },
53403
53404     getColumnId : function(index){
53405         return this.cm.getColumnId(index);
53406     },
53407
53408     getSplitters : function()
53409     {
53410         if(this.splitterSelector){
53411            return Roo.DomQuery.select(this.splitterSelector);
53412         }else{
53413             return null;
53414       }
53415     },
53416
53417     getSplitter : function(index){
53418         return this.getSplitters()[index];
53419     },
53420
53421     onRowOver : function(e, t){
53422         var row;
53423         if((row = this.findRowIndex(t)) !== false){
53424             this.getRowComposite(row).addClass("x-grid-row-over");
53425         }
53426     },
53427
53428     onRowOut : function(e, t){
53429         var row;
53430         if((row = this.findRowIndex(t)) !== false && row !== this.findRowIndex(e.getRelatedTarget())){
53431             this.getRowComposite(row).removeClass("x-grid-row-over");
53432         }
53433     },
53434
53435     renderHeaders : function(){
53436         var cm = this.cm;
53437         var ct = this.templates.hcell, ht = this.templates.header, st = this.templates.hsplit;
53438         var cb = [], lb = [], sb = [], lsb = [], p = {};
53439         for(var i = 0, len = cm.getColumnCount(); i < len; i++){
53440             p.cellId = "x-grid-hd-0-" + i;
53441             p.splitId = "x-grid-csplit-0-" + i;
53442             p.id = cm.getColumnId(i);
53443             p.title = cm.getColumnTooltip(i) || "";
53444             p.value = cm.getColumnHeader(i) || "";
53445             p.style = (this.grid.enableColumnResize === false || !cm.isResizable(i) || cm.isFixed(i)) ? 'cursor:default' : '';
53446             if(!cm.isLocked(i)){
53447                 cb[cb.length] = ct.apply(p);
53448                 sb[sb.length] = st.apply(p);
53449             }else{
53450                 lb[lb.length] = ct.apply(p);
53451                 lsb[lsb.length] = st.apply(p);
53452             }
53453         }
53454         return [ht.apply({cells: lb.join(""), splits:lsb.join("")}),
53455                 ht.apply({cells: cb.join(""), splits:sb.join("")})];
53456     },
53457
53458     updateHeaders : function(){
53459         var html = this.renderHeaders();
53460         this.lockedHd.update(html[0]);
53461         this.mainHd.update(html[1]);
53462     },
53463
53464     /**
53465      * Focuses the specified row.
53466      * @param {Number} row The row index
53467      */
53468     focusRow : function(row)
53469     {
53470         //Roo.log('GridView.focusRow');
53471         var x = this.scroller.dom.scrollLeft;
53472         this.focusCell(row, 0, false);
53473         this.scroller.dom.scrollLeft = x;
53474     },
53475
53476     /**
53477      * Focuses the specified cell.
53478      * @param {Number} row The row index
53479      * @param {Number} col The column index
53480      * @param {Boolean} hscroll false to disable horizontal scrolling
53481      */
53482     focusCell : function(row, col, hscroll)
53483     {
53484         //Roo.log('GridView.focusCell');
53485         var el = this.ensureVisible(row, col, hscroll);
53486         this.focusEl.alignTo(el, "tl-tl");
53487         if(Roo.isGecko){
53488             this.focusEl.focus();
53489         }else{
53490             this.focusEl.focus.defer(1, this.focusEl);
53491         }
53492     },
53493
53494     /**
53495      * Scrolls the specified cell into view
53496      * @param {Number} row The row index
53497      * @param {Number} col The column index
53498      * @param {Boolean} hscroll false to disable horizontal scrolling
53499      */
53500     ensureVisible : function(row, col, hscroll)
53501     {
53502         //Roo.log('GridView.ensureVisible,' + row + ',' + col);
53503         //return null; //disable for testing.
53504         if(typeof row != "number"){
53505             row = row.rowIndex;
53506         }
53507         if(row < 0 && row >= this.ds.getCount()){
53508             return  null;
53509         }
53510         col = (col !== undefined ? col : 0);
53511         var cm = this.grid.colModel;
53512         while(cm.isHidden(col)){
53513             col++;
53514         }
53515
53516         var el = this.getCell(row, col);
53517         if(!el){
53518             return null;
53519         }
53520         var c = this.scroller.dom;
53521
53522         var ctop = parseInt(el.offsetTop, 10);
53523         var cleft = parseInt(el.offsetLeft, 10);
53524         var cbot = ctop + el.offsetHeight;
53525         var cright = cleft + el.offsetWidth;
53526         
53527         var ch = c.clientHeight - this.mainHd.dom.offsetHeight;
53528         var stop = parseInt(c.scrollTop, 10);
53529         var sleft = parseInt(c.scrollLeft, 10);
53530         var sbot = stop + ch;
53531         var sright = sleft + c.clientWidth;
53532         /*
53533         Roo.log('GridView.ensureVisible:' +
53534                 ' ctop:' + ctop +
53535                 ' c.clientHeight:' + c.clientHeight +
53536                 ' this.mainHd.dom.offsetHeight:' + this.mainHd.dom.offsetHeight +
53537                 ' stop:' + stop +
53538                 ' cbot:' + cbot +
53539                 ' sbot:' + sbot +
53540                 ' ch:' + ch  
53541                 );
53542         */
53543         if(ctop < stop){
53544              c.scrollTop = ctop;
53545             //Roo.log("set scrolltop to ctop DISABLE?");
53546         }else if(cbot > sbot){
53547             //Roo.log("set scrolltop to cbot-ch");
53548             c.scrollTop = cbot-ch;
53549         }
53550         
53551         if(hscroll !== false){
53552             if(cleft < sleft){
53553                 c.scrollLeft = cleft;
53554             }else if(cright > sright){
53555                 c.scrollLeft = cright-c.clientWidth;
53556             }
53557         }
53558          
53559         return el;
53560     },
53561
53562     updateColumns : function(){
53563         this.grid.stopEditing();
53564         var cm = this.grid.colModel, colIds = this.getColumnIds();
53565         //var totalWidth = cm.getTotalWidth();
53566         var pos = 0;
53567         for(var i = 0, len = cm.getColumnCount(); i < len; i++){
53568             //if(cm.isHidden(i)) continue;
53569             var w = cm.getColumnWidth(i);
53570             this.css.updateRule(this.colSelector+this.idToCssName(colIds[i]), "width", (w - this.borderWidth) + "px");
53571             this.css.updateRule(this.hdSelector+this.idToCssName(colIds[i]), "width", (w - this.borderWidth) + "px");
53572         }
53573         this.updateSplitters();
53574     },
53575
53576     generateRules : function(cm){
53577         var ruleBuf = [], rulesId = this.idToCssName(this.grid.id)+ '-cssrules';
53578         Roo.util.CSS.removeStyleSheet(rulesId);
53579         for(var i = 0, len = cm.getColumnCount(); i < len; i++){
53580             var cid = cm.getColumnId(i);
53581             var align = '';
53582             if(cm.config[i].align){
53583                 align = 'text-align:'+cm.config[i].align+';';
53584             }
53585             var hidden = '';
53586             if(cm.isHidden(i)){
53587                 hidden = 'display:none;';
53588             }
53589             var width = "width:" + (cm.getColumnWidth(i) - this.borderWidth) + "px;";
53590             ruleBuf.push(
53591                     this.colSelector, cid, " {\n", cm.config[i].css, align, width, "\n}\n",
53592                     this.hdSelector, cid, " {\n", align, width, "}\n",
53593                     this.tdSelector, cid, " {\n",hidden,"\n}\n",
53594                     this.splitSelector, cid, " {\n", hidden , "\n}\n");
53595         }
53596         return Roo.util.CSS.createStyleSheet(ruleBuf.join(""), rulesId);
53597     },
53598
53599     updateSplitters : function(){
53600         var cm = this.cm, s = this.getSplitters();
53601         if(s){ // splitters not created yet
53602             var pos = 0, locked = true;
53603             for(var i = 0, len = cm.getColumnCount(); i < len; i++){
53604                 if(cm.isHidden(i)) continue;
53605                 var w = cm.getColumnWidth(i); // make sure it's a number
53606                 if(!cm.isLocked(i) && locked){
53607                     pos = 0;
53608                     locked = false;
53609                 }
53610                 pos += w;
53611                 s[i].style.left = (pos-this.splitOffset) + "px";
53612             }
53613         }
53614     },
53615
53616     handleHiddenChange : function(colModel, colIndex, hidden){
53617         if(hidden){
53618             this.hideColumn(colIndex);
53619         }else{
53620             this.unhideColumn(colIndex);
53621         }
53622     },
53623
53624     hideColumn : function(colIndex){
53625         var cid = this.getColumnId(colIndex);
53626         this.css.updateRule(this.tdSelector+this.idToCssName(cid), "display", "none");
53627         this.css.updateRule(this.splitSelector+this.idToCssName(cid), "display", "none");
53628         if(Roo.isSafari){
53629             this.updateHeaders();
53630         }
53631         this.updateSplitters();
53632         this.layout();
53633     },
53634
53635     unhideColumn : function(colIndex){
53636         var cid = this.getColumnId(colIndex);
53637         this.css.updateRule(this.tdSelector+this.idToCssName(cid), "display", "");
53638         this.css.updateRule(this.splitSelector+this.idToCssName(cid), "display", "");
53639
53640         if(Roo.isSafari){
53641             this.updateHeaders();
53642         }
53643         this.updateSplitters();
53644         this.layout();
53645     },
53646
53647     insertRows : function(dm, firstRow, lastRow, isUpdate){
53648         if(firstRow == 0 && lastRow == dm.getCount()-1){
53649             this.refresh();
53650         }else{
53651             if(!isUpdate){
53652                 this.fireEvent("beforerowsinserted", this, firstRow, lastRow);
53653             }
53654             var s = this.getScrollState();
53655             var markup = this.renderRows(firstRow, lastRow);
53656             this.bufferRows(markup[0], this.getLockedTable(), firstRow);
53657             this.bufferRows(markup[1], this.getBodyTable(), firstRow);
53658             this.restoreScroll(s);
53659             if(!isUpdate){
53660                 this.fireEvent("rowsinserted", this, firstRow, lastRow);
53661                 this.syncRowHeights(firstRow, lastRow);
53662                 this.stripeRows(firstRow);
53663                 this.layout();
53664             }
53665         }
53666     },
53667
53668     bufferRows : function(markup, target, index){
53669         var before = null, trows = target.rows, tbody = target.tBodies[0];
53670         if(index < trows.length){
53671             before = trows[index];
53672         }
53673         var b = document.createElement("div");
53674         b.innerHTML = "<table><tbody>"+markup+"</tbody></table>";
53675         var rows = b.firstChild.rows;
53676         for(var i = 0, len = rows.length; i < len; i++){
53677             if(before){
53678                 tbody.insertBefore(rows[0], before);
53679             }else{
53680                 tbody.appendChild(rows[0]);
53681             }
53682         }
53683         b.innerHTML = "";
53684         b = null;
53685     },
53686
53687     deleteRows : function(dm, firstRow, lastRow){
53688         if(dm.getRowCount()<1){
53689             this.fireEvent("beforerefresh", this);
53690             this.mainBody.update("");
53691             this.lockedBody.update("");
53692             this.fireEvent("refresh", this);
53693         }else{
53694             this.fireEvent("beforerowsdeleted", this, firstRow, lastRow);
53695             var bt = this.getBodyTable();
53696             var tbody = bt.firstChild;
53697             var rows = bt.rows;
53698             for(var rowIndex = firstRow; rowIndex <= lastRow; rowIndex++){
53699                 tbody.removeChild(rows[firstRow]);
53700             }
53701             this.stripeRows(firstRow);
53702             this.fireEvent("rowsdeleted", this, firstRow, lastRow);
53703         }
53704     },
53705
53706     updateRows : function(dataSource, firstRow, lastRow){
53707         var s = this.getScrollState();
53708         this.refresh();
53709         this.restoreScroll(s);
53710     },
53711
53712     handleSort : function(dataSource, sortColumnIndex, sortDir, noRefresh){
53713         if(!noRefresh){
53714            this.refresh();
53715         }
53716         this.updateHeaderSortState();
53717     },
53718
53719     getScrollState : function(){
53720         
53721         var sb = this.scroller.dom;
53722         return {left: sb.scrollLeft, top: sb.scrollTop};
53723     },
53724
53725     stripeRows : function(startRow){
53726         if(!this.grid.stripeRows || this.ds.getCount() < 1){
53727             return;
53728         }
53729         startRow = startRow || 0;
53730         var rows = this.getBodyTable().rows;
53731         var lrows = this.getLockedTable().rows;
53732         var cls = ' x-grid-row-alt ';
53733         for(var i = startRow, len = rows.length; i < len; i++){
53734             var row = rows[i], lrow = lrows[i];
53735             var isAlt = ((i+1) % 2 == 0);
53736             var hasAlt = (' '+row.className + ' ').indexOf(cls) != -1;
53737             if(isAlt == hasAlt){
53738                 continue;
53739             }
53740             if(isAlt){
53741                 row.className += " x-grid-row-alt";
53742             }else{
53743                 row.className = row.className.replace("x-grid-row-alt", "");
53744             }
53745             if(lrow){
53746                 lrow.className = row.className;
53747             }
53748         }
53749     },
53750
53751     restoreScroll : function(state){
53752         //Roo.log('GridView.restoreScroll');
53753         var sb = this.scroller.dom;
53754         sb.scrollLeft = state.left;
53755         sb.scrollTop = state.top;
53756         this.syncScroll();
53757     },
53758
53759     syncScroll : function(){
53760         //Roo.log('GridView.syncScroll');
53761         var sb = this.scroller.dom;
53762         var sh = this.mainHd.dom;
53763         var bs = this.mainBody.dom;
53764         var lv = this.lockedBody.dom;
53765         sh.scrollLeft = bs.scrollLeft = sb.scrollLeft;
53766         lv.scrollTop = bs.scrollTop = sb.scrollTop;
53767     },
53768
53769     handleScroll : function(e){
53770         this.syncScroll();
53771         var sb = this.scroller.dom;
53772         this.grid.fireEvent("bodyscroll", sb.scrollLeft, sb.scrollTop);
53773         e.stopEvent();
53774     },
53775
53776     handleWheel : function(e){
53777         var d = e.getWheelDelta();
53778         this.scroller.dom.scrollTop -= d*22;
53779         // set this here to prevent jumpy scrolling on large tables
53780         this.lockedBody.dom.scrollTop = this.mainBody.dom.scrollTop = this.scroller.dom.scrollTop;
53781         e.stopEvent();
53782     },
53783
53784     renderRows : function(startRow, endRow){
53785         // pull in all the crap needed to render rows
53786         var g = this.grid, cm = g.colModel, ds = g.dataSource, stripe = g.stripeRows;
53787         var colCount = cm.getColumnCount();
53788
53789         if(ds.getCount() < 1){
53790             return ["", ""];
53791         }
53792
53793         // build a map for all the columns
53794         var cs = [];
53795         for(var i = 0; i < colCount; i++){
53796             var name = cm.getDataIndex(i);
53797             cs[i] = {
53798                 name : typeof name == 'undefined' ? ds.fields.get(i).name : name,
53799                 renderer : cm.getRenderer(i),
53800                 id : cm.getColumnId(i),
53801                 locked : cm.isLocked(i)
53802             };
53803         }
53804
53805         startRow = startRow || 0;
53806         endRow = typeof endRow == "undefined"? ds.getCount()-1 : endRow;
53807
53808         // records to render
53809         var rs = ds.getRange(startRow, endRow);
53810
53811         return this.doRender(cs, rs, ds, startRow, colCount, stripe);
53812     },
53813
53814     // As much as I hate to duplicate code, this was branched because FireFox really hates
53815     // [].join("") on strings. The performance difference was substantial enough to
53816     // branch this function
53817     doRender : Roo.isGecko ?
53818             function(cs, rs, ds, startRow, colCount, stripe){
53819                 var ts = this.templates, ct = ts.cell, rt = ts.row;
53820                 // buffers
53821                 var buf = "", lbuf = "", cb, lcb, c, p = {}, rp = {}, r, rowIndex;
53822                 
53823                 var hasListener = this.grid.hasListener('rowclass');
53824                 var rowcfg = {};
53825                 for(var j = 0, len = rs.length; j < len; j++){
53826                     r = rs[j]; cb = ""; lcb = ""; rowIndex = (j+startRow);
53827                     for(var i = 0; i < colCount; i++){
53828                         c = cs[i];
53829                         p.cellId = "x-grid-cell-" + rowIndex + "-" + i;
53830                         p.id = c.id;
53831                         p.css = p.attr = "";
53832                         p.value = c.renderer(r.data[c.name], p, r, rowIndex, i, ds);
53833                         if(p.value == undefined || p.value === "") p.value = "&#160;";
53834                         if(r.dirty && typeof r.modified[c.name] !== 'undefined'){
53835                             p.css += p.css ? ' x-grid-dirty-cell' : 'x-grid-dirty-cell';
53836                         }
53837                         var markup = ct.apply(p);
53838                         if(!c.locked){
53839                             cb+= markup;
53840                         }else{
53841                             lcb+= markup;
53842                         }
53843                     }
53844                     var alt = [];
53845                     if(stripe && ((rowIndex+1) % 2 == 0)){
53846                         alt.push("x-grid-row-alt")
53847                     }
53848                     if(r.dirty){
53849                         alt.push(  " x-grid-dirty-row");
53850                     }
53851                     rp.cells = lcb;
53852                     if(this.getRowClass){
53853                         alt.push(this.getRowClass(r, rowIndex));
53854                     }
53855                     if (hasListener) {
53856                         rowcfg = {
53857                              
53858                             record: r,
53859                             rowIndex : rowIndex,
53860                             rowClass : ''
53861                         }
53862                         this.grid.fireEvent('rowclass', this, rowcfg);
53863                         alt.push(rowcfg.rowClass);
53864                     }
53865                     rp.alt = alt.join(" ");
53866                     lbuf+= rt.apply(rp);
53867                     rp.cells = cb;
53868                     buf+=  rt.apply(rp);
53869                 }
53870                 return [lbuf, buf];
53871             } :
53872             function(cs, rs, ds, startRow, colCount, stripe){
53873                 var ts = this.templates, ct = ts.cell, rt = ts.row;
53874                 // buffers
53875                 var buf = [], lbuf = [], cb, lcb, c, p = {}, rp = {}, r, rowIndex;
53876                 var hasListener = this.grid.hasListener('rowclass');
53877  
53878                 var rowcfg = {};
53879                 for(var j = 0, len = rs.length; j < len; j++){
53880                     r = rs[j]; cb = []; lcb = []; rowIndex = (j+startRow);
53881                     for(var i = 0; i < colCount; i++){
53882                         c = cs[i];
53883                         p.cellId = "x-grid-cell-" + rowIndex + "-" + i;
53884                         p.id = c.id;
53885                         p.css = p.attr = "";
53886                         p.value = c.renderer(r.data[c.name], p, r, rowIndex, i, ds);
53887                         if(p.value == undefined || p.value === "") p.value = "&#160;";
53888                         if(r.dirty && typeof r.modified[c.name] !== 'undefined'){
53889                             p.css += p.css ? ' x-grid-dirty-cell' : 'x-grid-dirty-cell';
53890                         }
53891                         
53892                         var markup = ct.apply(p);
53893                         if(!c.locked){
53894                             cb[cb.length] = markup;
53895                         }else{
53896                             lcb[lcb.length] = markup;
53897                         }
53898                     }
53899                     var alt = [];
53900                     if(stripe && ((rowIndex+1) % 2 == 0)){
53901                         alt.push( "x-grid-row-alt");
53902                     }
53903                     if(r.dirty){
53904                         alt.push(" x-grid-dirty-row");
53905                     }
53906                     rp.cells = lcb;
53907                     if(this.getRowClass){
53908                         alt.push( this.getRowClass(r, rowIndex));
53909                     }
53910                     if (hasListener) {
53911                         rowcfg = {
53912                              
53913                             record: r,
53914                             rowIndex : rowIndex,
53915                             rowClass : ''
53916                         }
53917                         this.grid.fireEvent('rowclass', this, rowcfg);
53918                         alt.push(rowcfg.rowClass);
53919                     }
53920                     rp.alt = alt.join(" ");
53921                     rp.cells = lcb.join("");
53922                     lbuf[lbuf.length] = rt.apply(rp);
53923                     rp.cells = cb.join("");
53924                     buf[buf.length] =  rt.apply(rp);
53925                 }
53926                 return [lbuf.join(""), buf.join("")];
53927             },
53928
53929     renderBody : function(){
53930         var markup = this.renderRows();
53931         var bt = this.templates.body;
53932         return [bt.apply({rows: markup[0]}), bt.apply({rows: markup[1]})];
53933     },
53934
53935     /**
53936      * Refreshes the grid
53937      * @param {Boolean} headersToo
53938      */
53939     refresh : function(headersToo){
53940         this.fireEvent("beforerefresh", this);
53941         this.grid.stopEditing();
53942         var result = this.renderBody();
53943         this.lockedBody.update(result[0]);
53944         this.mainBody.update(result[1]);
53945         if(headersToo === true){
53946             this.updateHeaders();
53947             this.updateColumns();
53948             this.updateSplitters();
53949             this.updateHeaderSortState();
53950         }
53951         this.syncRowHeights();
53952         this.layout();
53953         this.fireEvent("refresh", this);
53954     },
53955
53956     handleColumnMove : function(cm, oldIndex, newIndex){
53957         this.indexMap = null;
53958         var s = this.getScrollState();
53959         this.refresh(true);
53960         this.restoreScroll(s);
53961         this.afterMove(newIndex);
53962     },
53963
53964     afterMove : function(colIndex){
53965         if(this.enableMoveAnim && Roo.enableFx){
53966             this.fly(this.getHeaderCell(colIndex).firstChild).highlight(this.hlColor);
53967         }
53968         // if multisort - fix sortOrder, and reload..
53969         if (this.grid.dataSource.multiSort) {
53970             // the we can call sort again..
53971             var dm = this.grid.dataSource;
53972             var cm = this.grid.colModel;
53973             var so = [];
53974             for(var i = 0; i < cm.config.length; i++ ) {
53975                 
53976                 if ((typeof(dm.sortToggle[cm.config[i].dataIndex]) == 'undefined')) {
53977                     continue; // dont' bother, it's not in sort list or being set.
53978                 }
53979                 
53980                 so.push(cm.config[i].dataIndex);
53981             };
53982             dm.sortOrder = so;
53983             dm.load(dm.lastOptions);
53984             
53985             
53986         }
53987         
53988     },
53989
53990     updateCell : function(dm, rowIndex, dataIndex){
53991         var colIndex = this.getColumnIndexByDataIndex(dataIndex);
53992         if(typeof colIndex == "undefined"){ // not present in grid
53993             return;
53994         }
53995         var cm = this.grid.colModel;
53996         var cell = this.getCell(rowIndex, colIndex);
53997         var cellText = this.getCellText(rowIndex, colIndex);
53998
53999         var p = {
54000             cellId : "x-grid-cell-" + rowIndex + "-" + colIndex,
54001             id : cm.getColumnId(colIndex),
54002             css: colIndex == cm.getColumnCount()-1 ? "x-grid-col-last" : ""
54003         };
54004         var renderer = cm.getRenderer(colIndex);
54005         var val = renderer(dm.getValueAt(rowIndex, dataIndex), p, rowIndex, colIndex, dm);
54006         if(typeof val == "undefined" || val === "") val = "&#160;";
54007         cellText.innerHTML = val;
54008         cell.className = this.cellClass + " " + this.idToCssName(p.cellId) + " " + p.css;
54009         this.syncRowHeights(rowIndex, rowIndex);
54010     },
54011
54012     calcColumnWidth : function(colIndex, maxRowsToMeasure){
54013         var maxWidth = 0;
54014         if(this.grid.autoSizeHeaders){
54015             var h = this.getHeaderCellMeasure(colIndex);
54016             maxWidth = Math.max(maxWidth, h.scrollWidth);
54017         }
54018         var tb, index;
54019         if(this.cm.isLocked(colIndex)){
54020             tb = this.getLockedTable();
54021             index = colIndex;
54022         }else{
54023             tb = this.getBodyTable();
54024             index = colIndex - this.cm.getLockedCount();
54025         }
54026         if(tb && tb.rows){
54027             var rows = tb.rows;
54028             var stopIndex = Math.min(maxRowsToMeasure || rows.length, rows.length);
54029             for(var i = 0; i < stopIndex; i++){
54030                 var cell = rows[i].childNodes[index].firstChild;
54031                 maxWidth = Math.max(maxWidth, cell.scrollWidth);
54032             }
54033         }
54034         return maxWidth + /*margin for error in IE*/ 5;
54035     },
54036     /**
54037      * Autofit a column to its content.
54038      * @param {Number} colIndex
54039      * @param {Boolean} forceMinSize true to force the column to go smaller if possible
54040      */
54041      autoSizeColumn : function(colIndex, forceMinSize, suppressEvent){
54042          if(this.cm.isHidden(colIndex)){
54043              return; // can't calc a hidden column
54044          }
54045         if(forceMinSize){
54046             var cid = this.cm.getColumnId(colIndex);
54047             this.css.updateRule(this.colSelector +this.idToCssName( cid), "width", this.grid.minColumnWidth + "px");
54048            if(this.grid.autoSizeHeaders){
54049                this.css.updateRule(this.hdSelector + this.idToCssName(cid), "width", this.grid.minColumnWidth + "px");
54050            }
54051         }
54052         var newWidth = this.calcColumnWidth(colIndex);
54053         this.cm.setColumnWidth(colIndex,
54054             Math.max(this.grid.minColumnWidth, newWidth), suppressEvent);
54055         if(!suppressEvent){
54056             this.grid.fireEvent("columnresize", colIndex, newWidth);
54057         }
54058     },
54059
54060     /**
54061      * Autofits all columns to their content and then expands to fit any extra space in the grid
54062      */
54063      autoSizeColumns : function(){
54064         var cm = this.grid.colModel;
54065         var colCount = cm.getColumnCount();
54066         for(var i = 0; i < colCount; i++){
54067             this.autoSizeColumn(i, true, true);
54068         }
54069         if(cm.getTotalWidth() < this.scroller.dom.clientWidth){
54070             this.fitColumns();
54071         }else{
54072             this.updateColumns();
54073             this.layout();
54074         }
54075     },
54076
54077     /**
54078      * Autofits all columns to the grid's width proportionate with their current size
54079      * @param {Boolean} reserveScrollSpace Reserve space for a scrollbar
54080      */
54081     fitColumns : function(reserveScrollSpace){
54082         var cm = this.grid.colModel;
54083         var colCount = cm.getColumnCount();
54084         var cols = [];
54085         var width = 0;
54086         var i, w;
54087         for (i = 0; i < colCount; i++){
54088             if(!cm.isHidden(i) && !cm.isFixed(i)){
54089                 w = cm.getColumnWidth(i);
54090                 cols.push(i);
54091                 cols.push(w);
54092                 width += w;
54093             }
54094         }
54095         var avail = Math.min(this.scroller.dom.clientWidth, this.el.getWidth());
54096         if(reserveScrollSpace){
54097             avail -= 17;
54098         }
54099         var frac = (avail - cm.getTotalWidth())/width;
54100         while (cols.length){
54101             w = cols.pop();
54102             i = cols.pop();
54103             cm.setColumnWidth(i, Math.floor(w + w*frac), true);
54104         }
54105         this.updateColumns();
54106         this.layout();
54107     },
54108
54109     onRowSelect : function(rowIndex){
54110         var row = this.getRowComposite(rowIndex);
54111         row.addClass("x-grid-row-selected");
54112     },
54113
54114     onRowDeselect : function(rowIndex){
54115         var row = this.getRowComposite(rowIndex);
54116         row.removeClass("x-grid-row-selected");
54117     },
54118
54119     onCellSelect : function(row, col){
54120         var cell = this.getCell(row, col);
54121         if(cell){
54122             Roo.fly(cell).addClass("x-grid-cell-selected");
54123         }
54124     },
54125
54126     onCellDeselect : function(row, col){
54127         var cell = this.getCell(row, col);
54128         if(cell){
54129             Roo.fly(cell).removeClass("x-grid-cell-selected");
54130         }
54131     },
54132
54133     updateHeaderSortState : function(){
54134         
54135         // sort state can be single { field: xxx, direction : yyy}
54136         // or   { xxx=>ASC , yyy : DESC ..... }
54137         
54138         var mstate = {};
54139         if (!this.ds.multiSort) { 
54140             var state = this.ds.getSortState();
54141             if(!state){
54142                 return;
54143             }
54144             mstate[state.field] = state.direction;
54145             // FIXME... - this is not used here.. but might be elsewhere..
54146             this.sortState = state;
54147             
54148         } else {
54149             mstate = this.ds.sortToggle;
54150         }
54151         //remove existing sort classes..
54152         
54153         var sc = this.sortClasses;
54154         var hds = this.el.select(this.headerSelector).removeClass(sc);
54155         
54156         for(var f in mstate) {
54157         
54158             var sortColumn = this.cm.findColumnIndex(f);
54159             
54160             if(sortColumn != -1){
54161                 var sortDir = mstate[f];        
54162                 hds.item(sortColumn).addClass(sc[sortDir == "DESC" ? 1 : 0]);
54163             }
54164         }
54165         
54166          
54167         
54168     },
54169
54170
54171     handleHeaderClick : function(g, index,e){
54172         
54173         Roo.log("header click");
54174         
54175         if (Roo.isTouch) {
54176             // touch events on header are handled by context
54177             this.handleHdCtx(g,index,e);
54178             return;
54179         }
54180         
54181         
54182         if(this.headersDisabled){
54183             return;
54184         }
54185         var dm = g.dataSource, cm = g.colModel;
54186         if(!cm.isSortable(index)){
54187             return;
54188         }
54189         g.stopEditing();
54190         
54191         if (dm.multiSort) {
54192             // update the sortOrder
54193             var so = [];
54194             for(var i = 0; i < cm.config.length; i++ ) {
54195                 
54196                 if ((typeof(dm.sortToggle[cm.config[i].dataIndex]) == 'undefined') && (index != i)) {
54197                     continue; // dont' bother, it's not in sort list or being set.
54198                 }
54199                 
54200                 so.push(cm.config[i].dataIndex);
54201             };
54202             dm.sortOrder = so;
54203         }
54204         
54205         
54206         dm.sort(cm.getDataIndex(index));
54207     },
54208
54209
54210     destroy : function(){
54211         if(this.colMenu){
54212             this.colMenu.removeAll();
54213             Roo.menu.MenuMgr.unregister(this.colMenu);
54214             this.colMenu.getEl().remove();
54215             delete this.colMenu;
54216         }
54217         if(this.hmenu){
54218             this.hmenu.removeAll();
54219             Roo.menu.MenuMgr.unregister(this.hmenu);
54220             this.hmenu.getEl().remove();
54221             delete this.hmenu;
54222         }
54223         if(this.grid.enableColumnMove){
54224             var dds = Roo.dd.DDM.ids['gridHeader' + this.grid.getGridEl().id];
54225             if(dds){
54226                 for(var dd in dds){
54227                     if(!dds[dd].config.isTarget && dds[dd].dragElId){
54228                         var elid = dds[dd].dragElId;
54229                         dds[dd].unreg();
54230                         Roo.get(elid).remove();
54231                     } else if(dds[dd].config.isTarget){
54232                         dds[dd].proxyTop.remove();
54233                         dds[dd].proxyBottom.remove();
54234                         dds[dd].unreg();
54235                     }
54236                     if(Roo.dd.DDM.locationCache[dd]){
54237                         delete Roo.dd.DDM.locationCache[dd];
54238                     }
54239                 }
54240                 delete Roo.dd.DDM.ids['gridHeader' + this.grid.getGridEl().id];
54241             }
54242         }
54243         Roo.util.CSS.removeStyleSheet(this.idToCssName(this.grid.id) + '-cssrules');
54244         this.bind(null, null);
54245         Roo.EventManager.removeResizeListener(this.onWindowResize, this);
54246     },
54247
54248     handleLockChange : function(){
54249         this.refresh(true);
54250     },
54251
54252     onDenyColumnLock : function(){
54253
54254     },
54255
54256     onDenyColumnHide : function(){
54257
54258     },
54259
54260     handleHdMenuClick : function(item){
54261         var index = this.hdCtxIndex;
54262         var cm = this.cm, ds = this.ds;
54263         switch(item.id){
54264             case "asc":
54265                 ds.sort(cm.getDataIndex(index), "ASC");
54266                 break;
54267             case "desc":
54268                 ds.sort(cm.getDataIndex(index), "DESC");
54269                 break;
54270             case "lock":
54271                 var lc = cm.getLockedCount();
54272                 if(cm.getColumnCount(true) <= lc+1){
54273                     this.onDenyColumnLock();
54274                     return;
54275                 }
54276                 if(lc != index){
54277                     cm.setLocked(index, true, true);
54278                     cm.moveColumn(index, lc);
54279                     this.grid.fireEvent("columnmove", index, lc);
54280                 }else{
54281                     cm.setLocked(index, true);
54282                 }
54283             break;
54284             case "unlock":
54285                 var lc = cm.getLockedCount();
54286                 if((lc-1) != index){
54287                     cm.setLocked(index, false, true);
54288                     cm.moveColumn(index, lc-1);
54289                     this.grid.fireEvent("columnmove", index, lc-1);
54290                 }else{
54291                     cm.setLocked(index, false);
54292                 }
54293             break;
54294             case 'wider': // used to expand cols on touch..
54295             case 'narrow':
54296                 var cw = cm.getColumnWidth(index);
54297                 cw += (item.id == 'wider' ? 1 : -1) * 50;
54298                 cw = Math.max(0, cw);
54299                 cw = Math.min(cw,4000);
54300                 cm.setColumnWidth(index, cw);
54301                 break;
54302                 
54303             default:
54304                 index = cm.getIndexById(item.id.substr(4));
54305                 if(index != -1){
54306                     if(item.checked && cm.getColumnCount(true) <= 1){
54307                         this.onDenyColumnHide();
54308                         return false;
54309                     }
54310                     cm.setHidden(index, item.checked);
54311                 }
54312         }
54313         return true;
54314     },
54315
54316     beforeColMenuShow : function(){
54317         var cm = this.cm,  colCount = cm.getColumnCount();
54318         this.colMenu.removeAll();
54319         for(var i = 0; i < colCount; i++){
54320             this.colMenu.add(new Roo.menu.CheckItem({
54321                 id: "col-"+cm.getColumnId(i),
54322                 text: cm.getColumnHeader(i),
54323                 checked: !cm.isHidden(i),
54324                 hideOnClick:false
54325             }));
54326         }
54327     },
54328
54329     handleHdCtx : function(g, index, e){
54330         e.stopEvent();
54331         var hd = this.getHeaderCell(index);
54332         this.hdCtxIndex = index;
54333         var ms = this.hmenu.items, cm = this.cm;
54334         ms.get("asc").setDisabled(!cm.isSortable(index));
54335         ms.get("desc").setDisabled(!cm.isSortable(index));
54336         if(this.grid.enableColLock !== false){
54337             ms.get("lock").setDisabled(cm.isLocked(index));
54338             ms.get("unlock").setDisabled(!cm.isLocked(index));
54339         }
54340         this.hmenu.show(hd, "tl-bl");
54341     },
54342
54343     handleHdOver : function(e){
54344         var hd = this.findHeaderCell(e.getTarget());
54345         if(hd && !this.headersDisabled){
54346             if(this.grid.colModel.isSortable(this.getCellIndex(hd))){
54347                this.fly(hd).addClass("x-grid-hd-over");
54348             }
54349         }
54350     },
54351
54352     handleHdOut : function(e){
54353         var hd = this.findHeaderCell(e.getTarget());
54354         if(hd){
54355             this.fly(hd).removeClass("x-grid-hd-over");
54356         }
54357     },
54358
54359     handleSplitDblClick : function(e, t){
54360         var i = this.getCellIndex(t);
54361         if(this.grid.enableColumnResize !== false && this.cm.isResizable(i) && !this.cm.isFixed(i)){
54362             this.autoSizeColumn(i, true);
54363             this.layout();
54364         }
54365     },
54366
54367     render : function(){
54368
54369         var cm = this.cm;
54370         var colCount = cm.getColumnCount();
54371
54372         if(this.grid.monitorWindowResize === true){
54373             Roo.EventManager.onWindowResize(this.onWindowResize, this, true);
54374         }
54375         var header = this.renderHeaders();
54376         var body = this.templates.body.apply({rows:""});
54377         var html = this.templates.master.apply({
54378             lockedBody: body,
54379             body: body,
54380             lockedHeader: header[0],
54381             header: header[1]
54382         });
54383
54384         //this.updateColumns();
54385
54386         this.grid.getGridEl().dom.innerHTML = html;
54387
54388         this.initElements();
54389         
54390         // a kludge to fix the random scolling effect in webkit
54391         this.el.on("scroll", function() {
54392             this.el.dom.scrollTop=0; // hopefully not recursive..
54393         },this);
54394
54395         this.scroller.on("scroll", this.handleScroll, this);
54396         this.lockedBody.on("mousewheel", this.handleWheel, this);
54397         this.mainBody.on("mousewheel", this.handleWheel, this);
54398
54399         this.mainHd.on("mouseover", this.handleHdOver, this);
54400         this.mainHd.on("mouseout", this.handleHdOut, this);
54401         this.mainHd.on("dblclick", this.handleSplitDblClick, this,
54402                 {delegate: "."+this.splitClass});
54403
54404         this.lockedHd.on("mouseover", this.handleHdOver, this);
54405         this.lockedHd.on("mouseout", this.handleHdOut, this);
54406         this.lockedHd.on("dblclick", this.handleSplitDblClick, this,
54407                 {delegate: "."+this.splitClass});
54408
54409         if(this.grid.enableColumnResize !== false && Roo.grid.SplitDragZone){
54410             new Roo.grid.SplitDragZone(this.grid, this.lockedHd.dom, this.mainHd.dom);
54411         }
54412
54413         this.updateSplitters();
54414
54415         if(this.grid.enableColumnMove && Roo.grid.HeaderDragZone){
54416             new Roo.grid.HeaderDragZone(this.grid, this.lockedHd.dom, this.mainHd.dom);
54417             new Roo.grid.HeaderDropZone(this.grid, this.lockedHd.dom, this.mainHd.dom);
54418         }
54419
54420         if(this.grid.enableCtxMenu !== false && Roo.menu.Menu){
54421             this.hmenu = new Roo.menu.Menu({id: this.grid.id + "-hctx"});
54422             this.hmenu.add(
54423                 {id:"asc", text: this.sortAscText, cls: "xg-hmenu-sort-asc"},
54424                 {id:"desc", text: this.sortDescText, cls: "xg-hmenu-sort-desc"}
54425             );
54426             if(this.grid.enableColLock !== false){
54427                 this.hmenu.add('-',
54428                     {id:"lock", text: this.lockText, cls: "xg-hmenu-lock"},
54429                     {id:"unlock", text: this.unlockText, cls: "xg-hmenu-unlock"}
54430                 );
54431             }
54432             if (Roo.isTouch) {
54433                  this.hmenu.add('-',
54434                     {id:"wider", text: this.columnsWiderText},
54435                     {id:"narrow", text: this.columnsNarrowText }
54436                 );
54437                 
54438                  
54439             }
54440             
54441             if(this.grid.enableColumnHide !== false){
54442
54443                 this.colMenu = new Roo.menu.Menu({id:this.grid.id + "-hcols-menu"});
54444                 this.colMenu.on("beforeshow", this.beforeColMenuShow, this);
54445                 this.colMenu.on("itemclick", this.handleHdMenuClick, this);
54446
54447                 this.hmenu.add('-',
54448                     {id:"columns", text: this.columnsText, menu: this.colMenu}
54449                 );
54450             }
54451             this.hmenu.on("itemclick", this.handleHdMenuClick, this);
54452
54453             this.grid.on("headercontextmenu", this.handleHdCtx, this);
54454         }
54455
54456         if((this.grid.enableDragDrop || this.grid.enableDrag) && Roo.grid.GridDragZone){
54457             this.dd = new Roo.grid.GridDragZone(this.grid, {
54458                 ddGroup : this.grid.ddGroup || 'GridDD'
54459             });
54460             
54461         }
54462
54463         /*
54464         for(var i = 0; i < colCount; i++){
54465             if(cm.isHidden(i)){
54466                 this.hideColumn(i);
54467             }
54468             if(cm.config[i].align){
54469                 this.css.updateRule(this.colSelector + i, "textAlign", cm.config[i].align);
54470                 this.css.updateRule(this.hdSelector + i, "textAlign", cm.config[i].align);
54471             }
54472         }*/
54473         
54474         this.updateHeaderSortState();
54475
54476         this.beforeInitialResize();
54477         this.layout(true);
54478
54479         // two part rendering gives faster view to the user
54480         this.renderPhase2.defer(1, this);
54481     },
54482
54483     renderPhase2 : function(){
54484         // render the rows now
54485         this.refresh();
54486         if(this.grid.autoSizeColumns){
54487             this.autoSizeColumns();
54488         }
54489     },
54490
54491     beforeInitialResize : function(){
54492
54493     },
54494
54495     onColumnSplitterMoved : function(i, w){
54496         this.userResized = true;
54497         var cm = this.grid.colModel;
54498         cm.setColumnWidth(i, w, true);
54499         var cid = cm.getColumnId(i);
54500         this.css.updateRule(this.colSelector + this.idToCssName(cid), "width", (w-this.borderWidth) + "px");
54501         this.css.updateRule(this.hdSelector + this.idToCssName(cid), "width", (w-this.borderWidth) + "px");
54502         this.updateSplitters();
54503         this.layout();
54504         this.grid.fireEvent("columnresize", i, w);
54505     },
54506
54507     syncRowHeights : function(startIndex, endIndex){
54508         if(this.grid.enableRowHeightSync === true && this.cm.getLockedCount() > 0){
54509             startIndex = startIndex || 0;
54510             var mrows = this.getBodyTable().rows;
54511             var lrows = this.getLockedTable().rows;
54512             var len = mrows.length-1;
54513             endIndex = Math.min(endIndex || len, len);
54514             for(var i = startIndex; i <= endIndex; i++){
54515                 var m = mrows[i], l = lrows[i];
54516                 var h = Math.max(m.offsetHeight, l.offsetHeight);
54517                 m.style.height = l.style.height = h + "px";
54518             }
54519         }
54520     },
54521
54522     layout : function(initialRender, is2ndPass){
54523         var g = this.grid;
54524         var auto = g.autoHeight;
54525         var scrollOffset = 16;
54526         var c = g.getGridEl(), cm = this.cm,
54527                 expandCol = g.autoExpandColumn,
54528                 gv = this;
54529         //c.beginMeasure();
54530
54531         if(!c.dom.offsetWidth){ // display:none?
54532             if(initialRender){
54533                 this.lockedWrap.show();
54534                 this.mainWrap.show();
54535             }
54536             return;
54537         }
54538
54539         var hasLock = this.cm.isLocked(0);
54540
54541         var tbh = this.headerPanel.getHeight();
54542         var bbh = this.footerPanel.getHeight();
54543
54544         if(auto){
54545             var ch = this.getBodyTable().offsetHeight + tbh + bbh + this.mainHd.getHeight();
54546             var newHeight = ch + c.getBorderWidth("tb");
54547             if(g.maxHeight){
54548                 newHeight = Math.min(g.maxHeight, newHeight);
54549             }
54550             c.setHeight(newHeight);
54551         }
54552
54553         if(g.autoWidth){
54554             c.setWidth(cm.getTotalWidth()+c.getBorderWidth('lr'));
54555         }
54556
54557         var s = this.scroller;
54558
54559         var csize = c.getSize(true);
54560
54561         this.el.setSize(csize.width, csize.height);
54562
54563         this.headerPanel.setWidth(csize.width);
54564         this.footerPanel.setWidth(csize.width);
54565
54566         var hdHeight = this.mainHd.getHeight();
54567         var vw = csize.width;
54568         var vh = csize.height - (tbh + bbh);
54569
54570         s.setSize(vw, vh);
54571
54572         var bt = this.getBodyTable();
54573         var ltWidth = hasLock ?
54574                       Math.max(this.getLockedTable().offsetWidth, this.lockedHd.dom.firstChild.offsetWidth) : 0;
54575
54576         var scrollHeight = bt.offsetHeight;
54577         var scrollWidth = ltWidth + bt.offsetWidth;
54578         var vscroll = false, hscroll = false;
54579
54580         this.scrollSizer.setSize(scrollWidth, scrollHeight+hdHeight);
54581
54582         var lw = this.lockedWrap, mw = this.mainWrap;
54583         var lb = this.lockedBody, mb = this.mainBody;
54584
54585         setTimeout(function(){
54586             var t = s.dom.offsetTop;
54587             var w = s.dom.clientWidth,
54588                 h = s.dom.clientHeight;
54589
54590             lw.setTop(t);
54591             lw.setSize(ltWidth, h);
54592
54593             mw.setLeftTop(ltWidth, t);
54594             mw.setSize(w-ltWidth, h);
54595
54596             lb.setHeight(h-hdHeight);
54597             mb.setHeight(h-hdHeight);
54598
54599             if(is2ndPass !== true && !gv.userResized && expandCol){
54600                 // high speed resize without full column calculation
54601                 
54602                 var ci = cm.getIndexById(expandCol);
54603                 if (ci < 0) {
54604                     ci = cm.findColumnIndex(expandCol);
54605                 }
54606                 ci = Math.max(0, ci); // make sure it's got at least the first col.
54607                 var expandId = cm.getColumnId(ci);
54608                 var  tw = cm.getTotalWidth(false);
54609                 var currentWidth = cm.getColumnWidth(ci);
54610                 var cw = Math.min(Math.max(((w-tw)+currentWidth-2)-/*scrollbar*/(w <= s.dom.offsetWidth ? 0 : 18), g.autoExpandMin), g.autoExpandMax);
54611                 if(currentWidth != cw){
54612                     cm.setColumnWidth(ci, cw, true);
54613                     gv.css.updateRule(gv.colSelector+gv.idToCssName(expandId), "width", (cw - gv.borderWidth) + "px");
54614                     gv.css.updateRule(gv.hdSelector+gv.idToCssName(expandId), "width", (cw - gv.borderWidth) + "px");
54615                     gv.updateSplitters();
54616                     gv.layout(false, true);
54617                 }
54618             }
54619
54620             if(initialRender){
54621                 lw.show();
54622                 mw.show();
54623             }
54624             //c.endMeasure();
54625         }, 10);
54626     },
54627
54628     onWindowResize : function(){
54629         if(!this.grid.monitorWindowResize || this.grid.autoHeight){
54630             return;
54631         }
54632         this.layout();
54633     },
54634
54635     appendFooter : function(parentEl){
54636         return null;
54637     },
54638
54639     sortAscText : "Sort Ascending",
54640     sortDescText : "Sort Descending",
54641     lockText : "Lock Column",
54642     unlockText : "Unlock Column",
54643     columnsText : "Columns",
54644  
54645     columnsWiderText : "Wider",
54646     columnsNarrowText : "Thinner"
54647 });
54648
54649
54650 Roo.grid.GridView.ColumnDragZone = function(grid, hd){
54651     Roo.grid.GridView.ColumnDragZone.superclass.constructor.call(this, grid, hd, null);
54652     this.proxy.el.addClass('x-grid3-col-dd');
54653 };
54654
54655 Roo.extend(Roo.grid.GridView.ColumnDragZone, Roo.grid.HeaderDragZone, {
54656     handleMouseDown : function(e){
54657
54658     },
54659
54660     callHandleMouseDown : function(e){
54661         Roo.grid.GridView.ColumnDragZone.superclass.handleMouseDown.call(this, e);
54662     }
54663 });
54664 /*
54665  * Based on:
54666  * Ext JS Library 1.1.1
54667  * Copyright(c) 2006-2007, Ext JS, LLC.
54668  *
54669  * Originally Released Under LGPL - original licence link has changed is not relivant.
54670  *
54671  * Fork - LGPL
54672  * <script type="text/javascript">
54673  */
54674  
54675 // private
54676 // This is a support class used internally by the Grid components
54677 Roo.grid.SplitDragZone = function(grid, hd, hd2){
54678     this.grid = grid;
54679     this.view = grid.getView();
54680     this.proxy = this.view.resizeProxy;
54681     Roo.grid.SplitDragZone.superclass.constructor.call(this, hd,
54682         "gridSplitters" + this.grid.getGridEl().id, {
54683         dragElId : Roo.id(this.proxy.dom), resizeFrame:false
54684     });
54685     this.setHandleElId(Roo.id(hd));
54686     this.setOuterHandleElId(Roo.id(hd2));
54687     this.scroll = false;
54688 };
54689 Roo.extend(Roo.grid.SplitDragZone, Roo.dd.DDProxy, {
54690     fly: Roo.Element.fly,
54691
54692     b4StartDrag : function(x, y){
54693         this.view.headersDisabled = true;
54694         this.proxy.setHeight(this.view.mainWrap.getHeight());
54695         var w = this.cm.getColumnWidth(this.cellIndex);
54696         var minw = Math.max(w-this.grid.minColumnWidth, 0);
54697         this.resetConstraints();
54698         this.setXConstraint(minw, 1000);
54699         this.setYConstraint(0, 0);
54700         this.minX = x - minw;
54701         this.maxX = x + 1000;
54702         this.startPos = x;
54703         Roo.dd.DDProxy.prototype.b4StartDrag.call(this, x, y);
54704     },
54705
54706
54707     handleMouseDown : function(e){
54708         ev = Roo.EventObject.setEvent(e);
54709         var t = this.fly(ev.getTarget());
54710         if(t.hasClass("x-grid-split")){
54711             this.cellIndex = this.view.getCellIndex(t.dom);
54712             this.split = t.dom;
54713             this.cm = this.grid.colModel;
54714             if(this.cm.isResizable(this.cellIndex) && !this.cm.isFixed(this.cellIndex)){
54715                 Roo.grid.SplitDragZone.superclass.handleMouseDown.apply(this, arguments);
54716             }
54717         }
54718     },
54719
54720     endDrag : function(e){
54721         this.view.headersDisabled = false;
54722         var endX = Math.max(this.minX, Roo.lib.Event.getPageX(e));
54723         var diff = endX - this.startPos;
54724         this.view.onColumnSplitterMoved(this.cellIndex, this.cm.getColumnWidth(this.cellIndex)+diff);
54725     },
54726
54727     autoOffset : function(){
54728         this.setDelta(0,0);
54729     }
54730 });/*
54731  * Based on:
54732  * Ext JS Library 1.1.1
54733  * Copyright(c) 2006-2007, Ext JS, LLC.
54734  *
54735  * Originally Released Under LGPL - original licence link has changed is not relivant.
54736  *
54737  * Fork - LGPL
54738  * <script type="text/javascript">
54739  */
54740  
54741 // private
54742 // This is a support class used internally by the Grid components
54743 Roo.grid.GridDragZone = function(grid, config){
54744     this.view = grid.getView();
54745     Roo.grid.GridDragZone.superclass.constructor.call(this, this.view.mainBody.dom, config);
54746     if(this.view.lockedBody){
54747         this.setHandleElId(Roo.id(this.view.mainBody.dom));
54748         this.setOuterHandleElId(Roo.id(this.view.lockedBody.dom));
54749     }
54750     this.scroll = false;
54751     this.grid = grid;
54752     this.ddel = document.createElement('div');
54753     this.ddel.className = 'x-grid-dd-wrap';
54754 };
54755
54756 Roo.extend(Roo.grid.GridDragZone, Roo.dd.DragZone, {
54757     ddGroup : "GridDD",
54758
54759     getDragData : function(e){
54760         var t = Roo.lib.Event.getTarget(e);
54761         var rowIndex = this.view.findRowIndex(t);
54762         var sm = this.grid.selModel;
54763             
54764         //Roo.log(rowIndex);
54765         
54766         if (sm.getSelectedCell) {
54767             // cell selection..
54768             if (!sm.getSelectedCell()) {
54769                 return false;
54770             }
54771             if (rowIndex != sm.getSelectedCell()[0]) {
54772                 return false;
54773             }
54774         
54775         }
54776         
54777         if(rowIndex !== false){
54778             
54779             // if editorgrid.. 
54780             
54781             
54782             //Roo.log([ sm.getSelectedCell() ? sm.getSelectedCell()[0] : 'NO' , rowIndex ]);
54783                
54784             //if(!sm.isSelected(rowIndex) || e.hasModifier()){
54785               //  
54786             //}
54787             if (e.hasModifier()){
54788                 sm.handleMouseDown(e, t); // non modifier buttons are handled by row select.
54789             }
54790             
54791             Roo.log("getDragData");
54792             
54793             return {
54794                 grid: this.grid,
54795                 ddel: this.ddel,
54796                 rowIndex: rowIndex,
54797                 selections:sm.getSelections ? sm.getSelections() : (
54798                     sm.getSelectedCell() ? [ this.grid.ds.getAt(sm.getSelectedCell()[0]) ] : []
54799                 )
54800             };
54801         }
54802         return false;
54803     },
54804
54805     onInitDrag : function(e){
54806         var data = this.dragData;
54807         this.ddel.innerHTML = this.grid.getDragDropText();
54808         this.proxy.update(this.ddel);
54809         // fire start drag?
54810     },
54811
54812     afterRepair : function(){
54813         this.dragging = false;
54814     },
54815
54816     getRepairXY : function(e, data){
54817         return false;
54818     },
54819
54820     onEndDrag : function(data, e){
54821         // fire end drag?
54822     },
54823
54824     onValidDrop : function(dd, e, id){
54825         // fire drag drop?
54826         this.hideProxy();
54827     },
54828
54829     beforeInvalidDrop : function(e, id){
54830
54831     }
54832 });/*
54833  * Based on:
54834  * Ext JS Library 1.1.1
54835  * Copyright(c) 2006-2007, Ext JS, LLC.
54836  *
54837  * Originally Released Under LGPL - original licence link has changed is not relivant.
54838  *
54839  * Fork - LGPL
54840  * <script type="text/javascript">
54841  */
54842  
54843
54844 /**
54845  * @class Roo.grid.ColumnModel
54846  * @extends Roo.util.Observable
54847  * This is the default implementation of a ColumnModel used by the Grid. It defines
54848  * the columns in the grid.
54849  * <br>Usage:<br>
54850  <pre><code>
54851  var colModel = new Roo.grid.ColumnModel([
54852         {header: "Ticker", width: 60, sortable: true, locked: true},
54853         {header: "Company Name", width: 150, sortable: true},
54854         {header: "Market Cap.", width: 100, sortable: true},
54855         {header: "$ Sales", width: 100, sortable: true, renderer: money},
54856         {header: "Employees", width: 100, sortable: true, resizable: false}
54857  ]);
54858  </code></pre>
54859  * <p>
54860  
54861  * The config options listed for this class are options which may appear in each
54862  * individual column definition.
54863  * <br/>RooJS Fix - column id's are not sequential but use Roo.id() - fixes bugs with layouts.
54864  * @constructor
54865  * @param {Object} config An Array of column config objects. See this class's
54866  * config objects for details.
54867 */
54868 Roo.grid.ColumnModel = function(config){
54869         /**
54870      * The config passed into the constructor
54871      */
54872     this.config = config;
54873     this.lookup = {};
54874
54875     // if no id, create one
54876     // if the column does not have a dataIndex mapping,
54877     // map it to the order it is in the config
54878     for(var i = 0, len = config.length; i < len; i++){
54879         var c = config[i];
54880         if(typeof c.dataIndex == "undefined"){
54881             c.dataIndex = i;
54882         }
54883         if(typeof c.renderer == "string"){
54884             c.renderer = Roo.util.Format[c.renderer];
54885         }
54886         if(typeof c.id == "undefined"){
54887             c.id = Roo.id();
54888         }
54889         if(c.editor && c.editor.xtype){
54890             c.editor  = Roo.factory(c.editor, Roo.grid);
54891         }
54892         if(c.editor && c.editor.isFormField){
54893             c.editor = new Roo.grid.GridEditor(c.editor);
54894         }
54895         this.lookup[c.id] = c;
54896     }
54897
54898     /**
54899      * The width of columns which have no width specified (defaults to 100)
54900      * @type Number
54901      */
54902     this.defaultWidth = 100;
54903
54904     /**
54905      * Default sortable of columns which have no sortable specified (defaults to false)
54906      * @type Boolean
54907      */
54908     this.defaultSortable = false;
54909
54910     this.addEvents({
54911         /**
54912              * @event widthchange
54913              * Fires when the width of a column changes.
54914              * @param {ColumnModel} this
54915              * @param {Number} columnIndex The column index
54916              * @param {Number} newWidth The new width
54917              */
54918             "widthchange": true,
54919         /**
54920              * @event headerchange
54921              * Fires when the text of a header changes.
54922              * @param {ColumnModel} this
54923              * @param {Number} columnIndex The column index
54924              * @param {Number} newText The new header text
54925              */
54926             "headerchange": true,
54927         /**
54928              * @event hiddenchange
54929              * Fires when a column is hidden or "unhidden".
54930              * @param {ColumnModel} this
54931              * @param {Number} columnIndex The column index
54932              * @param {Boolean} hidden true if hidden, false otherwise
54933              */
54934             "hiddenchange": true,
54935             /**
54936          * @event columnmoved
54937          * Fires when a column is moved.
54938          * @param {ColumnModel} this
54939          * @param {Number} oldIndex
54940          * @param {Number} newIndex
54941          */
54942         "columnmoved" : true,
54943         /**
54944          * @event columlockchange
54945          * Fires when a column's locked state is changed
54946          * @param {ColumnModel} this
54947          * @param {Number} colIndex
54948          * @param {Boolean} locked true if locked
54949          */
54950         "columnlockchange" : true
54951     });
54952     Roo.grid.ColumnModel.superclass.constructor.call(this);
54953 };
54954 Roo.extend(Roo.grid.ColumnModel, Roo.util.Observable, {
54955     /**
54956      * @cfg {String} header The header text to display in the Grid view.
54957      */
54958     /**
54959      * @cfg {String} dataIndex (Optional) The name of the field in the grid's {@link Roo.data.Store}'s
54960      * {@link Roo.data.Record} definition from which to draw the column's value. If not
54961      * specified, the column's index is used as an index into the Record's data Array.
54962      */
54963     /**
54964      * @cfg {Number} width (Optional) The initial width in pixels of the column. Using this
54965      * instead of {@link Roo.grid.Grid#autoSizeColumns} is more efficient.
54966      */
54967     /**
54968      * @cfg {Boolean} sortable (Optional) True if sorting is to be allowed on this column.
54969      * Defaults to the value of the {@link #defaultSortable} property.
54970      * Whether local/remote sorting is used is specified in {@link Roo.data.Store#remoteSort}.
54971      */
54972     /**
54973      * @cfg {Boolean} locked (Optional) True to lock the column in place while scrolling the Grid.  Defaults to false.
54974      */
54975     /**
54976      * @cfg {Boolean} fixed (Optional) True if the column width cannot be changed.  Defaults to false.
54977      */
54978     /**
54979      * @cfg {Boolean} resizable (Optional) False to disable column resizing. Defaults to true.
54980      */
54981     /**
54982      * @cfg {Boolean} hidden (Optional) True to hide the column. Defaults to false.
54983      */
54984     /**
54985      * @cfg {Function} renderer (Optional) A function used to generate HTML markup for a cell
54986      * given the cell's data value. See {@link #setRenderer}. If not specified, the
54987      * default renderer uses the raw data value. If an object is returned (bootstrap only)
54988      * then it is treated as a Roo Component object instance, and it is rendered after the initial row is rendered
54989      */
54990        /**
54991      * @cfg {Roo.grid.GridEditor} editor (Optional) For grid editors - returns the grid editor 
54992      */
54993     /**
54994      * @cfg {String} align (Optional) Set the CSS text-align property of the column.  Defaults to undefined.
54995      */
54996
54997     /**
54998      * Returns the id of the column at the specified index.
54999      * @param {Number} index The column index
55000      * @return {String} the id
55001      */
55002     getColumnId : function(index){
55003         return this.config[index].id;
55004     },
55005
55006     /**
55007      * Returns the column for a specified id.
55008      * @param {String} id The column id
55009      * @return {Object} the column
55010      */
55011     getColumnById : function(id){
55012         return this.lookup[id];
55013     },
55014
55015     
55016     /**
55017      * Returns the column for a specified dataIndex.
55018      * @param {String} dataIndex The column dataIndex
55019      * @return {Object|Boolean} the column or false if not found
55020      */
55021     getColumnByDataIndex: function(dataIndex){
55022         var index = this.findColumnIndex(dataIndex);
55023         return index > -1 ? this.config[index] : false;
55024     },
55025     
55026     /**
55027      * Returns the index for a specified column id.
55028      * @param {String} id The column id
55029      * @return {Number} the index, or -1 if not found
55030      */
55031     getIndexById : function(id){
55032         for(var i = 0, len = this.config.length; i < len; i++){
55033             if(this.config[i].id == id){
55034                 return i;
55035             }
55036         }
55037         return -1;
55038     },
55039     
55040     /**
55041      * Returns the index for a specified column dataIndex.
55042      * @param {String} dataIndex The column dataIndex
55043      * @return {Number} the index, or -1 if not found
55044      */
55045     
55046     findColumnIndex : function(dataIndex){
55047         for(var i = 0, len = this.config.length; i < len; i++){
55048             if(this.config[i].dataIndex == dataIndex){
55049                 return i;
55050             }
55051         }
55052         return -1;
55053     },
55054     
55055     
55056     moveColumn : function(oldIndex, newIndex){
55057         var c = this.config[oldIndex];
55058         this.config.splice(oldIndex, 1);
55059         this.config.splice(newIndex, 0, c);
55060         this.dataMap = null;
55061         this.fireEvent("columnmoved", this, oldIndex, newIndex);
55062     },
55063
55064     isLocked : function(colIndex){
55065         return this.config[colIndex].locked === true;
55066     },
55067
55068     setLocked : function(colIndex, value, suppressEvent){
55069         if(this.isLocked(colIndex) == value){
55070             return;
55071         }
55072         this.config[colIndex].locked = value;
55073         if(!suppressEvent){
55074             this.fireEvent("columnlockchange", this, colIndex, value);
55075         }
55076     },
55077
55078     getTotalLockedWidth : function(){
55079         var totalWidth = 0;
55080         for(var i = 0; i < this.config.length; i++){
55081             if(this.isLocked(i) && !this.isHidden(i)){
55082                 this.totalWidth += this.getColumnWidth(i);
55083             }
55084         }
55085         return totalWidth;
55086     },
55087
55088     getLockedCount : function(){
55089         for(var i = 0, len = this.config.length; i < len; i++){
55090             if(!this.isLocked(i)){
55091                 return i;
55092             }
55093         }
55094     },
55095
55096     /**
55097      * Returns the number of columns.
55098      * @return {Number}
55099      */
55100     getColumnCount : function(visibleOnly){
55101         if(visibleOnly === true){
55102             var c = 0;
55103             for(var i = 0, len = this.config.length; i < len; i++){
55104                 if(!this.isHidden(i)){
55105                     c++;
55106                 }
55107             }
55108             return c;
55109         }
55110         return this.config.length;
55111     },
55112
55113     /**
55114      * Returns the column configs that return true by the passed function that is called with (columnConfig, index)
55115      * @param {Function} fn
55116      * @param {Object} scope (optional)
55117      * @return {Array} result
55118      */
55119     getColumnsBy : function(fn, scope){
55120         var r = [];
55121         for(var i = 0, len = this.config.length; i < len; i++){
55122             var c = this.config[i];
55123             if(fn.call(scope||this, c, i) === true){
55124                 r[r.length] = c;
55125             }
55126         }
55127         return r;
55128     },
55129
55130     /**
55131      * Returns true if the specified column is sortable.
55132      * @param {Number} col The column index
55133      * @return {Boolean}
55134      */
55135     isSortable : function(col){
55136         if(typeof this.config[col].sortable == "undefined"){
55137             return this.defaultSortable;
55138         }
55139         return this.config[col].sortable;
55140     },
55141
55142     /**
55143      * Returns the rendering (formatting) function defined for the column.
55144      * @param {Number} col The column index.
55145      * @return {Function} The function used to render the cell. See {@link #setRenderer}.
55146      */
55147     getRenderer : function(col){
55148         if(!this.config[col].renderer){
55149             return Roo.grid.ColumnModel.defaultRenderer;
55150         }
55151         return this.config[col].renderer;
55152     },
55153
55154     /**
55155      * Sets the rendering (formatting) function for a column.
55156      * @param {Number} col The column index
55157      * @param {Function} fn The function to use to process the cell's raw data
55158      * to return HTML markup for the grid view. The render function is called with
55159      * the following parameters:<ul>
55160      * <li>Data value.</li>
55161      * <li>Cell metadata. An object in which you may set the following attributes:<ul>
55162      * <li>css A CSS style string to apply to the table cell.</li>
55163      * <li>attr An HTML attribute definition string to apply to the data container element <i>within</i> the table cell.</li></ul>
55164      * <li>The {@link Roo.data.Record} from which the data was extracted.</li>
55165      * <li>Row index</li>
55166      * <li>Column index</li>
55167      * <li>The {@link Roo.data.Store} object from which the Record was extracted</li></ul>
55168      */
55169     setRenderer : function(col, fn){
55170         this.config[col].renderer = fn;
55171     },
55172
55173     /**
55174      * Returns the width for the specified column.
55175      * @param {Number} col The column index
55176      * @return {Number}
55177      */
55178     getColumnWidth : function(col){
55179         return this.config[col].width * 1 || this.defaultWidth;
55180     },
55181
55182     /**
55183      * Sets the width for a column.
55184      * @param {Number} col The column index
55185      * @param {Number} width The new width
55186      */
55187     setColumnWidth : function(col, width, suppressEvent){
55188         this.config[col].width = width;
55189         this.totalWidth = null;
55190         if(!suppressEvent){
55191              this.fireEvent("widthchange", this, col, width);
55192         }
55193     },
55194
55195     /**
55196      * Returns the total width of all columns.
55197      * @param {Boolean} includeHidden True to include hidden column widths
55198      * @return {Number}
55199      */
55200     getTotalWidth : function(includeHidden){
55201         if(!this.totalWidth){
55202             this.totalWidth = 0;
55203             for(var i = 0, len = this.config.length; i < len; i++){
55204                 if(includeHidden || !this.isHidden(i)){
55205                     this.totalWidth += this.getColumnWidth(i);
55206                 }
55207             }
55208         }
55209         return this.totalWidth;
55210     },
55211
55212     /**
55213      * Returns the header for the specified column.
55214      * @param {Number} col The column index
55215      * @return {String}
55216      */
55217     getColumnHeader : function(col){
55218         return this.config[col].header;
55219     },
55220
55221     /**
55222      * Sets the header for a column.
55223      * @param {Number} col The column index
55224      * @param {String} header The new header
55225      */
55226     setColumnHeader : function(col, header){
55227         this.config[col].header = header;
55228         this.fireEvent("headerchange", this, col, header);
55229     },
55230
55231     /**
55232      * Returns the tooltip for the specified column.
55233      * @param {Number} col The column index
55234      * @return {String}
55235      */
55236     getColumnTooltip : function(col){
55237             return this.config[col].tooltip;
55238     },
55239     /**
55240      * Sets the tooltip for a column.
55241      * @param {Number} col The column index
55242      * @param {String} tooltip The new tooltip
55243      */
55244     setColumnTooltip : function(col, tooltip){
55245             this.config[col].tooltip = tooltip;
55246     },
55247
55248     /**
55249      * Returns the dataIndex for the specified column.
55250      * @param {Number} col The column index
55251      * @return {Number}
55252      */
55253     getDataIndex : function(col){
55254         return this.config[col].dataIndex;
55255     },
55256
55257     /**
55258      * Sets the dataIndex for a column.
55259      * @param {Number} col The column index
55260      * @param {Number} dataIndex The new dataIndex
55261      */
55262     setDataIndex : function(col, dataIndex){
55263         this.config[col].dataIndex = dataIndex;
55264     },
55265
55266     
55267     
55268     /**
55269      * Returns true if the cell is editable.
55270      * @param {Number} colIndex The column index
55271      * @param {Number} rowIndex The row index
55272      * @return {Boolean}
55273      */
55274     isCellEditable : function(colIndex, rowIndex){
55275         return (this.config[colIndex].editable || (typeof this.config[colIndex].editable == "undefined" && this.config[colIndex].editor)) ? true : false;
55276     },
55277
55278     /**
55279      * Returns the editor defined for the cell/column.
55280      * return false or null to disable editing.
55281      * @param {Number} colIndex The column index
55282      * @param {Number} rowIndex The row index
55283      * @return {Object}
55284      */
55285     getCellEditor : function(colIndex, rowIndex){
55286         return this.config[colIndex].editor;
55287     },
55288
55289     /**
55290      * Sets if a column is editable.
55291      * @param {Number} col The column index
55292      * @param {Boolean} editable True if the column is editable
55293      */
55294     setEditable : function(col, editable){
55295         this.config[col].editable = editable;
55296     },
55297
55298
55299     /**
55300      * Returns true if the column is hidden.
55301      * @param {Number} colIndex The column index
55302      * @return {Boolean}
55303      */
55304     isHidden : function(colIndex){
55305         return this.config[colIndex].hidden;
55306     },
55307
55308
55309     /**
55310      * Returns true if the column width cannot be changed
55311      */
55312     isFixed : function(colIndex){
55313         return this.config[colIndex].fixed;
55314     },
55315
55316     /**
55317      * Returns true if the column can be resized
55318      * @return {Boolean}
55319      */
55320     isResizable : function(colIndex){
55321         return colIndex >= 0 && this.config[colIndex].resizable !== false && this.config[colIndex].fixed !== true;
55322     },
55323     /**
55324      * Sets if a column is hidden.
55325      * @param {Number} colIndex The column index
55326      * @param {Boolean} hidden True if the column is hidden
55327      */
55328     setHidden : function(colIndex, hidden){
55329         this.config[colIndex].hidden = hidden;
55330         this.totalWidth = null;
55331         this.fireEvent("hiddenchange", this, colIndex, hidden);
55332     },
55333
55334     /**
55335      * Sets the editor for a column.
55336      * @param {Number} col The column index
55337      * @param {Object} editor The editor object
55338      */
55339     setEditor : function(col, editor){
55340         this.config[col].editor = editor;
55341     }
55342 });
55343
55344 Roo.grid.ColumnModel.defaultRenderer = function(value){
55345         if(typeof value == "string" && value.length < 1){
55346             return "&#160;";
55347         }
55348         return value;
55349 };
55350
55351 // Alias for backwards compatibility
55352 Roo.grid.DefaultColumnModel = Roo.grid.ColumnModel;
55353 /*
55354  * Based on:
55355  * Ext JS Library 1.1.1
55356  * Copyright(c) 2006-2007, Ext JS, LLC.
55357  *
55358  * Originally Released Under LGPL - original licence link has changed is not relivant.
55359  *
55360  * Fork - LGPL
55361  * <script type="text/javascript">
55362  */
55363
55364 /**
55365  * @class Roo.grid.AbstractSelectionModel
55366  * @extends Roo.util.Observable
55367  * Abstract base class for grid SelectionModels.  It provides the interface that should be
55368  * implemented by descendant classes.  This class should not be directly instantiated.
55369  * @constructor
55370  */
55371 Roo.grid.AbstractSelectionModel = function(){
55372     this.locked = false;
55373     Roo.grid.AbstractSelectionModel.superclass.constructor.call(this);
55374 };
55375
55376 Roo.extend(Roo.grid.AbstractSelectionModel, Roo.util.Observable,  {
55377     /** @ignore Called by the grid automatically. Do not call directly. */
55378     init : function(grid){
55379         this.grid = grid;
55380         this.initEvents();
55381     },
55382
55383     /**
55384      * Locks the selections.
55385      */
55386     lock : function(){
55387         this.locked = true;
55388     },
55389
55390     /**
55391      * Unlocks the selections.
55392      */
55393     unlock : function(){
55394         this.locked = false;
55395     },
55396
55397     /**
55398      * Returns true if the selections are locked.
55399      * @return {Boolean}
55400      */
55401     isLocked : function(){
55402         return this.locked;
55403     }
55404 });/*
55405  * Based on:
55406  * Ext JS Library 1.1.1
55407  * Copyright(c) 2006-2007, Ext JS, LLC.
55408  *
55409  * Originally Released Under LGPL - original licence link has changed is not relivant.
55410  *
55411  * Fork - LGPL
55412  * <script type="text/javascript">
55413  */
55414 /**
55415  * @extends Roo.grid.AbstractSelectionModel
55416  * @class Roo.grid.RowSelectionModel
55417  * The default SelectionModel used by {@link Roo.grid.Grid}.
55418  * It supports multiple selections and keyboard selection/navigation. 
55419  * @constructor
55420  * @param {Object} config
55421  */
55422 Roo.grid.RowSelectionModel = function(config){
55423     Roo.apply(this, config);
55424     this.selections = new Roo.util.MixedCollection(false, function(o){
55425         return o.id;
55426     });
55427
55428     this.last = false;
55429     this.lastActive = false;
55430
55431     this.addEvents({
55432         /**
55433              * @event selectionchange
55434              * Fires when the selection changes
55435              * @param {SelectionModel} this
55436              */
55437             "selectionchange" : true,
55438         /**
55439              * @event afterselectionchange
55440              * Fires after the selection changes (eg. by key press or clicking)
55441              * @param {SelectionModel} this
55442              */
55443             "afterselectionchange" : true,
55444         /**
55445              * @event beforerowselect
55446              * Fires when a row is selected being selected, return false to cancel.
55447              * @param {SelectionModel} this
55448              * @param {Number} rowIndex The selected index
55449              * @param {Boolean} keepExisting False if other selections will be cleared
55450              */
55451             "beforerowselect" : true,
55452         /**
55453              * @event rowselect
55454              * Fires when a row is selected.
55455              * @param {SelectionModel} this
55456              * @param {Number} rowIndex The selected index
55457              * @param {Roo.data.Record} r The record
55458              */
55459             "rowselect" : true,
55460         /**
55461              * @event rowdeselect
55462              * Fires when a row is deselected.
55463              * @param {SelectionModel} this
55464              * @param {Number} rowIndex The selected index
55465              */
55466         "rowdeselect" : true
55467     });
55468     Roo.grid.RowSelectionModel.superclass.constructor.call(this);
55469     this.locked = false;
55470 };
55471
55472 Roo.extend(Roo.grid.RowSelectionModel, Roo.grid.AbstractSelectionModel,  {
55473     /**
55474      * @cfg {Boolean} singleSelect
55475      * True to allow selection of only one row at a time (defaults to false)
55476      */
55477     singleSelect : false,
55478
55479     // private
55480     initEvents : function(){
55481
55482         if(!this.grid.enableDragDrop && !this.grid.enableDrag){
55483             this.grid.on("mousedown", this.handleMouseDown, this);
55484         }else{ // allow click to work like normal
55485             this.grid.on("rowclick", this.handleDragableRowClick, this);
55486         }
55487
55488         this.rowNav = new Roo.KeyNav(this.grid.getGridEl(), {
55489             "up" : function(e){
55490                 if(!e.shiftKey){
55491                     this.selectPrevious(e.shiftKey);
55492                 }else if(this.last !== false && this.lastActive !== false){
55493                     var last = this.last;
55494                     this.selectRange(this.last,  this.lastActive-1);
55495                     this.grid.getView().focusRow(this.lastActive);
55496                     if(last !== false){
55497                         this.last = last;
55498                     }
55499                 }else{
55500                     this.selectFirstRow();
55501                 }
55502                 this.fireEvent("afterselectionchange", this);
55503             },
55504             "down" : function(e){
55505                 if(!e.shiftKey){
55506                     this.selectNext(e.shiftKey);
55507                 }else if(this.last !== false && this.lastActive !== false){
55508                     var last = this.last;
55509                     this.selectRange(this.last,  this.lastActive+1);
55510                     this.grid.getView().focusRow(this.lastActive);
55511                     if(last !== false){
55512                         this.last = last;
55513                     }
55514                 }else{
55515                     this.selectFirstRow();
55516                 }
55517                 this.fireEvent("afterselectionchange", this);
55518             },
55519             scope: this
55520         });
55521
55522         var view = this.grid.view;
55523         view.on("refresh", this.onRefresh, this);
55524         view.on("rowupdated", this.onRowUpdated, this);
55525         view.on("rowremoved", this.onRemove, this);
55526     },
55527
55528     // private
55529     onRefresh : function(){
55530         var ds = this.grid.dataSource, i, v = this.grid.view;
55531         var s = this.selections;
55532         s.each(function(r){
55533             if((i = ds.indexOfId(r.id)) != -1){
55534                 v.onRowSelect(i);
55535             }else{
55536                 s.remove(r);
55537             }
55538         });
55539     },
55540
55541     // private
55542     onRemove : function(v, index, r){
55543         this.selections.remove(r);
55544     },
55545
55546     // private
55547     onRowUpdated : function(v, index, r){
55548         if(this.isSelected(r)){
55549             v.onRowSelect(index);
55550         }
55551     },
55552
55553     /**
55554      * Select records.
55555      * @param {Array} records The records to select
55556      * @param {Boolean} keepExisting (optional) True to keep existing selections
55557      */
55558     selectRecords : function(records, keepExisting){
55559         if(!keepExisting){
55560             this.clearSelections();
55561         }
55562         var ds = this.grid.dataSource;
55563         for(var i = 0, len = records.length; i < len; i++){
55564             this.selectRow(ds.indexOf(records[i]), true);
55565         }
55566     },
55567
55568     /**
55569      * Gets the number of selected rows.
55570      * @return {Number}
55571      */
55572     getCount : function(){
55573         return this.selections.length;
55574     },
55575
55576     /**
55577      * Selects the first row in the grid.
55578      */
55579     selectFirstRow : function(){
55580         this.selectRow(0);
55581     },
55582
55583     /**
55584      * Select the last row.
55585      * @param {Boolean} keepExisting (optional) True to keep existing selections
55586      */
55587     selectLastRow : function(keepExisting){
55588         this.selectRow(this.grid.dataSource.getCount() - 1, keepExisting);
55589     },
55590
55591     /**
55592      * Selects the row immediately following the last selected row.
55593      * @param {Boolean} keepExisting (optional) True to keep existing selections
55594      */
55595     selectNext : function(keepExisting){
55596         if(this.last !== false && (this.last+1) < this.grid.dataSource.getCount()){
55597             this.selectRow(this.last+1, keepExisting);
55598             this.grid.getView().focusRow(this.last);
55599         }
55600     },
55601
55602     /**
55603      * Selects the row that precedes the last selected row.
55604      * @param {Boolean} keepExisting (optional) True to keep existing selections
55605      */
55606     selectPrevious : function(keepExisting){
55607         if(this.last){
55608             this.selectRow(this.last-1, keepExisting);
55609             this.grid.getView().focusRow(this.last);
55610         }
55611     },
55612
55613     /**
55614      * Returns the selected records
55615      * @return {Array} Array of selected records
55616      */
55617     getSelections : function(){
55618         return [].concat(this.selections.items);
55619     },
55620
55621     /**
55622      * Returns the first selected record.
55623      * @return {Record}
55624      */
55625     getSelected : function(){
55626         return this.selections.itemAt(0);
55627     },
55628
55629
55630     /**
55631      * Clears all selections.
55632      */
55633     clearSelections : function(fast){
55634         if(this.locked) return;
55635         if(fast !== true){
55636             var ds = this.grid.dataSource;
55637             var s = this.selections;
55638             s.each(function(r){
55639                 this.deselectRow(ds.indexOfId(r.id));
55640             }, this);
55641             s.clear();
55642         }else{
55643             this.selections.clear();
55644         }
55645         this.last = false;
55646     },
55647
55648
55649     /**
55650      * Selects all rows.
55651      */
55652     selectAll : function(){
55653         if(this.locked) return;
55654         this.selections.clear();
55655         for(var i = 0, len = this.grid.dataSource.getCount(); i < len; i++){
55656             this.selectRow(i, true);
55657         }
55658     },
55659
55660     /**
55661      * Returns True if there is a selection.
55662      * @return {Boolean}
55663      */
55664     hasSelection : function(){
55665         return this.selections.length > 0;
55666     },
55667
55668     /**
55669      * Returns True if the specified row is selected.
55670      * @param {Number/Record} record The record or index of the record to check
55671      * @return {Boolean}
55672      */
55673     isSelected : function(index){
55674         var r = typeof index == "number" ? this.grid.dataSource.getAt(index) : index;
55675         return (r && this.selections.key(r.id) ? true : false);
55676     },
55677
55678     /**
55679      * Returns True if the specified record id is selected.
55680      * @param {String} id The id of record to check
55681      * @return {Boolean}
55682      */
55683     isIdSelected : function(id){
55684         return (this.selections.key(id) ? true : false);
55685     },
55686
55687     // private
55688     handleMouseDown : function(e, t){
55689         var view = this.grid.getView(), rowIndex;
55690         if(this.isLocked() || (rowIndex = view.findRowIndex(t)) === false){
55691             return;
55692         };
55693         if(e.shiftKey && this.last !== false){
55694             var last = this.last;
55695             this.selectRange(last, rowIndex, e.ctrlKey);
55696             this.last = last; // reset the last
55697             view.focusRow(rowIndex);
55698         }else{
55699             var isSelected = this.isSelected(rowIndex);
55700             if(e.button !== 0 && isSelected){
55701                 view.focusRow(rowIndex);
55702             }else if(e.ctrlKey && isSelected){
55703                 this.deselectRow(rowIndex);
55704             }else if(!isSelected){
55705                 this.selectRow(rowIndex, e.button === 0 && (e.ctrlKey || e.shiftKey));
55706                 view.focusRow(rowIndex);
55707             }
55708         }
55709         this.fireEvent("afterselectionchange", this);
55710     },
55711     // private
55712     handleDragableRowClick :  function(grid, rowIndex, e) 
55713     {
55714         if(e.button === 0 && !e.shiftKey && !e.ctrlKey) {
55715             this.selectRow(rowIndex, false);
55716             grid.view.focusRow(rowIndex);
55717              this.fireEvent("afterselectionchange", this);
55718         }
55719     },
55720     
55721     /**
55722      * Selects multiple rows.
55723      * @param {Array} rows Array of the indexes of the row to select
55724      * @param {Boolean} keepExisting (optional) True to keep existing selections
55725      */
55726     selectRows : function(rows, keepExisting){
55727         if(!keepExisting){
55728             this.clearSelections();
55729         }
55730         for(var i = 0, len = rows.length; i < len; i++){
55731             this.selectRow(rows[i], true);
55732         }
55733     },
55734
55735     /**
55736      * Selects a range of rows. All rows in between startRow and endRow are also selected.
55737      * @param {Number} startRow The index of the first row in the range
55738      * @param {Number} endRow The index of the last row in the range
55739      * @param {Boolean} keepExisting (optional) True to retain existing selections
55740      */
55741     selectRange : function(startRow, endRow, keepExisting){
55742         if(this.locked) return;
55743         if(!keepExisting){
55744             this.clearSelections();
55745         }
55746         if(startRow <= endRow){
55747             for(var i = startRow; i <= endRow; i++){
55748                 this.selectRow(i, true);
55749             }
55750         }else{
55751             for(var i = startRow; i >= endRow; i--){
55752                 this.selectRow(i, true);
55753             }
55754         }
55755     },
55756
55757     /**
55758      * Deselects a range of rows. All rows in between startRow and endRow are also deselected.
55759      * @param {Number} startRow The index of the first row in the range
55760      * @param {Number} endRow The index of the last row in the range
55761      */
55762     deselectRange : function(startRow, endRow, preventViewNotify){
55763         if(this.locked) return;
55764         for(var i = startRow; i <= endRow; i++){
55765             this.deselectRow(i, preventViewNotify);
55766         }
55767     },
55768
55769     /**
55770      * Selects a row.
55771      * @param {Number} row The index of the row to select
55772      * @param {Boolean} keepExisting (optional) True to keep existing selections
55773      */
55774     selectRow : function(index, keepExisting, preventViewNotify){
55775         if(this.locked || (index < 0 || index >= this.grid.dataSource.getCount())) return;
55776         if(this.fireEvent("beforerowselect", this, index, keepExisting) !== false){
55777             if(!keepExisting || this.singleSelect){
55778                 this.clearSelections();
55779             }
55780             var r = this.grid.dataSource.getAt(index);
55781             this.selections.add(r);
55782             this.last = this.lastActive = index;
55783             if(!preventViewNotify){
55784                 this.grid.getView().onRowSelect(index);
55785             }
55786             this.fireEvent("rowselect", this, index, r);
55787             this.fireEvent("selectionchange", this);
55788         }
55789     },
55790
55791     /**
55792      * Deselects a row.
55793      * @param {Number} row The index of the row to deselect
55794      */
55795     deselectRow : function(index, preventViewNotify){
55796         if(this.locked) return;
55797         if(this.last == index){
55798             this.last = false;
55799         }
55800         if(this.lastActive == index){
55801             this.lastActive = false;
55802         }
55803         var r = this.grid.dataSource.getAt(index);
55804         this.selections.remove(r);
55805         if(!preventViewNotify){
55806             this.grid.getView().onRowDeselect(index);
55807         }
55808         this.fireEvent("rowdeselect", this, index);
55809         this.fireEvent("selectionchange", this);
55810     },
55811
55812     // private
55813     restoreLast : function(){
55814         if(this._last){
55815             this.last = this._last;
55816         }
55817     },
55818
55819     // private
55820     acceptsNav : function(row, col, cm){
55821         return !cm.isHidden(col) && cm.isCellEditable(col, row);
55822     },
55823
55824     // private
55825     onEditorKey : function(field, e){
55826         var k = e.getKey(), newCell, g = this.grid, ed = g.activeEditor;
55827         if(k == e.TAB){
55828             e.stopEvent();
55829             ed.completeEdit();
55830             if(e.shiftKey){
55831                 newCell = g.walkCells(ed.row, ed.col-1, -1, this.acceptsNav, this);
55832             }else{
55833                 newCell = g.walkCells(ed.row, ed.col+1, 1, this.acceptsNav, this);
55834             }
55835         }else if(k == e.ENTER && !e.ctrlKey){
55836             e.stopEvent();
55837             ed.completeEdit();
55838             if(e.shiftKey){
55839                 newCell = g.walkCells(ed.row-1, ed.col, -1, this.acceptsNav, this);
55840             }else{
55841                 newCell = g.walkCells(ed.row+1, ed.col, 1, this.acceptsNav, this);
55842             }
55843         }else if(k == e.ESC){
55844             ed.cancelEdit();
55845         }
55846         if(newCell){
55847             g.startEditing(newCell[0], newCell[1]);
55848         }
55849     }
55850 });/*
55851  * Based on:
55852  * Ext JS Library 1.1.1
55853  * Copyright(c) 2006-2007, Ext JS, LLC.
55854  *
55855  * Originally Released Under LGPL - original licence link has changed is not relivant.
55856  *
55857  * Fork - LGPL
55858  * <script type="text/javascript">
55859  */
55860 /**
55861  * @class Roo.grid.CellSelectionModel
55862  * @extends Roo.grid.AbstractSelectionModel
55863  * This class provides the basic implementation for cell selection in a grid.
55864  * @constructor
55865  * @param {Object} config The object containing the configuration of this model.
55866  * @cfg {Boolean} enter_is_tab Enter behaves the same as tab. (eg. goes to next cell) default: false
55867  */
55868 Roo.grid.CellSelectionModel = function(config){
55869     Roo.apply(this, config);
55870
55871     this.selection = null;
55872
55873     this.addEvents({
55874         /**
55875              * @event beforerowselect
55876              * Fires before a cell is selected.
55877              * @param {SelectionModel} this
55878              * @param {Number} rowIndex The selected row index
55879              * @param {Number} colIndex The selected cell index
55880              */
55881             "beforecellselect" : true,
55882         /**
55883              * @event cellselect
55884              * Fires when a cell is selected.
55885              * @param {SelectionModel} this
55886              * @param {Number} rowIndex The selected row index
55887              * @param {Number} colIndex The selected cell index
55888              */
55889             "cellselect" : true,
55890         /**
55891              * @event selectionchange
55892              * Fires when the active selection changes.
55893              * @param {SelectionModel} this
55894              * @param {Object} selection null for no selection or an object (o) with two properties
55895                 <ul>
55896                 <li>o.record: the record object for the row the selection is in</li>
55897                 <li>o.cell: An array of [rowIndex, columnIndex]</li>
55898                 </ul>
55899              */
55900             "selectionchange" : true,
55901         /**
55902              * @event tabend
55903              * Fires when the tab (or enter) was pressed on the last editable cell
55904              * You can use this to trigger add new row.
55905              * @param {SelectionModel} this
55906              */
55907             "tabend" : true,
55908          /**
55909              * @event beforeeditnext
55910              * Fires before the next editable sell is made active
55911              * You can use this to skip to another cell or fire the tabend
55912              *    if you set cell to false
55913              * @param {Object} eventdata object : { cell : [ row, col ] } 
55914              */
55915             "beforeeditnext" : true
55916     });
55917     Roo.grid.CellSelectionModel.superclass.constructor.call(this);
55918 };
55919
55920 Roo.extend(Roo.grid.CellSelectionModel, Roo.grid.AbstractSelectionModel,  {
55921     
55922     enter_is_tab: false,
55923
55924     /** @ignore */
55925     initEvents : function(){
55926         this.grid.on("mousedown", this.handleMouseDown, this);
55927         this.grid.getGridEl().on(Roo.isIE ? "keydown" : "keypress", this.handleKeyDown, this);
55928         var view = this.grid.view;
55929         view.on("refresh", this.onViewChange, this);
55930         view.on("rowupdated", this.onRowUpdated, this);
55931         view.on("beforerowremoved", this.clearSelections, this);
55932         view.on("beforerowsinserted", this.clearSelections, this);
55933         if(this.grid.isEditor){
55934             this.grid.on("beforeedit", this.beforeEdit,  this);
55935         }
55936     },
55937
55938         //private
55939     beforeEdit : function(e){
55940         this.select(e.row, e.column, false, true, e.record);
55941     },
55942
55943         //private
55944     onRowUpdated : function(v, index, r){
55945         if(this.selection && this.selection.record == r){
55946             v.onCellSelect(index, this.selection.cell[1]);
55947         }
55948     },
55949
55950         //private
55951     onViewChange : function(){
55952         this.clearSelections(true);
55953     },
55954
55955         /**
55956          * Returns the currently selected cell,.
55957          * @return {Array} The selected cell (row, column) or null if none selected.
55958          */
55959     getSelectedCell : function(){
55960         return this.selection ? this.selection.cell : null;
55961     },
55962
55963     /**
55964      * Clears all selections.
55965      * @param {Boolean} true to prevent the gridview from being notified about the change.
55966      */
55967     clearSelections : function(preventNotify){
55968         var s = this.selection;
55969         if(s){
55970             if(preventNotify !== true){
55971                 this.grid.view.onCellDeselect(s.cell[0], s.cell[1]);
55972             }
55973             this.selection = null;
55974             this.fireEvent("selectionchange", this, null);
55975         }
55976     },
55977
55978     /**
55979      * Returns true if there is a selection.
55980      * @return {Boolean}
55981      */
55982     hasSelection : function(){
55983         return this.selection ? true : false;
55984     },
55985
55986     /** @ignore */
55987     handleMouseDown : function(e, t){
55988         var v = this.grid.getView();
55989         if(this.isLocked()){
55990             return;
55991         };
55992         var row = v.findRowIndex(t);
55993         var cell = v.findCellIndex(t);
55994         if(row !== false && cell !== false){
55995             this.select(row, cell);
55996         }
55997     },
55998
55999     /**
56000      * Selects a cell.
56001      * @param {Number} rowIndex
56002      * @param {Number} collIndex
56003      */
56004     select : function(rowIndex, colIndex, preventViewNotify, preventFocus, /*internal*/ r){
56005         if(this.fireEvent("beforecellselect", this, rowIndex, colIndex) !== false){
56006             this.clearSelections();
56007             r = r || this.grid.dataSource.getAt(rowIndex);
56008             this.selection = {
56009                 record : r,
56010                 cell : [rowIndex, colIndex]
56011             };
56012             if(!preventViewNotify){
56013                 var v = this.grid.getView();
56014                 v.onCellSelect(rowIndex, colIndex);
56015                 if(preventFocus !== true){
56016                     v.focusCell(rowIndex, colIndex);
56017                 }
56018             }
56019             this.fireEvent("cellselect", this, rowIndex, colIndex);
56020             this.fireEvent("selectionchange", this, this.selection);
56021         }
56022     },
56023
56024         //private
56025     isSelectable : function(rowIndex, colIndex, cm){
56026         return !cm.isHidden(colIndex);
56027     },
56028
56029     /** @ignore */
56030     handleKeyDown : function(e){
56031         //Roo.log('Cell Sel Model handleKeyDown');
56032         if(!e.isNavKeyPress()){
56033             return;
56034         }
56035         var g = this.grid, s = this.selection;
56036         if(!s){
56037             e.stopEvent();
56038             var cell = g.walkCells(0, 0, 1, this.isSelectable,  this);
56039             if(cell){
56040                 this.select(cell[0], cell[1]);
56041             }
56042             return;
56043         }
56044         var sm = this;
56045         var walk = function(row, col, step){
56046             return g.walkCells(row, col, step, sm.isSelectable,  sm);
56047         };
56048         var k = e.getKey(), r = s.cell[0], c = s.cell[1];
56049         var newCell;
56050
56051       
56052
56053         switch(k){
56054             case e.TAB:
56055                 // handled by onEditorKey
56056                 if (g.isEditor && g.editing) {
56057                     return;
56058                 }
56059                 if(e.shiftKey) {
56060                     newCell = walk(r, c-1, -1);
56061                 } else {
56062                     newCell = walk(r, c+1, 1);
56063                 }
56064                 break;
56065             
56066             case e.DOWN:
56067                newCell = walk(r+1, c, 1);
56068                 break;
56069             
56070             case e.UP:
56071                 newCell = walk(r-1, c, -1);
56072                 break;
56073             
56074             case e.RIGHT:
56075                 newCell = walk(r, c+1, 1);
56076                 break;
56077             
56078             case e.LEFT:
56079                 newCell = walk(r, c-1, -1);
56080                 break;
56081             
56082             case e.ENTER:
56083                 
56084                 if(g.isEditor && !g.editing){
56085                    g.startEditing(r, c);
56086                    e.stopEvent();
56087                    return;
56088                 }
56089                 
56090                 
56091              break;
56092         };
56093         if(newCell){
56094             this.select(newCell[0], newCell[1]);
56095             e.stopEvent();
56096             
56097         }
56098     },
56099
56100     acceptsNav : function(row, col, cm){
56101         return !cm.isHidden(col) && cm.isCellEditable(col, row);
56102     },
56103     /**
56104      * Selects a cell.
56105      * @param {Number} field (not used) - as it's normally used as a listener
56106      * @param {Number} e - event - fake it by using
56107      *
56108      * var e = Roo.EventObjectImpl.prototype;
56109      * e.keyCode = e.TAB
56110      *
56111      * 
56112      */
56113     onEditorKey : function(field, e){
56114         
56115         var k = e.getKey(),
56116             newCell,
56117             g = this.grid,
56118             ed = g.activeEditor,
56119             forward = false;
56120         ///Roo.log('onEditorKey' + k);
56121         
56122         
56123         if (this.enter_is_tab && k == e.ENTER) {
56124             k = e.TAB;
56125         }
56126         
56127         if(k == e.TAB){
56128             if(e.shiftKey){
56129                 newCell = g.walkCells(ed.row, ed.col-1, -1, this.acceptsNav, this);
56130             }else{
56131                 newCell = g.walkCells(ed.row, ed.col+1, 1, this.acceptsNav, this);
56132                 forward = true;
56133             }
56134             
56135             e.stopEvent();
56136             
56137         } else if(k == e.ENTER &&  !e.ctrlKey){
56138             ed.completeEdit();
56139             e.stopEvent();
56140             newCell = g.walkCells(ed.row, ed.col+1, 1, this.acceptsNav, this);
56141         
56142                 } else if(k == e.ESC){
56143             ed.cancelEdit();
56144         }
56145                 
56146         if (newCell) {
56147             var ecall = { cell : newCell, forward : forward };
56148             this.fireEvent('beforeeditnext', ecall );
56149             newCell = ecall.cell;
56150                         forward = ecall.forward;
56151         }
56152                 
56153         if(newCell){
56154             //Roo.log('next cell after edit');
56155             g.startEditing.defer(100, g, [newCell[0], newCell[1]]);
56156         } else if (forward) {
56157             // tabbed past last
56158             this.fireEvent.defer(100, this, ['tabend',this]);
56159         }
56160     }
56161 });/*
56162  * Based on:
56163  * Ext JS Library 1.1.1
56164  * Copyright(c) 2006-2007, Ext JS, LLC.
56165  *
56166  * Originally Released Under LGPL - original licence link has changed is not relivant.
56167  *
56168  * Fork - LGPL
56169  * <script type="text/javascript">
56170  */
56171  
56172 /**
56173  * @class Roo.grid.EditorGrid
56174  * @extends Roo.grid.Grid
56175  * Class for creating and editable grid.
56176  * @param {String/HTMLElement/Roo.Element} container The element into which this grid will be rendered - 
56177  * The container MUST have some type of size defined for the grid to fill. The container will be 
56178  * automatically set to position relative if it isn't already.
56179  * @param {Object} dataSource The data model to bind to
56180  * @param {Object} colModel The column model with info about this grid's columns
56181  */
56182 Roo.grid.EditorGrid = function(container, config){
56183     Roo.grid.EditorGrid.superclass.constructor.call(this, container, config);
56184     this.getGridEl().addClass("xedit-grid");
56185
56186     if(!this.selModel){
56187         this.selModel = new Roo.grid.CellSelectionModel();
56188     }
56189
56190     this.activeEditor = null;
56191
56192         this.addEvents({
56193             /**
56194              * @event beforeedit
56195              * Fires before cell editing is triggered. The edit event object has the following properties <br />
56196              * <ul style="padding:5px;padding-left:16px;">
56197              * <li>grid - This grid</li>
56198              * <li>record - The record being edited</li>
56199              * <li>field - The field name being edited</li>
56200              * <li>value - The value for the field being edited.</li>
56201              * <li>row - The grid row index</li>
56202              * <li>column - The grid column index</li>
56203              * <li>cancel - Set this to true to cancel the edit or return false from your handler.</li>
56204              * </ul>
56205              * @param {Object} e An edit event (see above for description)
56206              */
56207             "beforeedit" : true,
56208             /**
56209              * @event afteredit
56210              * Fires after a cell is edited. <br />
56211              * <ul style="padding:5px;padding-left:16px;">
56212              * <li>grid - This grid</li>
56213              * <li>record - The record being edited</li>
56214              * <li>field - The field name being edited</li>
56215              * <li>value - The value being set</li>
56216              * <li>originalValue - The original value for the field, before the edit.</li>
56217              * <li>row - The grid row index</li>
56218              * <li>column - The grid column index</li>
56219              * </ul>
56220              * @param {Object} e An edit event (see above for description)
56221              */
56222             "afteredit" : true,
56223             /**
56224              * @event validateedit
56225              * Fires after a cell is edited, but before the value is set in the record. 
56226          * You can use this to modify the value being set in the field, Return false
56227              * to cancel the change. The edit event object has the following properties <br />
56228              * <ul style="padding:5px;padding-left:16px;">
56229          * <li>editor - This editor</li>
56230              * <li>grid - This grid</li>
56231              * <li>record - The record being edited</li>
56232              * <li>field - The field name being edited</li>
56233              * <li>value - The value being set</li>
56234              * <li>originalValue - The original value for the field, before the edit.</li>
56235              * <li>row - The grid row index</li>
56236              * <li>column - The grid column index</li>
56237              * <li>cancel - Set this to true to cancel the edit or return false from your handler.</li>
56238              * </ul>
56239              * @param {Object} e An edit event (see above for description)
56240              */
56241             "validateedit" : true
56242         });
56243     this.on("bodyscroll", this.stopEditing,  this);
56244     this.on(this.clicksToEdit == 1 ? "cellclick" : "celldblclick", this.onCellDblClick,  this);
56245 };
56246
56247 Roo.extend(Roo.grid.EditorGrid, Roo.grid.Grid, {
56248     /**
56249      * @cfg {Number} clicksToEdit
56250      * The number of clicks on a cell required to display the cell's editor (defaults to 2)
56251      */
56252     clicksToEdit: 2,
56253
56254     // private
56255     isEditor : true,
56256     // private
56257     trackMouseOver: false, // causes very odd FF errors
56258
56259     onCellDblClick : function(g, row, col){
56260         this.startEditing(row, col);
56261     },
56262
56263     onEditComplete : function(ed, value, startValue){
56264         this.editing = false;
56265         this.activeEditor = null;
56266         ed.un("specialkey", this.selModel.onEditorKey, this.selModel);
56267         var r = ed.record;
56268         var field = this.colModel.getDataIndex(ed.col);
56269         var e = {
56270             grid: this,
56271             record: r,
56272             field: field,
56273             originalValue: startValue,
56274             value: value,
56275             row: ed.row,
56276             column: ed.col,
56277             cancel:false,
56278             editor: ed
56279         };
56280         var cell = Roo.get(this.view.getCell(ed.row,ed.col))
56281         cell.show();
56282           
56283         if(String(value) !== String(startValue)){
56284             
56285             if(this.fireEvent("validateedit", e) !== false && !e.cancel){
56286                 r.set(field, e.value);
56287                 // if we are dealing with a combo box..
56288                 // then we also set the 'name' colum to be the displayField
56289                 if (ed.field.displayField && ed.field.name) {
56290                     r.set(ed.field.name, ed.field.el.dom.value);
56291                 }
56292                 
56293                 delete e.cancel; //?? why!!!
56294                 this.fireEvent("afteredit", e);
56295             }
56296         } else {
56297             this.fireEvent("afteredit", e); // always fire it!
56298         }
56299         this.view.focusCell(ed.row, ed.col);
56300     },
56301
56302     /**
56303      * Starts editing the specified for the specified row/column
56304      * @param {Number} rowIndex
56305      * @param {Number} colIndex
56306      */
56307     startEditing : function(row, col){
56308         this.stopEditing();
56309         if(this.colModel.isCellEditable(col, row)){
56310             this.view.ensureVisible(row, col, true);
56311           
56312             var r = this.dataSource.getAt(row);
56313             var field = this.colModel.getDataIndex(col);
56314             var cell = Roo.get(this.view.getCell(row,col));
56315             var e = {
56316                 grid: this,
56317                 record: r,
56318                 field: field,
56319                 value: r.data[field],
56320                 row: row,
56321                 column: col,
56322                 cancel:false 
56323             };
56324             if(this.fireEvent("beforeedit", e) !== false && !e.cancel){
56325                 this.editing = true;
56326                 var ed = this.colModel.getCellEditor(col, row);
56327                 
56328                 if (!ed) {
56329                     return;
56330                 }
56331                 if(!ed.rendered){
56332                     ed.render(ed.parentEl || document.body);
56333                 }
56334                 ed.field.reset();
56335                
56336                 cell.hide();
56337                 
56338                 (function(){ // complex but required for focus issues in safari, ie and opera
56339                     ed.row = row;
56340                     ed.col = col;
56341                     ed.record = r;
56342                     ed.on("complete",   this.onEditComplete,        this,       {single: true});
56343                     ed.on("specialkey", this.selModel.onEditorKey,  this.selModel);
56344                     this.activeEditor = ed;
56345                     var v = r.data[field];
56346                     ed.startEdit(this.view.getCell(row, col), v);
56347                     // combo's with 'displayField and name set
56348                     if (ed.field.displayField && ed.field.name) {
56349                         ed.field.el.dom.value = r.data[ed.field.name];
56350                     }
56351                     
56352                     
56353                 }).defer(50, this);
56354             }
56355         }
56356     },
56357         
56358     /**
56359      * Stops any active editing
56360      */
56361     stopEditing : function(){
56362         if(this.activeEditor){
56363             this.activeEditor.completeEdit();
56364         }
56365         this.activeEditor = null;
56366     },
56367         
56368          /**
56369      * Called to get grid's drag proxy text, by default returns this.ddText.
56370      * @return {String}
56371      */
56372     getDragDropText : function(){
56373         var count = this.selModel.getSelectedCell() ? 1 : 0;
56374         return String.format(this.ddText, count, count == 1 ? '' : 's');
56375     }
56376         
56377 });/*
56378  * Based on:
56379  * Ext JS Library 1.1.1
56380  * Copyright(c) 2006-2007, Ext JS, LLC.
56381  *
56382  * Originally Released Under LGPL - original licence link has changed is not relivant.
56383  *
56384  * Fork - LGPL
56385  * <script type="text/javascript">
56386  */
56387
56388 // private - not really -- you end up using it !
56389 // This is a support class used internally by the Grid components
56390
56391 /**
56392  * @class Roo.grid.GridEditor
56393  * @extends Roo.Editor
56394  * Class for creating and editable grid elements.
56395  * @param {Object} config any settings (must include field)
56396  */
56397 Roo.grid.GridEditor = function(field, config){
56398     if (!config && field.field) {
56399         config = field;
56400         field = Roo.factory(config.field, Roo.form);
56401     }
56402     Roo.grid.GridEditor.superclass.constructor.call(this, field, config);
56403     field.monitorTab = false;
56404 };
56405
56406 Roo.extend(Roo.grid.GridEditor, Roo.Editor, {
56407     
56408     /**
56409      * @cfg {Roo.form.Field} field Field to wrap (or xtyped)
56410      */
56411     
56412     alignment: "tl-tl",
56413     autoSize: "width",
56414     hideEl : false,
56415     cls: "x-small-editor x-grid-editor",
56416     shim:false,
56417     shadow:"frame"
56418 });/*
56419  * Based on:
56420  * Ext JS Library 1.1.1
56421  * Copyright(c) 2006-2007, Ext JS, LLC.
56422  *
56423  * Originally Released Under LGPL - original licence link has changed is not relivant.
56424  *
56425  * Fork - LGPL
56426  * <script type="text/javascript">
56427  */
56428   
56429
56430   
56431 Roo.grid.PropertyRecord = Roo.data.Record.create([
56432     {name:'name',type:'string'},  'value'
56433 ]);
56434
56435
56436 Roo.grid.PropertyStore = function(grid, source){
56437     this.grid = grid;
56438     this.store = new Roo.data.Store({
56439         recordType : Roo.grid.PropertyRecord
56440     });
56441     this.store.on('update', this.onUpdate,  this);
56442     if(source){
56443         this.setSource(source);
56444     }
56445     Roo.grid.PropertyStore.superclass.constructor.call(this);
56446 };
56447
56448
56449
56450 Roo.extend(Roo.grid.PropertyStore, Roo.util.Observable, {
56451     setSource : function(o){
56452         this.source = o;
56453         this.store.removeAll();
56454         var data = [];
56455         for(var k in o){
56456             if(this.isEditableValue(o[k])){
56457                 data.push(new Roo.grid.PropertyRecord({name: k, value: o[k]}, k));
56458             }
56459         }
56460         this.store.loadRecords({records: data}, {}, true);
56461     },
56462
56463     onUpdate : function(ds, record, type){
56464         if(type == Roo.data.Record.EDIT){
56465             var v = record.data['value'];
56466             var oldValue = record.modified['value'];
56467             if(this.grid.fireEvent('beforepropertychange', this.source, record.id, v, oldValue) !== false){
56468                 this.source[record.id] = v;
56469                 record.commit();
56470                 this.grid.fireEvent('propertychange', this.source, record.id, v, oldValue);
56471             }else{
56472                 record.reject();
56473             }
56474         }
56475     },
56476
56477     getProperty : function(row){
56478        return this.store.getAt(row);
56479     },
56480
56481     isEditableValue: function(val){
56482         if(val && val instanceof Date){
56483             return true;
56484         }else if(typeof val == 'object' || typeof val == 'function'){
56485             return false;
56486         }
56487         return true;
56488     },
56489
56490     setValue : function(prop, value){
56491         this.source[prop] = value;
56492         this.store.getById(prop).set('value', value);
56493     },
56494
56495     getSource : function(){
56496         return this.source;
56497     }
56498 });
56499
56500 Roo.grid.PropertyColumnModel = function(grid, store){
56501     this.grid = grid;
56502     var g = Roo.grid;
56503     g.PropertyColumnModel.superclass.constructor.call(this, [
56504         {header: this.nameText, sortable: true, dataIndex:'name', id: 'name'},
56505         {header: this.valueText, resizable:false, dataIndex: 'value', id: 'value'}
56506     ]);
56507     this.store = store;
56508     this.bselect = Roo.DomHelper.append(document.body, {
56509         tag: 'select', style:'display:none', cls: 'x-grid-editor', children: [
56510             {tag: 'option', value: 'true', html: 'true'},
56511             {tag: 'option', value: 'false', html: 'false'}
56512         ]
56513     });
56514     Roo.id(this.bselect);
56515     var f = Roo.form;
56516     this.editors = {
56517         'date' : new g.GridEditor(new f.DateField({selectOnFocus:true})),
56518         'string' : new g.GridEditor(new f.TextField({selectOnFocus:true})),
56519         'number' : new g.GridEditor(new f.NumberField({selectOnFocus:true, style:'text-align:left;'})),
56520         'int' : new g.GridEditor(new f.NumberField({selectOnFocus:true, allowDecimals:false, style:'text-align:left;'})),
56521         'boolean' : new g.GridEditor(new f.Field({el:this.bselect,selectOnFocus:true}))
56522     };
56523     this.renderCellDelegate = this.renderCell.createDelegate(this);
56524     this.renderPropDelegate = this.renderProp.createDelegate(this);
56525 };
56526
56527 Roo.extend(Roo.grid.PropertyColumnModel, Roo.grid.ColumnModel, {
56528     
56529     
56530     nameText : 'Name',
56531     valueText : 'Value',
56532     
56533     dateFormat : 'm/j/Y',
56534     
56535     
56536     renderDate : function(dateVal){
56537         return dateVal.dateFormat(this.dateFormat);
56538     },
56539
56540     renderBool : function(bVal){
56541         return bVal ? 'true' : 'false';
56542     },
56543
56544     isCellEditable : function(colIndex, rowIndex){
56545         return colIndex == 1;
56546     },
56547
56548     getRenderer : function(col){
56549         return col == 1 ?
56550             this.renderCellDelegate : this.renderPropDelegate;
56551     },
56552
56553     renderProp : function(v){
56554         return this.getPropertyName(v);
56555     },
56556
56557     renderCell : function(val){
56558         var rv = val;
56559         if(val instanceof Date){
56560             rv = this.renderDate(val);
56561         }else if(typeof val == 'boolean'){
56562             rv = this.renderBool(val);
56563         }
56564         return Roo.util.Format.htmlEncode(rv);
56565     },
56566
56567     getPropertyName : function(name){
56568         var pn = this.grid.propertyNames;
56569         return pn && pn[name] ? pn[name] : name;
56570     },
56571
56572     getCellEditor : function(colIndex, rowIndex){
56573         var p = this.store.getProperty(rowIndex);
56574         var n = p.data['name'], val = p.data['value'];
56575         
56576         if(typeof(this.grid.customEditors[n]) == 'string'){
56577             return this.editors[this.grid.customEditors[n]];
56578         }
56579         if(typeof(this.grid.customEditors[n]) != 'undefined'){
56580             return this.grid.customEditors[n];
56581         }
56582         if(val instanceof Date){
56583             return this.editors['date'];
56584         }else if(typeof val == 'number'){
56585             return this.editors['number'];
56586         }else if(typeof val == 'boolean'){
56587             return this.editors['boolean'];
56588         }else{
56589             return this.editors['string'];
56590         }
56591     }
56592 });
56593
56594 /**
56595  * @class Roo.grid.PropertyGrid
56596  * @extends Roo.grid.EditorGrid
56597  * This class represents the  interface of a component based property grid control.
56598  * <br><br>Usage:<pre><code>
56599  var grid = new Roo.grid.PropertyGrid("my-container-id", {
56600       
56601  });
56602  // set any options
56603  grid.render();
56604  * </code></pre>
56605   
56606  * @constructor
56607  * @param {String/HTMLElement/Roo.Element} container The element into which this grid will be rendered -
56608  * The container MUST have some type of size defined for the grid to fill. The container will be
56609  * automatically set to position relative if it isn't already.
56610  * @param {Object} config A config object that sets properties on this grid.
56611  */
56612 Roo.grid.PropertyGrid = function(container, config){
56613     config = config || {};
56614     var store = new Roo.grid.PropertyStore(this);
56615     this.store = store;
56616     var cm = new Roo.grid.PropertyColumnModel(this, store);
56617     store.store.sort('name', 'ASC');
56618     Roo.grid.PropertyGrid.superclass.constructor.call(this, container, Roo.apply({
56619         ds: store.store,
56620         cm: cm,
56621         enableColLock:false,
56622         enableColumnMove:false,
56623         stripeRows:false,
56624         trackMouseOver: false,
56625         clicksToEdit:1
56626     }, config));
56627     this.getGridEl().addClass('x-props-grid');
56628     this.lastEditRow = null;
56629     this.on('columnresize', this.onColumnResize, this);
56630     this.addEvents({
56631          /**
56632              * @event beforepropertychange
56633              * Fires before a property changes (return false to stop?)
56634              * @param {Roo.grid.PropertyGrid} grid property grid? (check could be store)
56635              * @param {String} id Record Id
56636              * @param {String} newval New Value
56637          * @param {String} oldval Old Value
56638              */
56639         "beforepropertychange": true,
56640         /**
56641              * @event propertychange
56642              * Fires after a property changes
56643              * @param {Roo.grid.PropertyGrid} grid property grid? (check could be store)
56644              * @param {String} id Record Id
56645              * @param {String} newval New Value
56646          * @param {String} oldval Old Value
56647              */
56648         "propertychange": true
56649     });
56650     this.customEditors = this.customEditors || {};
56651 };
56652 Roo.extend(Roo.grid.PropertyGrid, Roo.grid.EditorGrid, {
56653     
56654      /**
56655      * @cfg {Object} customEditors map of colnames=> custom editors.
56656      * the custom editor can be one of the standard ones (date|string|number|int|boolean), or a
56657      * grid editor eg. Roo.grid.GridEditor(new Roo.form.TextArea({selectOnFocus:true})),
56658      * false disables editing of the field.
56659          */
56660     
56661       /**
56662      * @cfg {Object} propertyNames map of property Names to their displayed value
56663          */
56664     
56665     render : function(){
56666         Roo.grid.PropertyGrid.superclass.render.call(this);
56667         this.autoSize.defer(100, this);
56668     },
56669
56670     autoSize : function(){
56671         Roo.grid.PropertyGrid.superclass.autoSize.call(this);
56672         if(this.view){
56673             this.view.fitColumns();
56674         }
56675     },
56676
56677     onColumnResize : function(){
56678         this.colModel.setColumnWidth(1, this.container.getWidth(true)-this.colModel.getColumnWidth(0));
56679         this.autoSize();
56680     },
56681     /**
56682      * Sets the data for the Grid
56683      * accepts a Key => Value object of all the elements avaiable.
56684      * @param {Object} data  to appear in grid.
56685      */
56686     setSource : function(source){
56687         this.store.setSource(source);
56688         //this.autoSize();
56689     },
56690     /**
56691      * Gets all the data from the grid.
56692      * @return {Object} data  data stored in grid
56693      */
56694     getSource : function(){
56695         return this.store.getSource();
56696     }
56697 });/*
56698   
56699  * Licence LGPL
56700  
56701  */
56702  
56703 /**
56704  * @class Roo.grid.Calendar
56705  * @extends Roo.util.Grid
56706  * This class extends the Grid to provide a calendar widget
56707  * <br><br>Usage:<pre><code>
56708  var grid = new Roo.grid.Calendar("my-container-id", {
56709      ds: myDataStore,
56710      cm: myColModel,
56711      selModel: mySelectionModel,
56712      autoSizeColumns: true,
56713      monitorWindowResize: false,
56714      trackMouseOver: true
56715      eventstore : real data store..
56716  });
56717  // set any options
56718  grid.render();
56719   
56720   * @constructor
56721  * @param {String/HTMLElement/Roo.Element} container The element into which this grid will be rendered -
56722  * The container MUST have some type of size defined for the grid to fill. The container will be
56723  * automatically set to position relative if it isn't already.
56724  * @param {Object} config A config object that sets properties on this grid.
56725  */
56726 Roo.grid.Calendar = function(container, config){
56727         // initialize the container
56728         this.container = Roo.get(container);
56729         this.container.update("");
56730         this.container.setStyle("overflow", "hidden");
56731     this.container.addClass('x-grid-container');
56732
56733     this.id = this.container.id;
56734
56735     Roo.apply(this, config);
56736     // check and correct shorthanded configs
56737     
56738     var rows = [];
56739     var d =1;
56740     for (var r = 0;r < 6;r++) {
56741         
56742         rows[r]=[];
56743         for (var c =0;c < 7;c++) {
56744             rows[r][c]= '';
56745         }
56746     }
56747     if (this.eventStore) {
56748         this.eventStore= Roo.factory(this.eventStore, Roo.data);
56749         this.eventStore.on('load',this.onLoad, this);
56750         this.eventStore.on('beforeload',this.clearEvents, this);
56751          
56752     }
56753     
56754     this.dataSource = new Roo.data.Store({
56755             proxy: new Roo.data.MemoryProxy(rows),
56756             reader: new Roo.data.ArrayReader({}, [
56757                    'weekday0', 'weekday1', 'weekday2', 'weekday3', 'weekday4', 'weekday5', 'weekday6' ])
56758     });
56759
56760     this.dataSource.load();
56761     this.ds = this.dataSource;
56762     this.ds.xmodule = this.xmodule || false;
56763     
56764     
56765     var cellRender = function(v,x,r)
56766     {
56767         return String.format(
56768             '<div class="fc-day  fc-widget-content"><div>' +
56769                 '<div class="fc-event-container"></div>' +
56770                 '<div class="fc-day-number">{0}</div>'+
56771                 
56772                 '<div class="fc-day-content"><div style="position:relative"></div></div>' +
56773             '</div></div>', v);
56774     
56775     }
56776     
56777     
56778     this.colModel = new Roo.grid.ColumnModel( [
56779         {
56780             xtype: 'ColumnModel',
56781             xns: Roo.grid,
56782             dataIndex : 'weekday0',
56783             header : 'Sunday',
56784             renderer : cellRender
56785         },
56786         {
56787             xtype: 'ColumnModel',
56788             xns: Roo.grid,
56789             dataIndex : 'weekday1',
56790             header : 'Monday',
56791             renderer : cellRender
56792         },
56793         {
56794             xtype: 'ColumnModel',
56795             xns: Roo.grid,
56796             dataIndex : 'weekday2',
56797             header : 'Tuesday',
56798             renderer : cellRender
56799         },
56800         {
56801             xtype: 'ColumnModel',
56802             xns: Roo.grid,
56803             dataIndex : 'weekday3',
56804             header : 'Wednesday',
56805             renderer : cellRender
56806         },
56807         {
56808             xtype: 'ColumnModel',
56809             xns: Roo.grid,
56810             dataIndex : 'weekday4',
56811             header : 'Thursday',
56812             renderer : cellRender
56813         },
56814         {
56815             xtype: 'ColumnModel',
56816             xns: Roo.grid,
56817             dataIndex : 'weekday5',
56818             header : 'Friday',
56819             renderer : cellRender
56820         },
56821         {
56822             xtype: 'ColumnModel',
56823             xns: Roo.grid,
56824             dataIndex : 'weekday6',
56825             header : 'Saturday',
56826             renderer : cellRender
56827         }
56828     ]);
56829     this.cm = this.colModel;
56830     this.cm.xmodule = this.xmodule || false;
56831  
56832         
56833           
56834     //this.selModel = new Roo.grid.CellSelectionModel();
56835     //this.sm = this.selModel;
56836     //this.selModel.init(this);
56837     
56838     
56839     if(this.width){
56840         this.container.setWidth(this.width);
56841     }
56842
56843     if(this.height){
56844         this.container.setHeight(this.height);
56845     }
56846     /** @private */
56847         this.addEvents({
56848         // raw events
56849         /**
56850          * @event click
56851          * The raw click event for the entire grid.
56852          * @param {Roo.EventObject} e
56853          */
56854         "click" : true,
56855         /**
56856          * @event dblclick
56857          * The raw dblclick event for the entire grid.
56858          * @param {Roo.EventObject} e
56859          */
56860         "dblclick" : true,
56861         /**
56862          * @event contextmenu
56863          * The raw contextmenu event for the entire grid.
56864          * @param {Roo.EventObject} e
56865          */
56866         "contextmenu" : true,
56867         /**
56868          * @event mousedown
56869          * The raw mousedown event for the entire grid.
56870          * @param {Roo.EventObject} e
56871          */
56872         "mousedown" : true,
56873         /**
56874          * @event mouseup
56875          * The raw mouseup event for the entire grid.
56876          * @param {Roo.EventObject} e
56877          */
56878         "mouseup" : true,
56879         /**
56880          * @event mouseover
56881          * The raw mouseover event for the entire grid.
56882          * @param {Roo.EventObject} e
56883          */
56884         "mouseover" : true,
56885         /**
56886          * @event mouseout
56887          * The raw mouseout event for the entire grid.
56888          * @param {Roo.EventObject} e
56889          */
56890         "mouseout" : true,
56891         /**
56892          * @event keypress
56893          * The raw keypress event for the entire grid.
56894          * @param {Roo.EventObject} e
56895          */
56896         "keypress" : true,
56897         /**
56898          * @event keydown
56899          * The raw keydown event for the entire grid.
56900          * @param {Roo.EventObject} e
56901          */
56902         "keydown" : true,
56903
56904         // custom events
56905
56906         /**
56907          * @event cellclick
56908          * Fires when a cell is clicked
56909          * @param {Grid} this
56910          * @param {Number} rowIndex
56911          * @param {Number} columnIndex
56912          * @param {Roo.EventObject} e
56913          */
56914         "cellclick" : true,
56915         /**
56916          * @event celldblclick
56917          * Fires when a cell is double clicked
56918          * @param {Grid} this
56919          * @param {Number} rowIndex
56920          * @param {Number} columnIndex
56921          * @param {Roo.EventObject} e
56922          */
56923         "celldblclick" : true,
56924         /**
56925          * @event rowclick
56926          * Fires when a row is clicked
56927          * @param {Grid} this
56928          * @param {Number} rowIndex
56929          * @param {Roo.EventObject} e
56930          */
56931         "rowclick" : true,
56932         /**
56933          * @event rowdblclick
56934          * Fires when a row is double clicked
56935          * @param {Grid} this
56936          * @param {Number} rowIndex
56937          * @param {Roo.EventObject} e
56938          */
56939         "rowdblclick" : true,
56940         /**
56941          * @event headerclick
56942          * Fires when a header is clicked
56943          * @param {Grid} this
56944          * @param {Number} columnIndex
56945          * @param {Roo.EventObject} e
56946          */
56947         "headerclick" : true,
56948         /**
56949          * @event headerdblclick
56950          * Fires when a header cell is double clicked
56951          * @param {Grid} this
56952          * @param {Number} columnIndex
56953          * @param {Roo.EventObject} e
56954          */
56955         "headerdblclick" : true,
56956         /**
56957          * @event rowcontextmenu
56958          * Fires when a row is right clicked
56959          * @param {Grid} this
56960          * @param {Number} rowIndex
56961          * @param {Roo.EventObject} e
56962          */
56963         "rowcontextmenu" : true,
56964         /**
56965          * @event cellcontextmenu
56966          * Fires when a cell is right clicked
56967          * @param {Grid} this
56968          * @param {Number} rowIndex
56969          * @param {Number} cellIndex
56970          * @param {Roo.EventObject} e
56971          */
56972          "cellcontextmenu" : true,
56973         /**
56974          * @event headercontextmenu
56975          * Fires when a header is right clicked
56976          * @param {Grid} this
56977          * @param {Number} columnIndex
56978          * @param {Roo.EventObject} e
56979          */
56980         "headercontextmenu" : true,
56981         /**
56982          * @event bodyscroll
56983          * Fires when the body element is scrolled
56984          * @param {Number} scrollLeft
56985          * @param {Number} scrollTop
56986          */
56987         "bodyscroll" : true,
56988         /**
56989          * @event columnresize
56990          * Fires when the user resizes a column
56991          * @param {Number} columnIndex
56992          * @param {Number} newSize
56993          */
56994         "columnresize" : true,
56995         /**
56996          * @event columnmove
56997          * Fires when the user moves a column
56998          * @param {Number} oldIndex
56999          * @param {Number} newIndex
57000          */
57001         "columnmove" : true,
57002         /**
57003          * @event startdrag
57004          * Fires when row(s) start being dragged
57005          * @param {Grid} this
57006          * @param {Roo.GridDD} dd The drag drop object
57007          * @param {event} e The raw browser event
57008          */
57009         "startdrag" : true,
57010         /**
57011          * @event enddrag
57012          * Fires when a drag operation is complete
57013          * @param {Grid} this
57014          * @param {Roo.GridDD} dd The drag drop object
57015          * @param {event} e The raw browser event
57016          */
57017         "enddrag" : true,
57018         /**
57019          * @event dragdrop
57020          * Fires when dragged row(s) are dropped on a valid DD target
57021          * @param {Grid} this
57022          * @param {Roo.GridDD} dd The drag drop object
57023          * @param {String} targetId The target drag drop object
57024          * @param {event} e The raw browser event
57025          */
57026         "dragdrop" : true,
57027         /**
57028          * @event dragover
57029          * Fires while row(s) are being dragged. "targetId" is the id of the Yahoo.util.DD object the selected rows are being dragged over.
57030          * @param {Grid} this
57031          * @param {Roo.GridDD} dd The drag drop object
57032          * @param {String} targetId The target drag drop object
57033          * @param {event} e The raw browser event
57034          */
57035         "dragover" : true,
57036         /**
57037          * @event dragenter
57038          *  Fires when the dragged row(s) first cross another DD target while being dragged
57039          * @param {Grid} this
57040          * @param {Roo.GridDD} dd The drag drop object
57041          * @param {String} targetId The target drag drop object
57042          * @param {event} e The raw browser event
57043          */
57044         "dragenter" : true,
57045         /**
57046          * @event dragout
57047          * Fires when the dragged row(s) leave another DD target while being dragged
57048          * @param {Grid} this
57049          * @param {Roo.GridDD} dd The drag drop object
57050          * @param {String} targetId The target drag drop object
57051          * @param {event} e The raw browser event
57052          */
57053         "dragout" : true,
57054         /**
57055          * @event rowclass
57056          * Fires when a row is rendered, so you can change add a style to it.
57057          * @param {GridView} gridview   The grid view
57058          * @param {Object} rowcfg   contains record  rowIndex and rowClass - set rowClass to add a style.
57059          */
57060         'rowclass' : true,
57061
57062         /**
57063          * @event render
57064          * Fires when the grid is rendered
57065          * @param {Grid} grid
57066          */
57067         'render' : true,
57068             /**
57069              * @event select
57070              * Fires when a date is selected
57071              * @param {DatePicker} this
57072              * @param {Date} date The selected date
57073              */
57074         'select': true,
57075         /**
57076              * @event monthchange
57077              * Fires when the displayed month changes 
57078              * @param {DatePicker} this
57079              * @param {Date} date The selected month
57080              */
57081         'monthchange': true,
57082         /**
57083              * @event evententer
57084              * Fires when mouse over an event
57085              * @param {Calendar} this
57086              * @param {event} Event
57087              */
57088         'evententer': true,
57089         /**
57090              * @event eventleave
57091              * Fires when the mouse leaves an
57092              * @param {Calendar} this
57093              * @param {event}
57094              */
57095         'eventleave': true,
57096         /**
57097              * @event eventclick
57098              * Fires when the mouse click an
57099              * @param {Calendar} this
57100              * @param {event}
57101              */
57102         'eventclick': true,
57103         /**
57104              * @event eventrender
57105              * Fires before each cell is rendered, so you can modify the contents, like cls / title / qtip
57106              * @param {Calendar} this
57107              * @param {data} data to be modified
57108              */
57109         'eventrender': true
57110         
57111     });
57112
57113     Roo.grid.Grid.superclass.constructor.call(this);
57114     this.on('render', function() {
57115         this.view.el.addClass('x-grid-cal'); 
57116         
57117         (function() { this.setDate(new Date()); }).defer(100,this); //default today..
57118
57119     },this);
57120     
57121     if (!Roo.grid.Calendar.style) {
57122         Roo.grid.Calendar.style = Roo.util.CSS.createStyleSheet({
57123             
57124             
57125             '.x-grid-cal .x-grid-col' :  {
57126                 height: 'auto !important',
57127                 'vertical-align': 'top'
57128             },
57129             '.x-grid-cal  .fc-event-hori' : {
57130                 height: '14px'
57131             }
57132              
57133             
57134         }, Roo.id());
57135     }
57136
57137     
57138     
57139 };
57140 Roo.extend(Roo.grid.Calendar, Roo.grid.Grid, {
57141     /**
57142      * @cfg {Store} eventStore The store that loads events.
57143      */
57144     eventStore : 25,
57145
57146      
57147     activeDate : false,
57148     startDay : 0,
57149     autoWidth : true,
57150     monitorWindowResize : false,
57151
57152     
57153     resizeColumns : function() {
57154         var col = (this.view.el.getWidth() / 7) - 3;
57155         // loop through cols, and setWidth
57156         for(var i =0 ; i < 7 ; i++){
57157             this.cm.setColumnWidth(i, col);
57158         }
57159     },
57160      setDate :function(date) {
57161         
57162         Roo.log('setDate?');
57163         
57164         this.resizeColumns();
57165         var vd = this.activeDate;
57166         this.activeDate = date;
57167 //        if(vd && this.el){
57168 //            var t = date.getTime();
57169 //            if(vd.getMonth() == date.getMonth() && vd.getFullYear() == date.getFullYear()){
57170 //                Roo.log('using add remove');
57171 //                
57172 //                this.fireEvent('monthchange', this, date);
57173 //                
57174 //                this.cells.removeClass("fc-state-highlight");
57175 //                this.cells.each(function(c){
57176 //                   if(c.dateValue == t){
57177 //                       c.addClass("fc-state-highlight");
57178 //                       setTimeout(function(){
57179 //                            try{c.dom.firstChild.focus();}catch(e){}
57180 //                       }, 50);
57181 //                       return false;
57182 //                   }
57183 //                   return true;
57184 //                });
57185 //                return;
57186 //            }
57187 //        }
57188         
57189         var days = date.getDaysInMonth();
57190         
57191         var firstOfMonth = date.getFirstDateOfMonth();
57192         var startingPos = firstOfMonth.getDay()-this.startDay;
57193         
57194         if(startingPos < this.startDay){
57195             startingPos += 7;
57196         }
57197         
57198         var pm = date.add(Date.MONTH, -1);
57199         var prevStart = pm.getDaysInMonth()-startingPos;
57200 //        
57201         
57202         
57203         this.cells = this.view.el.select('.x-grid-row .x-grid-col',true);
57204         
57205         this.textNodes = this.view.el.query('.x-grid-row .x-grid-col .x-grid-cell-text');
57206         //this.cells.addClassOnOver('fc-state-hover');
57207         
57208         var cells = this.cells.elements;
57209         var textEls = this.textNodes;
57210         
57211         //Roo.each(cells, function(cell){
57212         //    cell.removeClass([ 'fc-past', 'fc-other-month', 'fc-future', 'fc-state-highlight', 'fc-state-disabled']);
57213         //});
57214         
57215         days += startingPos;
57216
57217         // convert everything to numbers so it's fast
57218         var day = 86400000;
57219         var d = (new Date(pm.getFullYear(), pm.getMonth(), prevStart)).clearTime();
57220         //Roo.log(d);
57221         //Roo.log(pm);
57222         //Roo.log(prevStart);
57223         
57224         var today = new Date().clearTime().getTime();
57225         var sel = date.clearTime().getTime();
57226         var min = this.minDate ? this.minDate.clearTime() : Number.NEGATIVE_INFINITY;
57227         var max = this.maxDate ? this.maxDate.clearTime() : Number.POSITIVE_INFINITY;
57228         var ddMatch = this.disabledDatesRE;
57229         var ddText = this.disabledDatesText;
57230         var ddays = this.disabledDays ? this.disabledDays.join("") : false;
57231         var ddaysText = this.disabledDaysText;
57232         var format = this.format;
57233         
57234         var setCellClass = function(cal, cell){
57235             
57236             //Roo.log('set Cell Class');
57237             cell.title = "";
57238             var t = d.getTime();
57239             
57240             //Roo.log(d);
57241             
57242             
57243             cell.dateValue = t;
57244             if(t == today){
57245                 cell.className += " fc-today";
57246                 cell.className += " fc-state-highlight";
57247                 cell.title = cal.todayText;
57248             }
57249             if(t == sel){
57250                 // disable highlight in other month..
57251                 cell.className += " fc-state-highlight";
57252                 
57253             }
57254             // disabling
57255             if(t < min) {
57256                 //cell.className = " fc-state-disabled";
57257                 cell.title = cal.minText;
57258                 return;
57259             }
57260             if(t > max) {
57261                 //cell.className = " fc-state-disabled";
57262                 cell.title = cal.maxText;
57263                 return;
57264             }
57265             if(ddays){
57266                 if(ddays.indexOf(d.getDay()) != -1){
57267                     // cell.title = ddaysText;
57268                    // cell.className = " fc-state-disabled";
57269                 }
57270             }
57271             if(ddMatch && format){
57272                 var fvalue = d.dateFormat(format);
57273                 if(ddMatch.test(fvalue)){
57274                     cell.title = ddText.replace("%0", fvalue);
57275                    cell.className = " fc-state-disabled";
57276                 }
57277             }
57278             
57279             if (!cell.initialClassName) {
57280                 cell.initialClassName = cell.dom.className;
57281             }
57282             
57283             cell.dom.className = cell.initialClassName  + ' ' +  cell.className;
57284         };
57285
57286         var i = 0;
57287         
57288         for(; i < startingPos; i++) {
57289             cells[i].dayName =  (++prevStart);
57290             Roo.log(textEls[i]);
57291             d.setDate(d.getDate()+1);
57292             
57293             //cells[i].className = "fc-past fc-other-month";
57294             setCellClass(this, cells[i]);
57295         }
57296         
57297         var intDay = 0;
57298         
57299         for(; i < days; i++){
57300             intDay = i - startingPos + 1;
57301             cells[i].dayName =  (intDay);
57302             d.setDate(d.getDate()+1);
57303             
57304             cells[i].className = ''; // "x-date-active";
57305             setCellClass(this, cells[i]);
57306         }
57307         var extraDays = 0;
57308         
57309         for(; i < 42; i++) {
57310             //textEls[i].innerHTML = (++extraDays);
57311             
57312             d.setDate(d.getDate()+1);
57313             cells[i].dayName = (++extraDays);
57314             cells[i].className = "fc-future fc-other-month";
57315             setCellClass(this, cells[i]);
57316         }
57317         
57318         //this.el.select('.fc-header-title h2',true).update(Date.monthNames[date.getMonth()] + " " + date.getFullYear());
57319         
57320         var totalRows = Math.ceil((date.getDaysInMonth() + date.getFirstDateOfMonth().getDay()) / 7);
57321         
57322         // this will cause all the cells to mis
57323         var rows= [];
57324         var i =0;
57325         for (var r = 0;r < 6;r++) {
57326             for (var c =0;c < 7;c++) {
57327                 this.ds.getAt(r).set('weekday' + c ,cells[i++].dayName );
57328             }    
57329         }
57330         
57331         this.cells = this.view.el.select('.x-grid-row .x-grid-col',true);
57332         for(i=0;i<cells.length;i++) {
57333             
57334             this.cells.elements[i].dayName = cells[i].dayName ;
57335             this.cells.elements[i].className = cells[i].className;
57336             this.cells.elements[i].initialClassName = cells[i].initialClassName ;
57337             this.cells.elements[i].title = cells[i].title ;
57338             this.cells.elements[i].dateValue = cells[i].dateValue ;
57339         }
57340         
57341         
57342         
57343         
57344         //this.el.select('tr.fc-week.fc-prev-last',true).removeClass('fc-last');
57345         //this.el.select('tr.fc-week.fc-next-last',true).addClass('fc-last').show();
57346         
57347         ////if(totalRows != 6){
57348             //this.el.select('tr.fc-week.fc-last',true).removeClass('fc-last').addClass('fc-next-last').hide();
57349            // this.el.select('tr.fc-week.fc-prev-last',true).addClass('fc-last');
57350        // }
57351         
57352         this.fireEvent('monthchange', this, date);
57353         
57354         
57355     },
57356  /**
57357      * Returns the grid's SelectionModel.
57358      * @return {SelectionModel}
57359      */
57360     getSelectionModel : function(){
57361         if(!this.selModel){
57362             this.selModel = new Roo.grid.CellSelectionModel();
57363         }
57364         return this.selModel;
57365     },
57366
57367     load: function() {
57368         this.eventStore.load()
57369         
57370         
57371         
57372     },
57373     
57374     findCell : function(dt) {
57375         dt = dt.clearTime().getTime();
57376         var ret = false;
57377         this.cells.each(function(c){
57378             //Roo.log("check " +c.dateValue + '?=' + dt);
57379             if(c.dateValue == dt){
57380                 ret = c;
57381                 return false;
57382             }
57383             return true;
57384         });
57385         
57386         return ret;
57387     },
57388     
57389     findCells : function(rec) {
57390         var s = rec.data.start_dt.clone().clearTime().getTime();
57391        // Roo.log(s);
57392         var e= rec.data.end_dt.clone().clearTime().getTime();
57393        // Roo.log(e);
57394         var ret = [];
57395         this.cells.each(function(c){
57396              ////Roo.log("check " +c.dateValue + '<' + e + ' > ' + s);
57397             
57398             if(c.dateValue > e){
57399                 return ;
57400             }
57401             if(c.dateValue < s){
57402                 return ;
57403             }
57404             ret.push(c);
57405         });
57406         
57407         return ret;    
57408     },
57409     
57410     findBestRow: function(cells)
57411     {
57412         var ret = 0;
57413         
57414         for (var i =0 ; i < cells.length;i++) {
57415             ret  = Math.max(cells[i].rows || 0,ret);
57416         }
57417         return ret;
57418         
57419     },
57420     
57421     
57422     addItem : function(rec)
57423     {
57424         // look for vertical location slot in
57425         var cells = this.findCells(rec);
57426         
57427         rec.row = this.findBestRow(cells);
57428         
57429         // work out the location.
57430         
57431         var crow = false;
57432         var rows = [];
57433         for(var i =0; i < cells.length; i++) {
57434             if (!crow) {
57435                 crow = {
57436                     start : cells[i],
57437                     end :  cells[i]
57438                 };
57439                 continue;
57440             }
57441             if (crow.start.getY() == cells[i].getY()) {
57442                 // on same row.
57443                 crow.end = cells[i];
57444                 continue;
57445             }
57446             // different row.
57447             rows.push(crow);
57448             crow = {
57449                 start: cells[i],
57450                 end : cells[i]
57451             };
57452             
57453         }
57454         
57455         rows.push(crow);
57456         rec.els = [];
57457         rec.rows = rows;
57458         rec.cells = cells;
57459         for (var i = 0; i < cells.length;i++) {
57460             cells[i].rows = Math.max(cells[i].rows || 0 , rec.row + 1 );
57461             
57462         }
57463         
57464         
57465     },
57466     
57467     clearEvents: function() {
57468         
57469         if (!this.eventStore.getCount()) {
57470             return;
57471         }
57472         // reset number of rows in cells.
57473         Roo.each(this.cells.elements, function(c){
57474             c.rows = 0;
57475         });
57476         
57477         this.eventStore.each(function(e) {
57478             this.clearEvent(e);
57479         },this);
57480         
57481     },
57482     
57483     clearEvent : function(ev)
57484     {
57485         if (ev.els) {
57486             Roo.each(ev.els, function(el) {
57487                 el.un('mouseenter' ,this.onEventEnter, this);
57488                 el.un('mouseleave' ,this.onEventLeave, this);
57489                 el.remove();
57490             },this);
57491             ev.els = [];
57492         }
57493     },
57494     
57495     
57496     renderEvent : function(ev,ctr) {
57497         if (!ctr) {
57498              ctr = this.view.el.select('.fc-event-container',true).first();
57499         }
57500         
57501          
57502         this.clearEvent(ev);
57503             //code
57504        
57505         
57506         
57507         ev.els = [];
57508         var cells = ev.cells;
57509         var rows = ev.rows;
57510         this.fireEvent('eventrender', this, ev);
57511         
57512         for(var i =0; i < rows.length; i++) {
57513             
57514             cls = '';
57515             if (i == 0) {
57516                 cls += ' fc-event-start';
57517             }
57518             if ((i+1) == rows.length) {
57519                 cls += ' fc-event-end';
57520             }
57521             
57522             //Roo.log(ev.data);
57523             // how many rows should it span..
57524             var cg = this.eventTmpl.append(ctr,Roo.apply({
57525                 fccls : cls
57526                 
57527             }, ev.data) , true);
57528             
57529             
57530             cg.on('mouseenter' ,this.onEventEnter, this, ev);
57531             cg.on('mouseleave' ,this.onEventLeave, this, ev);
57532             cg.on('click', this.onEventClick, this, ev);
57533             
57534             ev.els.push(cg);
57535             
57536             var sbox = rows[i].start.select('.fc-day-content',true).first().getBox();
57537             var ebox = rows[i].end.select('.fc-day-content',true).first().getBox();
57538             //Roo.log(cg);
57539              
57540             cg.setXY([sbox.x +2, sbox.y +(ev.row * 20)]);    
57541             cg.setWidth(ebox.right - sbox.x -2);
57542         }
57543     },
57544     
57545     renderEvents: function()
57546     {   
57547         // first make sure there is enough space..
57548         
57549         if (!this.eventTmpl) {
57550             this.eventTmpl = new Roo.Template(
57551                 '<div class="roo-dynamic fc-event fc-event-hori fc-event-draggable ui-draggable {fccls} {cls}"  style="position: absolute" unselectable="on">' +
57552                     '<div class="fc-event-inner">' +
57553                         '<span class="fc-event-time">{time}</span>' +
57554                         '<span class="fc-event-title" qtip="{qtip}">{title}</span>' +
57555                     '</div>' +
57556                     '<div class="ui-resizable-heandle ui-resizable-e">&nbsp;&nbsp;&nbsp;</div>' +
57557                 '</div>'
57558             );
57559                 
57560         }
57561                
57562         
57563         
57564         this.cells.each(function(c) {
57565             //Roo.log(c.select('.fc-day-content div',true).first());
57566             c.select('.fc-day-content div',true).first().setHeight(Math.max(34, (c.rows || 1) * 20));
57567         });
57568         
57569         var ctr = this.view.el.select('.fc-event-container',true).first();
57570         
57571         var cls;
57572         this.eventStore.each(function(ev){
57573             
57574             this.renderEvent(ev);
57575              
57576              
57577         }, this);
57578         this.view.layout();
57579         
57580     },
57581     
57582     onEventEnter: function (e, el,event,d) {
57583         this.fireEvent('evententer', this, el, event);
57584     },
57585     
57586     onEventLeave: function (e, el,event,d) {
57587         this.fireEvent('eventleave', this, el, event);
57588     },
57589     
57590     onEventClick: function (e, el,event,d) {
57591         this.fireEvent('eventclick', this, el, event);
57592     },
57593     
57594     onMonthChange: function () {
57595         this.store.load();
57596     },
57597     
57598     onLoad: function () {
57599         
57600         //Roo.log('calendar onload');
57601 //         
57602         if(this.eventStore.getCount() > 0){
57603             
57604            
57605             
57606             this.eventStore.each(function(d){
57607                 
57608                 
57609                 // FIXME..
57610                 var add =   d.data;
57611                 if (typeof(add.end_dt) == 'undefined')  {
57612                     Roo.log("Missing End time in calendar data: ");
57613                     Roo.log(d);
57614                     return;
57615                 }
57616                 if (typeof(add.start_dt) == 'undefined')  {
57617                     Roo.log("Missing Start time in calendar data: ");
57618                     Roo.log(d);
57619                     return;
57620                 }
57621                 add.start_dt = typeof(add.start_dt) == 'string' ? Date.parseDate(add.start_dt,'Y-m-d H:i:s') : add.start_dt,
57622                 add.end_dt = typeof(add.end_dt) == 'string' ? Date.parseDate(add.end_dt,'Y-m-d H:i:s') : add.end_dt,
57623                 add.id = add.id || d.id;
57624                 add.title = add.title || '??';
57625                 
57626                 this.addItem(d);
57627                 
57628              
57629             },this);
57630         }
57631         
57632         this.renderEvents();
57633     }
57634     
57635
57636 });
57637 /*
57638  grid : {
57639                 xtype: 'Grid',
57640                 xns: Roo.grid,
57641                 listeners : {
57642                     render : function ()
57643                     {
57644                         _this.grid = this;
57645                         
57646                         if (!this.view.el.hasClass('course-timesheet')) {
57647                             this.view.el.addClass('course-timesheet');
57648                         }
57649                         if (this.tsStyle) {
57650                             this.ds.load({});
57651                             return; 
57652                         }
57653                         Roo.log('width');
57654                         Roo.log(_this.grid.view.el.getWidth());
57655                         
57656                         
57657                         this.tsStyle =  Roo.util.CSS.createStyleSheet({
57658                             '.course-timesheet .x-grid-row' : {
57659                                 height: '80px'
57660                             },
57661                             '.x-grid-row td' : {
57662                                 'vertical-align' : 0
57663                             },
57664                             '.course-edit-link' : {
57665                                 'color' : 'blue',
57666                                 'text-overflow' : 'ellipsis',
57667                                 'overflow' : 'hidden',
57668                                 'white-space' : 'nowrap',
57669                                 'cursor' : 'pointer'
57670                             },
57671                             '.sub-link' : {
57672                                 'color' : 'green'
57673                             },
57674                             '.de-act-sup-link' : {
57675                                 'color' : 'purple',
57676                                 'text-decoration' : 'line-through'
57677                             },
57678                             '.de-act-link' : {
57679                                 'color' : 'red',
57680                                 'text-decoration' : 'line-through'
57681                             },
57682                             '.course-timesheet .course-highlight' : {
57683                                 'border-top-style': 'dashed !important',
57684                                 'border-bottom-bottom': 'dashed !important'
57685                             },
57686                             '.course-timesheet .course-item' : {
57687                                 'font-family'   : 'tahoma, arial, helvetica',
57688                                 'font-size'     : '11px',
57689                                 'overflow'      : 'hidden',
57690                                 'padding-left'  : '10px',
57691                                 'padding-right' : '10px',
57692                                 'padding-top' : '10px' 
57693                             }
57694                             
57695                         }, Roo.id());
57696                                 this.ds.load({});
57697                     }
57698                 },
57699                 autoWidth : true,
57700                 monitorWindowResize : false,
57701                 cellrenderer : function(v,x,r)
57702                 {
57703                     return v;
57704                 },
57705                 sm : {
57706                     xtype: 'CellSelectionModel',
57707                     xns: Roo.grid
57708                 },
57709                 dataSource : {
57710                     xtype: 'Store',
57711                     xns: Roo.data,
57712                     listeners : {
57713                         beforeload : function (_self, options)
57714                         {
57715                             options.params = options.params || {};
57716                             options.params._month = _this.monthField.getValue();
57717                             options.params.limit = 9999;
57718                             options.params['sort'] = 'when_dt';    
57719                             options.params['dir'] = 'ASC';    
57720                             this.proxy.loadResponse = this.loadResponse;
57721                             Roo.log("load?");
57722                             //this.addColumns();
57723                         },
57724                         load : function (_self, records, options)
57725                         {
57726                             _this.grid.view.el.select('.course-edit-link', true).on('click', function() {
57727                                 // if you click on the translation.. you can edit it...
57728                                 var el = Roo.get(this);
57729                                 var id = el.dom.getAttribute('data-id');
57730                                 var d = el.dom.getAttribute('data-date');
57731                                 var t = el.dom.getAttribute('data-time');
57732                                 //var id = this.child('span').dom.textContent;
57733                                 
57734                                 //Roo.log(this);
57735                                 Pman.Dialog.CourseCalendar.show({
57736                                     id : id,
57737                                     when_d : d,
57738                                     when_t : t,
57739                                     productitem_active : id ? 1 : 0
57740                                 }, function() {
57741                                     _this.grid.ds.load({});
57742                                 });
57743                            
57744                            });
57745                            
57746                            _this.panel.fireEvent('resize', [ '', '' ]);
57747                         }
57748                     },
57749                     loadResponse : function(o, success, response){
57750                             // this is overridden on before load..
57751                             
57752                             Roo.log("our code?");       
57753                             //Roo.log(success);
57754                             //Roo.log(response)
57755                             delete this.activeRequest;
57756                             if(!success){
57757                                 this.fireEvent("loadexception", this, o, response);
57758                                 o.request.callback.call(o.request.scope, null, o.request.arg, false);
57759                                 return;
57760                             }
57761                             var result;
57762                             try {
57763                                 result = o.reader.read(response);
57764                             }catch(e){
57765                                 Roo.log("load exception?");
57766                                 this.fireEvent("loadexception", this, o, response, e);
57767                                 o.request.callback.call(o.request.scope, null, o.request.arg, false);
57768                                 return;
57769                             }
57770                             Roo.log("ready...");        
57771                             // loop through result.records;
57772                             // and set this.tdate[date] = [] << array of records..
57773                             _this.tdata  = {};
57774                             Roo.each(result.records, function(r){
57775                                 //Roo.log(r.data);
57776                                 if(typeof(_this.tdata[r.data.when_dt.format('j')]) == 'undefined'){
57777                                     _this.tdata[r.data.when_dt.format('j')] = [];
57778                                 }
57779                                 _this.tdata[r.data.when_dt.format('j')].push(r.data);
57780                             });
57781                             
57782                             //Roo.log(_this.tdata);
57783                             
57784                             result.records = [];
57785                             result.totalRecords = 6;
57786                     
57787                             // let's generate some duumy records for the rows.
57788                             //var st = _this.dateField.getValue();
57789                             
57790                             // work out monday..
57791                             //st = st.add(Date.DAY, -1 * st.format('w'));
57792                             
57793                             var date = Date.parseDate(_this.monthField.getValue(), "Y-m-d");
57794                             
57795                             var firstOfMonth = date.getFirstDayOfMonth();
57796                             var days = date.getDaysInMonth();
57797                             var d = 1;
57798                             var firstAdded = false;
57799                             for (var i = 0; i < result.totalRecords ; i++) {
57800                                 //var d= st.add(Date.DAY, i);
57801                                 var row = {};
57802                                 var added = 0;
57803                                 for(var w = 0 ; w < 7 ; w++){
57804                                     if(!firstAdded && firstOfMonth != w){
57805                                         continue;
57806                                     }
57807                                     if(d > days){
57808                                         continue;
57809                                     }
57810                                     firstAdded = true;
57811                                     var dd = (d > 0 && d < 10) ? "0"+d : d;
57812                                     row['weekday'+w] = String.format(
57813                                                     '<span style="font-size: 16px;"><b>{0}</b></span>'+
57814                                                     '<span class="course-edit-link" style="color:blue;" data-id="0" data-date="{1}"> Add New</span>',
57815                                                     d,
57816                                                     date.format('Y-m-')+dd
57817                                                 );
57818                                     added++;
57819                                     if(typeof(_this.tdata[d]) != 'undefined'){
57820                                         Roo.each(_this.tdata[d], function(r){
57821                                             var is_sub = '';
57822                                             var deactive = '';
57823                                             var id = r.id;
57824                                             var desc = (r.productitem_id_descrip) ? r.productitem_id_descrip : '';
57825                                             if(r.parent_id*1>0){
57826                                                 is_sub = (r.productitem_id_visible*1 < 1) ? 'de-act-sup-link' :'sub-link';
57827                                                 id = r.parent_id;
57828                                             }
57829                                             if(r.productitem_id_visible*1 < 1 && r.parent_id*1 < 1){
57830                                                 deactive = 'de-act-link';
57831                                             }
57832                                             
57833                                             row['weekday'+w] += String.format(
57834                                                     '<br /><span class="course-edit-link {3} {4}" qtip="{5}" data-id="{0}">{2} - {1}</span>',
57835                                                     id, //0
57836                                                     r.product_id_name, //1
57837                                                     r.when_dt.format('h:ia'), //2
57838                                                     is_sub, //3
57839                                                     deactive, //4
57840                                                     desc // 5
57841                                             );
57842                                         });
57843                                     }
57844                                     d++;
57845                                 }
57846                                 
57847                                 // only do this if something added..
57848                                 if(added > 0){ 
57849                                     result.records.push(_this.grid.dataSource.reader.newRow(row));
57850                                 }
57851                                 
57852                                 
57853                                 // push it twice. (second one with an hour..
57854                                 
57855                             }
57856                             //Roo.log(result);
57857                             this.fireEvent("load", this, o, o.request.arg);
57858                             o.request.callback.call(o.request.scope, result, o.request.arg, true);
57859                         },
57860                     sortInfo : {field: 'when_dt', direction : 'ASC' },
57861                     proxy : {
57862                         xtype: 'HttpProxy',
57863                         xns: Roo.data,
57864                         method : 'GET',
57865                         url : baseURL + '/Roo/Shop_course.php'
57866                     },
57867                     reader : {
57868                         xtype: 'JsonReader',
57869                         xns: Roo.data,
57870                         id : 'id',
57871                         fields : [
57872                             {
57873                                 'name': 'id',
57874                                 'type': 'int'
57875                             },
57876                             {
57877                                 'name': 'when_dt',
57878                                 'type': 'string'
57879                             },
57880                             {
57881                                 'name': 'end_dt',
57882                                 'type': 'string'
57883                             },
57884                             {
57885                                 'name': 'parent_id',
57886                                 'type': 'int'
57887                             },
57888                             {
57889                                 'name': 'product_id',
57890                                 'type': 'int'
57891                             },
57892                             {
57893                                 'name': 'productitem_id',
57894                                 'type': 'int'
57895                             },
57896                             {
57897                                 'name': 'guid',
57898                                 'type': 'int'
57899                             }
57900                         ]
57901                     }
57902                 },
57903                 toolbar : {
57904                     xtype: 'Toolbar',
57905                     xns: Roo,
57906                     items : [
57907                         {
57908                             xtype: 'Button',
57909                             xns: Roo.Toolbar,
57910                             listeners : {
57911                                 click : function (_self, e)
57912                                 {
57913                                     var sd = Date.parseDate(_this.monthField.getValue(), "Y-m-d");
57914                                     sd.setMonth(sd.getMonth()-1);
57915                                     _this.monthField.setValue(sd.format('Y-m-d'));
57916                                     _this.grid.ds.load({});
57917                                 }
57918                             },
57919                             text : "Back"
57920                         },
57921                         {
57922                             xtype: 'Separator',
57923                             xns: Roo.Toolbar
57924                         },
57925                         {
57926                             xtype: 'MonthField',
57927                             xns: Roo.form,
57928                             listeners : {
57929                                 render : function (_self)
57930                                 {
57931                                     _this.monthField = _self;
57932                                    // _this.monthField.set  today
57933                                 },
57934                                 select : function (combo, date)
57935                                 {
57936                                     _this.grid.ds.load({});
57937                                 }
57938                             },
57939                             value : (function() { return new Date(); })()
57940                         },
57941                         {
57942                             xtype: 'Separator',
57943                             xns: Roo.Toolbar
57944                         },
57945                         {
57946                             xtype: 'TextItem',
57947                             xns: Roo.Toolbar,
57948                             text : "Blue: in-active, green: in-active sup-event, red: de-active, purple: de-active sup-event"
57949                         },
57950                         {
57951                             xtype: 'Fill',
57952                             xns: Roo.Toolbar
57953                         },
57954                         {
57955                             xtype: 'Button',
57956                             xns: Roo.Toolbar,
57957                             listeners : {
57958                                 click : function (_self, e)
57959                                 {
57960                                     var sd = Date.parseDate(_this.monthField.getValue(), "Y-m-d");
57961                                     sd.setMonth(sd.getMonth()+1);
57962                                     _this.monthField.setValue(sd.format('Y-m-d'));
57963                                     _this.grid.ds.load({});
57964                                 }
57965                             },
57966                             text : "Next"
57967                         }
57968                     ]
57969                 },
57970                  
57971             }
57972         };
57973         
57974         *//*
57975  * Based on:
57976  * Ext JS Library 1.1.1
57977  * Copyright(c) 2006-2007, Ext JS, LLC.
57978  *
57979  * Originally Released Under LGPL - original licence link has changed is not relivant.
57980  *
57981  * Fork - LGPL
57982  * <script type="text/javascript">
57983  */
57984  
57985 /**
57986  * @class Roo.LoadMask
57987  * A simple utility class for generically masking elements while loading data.  If the element being masked has
57988  * an underlying {@link Roo.data.Store}, the masking will be automatically synchronized with the store's loading
57989  * process and the mask element will be cached for reuse.  For all other elements, this mask will replace the
57990  * element's UpdateManager load indicator and will be destroyed after the initial load.
57991  * @constructor
57992  * Create a new LoadMask
57993  * @param {String/HTMLElement/Roo.Element} el The element or DOM node, or its id
57994  * @param {Object} config The config object
57995  */
57996 Roo.LoadMask = function(el, config){
57997     this.el = Roo.get(el);
57998     Roo.apply(this, config);
57999     if(this.store){
58000         this.store.on('beforeload', this.onBeforeLoad, this);
58001         this.store.on('load', this.onLoad, this);
58002         this.store.on('loadexception', this.onLoadException, this);
58003         this.removeMask = false;
58004     }else{
58005         var um = this.el.getUpdateManager();
58006         um.showLoadIndicator = false; // disable the default indicator
58007         um.on('beforeupdate', this.onBeforeLoad, this);
58008         um.on('update', this.onLoad, this);
58009         um.on('failure', this.onLoad, this);
58010         this.removeMask = true;
58011     }
58012 };
58013
58014 Roo.LoadMask.prototype = {
58015     /**
58016      * @cfg {Boolean} removeMask
58017      * True to create a single-use mask that is automatically destroyed after loading (useful for page loads),
58018      * False to persist the mask element reference for multiple uses (e.g., for paged data widgets).  Defaults to false.
58019      */
58020     /**
58021      * @cfg {String} msg
58022      * The text to display in a centered loading message box (defaults to 'Loading...')
58023      */
58024     msg : 'Loading...',
58025     /**
58026      * @cfg {String} msgCls
58027      * The CSS class to apply to the loading message element (defaults to "x-mask-loading")
58028      */
58029     msgCls : 'x-mask-loading',
58030
58031     /**
58032      * Read-only. True if the mask is currently disabled so that it will not be displayed (defaults to false)
58033      * @type Boolean
58034      */
58035     disabled: false,
58036
58037     /**
58038      * Disables the mask to prevent it from being displayed
58039      */
58040     disable : function(){
58041        this.disabled = true;
58042     },
58043
58044     /**
58045      * Enables the mask so that it can be displayed
58046      */
58047     enable : function(){
58048         this.disabled = false;
58049     },
58050     
58051     onLoadException : function()
58052     {
58053         Roo.log(arguments);
58054         
58055         if (typeof(arguments[3]) != 'undefined') {
58056             Roo.MessageBox.alert("Error loading",arguments[3]);
58057         } 
58058         /*
58059         try {
58060             if (this.store && typeof(this.store.reader.jsonData.errorMsg) != 'undefined') {
58061                 Roo.MessageBox.alert("Error loading",this.store.reader.jsonData.errorMsg);
58062             }   
58063         } catch(e) {
58064             
58065         }
58066         */
58067     
58068         
58069         
58070         this.el.unmask(this.removeMask);
58071     },
58072     // private
58073     onLoad : function()
58074     {
58075         this.el.unmask(this.removeMask);
58076     },
58077
58078     // private
58079     onBeforeLoad : function(){
58080         if(!this.disabled){
58081             this.el.mask(this.msg, this.msgCls);
58082         }
58083     },
58084
58085     // private
58086     destroy : function(){
58087         if(this.store){
58088             this.store.un('beforeload', this.onBeforeLoad, this);
58089             this.store.un('load', this.onLoad, this);
58090             this.store.un('loadexception', this.onLoadException, this);
58091         }else{
58092             var um = this.el.getUpdateManager();
58093             um.un('beforeupdate', this.onBeforeLoad, this);
58094             um.un('update', this.onLoad, this);
58095             um.un('failure', this.onLoad, this);
58096         }
58097     }
58098 };/*
58099  * Based on:
58100  * Ext JS Library 1.1.1
58101  * Copyright(c) 2006-2007, Ext JS, LLC.
58102  *
58103  * Originally Released Under LGPL - original licence link has changed is not relivant.
58104  *
58105  * Fork - LGPL
58106  * <script type="text/javascript">
58107  */
58108
58109
58110 /**
58111  * @class Roo.XTemplate
58112  * @extends Roo.Template
58113  * Provides a template that can have nested templates for loops or conditionals. The syntax is:
58114 <pre><code>
58115 var t = new Roo.XTemplate(
58116         '&lt;select name="{name}"&gt;',
58117                 '&lt;tpl for="options"&gt;&lt;option value="{value:trim}"&gt;{text:ellipsis(10)}&lt;/option&gt;&lt;/tpl&gt;',
58118         '&lt;/select&gt;'
58119 );
58120  
58121 // then append, applying the master template values
58122  </code></pre>
58123  *
58124  * Supported features:
58125  *
58126  *  Tags:
58127
58128 <pre><code>
58129       {a_variable} - output encoded.
58130       {a_variable.format:("Y-m-d")} - call a method on the variable
58131       {a_variable:raw} - unencoded output
58132       {a_variable:toFixed(1,2)} - Roo.util.Format."toFixed"
58133       {a_variable:this.method_on_template(...)} - call a method on the template object.
58134  
58135 </code></pre>
58136  *  The tpl tag:
58137 <pre><code>
58138         &lt;tpl for="a_variable or condition.."&gt;&lt;/tpl&gt;
58139         &lt;tpl if="a_variable or condition"&gt;&lt;/tpl&gt;
58140         &lt;tpl exec="some javascript"&gt;&lt;/tpl&gt;
58141         &lt;tpl name="named_template"&gt;&lt;/tpl&gt; (experimental)
58142   
58143         &lt;tpl for="."&gt;&lt;/tpl&gt; - just iterate the property..
58144         &lt;tpl for=".."&gt;&lt;/tpl&gt; - iterates with the parent (probably the template) 
58145 </code></pre>
58146  *      
58147  */
58148 Roo.XTemplate = function()
58149 {
58150     Roo.XTemplate.superclass.constructor.apply(this, arguments);
58151     if (this.html) {
58152         this.compile();
58153     }
58154 };
58155
58156
58157 Roo.extend(Roo.XTemplate, Roo.Template, {
58158
58159     /**
58160      * The various sub templates
58161      */
58162     tpls : false,
58163     /**
58164      *
58165      * basic tag replacing syntax
58166      * WORD:WORD()
58167      *
58168      * // you can fake an object call by doing this
58169      *  x.t:(test,tesT) 
58170      * 
58171      */
58172     re : /\{([\w-\.]+)(?:\:([\w\.]*)(?:\((.*?)?\))?)?\}/g,
58173
58174     /**
58175      * compile the template
58176      *
58177      * This is not recursive, so I'm not sure how nested templates are really going to be handled..
58178      *
58179      */
58180     compile: function()
58181     {
58182         var s = this.html;
58183      
58184         s = ['<tpl>', s, '</tpl>'].join('');
58185     
58186         var re     = /<tpl\b[^>]*>((?:(?=([^<]+))\2|<(?!tpl\b[^>]*>))*?)<\/tpl>/,
58187             nameRe = /^<tpl\b[^>]*?for="(.*?)"/,
58188             ifRe   = /^<tpl\b[^>]*?if="(.*?)"/,
58189             execRe = /^<tpl\b[^>]*?exec="(.*?)"/,
58190             namedRe = /^<tpl\b[^>]*?name="(\w+)"/,  // named templates..
58191             m,
58192             id     = 0,
58193             tpls   = [];
58194     
58195         while(true == !!(m = s.match(re))){
58196             var forMatch   = m[0].match(nameRe),
58197                 ifMatch   = m[0].match(ifRe),
58198                 execMatch   = m[0].match(execRe),
58199                 namedMatch   = m[0].match(namedRe),
58200                 
58201                 exp  = null, 
58202                 fn   = null,
58203                 exec = null,
58204                 name = forMatch && forMatch[1] ? forMatch[1] : '';
58205                 
58206             if (ifMatch) {
58207                 // if - puts fn into test..
58208                 exp = ifMatch && ifMatch[1] ? ifMatch[1] : null;
58209                 if(exp){
58210                    fn = new Function('values', 'parent', 'with(values){ return '+(Roo.util.Format.htmlDecode(exp))+'; }');
58211                 }
58212             }
58213             
58214             if (execMatch) {
58215                 // exec - calls a function... returns empty if true is  returned.
58216                 exp = execMatch && execMatch[1] ? execMatch[1] : null;
58217                 if(exp){
58218                    exec = new Function('values', 'parent', 'with(values){ '+(Roo.util.Format.htmlDecode(exp))+'; }');
58219                 }
58220             }
58221             
58222             
58223             if (name) {
58224                 // for = 
58225                 switch(name){
58226                     case '.':  name = new Function('values', 'parent', 'with(values){ return values; }'); break;
58227                     case '..': name = new Function('values', 'parent', 'with(values){ return parent; }'); break;
58228                     default:   name = new Function('values', 'parent', 'with(values){ return '+name+'; }');
58229                 }
58230             }
58231             var uid = namedMatch ? namedMatch[1] : id;
58232             
58233             
58234             tpls.push({
58235                 id:     namedMatch ? namedMatch[1] : id,
58236                 target: name,
58237                 exec:   exec,
58238                 test:   fn,
58239                 body:   m[1] || ''
58240             });
58241             if (namedMatch) {
58242                 s = s.replace(m[0], '');
58243             } else { 
58244                 s = s.replace(m[0], '{xtpl'+ id + '}');
58245             }
58246             ++id;
58247         }
58248         this.tpls = [];
58249         for(var i = tpls.length-1; i >= 0; --i){
58250             this.compileTpl(tpls[i]);
58251             this.tpls[tpls[i].id] = tpls[i];
58252         }
58253         this.master = tpls[tpls.length-1];
58254         return this;
58255     },
58256     /**
58257      * same as applyTemplate, except it's done to one of the subTemplates
58258      * when using named templates, you can do:
58259      *
58260      * var str = pl.applySubTemplate('your-name', values);
58261      *
58262      * 
58263      * @param {Number} id of the template
58264      * @param {Object} values to apply to template
58265      * @param {Object} parent (normaly the instance of this object)
58266      */
58267     applySubTemplate : function(id, values, parent)
58268     {
58269         
58270         
58271         var t = this.tpls[id];
58272         
58273         
58274         try { 
58275             if(t.test && !t.test.call(this, values, parent)){
58276                 return '';
58277             }
58278         } catch(e) {
58279             Roo.log("Xtemplate.applySubTemplate 'test': Exception thrown");
58280             Roo.log(e.toString());
58281             Roo.log(t.test);
58282             return ''
58283         }
58284         try { 
58285             
58286             if(t.exec && t.exec.call(this, values, parent)){
58287                 return '';
58288             }
58289         } catch(e) {
58290             Roo.log("Xtemplate.applySubTemplate 'exec': Exception thrown");
58291             Roo.log(e.toString());
58292             Roo.log(t.exec);
58293             return ''
58294         }
58295         try {
58296             var vs = t.target ? t.target.call(this, values, parent) : values;
58297             parent = t.target ? values : parent;
58298             if(t.target && vs instanceof Array){
58299                 var buf = [];
58300                 for(var i = 0, len = vs.length; i < len; i++){
58301                     buf[buf.length] = t.compiled.call(this, vs[i], parent);
58302                 }
58303                 return buf.join('');
58304             }
58305             return t.compiled.call(this, vs, parent);
58306         } catch (e) {
58307             Roo.log("Xtemplate.applySubTemplate : Exception thrown");
58308             Roo.log(e.toString());
58309             Roo.log(t.compiled);
58310             return '';
58311         }
58312     },
58313
58314     compileTpl : function(tpl)
58315     {
58316         var fm = Roo.util.Format;
58317         var useF = this.disableFormats !== true;
58318         var sep = Roo.isGecko ? "+" : ",";
58319         var undef = function(str) {
58320             Roo.log("Property not found :"  + str);
58321             return '';
58322         };
58323         
58324         var fn = function(m, name, format, args)
58325         {
58326             //Roo.log(arguments);
58327             args = args ? args.replace(/\\'/g,"'") : args;
58328             //["{TEST:(a,b,c)}", "TEST", "", "a,b,c", 0, "{TEST:(a,b,c)}"]
58329             if (typeof(format) == 'undefined') {
58330                 format= 'htmlEncode';
58331             }
58332             if (format == 'raw' ) {
58333                 format = false;
58334             }
58335             
58336             if(name.substr(0, 4) == 'xtpl'){
58337                 return "'"+ sep +'this.applySubTemplate('+name.substr(4)+', values, parent)'+sep+"'";
58338             }
58339             
58340             // build an array of options to determine if value is undefined..
58341             
58342             // basically get 'xxxx.yyyy' then do
58343             // (typeof(xxxx) == 'undefined' || typeof(xxx.yyyy) == 'undefined') ?
58344             //    (function () { Roo.log("Property not found"); return ''; })() :
58345             //    ......
58346             
58347             var udef_ar = [];
58348             var lookfor = '';
58349             Roo.each(name.split('.'), function(st) {
58350                 lookfor += (lookfor.length ? '.': '') + st;
58351                 udef_ar.push(  "(typeof(" + lookfor + ") == 'undefined')"  );
58352             });
58353             
58354             var udef_st = '((' + udef_ar.join(" || ") +") ? undef('" + name + "') : "; // .. needs )
58355             
58356             
58357             if(format && useF){
58358                 
58359                 args = args ? ',' + args : "";
58360                  
58361                 if(format.substr(0, 5) != "this."){
58362                     format = "fm." + format + '(';
58363                 }else{
58364                     format = 'this.call("'+ format.substr(5) + '", ';
58365                     args = ", values";
58366                 }
58367                 
58368                 return "'"+ sep +   udef_st   +    format + name + args + "))"+sep+"'";
58369             }
58370              
58371             if (args.length) {
58372                 // called with xxyx.yuu:(test,test)
58373                 // change to ()
58374                 return "'"+ sep + udef_st  + name + '(' +  args + "))"+sep+"'";
58375             }
58376             // raw.. - :raw modifier..
58377             return "'"+ sep + udef_st  + name + ")"+sep+"'";
58378             
58379         };
58380         var body;
58381         // branched to use + in gecko and [].join() in others
58382         if(Roo.isGecko){
58383             body = "tpl.compiled = function(values, parent){  with(values) { return '" +
58384                    tpl.body.replace(/(\r\n|\n)/g, '\\n').replace(/'/g, "\\'").replace(this.re, fn) +
58385                     "';};};";
58386         }else{
58387             body = ["tpl.compiled = function(values, parent){  with (values) { return ['"];
58388             body.push(tpl.body.replace(/(\r\n|\n)/g,
58389                             '\\n').replace(/'/g, "\\'").replace(this.re, fn));
58390             body.push("'].join('');};};");
58391             body = body.join('');
58392         }
58393         
58394         Roo.debug && Roo.log(body.replace(/\\n/,'\n'));
58395        
58396         /** eval:var:tpl eval:var:fm eval:var:useF eval:var:undef  */
58397         eval(body);
58398         
58399         return this;
58400     },
58401
58402     applyTemplate : function(values){
58403         return this.master.compiled.call(this, values, {});
58404         //var s = this.subs;
58405     },
58406
58407     apply : function(){
58408         return this.applyTemplate.apply(this, arguments);
58409     }
58410
58411  });
58412
58413 Roo.XTemplate.from = function(el){
58414     el = Roo.getDom(el);
58415     return new Roo.XTemplate(el.value || el.innerHTML);
58416 };