roojs-core.js
[roojs1] / roojs-debug.js
1 /*
2  * Based on:
3  * Ext JS Library 1.1.1
4  * Copyright(c) 2006-2007, Ext JS, LLC.
5  *
6  * Originally Released Under LGPL - original licence link has changed is not relivant.
7  *
8  * Fork - LGPL
9  * <script type="text/javascript">
10  */
11  
12
13
14
15
16 // for old browsers
17 window["undefined"] = window["undefined"];
18
19 /**
20  * @class Roo
21  * Roo core utilities and functions.
22  * @singleton
23  */
24 var Roo = {}; 
25 /**
26  * Copies all the properties of config to obj.
27  * @param {Object} obj The receiver of the properties
28  * @param {Object} config The source of the properties
29  * @param {Object} defaults A different object that will also be applied for default values
30  * @return {Object} returns obj
31  * @member Roo apply
32  */
33
34  
35 Roo.apply = function(o, c, defaults){
36     if(defaults){
37         // no "this" reference for friendly out of scope calls
38         Roo.apply(o, defaults);
39     }
40     if(o && c && typeof c == 'object'){
41         for(var p in c){
42             o[p] = c[p];
43         }
44     }
45     return o;
46 };
47
48
49 (function(){
50     var idSeed = 0;
51     var ua = navigator.userAgent.toLowerCase();
52
53     var isStrict = document.compatMode == "CSS1Compat",
54         isOpera = ua.indexOf("opera") > -1,
55         isSafari = (/webkit|khtml/).test(ua),
56         isIE = ua.indexOf("msie") > -1,
57         isIE7 = ua.indexOf("msie 7") > -1,
58         isGecko = !isSafari && ua.indexOf("gecko") > -1,
59         isBorderBox = isIE && !isStrict,
60         isWindows = (ua.indexOf("windows") != -1 || ua.indexOf("win32") != -1),
61         isMac = (ua.indexOf("macintosh") != -1 || ua.indexOf("mac os x") != -1),
62         isLinux = (ua.indexOf("linux") != -1),
63         isSecure = window.location.href.toLowerCase().indexOf("https") === 0,
64         isTouch =  'ontouchstart' in window || window.DocumentTouch && document instanceof DocumentTouch;
65     // remove css image flicker
66         if(isIE && !isIE7){
67         try{
68             document.execCommand("BackgroundImageCache", false, true);
69         }catch(e){}
70     }
71     
72     Roo.apply(Roo, {
73         /**
74          * True if the browser is in strict mode
75          * @type Boolean
76          */
77         isStrict : isStrict,
78         /**
79          * True if the page is running over SSL
80          * @type Boolean
81          */
82         isSecure : isSecure,
83         /**
84          * True when the document is fully initialized and ready for action
85          * @type Boolean
86          */
87         isReady : false,
88         /**
89          * Turn on debugging output (currently only the factory uses this)
90          * @type Boolean
91          */
92         
93         debug: false,
94
95         /**
96          * True to automatically uncache orphaned Roo.Elements periodically (defaults to true)
97          * @type Boolean
98          */
99         enableGarbageCollector : true,
100
101         /**
102          * True to automatically purge event listeners after uncaching an element (defaults to false).
103          * Note: this only happens if enableGarbageCollector is true.
104          * @type Boolean
105          */
106         enableListenerCollection:false,
107
108         /**
109          * URL to a blank file used by Roo when in secure mode for iframe src and onReady src to prevent
110          * the IE insecure content warning (defaults to javascript:false).
111          * @type String
112          */
113         SSL_SECURE_URL : "javascript:false",
114
115         /**
116          * URL to a 1x1 transparent gif image used by Roo to create inline icons with CSS background images. (Defaults to
117          * "http://Roojs.com/s.gif" and you should change this to a URL on your server).
118          * @type String
119          */
120         BLANK_IMAGE_URL : "http:/"+"/localhost/s.gif",
121
122         emptyFn : function(){},
123         
124         /**
125          * Copies all the properties of config to obj if they don't already exist.
126          * @param {Object} obj The receiver of the properties
127          * @param {Object} config The source of the properties
128          * @return {Object} returns obj
129          */
130         applyIf : function(o, c){
131             if(o && c){
132                 for(var p in c){
133                     if(typeof o[p] == "undefined"){ o[p] = c[p]; }
134                 }
135             }
136             return o;
137         },
138
139         /**
140          * Applies event listeners to elements by selectors when the document is ready.
141          * The event name is specified with an @ suffix.
142 <pre><code>
143 Roo.addBehaviors({
144    // add a listener for click on all anchors in element with id foo
145    '#foo a@click' : function(e, t){
146        // do something
147    },
148
149    // add the same listener to multiple selectors (separated by comma BEFORE the @)
150    '#foo a, #bar span.some-class@mouseover' : function(){
151        // do something
152    }
153 });
154 </code></pre>
155          * @param {Object} obj The list of behaviors to apply
156          */
157         addBehaviors : function(o){
158             if(!Roo.isReady){
159                 Roo.onReady(function(){
160                     Roo.addBehaviors(o);
161                 });
162                 return;
163             }
164             var cache = {}; // simple cache for applying multiple behaviors to same selector does query multiple times
165             for(var b in o){
166                 var parts = b.split('@');
167                 if(parts[1]){ // for Object prototype breakers
168                     var s = parts[0];
169                     if(!cache[s]){
170                         cache[s] = Roo.select(s);
171                     }
172                     cache[s].on(parts[1], o[b]);
173                 }
174             }
175             cache = null;
176         },
177
178         /**
179          * Generates unique ids. If the element already has an id, it is unchanged
180          * @param {String/HTMLElement/Element} el (optional) The element to generate an id for
181          * @param {String} prefix (optional) Id prefix (defaults "Roo-gen")
182          * @return {String} The generated Id.
183          */
184         id : function(el, prefix){
185             prefix = prefix || "roo-gen";
186             el = Roo.getDom(el);
187             var id = prefix + (++idSeed);
188             return el ? (el.id ? el.id : (el.id = id)) : id;
189         },
190          
191        
192         /**
193          * Extends one class with another class and optionally overrides members with the passed literal. This class
194          * also adds the function "override()" to the class that can be used to override
195          * members on an instance.
196          * @param {Object} subclass The class inheriting the functionality
197          * @param {Object} superclass The class being extended
198          * @param {Object} overrides (optional) A literal with members
199          * @method extend
200          */
201         extend : function(){
202             // inline overrides
203             var io = function(o){
204                 for(var m in o){
205                     this[m] = o[m];
206                 }
207             };
208             return function(sb, sp, overrides){
209                 if(typeof sp == 'object'){ // eg. prototype, rather than function constructor..
210                     overrides = sp;
211                     sp = sb;
212                     sb = function(){sp.apply(this, arguments);};
213                 }
214                 var F = function(){}, sbp, spp = sp.prototype;
215                 F.prototype = spp;
216                 sbp = sb.prototype = new F();
217                 sbp.constructor=sb;
218                 sb.superclass=spp;
219                 
220                 if(spp.constructor == Object.prototype.constructor){
221                     spp.constructor=sp;
222                    
223                 }
224                 
225                 sb.override = function(o){
226                     Roo.override(sb, o);
227                 };
228                 sbp.override = io;
229                 Roo.override(sb, overrides);
230                 return sb;
231             };
232         }(),
233
234         /**
235          * Adds a list of functions to the prototype of an existing class, overwriting any existing methods with the same name.
236          * Usage:<pre><code>
237 Roo.override(MyClass, {
238     newMethod1: function(){
239         // etc.
240     },
241     newMethod2: function(foo){
242         // etc.
243     }
244 });
245  </code></pre>
246          * @param {Object} origclass The class to override
247          * @param {Object} overrides The list of functions to add to origClass.  This should be specified as an object literal
248          * containing one or more methods.
249          * @method override
250          */
251         override : function(origclass, overrides){
252             if(overrides){
253                 var p = origclass.prototype;
254                 for(var method in overrides){
255                     p[method] = overrides[method];
256                 }
257             }
258         },
259         /**
260          * Creates namespaces to be used for scoping variables and classes so that they are not global.  Usage:
261          * <pre><code>
262 Roo.namespace('Company', 'Company.data');
263 Company.Widget = function() { ... }
264 Company.data.CustomStore = function(config) { ... }
265 </code></pre>
266          * @param {String} namespace1
267          * @param {String} namespace2
268          * @param {String} etc
269          * @method namespace
270          */
271         namespace : function(){
272             var a=arguments, o=null, i, j, d, rt;
273             for (i=0; i<a.length; ++i) {
274                 d=a[i].split(".");
275                 rt = d[0];
276                 /** eval:var:o */
277                 eval('if (typeof ' + rt + ' == "undefined"){' + rt + ' = {};} o = ' + rt + ';');
278                 for (j=1; j<d.length; ++j) {
279                     o[d[j]]=o[d[j]] || {};
280                     o=o[d[j]];
281                 }
282             }
283         },
284         /**
285          * Creates namespaces to be used for scoping variables and classes so that they are not global.  Usage:
286          * <pre><code>
287 Roo.factory({ xns: Roo.data, xtype : 'Store', .....});
288 Roo.factory(conf, Roo.data);
289 </code></pre>
290          * @param {String} classname
291          * @param {String} namespace (optional)
292          * @method factory
293          */
294          
295         factory : function(c, ns)
296         {
297             // no xtype, no ns or c.xns - or forced off by c.xns
298             if (!c.xtype   || (!ns && !c.xns) ||  (c.xns === false)) { // not enough info...
299                 return c;
300             }
301             ns = c.xns ? c.xns : ns; // if c.xns is set, then use that..
302             if (c.constructor == ns[c.xtype]) {// already created...
303                 return c;
304             }
305             if (ns[c.xtype]) {
306                 if (Roo.debug) Roo.log("Roo.Factory(" + c.xtype + ")");
307                 var ret = new ns[c.xtype](c);
308                 ret.xns = false;
309                 return ret;
310             }
311             c.xns = false; // prevent recursion..
312             return c;
313         },
314          /**
315          * Logs to console if it can.
316          *
317          * @param {String|Object} string
318          * @method log
319          */
320         log : function(s)
321         {
322             if ((typeof(console) == 'undefined') || (typeof(console.log) == 'undefined')) {
323                 return; // alerT?
324             }
325             console.log(s);
326             
327         },
328         /**
329          * Takes an object and converts it to an encoded URL. e.g. Roo.urlEncode({foo: 1, bar: 2}); would return "foo=1&bar=2".  Optionally, property values can be arrays, instead of keys and the resulting string that's returned will contain a name/value pair for each array value.
330          * @param {Object} o
331          * @return {String}
332          */
333         urlEncode : function(o){
334             if(!o){
335                 return "";
336             }
337             var buf = [];
338             for(var key in o){
339                 var ov = o[key], k = Roo.encodeURIComponent(key);
340                 var type = typeof ov;
341                 if(type == 'undefined'){
342                     buf.push(k, "=&");
343                 }else if(type != "function" && type != "object"){
344                     buf.push(k, "=", Roo.encodeURIComponent(ov), "&");
345                 }else if(ov instanceof Array){
346                     if (ov.length) {
347                             for(var i = 0, len = ov.length; i < len; i++) {
348                                 buf.push(k, "=", Roo.encodeURIComponent(ov[i] === undefined ? '' : ov[i]), "&");
349                             }
350                         } else {
351                             buf.push(k, "=&");
352                         }
353                 }
354             }
355             buf.pop();
356             return buf.join("");
357         },
358          /**
359          * Safe version of encodeURIComponent
360          * @param {String} data 
361          * @return {String} 
362          */
363         
364         encodeURIComponent : function (data)
365         {
366             try {
367                 return encodeURIComponent(data);
368             } catch(e) {} // should be an uri encode error.
369             
370             if (data == '' || data == null){
371                return '';
372             }
373             // http://stackoverflow.com/questions/2596483/unicode-and-uri-encoding-decoding-and-escaping-in-javascript
374             function nibble_to_hex(nibble){
375                 var chars = '0123456789ABCDEF';
376                 return chars.charAt(nibble);
377             }
378             data = data.toString();
379             var buffer = '';
380             for(var i=0; i<data.length; i++){
381                 var c = data.charCodeAt(i);
382                 var bs = new Array();
383                 if (c > 0x10000){
384                         // 4 bytes
385                     bs[0] = 0xF0 | ((c & 0x1C0000) >>> 18);
386                     bs[1] = 0x80 | ((c & 0x3F000) >>> 12);
387                     bs[2] = 0x80 | ((c & 0xFC0) >>> 6);
388                     bs[3] = 0x80 | (c & 0x3F);
389                 }else if (c > 0x800){
390                          // 3 bytes
391                     bs[0] = 0xE0 | ((c & 0xF000) >>> 12);
392                     bs[1] = 0x80 | ((c & 0xFC0) >>> 6);
393                     bs[2] = 0x80 | (c & 0x3F);
394                 }else if (c > 0x80){
395                        // 2 bytes
396                     bs[0] = 0xC0 | ((c & 0x7C0) >>> 6);
397                     bs[1] = 0x80 | (c & 0x3F);
398                 }else{
399                         // 1 byte
400                     bs[0] = c;
401                 }
402                 for(var j=0; j<bs.length; j++){
403                     var b = bs[j];
404                     var hex = nibble_to_hex((b & 0xF0) >>> 4) 
405                             + nibble_to_hex(b &0x0F);
406                     buffer += '%'+hex;
407                }
408             }
409             return buffer;    
410              
411         },
412
413         /**
414          * Takes an encoded URL and and converts it to an object. e.g. Roo.urlDecode("foo=1&bar=2"); would return {foo: 1, bar: 2} or Roo.urlDecode("foo=1&bar=2&bar=3&bar=4", true); would return {foo: 1, bar: [2, 3, 4]}.
415          * @param {String} string
416          * @param {Boolean} overwrite (optional) Items of the same name will overwrite previous values instead of creating an an array (Defaults to false).
417          * @return {Object} A literal with members
418          */
419         urlDecode : function(string, overwrite){
420             if(!string || !string.length){
421                 return {};
422             }
423             var obj = {};
424             var pairs = string.split('&');
425             var pair, name, value;
426             for(var i = 0, len = pairs.length; i < len; i++){
427                 pair = pairs[i].split('=');
428                 name = decodeURIComponent(pair[0]);
429                 value = decodeURIComponent(pair[1]);
430                 if(overwrite !== true){
431                     if(typeof obj[name] == "undefined"){
432                         obj[name] = value;
433                     }else if(typeof obj[name] == "string"){
434                         obj[name] = [obj[name]];
435                         obj[name].push(value);
436                     }else{
437                         obj[name].push(value);
438                     }
439                 }else{
440                     obj[name] = value;
441                 }
442             }
443             return obj;
444         },
445
446         /**
447          * Iterates an array calling the passed function with each item, stopping if your function returns false. If the
448          * passed array is not really an array, your function is called once with it.
449          * The supplied function is called with (Object item, Number index, Array allItems).
450          * @param {Array/NodeList/Mixed} array
451          * @param {Function} fn
452          * @param {Object} scope
453          */
454         each : function(array, fn, scope){
455             if(typeof array.length == "undefined" || typeof array == "string"){
456                 array = [array];
457             }
458             for(var i = 0, len = array.length; i < len; i++){
459                 if(fn.call(scope || array[i], array[i], i, array) === false){ return i; };
460             }
461         },
462
463         // deprecated
464         combine : function(){
465             var as = arguments, l = as.length, r = [];
466             for(var i = 0; i < l; i++){
467                 var a = as[i];
468                 if(a instanceof Array){
469                     r = r.concat(a);
470                 }else if(a.length !== undefined && !a.substr){
471                     r = r.concat(Array.prototype.slice.call(a, 0));
472                 }else{
473                     r.push(a);
474                 }
475             }
476             return r;
477         },
478
479         /**
480          * Escapes the passed string for use in a regular expression
481          * @param {String} str
482          * @return {String}
483          */
484         escapeRe : function(s) {
485             return s.replace(/([.*+?^${}()|[\]\/\\])/g, "\\$1");
486         },
487
488         // internal
489         callback : function(cb, scope, args, delay){
490             if(typeof cb == "function"){
491                 if(delay){
492                     cb.defer(delay, scope, args || []);
493                 }else{
494                     cb.apply(scope, args || []);
495                 }
496             }
497         },
498
499         /**
500          * Return the dom node for the passed string (id), dom node, or Roo.Element
501          * @param {String/HTMLElement/Roo.Element} el
502          * @return HTMLElement
503          */
504         getDom : function(el){
505             if(!el){
506                 return null;
507             }
508             return el.dom ? el.dom : (typeof el == 'string' ? document.getElementById(el) : el);
509         },
510
511         /**
512         * Shorthand for {@link Roo.ComponentMgr#get}
513         * @param {String} id
514         * @return Roo.Component
515         */
516         getCmp : function(id){
517             return Roo.ComponentMgr.get(id);
518         },
519          
520         num : function(v, defaultValue){
521             if(typeof v != 'number'){
522                 return defaultValue;
523             }
524             return v;
525         },
526
527         destroy : function(){
528             for(var i = 0, a = arguments, len = a.length; i < len; i++) {
529                 var as = a[i];
530                 if(as){
531                     if(as.dom){
532                         as.removeAllListeners();
533                         as.remove();
534                         continue;
535                     }
536                     if(typeof as.purgeListeners == 'function'){
537                         as.purgeListeners();
538                     }
539                     if(typeof as.destroy == 'function'){
540                         as.destroy();
541                     }
542                 }
543             }
544         },
545
546         // inpired by a similar function in mootools library
547         /**
548          * Returns the type of object that is passed in. If the object passed in is null or undefined it
549          * return false otherwise it returns one of the following values:<ul>
550          * <li><b>string</b>: If the object passed is a string</li>
551          * <li><b>number</b>: If the object passed is a number</li>
552          * <li><b>boolean</b>: If the object passed is a boolean value</li>
553          * <li><b>function</b>: If the object passed is a function reference</li>
554          * <li><b>object</b>: If the object passed is an object</li>
555          * <li><b>array</b>: If the object passed is an array</li>
556          * <li><b>regexp</b>: If the object passed is a regular expression</li>
557          * <li><b>element</b>: If the object passed is a DOM Element</li>
558          * <li><b>nodelist</b>: If the object passed is a DOM NodeList</li>
559          * <li><b>textnode</b>: If the object passed is a DOM text node and contains something other than whitespace</li>
560          * <li><b>whitespace</b>: If the object passed is a DOM text node and contains only whitespace</li>
561          * @param {Mixed} object
562          * @return {String}
563          */
564         type : function(o){
565             if(o === undefined || o === null){
566                 return false;
567             }
568             if(o.htmlElement){
569                 return 'element';
570             }
571             var t = typeof o;
572             if(t == 'object' && o.nodeName) {
573                 switch(o.nodeType) {
574                     case 1: return 'element';
575                     case 3: return (/\S/).test(o.nodeValue) ? 'textnode' : 'whitespace';
576                 }
577             }
578             if(t == 'object' || t == 'function') {
579                 switch(o.constructor) {
580                     case Array: return 'array';
581                     case RegExp: return 'regexp';
582                 }
583                 if(typeof o.length == 'number' && typeof o.item == 'function') {
584                     return 'nodelist';
585                 }
586             }
587             return t;
588         },
589
590         /**
591          * Returns true if the passed value is null, undefined or an empty string (optional).
592          * @param {Mixed} value The value to test
593          * @param {Boolean} allowBlank (optional) Pass true if an empty string is not considered empty
594          * @return {Boolean}
595          */
596         isEmpty : function(v, allowBlank){
597             return v === null || v === undefined || (!allowBlank ? v === '' : false);
598         },
599         
600         /** @type Boolean */
601         isOpera : isOpera,
602         /** @type Boolean */
603         isSafari : isSafari,
604         /** @type Boolean */
605         isIE : isIE,
606         /** @type Boolean */
607         isIE7 : isIE7,
608         /** @type Boolean */
609         isGecko : isGecko,
610         /** @type Boolean */
611         isBorderBox : isBorderBox,
612         /** @type Boolean */
613         isWindows : isWindows,
614         /** @type Boolean */
615         isLinux : isLinux,
616         /** @type Boolean */
617         isMac : isMac,
618         /** @type Boolean */
619         isTouch : isTouch,
620
621         /**
622          * By default, Ext intelligently decides whether floating elements should be shimmed. If you are using flash,
623          * you may want to set this to true.
624          * @type Boolean
625          */
626         useShims : ((isIE && !isIE7) || (isGecko && isMac)),
627         
628         
629                 
630         /**
631          * Selects a single element as a Roo Element
632          * This is about as close as you can get to jQuery's $('do crazy stuff')
633          * @param {String} selector The selector/xpath query
634          * @param {Node} root (optional) The start of the query (defaults to document).
635          * @return {Roo.Element}
636          */
637         selectNode : function(selector, root) 
638         {
639             var node = Roo.DomQuery.selectNode(selector,root);
640             return node ? Roo.get(node) : new Roo.Element(false);
641         }
642         
643     });
644
645
646 })();
647
648 Roo.namespace("Roo", "Roo.util", "Roo.grid", "Roo.dd", "Roo.tree", "Roo.data",
649                 "Roo.form", "Roo.menu", "Roo.state", "Roo.lib", "Roo.layout",
650                 "Roo.app", "Roo.ux",
651                 "Roo.bootstrap",
652                 "Roo.bootstrap.dash");
653 /*
654  * Based on:
655  * Ext JS Library 1.1.1
656  * Copyright(c) 2006-2007, Ext JS, LLC.
657  *
658  * Originally Released Under LGPL - original licence link has changed is not relivant.
659  *
660  * Fork - LGPL
661  * <script type="text/javascript">
662  */
663
664 (function() {    
665     // wrappedn so fnCleanup is not in global scope...
666     if(Roo.isIE) {
667         function fnCleanUp() {
668             var p = Function.prototype;
669             delete p.createSequence;
670             delete p.defer;
671             delete p.createDelegate;
672             delete p.createCallback;
673             delete p.createInterceptor;
674
675             window.detachEvent("onunload", fnCleanUp);
676         }
677         window.attachEvent("onunload", fnCleanUp);
678     }
679 })();
680
681
682 /**
683  * @class Function
684  * These functions are available on every Function object (any JavaScript function).
685  */
686 Roo.apply(Function.prototype, {
687      /**
688      * Creates a callback that passes arguments[0], arguments[1], arguments[2], ...
689      * Call directly on any function. Example: <code>myFunction.createCallback(myarg, myarg2)</code>
690      * Will create a function that is bound to those 2 args.
691      * @return {Function} The new function
692     */
693     createCallback : function(/*args...*/){
694         // make args available, in function below
695         var args = arguments;
696         var method = this;
697         return function() {
698             return method.apply(window, args);
699         };
700     },
701
702     /**
703      * Creates a delegate (callback) that sets the scope to obj.
704      * Call directly on any function. Example: <code>this.myFunction.createDelegate(this)</code>
705      * Will create a function that is automatically scoped to this.
706      * @param {Object} obj (optional) The object for which the scope is set
707      * @param {Array} args (optional) Overrides arguments for the call. (Defaults to the arguments passed by the caller)
708      * @param {Boolean/Number} appendArgs (optional) if True args are appended to call args instead of overriding,
709      *                                             if a number the args are inserted at the specified position
710      * @return {Function} The new function
711      */
712     createDelegate : function(obj, args, appendArgs){
713         var method = this;
714         return function() {
715             var callArgs = args || arguments;
716             if(appendArgs === true){
717                 callArgs = Array.prototype.slice.call(arguments, 0);
718                 callArgs = callArgs.concat(args);
719             }else if(typeof appendArgs == "number"){
720                 callArgs = Array.prototype.slice.call(arguments, 0); // copy arguments first
721                 var applyArgs = [appendArgs, 0].concat(args); // create method call params
722                 Array.prototype.splice.apply(callArgs, applyArgs); // splice them in
723             }
724             return method.apply(obj || window, callArgs);
725         };
726     },
727
728     /**
729      * Calls this function after the number of millseconds specified.
730      * @param {Number} millis The number of milliseconds for the setTimeout call (if 0 the function is executed immediately)
731      * @param {Object} obj (optional) The object for which the scope is set
732      * @param {Array} args (optional) Overrides arguments for the call. (Defaults to the arguments passed by the caller)
733      * @param {Boolean/Number} appendArgs (optional) if True args are appended to call args instead of overriding,
734      *                                             if a number the args are inserted at the specified position
735      * @return {Number} The timeout id that can be used with clearTimeout
736      */
737     defer : function(millis, obj, args, appendArgs){
738         var fn = this.createDelegate(obj, args, appendArgs);
739         if(millis){
740             return setTimeout(fn, millis);
741         }
742         fn();
743         return 0;
744     },
745     /**
746      * Create a combined function call sequence of the original function + the passed function.
747      * The resulting function returns the results of the original function.
748      * The passed fcn is called with the parameters of the original function
749      * @param {Function} fcn The function to sequence
750      * @param {Object} scope (optional) The scope of the passed fcn (Defaults to scope of original function or window)
751      * @return {Function} The new function
752      */
753     createSequence : function(fcn, scope){
754         if(typeof fcn != "function"){
755             return this;
756         }
757         var method = this;
758         return function() {
759             var retval = method.apply(this || window, arguments);
760             fcn.apply(scope || this || window, arguments);
761             return retval;
762         };
763     },
764
765     /**
766      * Creates an interceptor function. The passed fcn is called before the original one. If it returns false, the original one is not called.
767      * The resulting function returns the results of the original function.
768      * The passed fcn is called with the parameters of the original function.
769      * @addon
770      * @param {Function} fcn The function to call before the original
771      * @param {Object} scope (optional) The scope of the passed fcn (Defaults to scope of original function or window)
772      * @return {Function} The new function
773      */
774     createInterceptor : function(fcn, scope){
775         if(typeof fcn != "function"){
776             return this;
777         }
778         var method = this;
779         return function() {
780             fcn.target = this;
781             fcn.method = method;
782             if(fcn.apply(scope || this || window, arguments) === false){
783                 return;
784             }
785             return method.apply(this || window, arguments);
786         };
787     }
788 });
789 /*
790  * Based on:
791  * Ext JS Library 1.1.1
792  * Copyright(c) 2006-2007, Ext JS, LLC.
793  *
794  * Originally Released Under LGPL - original licence link has changed is not relivant.
795  *
796  * Fork - LGPL
797  * <script type="text/javascript">
798  */
799
800 Roo.applyIf(String, {
801     
802     /** @scope String */
803     
804     /**
805      * Escapes the passed string for ' and \
806      * @param {String} string The string to escape
807      * @return {String} The escaped string
808      * @static
809      */
810     escape : function(string) {
811         return string.replace(/('|\\)/g, "\\$1");
812     },
813
814     /**
815      * Pads the left side of a string with a specified character.  This is especially useful
816      * for normalizing number and date strings.  Example usage:
817      * <pre><code>
818 var s = String.leftPad('123', 5, '0');
819 // s now contains the string: '00123'
820 </code></pre>
821      * @param {String} string The original string
822      * @param {Number} size The total length of the output string
823      * @param {String} char (optional) The character with which to pad the original string (defaults to empty string " ")
824      * @return {String} The padded string
825      * @static
826      */
827     leftPad : function (val, size, ch) {
828         var result = new String(val);
829         if(ch === null || ch === undefined || ch === '') {
830             ch = " ";
831         }
832         while (result.length < size) {
833             result = ch + result;
834         }
835         return result;
836     },
837
838     /**
839      * Allows you to define a tokenized string and pass an arbitrary number of arguments to replace the tokens.  Each
840      * token must be unique, and must increment in the format {0}, {1}, etc.  Example usage:
841      * <pre><code>
842 var cls = 'my-class', text = 'Some text';
843 var s = String.format('<div class="{0}">{1}</div>', cls, text);
844 // s now contains the string: '<div class="my-class">Some text</div>'
845 </code></pre>
846      * @param {String} string The tokenized string to be formatted
847      * @param {String} value1 The value to replace token {0}
848      * @param {String} value2 Etc...
849      * @return {String} The formatted string
850      * @static
851      */
852     format : function(format){
853         var args = Array.prototype.slice.call(arguments, 1);
854         return format.replace(/\{(\d+)\}/g, function(m, i){
855             return Roo.util.Format.htmlEncode(args[i]);
856         });
857     }
858 });
859
860 /**
861  * Utility function that allows you to easily switch a string between two alternating values.  The passed value
862  * is compared to the current string, and if they are equal, the other value that was passed in is returned.  If
863  * they are already different, the first value passed in is returned.  Note that this method returns the new value
864  * but does not change the current string.
865  * <pre><code>
866 // alternate sort directions
867 sort = sort.toggle('ASC', 'DESC');
868
869 // instead of conditional logic:
870 sort = (sort == 'ASC' ? 'DESC' : 'ASC');
871 </code></pre>
872  * @param {String} value The value to compare to the current string
873  * @param {String} other The new value to use if the string already equals the first value passed in
874  * @return {String} The new value
875  */
876  
877 String.prototype.toggle = function(value, other){
878     return this == value ? other : value;
879 };/*
880  * Based on:
881  * Ext JS Library 1.1.1
882  * Copyright(c) 2006-2007, Ext JS, LLC.
883  *
884  * Originally Released Under LGPL - original licence link has changed is not relivant.
885  *
886  * Fork - LGPL
887  * <script type="text/javascript">
888  */
889
890  /**
891  * @class Number
892  */
893 Roo.applyIf(Number.prototype, {
894     /**
895      * Checks whether or not the current number is within a desired range.  If the number is already within the
896      * range it is returned, otherwise the min or max value is returned depending on which side of the range is
897      * exceeded.  Note that this method returns the constrained value but does not change the current number.
898      * @param {Number} min The minimum number in the range
899      * @param {Number} max The maximum number in the range
900      * @return {Number} The constrained value if outside the range, otherwise the current value
901      */
902     constrain : function(min, max){
903         return Math.min(Math.max(this, min), max);
904     }
905 });/*
906  * Based on:
907  * Ext JS Library 1.1.1
908  * Copyright(c) 2006-2007, Ext JS, LLC.
909  *
910  * Originally Released Under LGPL - original licence link has changed is not relivant.
911  *
912  * Fork - LGPL
913  * <script type="text/javascript">
914  */
915  /**
916  * @class Array
917  */
918 Roo.applyIf(Array.prototype, {
919     /**
920      * Checks whether or not the specified object exists in the array.
921      * @param {Object} o The object to check for
922      * @return {Number} The index of o in the array (or -1 if it is not found)
923      */
924     indexOf : function(o){
925        for (var i = 0, len = this.length; i < len; i++){
926               if(this[i] == o) return i;
927        }
928            return -1;
929     },
930
931     /**
932      * Removes the specified object from the array.  If the object is not found nothing happens.
933      * @param {Object} o The object to remove
934      */
935     remove : function(o){
936        var index = this.indexOf(o);
937        if(index != -1){
938            this.splice(index, 1);
939        }
940     },
941     /**
942      * Map (JS 1.6 compatibility)
943      * @param {Function} function  to call
944      */
945     map : function(fun )
946     {
947         var len = this.length >>> 0;
948         if (typeof fun != "function")
949             throw new TypeError();
950
951         var res = new Array(len);
952         var thisp = arguments[1];
953         for (var i = 0; i < len; i++)
954         {
955             if (i in this)
956                 res[i] = fun.call(thisp, this[i], i, this);
957         }
958
959         return res;
960     }
961     
962 });
963
964
965  /*
966  * Based on:
967  * Ext JS Library 1.1.1
968  * Copyright(c) 2006-2007, Ext JS, LLC.
969  *
970  * Originally Released Under LGPL - original licence link has changed is not relivant.
971  *
972  * Fork - LGPL
973  * <script type="text/javascript">
974  */
975
976 /**
977  * @class Date
978  *
979  * The date parsing and format syntax is a subset of
980  * <a href="http://www.php.net/date">PHP's date() function</a>, and the formats that are
981  * supported will provide results equivalent to their PHP versions.
982  *
983  * Following is the list of all currently supported formats:
984  *<pre>
985 Sample date:
986 'Wed Jan 10 2007 15:05:01 GMT-0600 (Central Standard Time)'
987
988 Format  Output      Description
989 ------  ----------  --------------------------------------------------------------
990   d      10         Day of the month, 2 digits with leading zeros
991   D      Wed        A textual representation of a day, three letters
992   j      10         Day of the month without leading zeros
993   l      Wednesday  A full textual representation of the day of the week
994   S      th         English ordinal day of month suffix, 2 chars (use with j)
995   w      3          Numeric representation of the day of the week
996   z      9          The julian date, or day of the year (0-365)
997   W      01         ISO-8601 2-digit week number of year, weeks starting on Monday (00-52)
998   F      January    A full textual representation of the month
999   m      01         Numeric representation of a month, with leading zeros
1000   M      Jan        Month name abbreviation, three letters
1001   n      1          Numeric representation of a month, without leading zeros
1002   t      31         Number of days in the given month
1003   L      0          Whether it's a leap year (1 if it is a leap year, else 0)
1004   Y      2007       A full numeric representation of a year, 4 digits
1005   y      07         A two digit representation of a year
1006   a      pm         Lowercase Ante meridiem and Post meridiem
1007   A      PM         Uppercase Ante meridiem and Post meridiem
1008   g      3          12-hour format of an hour without leading zeros
1009   G      15         24-hour format of an hour without leading zeros
1010   h      03         12-hour format of an hour with leading zeros
1011   H      15         24-hour format of an hour with leading zeros
1012   i      05         Minutes with leading zeros
1013   s      01         Seconds, with leading zeros
1014   O      -0600      Difference to Greenwich time (GMT) in hours (Allows +08, without minutes)
1015   P      -06:00     Difference to Greenwich time (GMT) with colon between hours and minutes
1016   T      CST        Timezone setting of the machine running the code
1017   Z      -21600     Timezone offset in seconds (negative if west of UTC, positive if east)
1018 </pre>
1019  *
1020  * Example usage (note that you must escape format specifiers with '\\' to render them as character literals):
1021  * <pre><code>
1022 var dt = new Date('1/10/2007 03:05:01 PM GMT-0600');
1023 document.write(dt.format('Y-m-d'));                         //2007-01-10
1024 document.write(dt.format('F j, Y, g:i a'));                 //January 10, 2007, 3:05 pm
1025 document.write(dt.format('l, \\t\\he dS of F Y h:i:s A'));  //Wednesday, the 10th of January 2007 03:05:01 PM
1026  </code></pre>
1027  *
1028  * Here are some standard date/time patterns that you might find helpful.  They
1029  * are not part of the source of Date.js, but to use them you can simply copy this
1030  * block of code into any script that is included after Date.js and they will also become
1031  * globally available on the Date object.  Feel free to add or remove patterns as needed in your code.
1032  * <pre><code>
1033 Date.patterns = {
1034     ISO8601Long:"Y-m-d H:i:s",
1035     ISO8601Short:"Y-m-d",
1036     ShortDate: "n/j/Y",
1037     LongDate: "l, F d, Y",
1038     FullDateTime: "l, F d, Y g:i:s A",
1039     MonthDay: "F d",
1040     ShortTime: "g:i A",
1041     LongTime: "g:i:s A",
1042     SortableDateTime: "Y-m-d\\TH:i:s",
1043     UniversalSortableDateTime: "Y-m-d H:i:sO",
1044     YearMonth: "F, Y"
1045 };
1046 </code></pre>
1047  *
1048  * Example usage:
1049  * <pre><code>
1050 var dt = new Date();
1051 document.write(dt.format(Date.patterns.ShortDate));
1052  </code></pre>
1053  */
1054
1055 /*
1056  * Most of the date-formatting functions below are the excellent work of Baron Schwartz.
1057  * They generate precompiled functions from date formats instead of parsing and
1058  * processing the pattern every time you format a date.  These functions are available
1059  * on every Date object (any javascript function).
1060  *
1061  * The original article and download are here:
1062  * http://www.xaprb.com/blog/2005/12/12/javascript-closures-for-runtime-efficiency/
1063  *
1064  */
1065  
1066  
1067  // was in core
1068 /**
1069  Returns the number of milliseconds between this date and date
1070  @param {Date} date (optional) Defaults to now
1071  @return {Number} The diff in milliseconds
1072  @member Date getElapsed
1073  */
1074 Date.prototype.getElapsed = function(date) {
1075         return Math.abs((date || new Date()).getTime()-this.getTime());
1076 };
1077 // was in date file..
1078
1079
1080 // private
1081 Date.parseFunctions = {count:0};
1082 // private
1083 Date.parseRegexes = [];
1084 // private
1085 Date.formatFunctions = {count:0};
1086
1087 // private
1088 Date.prototype.dateFormat = function(format) {
1089     if (Date.formatFunctions[format] == null) {
1090         Date.createNewFormat(format);
1091     }
1092     var func = Date.formatFunctions[format];
1093     return this[func]();
1094 };
1095
1096
1097 /**
1098  * Formats a date given the supplied format string
1099  * @param {String} format The format string
1100  * @return {String} The formatted date
1101  * @method
1102  */
1103 Date.prototype.format = Date.prototype.dateFormat;
1104
1105 // private
1106 Date.createNewFormat = function(format) {
1107     var funcName = "format" + Date.formatFunctions.count++;
1108     Date.formatFunctions[format] = funcName;
1109     var code = "Date.prototype." + funcName + " = function(){return ";
1110     var special = false;
1111     var ch = '';
1112     for (var i = 0; i < format.length; ++i) {
1113         ch = format.charAt(i);
1114         if (!special && ch == "\\") {
1115             special = true;
1116         }
1117         else if (special) {
1118             special = false;
1119             code += "'" + String.escape(ch) + "' + ";
1120         }
1121         else {
1122             code += Date.getFormatCode(ch);
1123         }
1124     }
1125     /** eval:var:zzzzzzzzzzzzz */
1126     eval(code.substring(0, code.length - 3) + ";}");
1127 };
1128
1129 // private
1130 Date.getFormatCode = function(character) {
1131     switch (character) {
1132     case "d":
1133         return "String.leftPad(this.getDate(), 2, '0') + ";
1134     case "D":
1135         return "Date.dayNames[this.getDay()].substring(0, 3) + ";
1136     case "j":
1137         return "this.getDate() + ";
1138     case "l":
1139         return "Date.dayNames[this.getDay()] + ";
1140     case "S":
1141         return "this.getSuffix() + ";
1142     case "w":
1143         return "this.getDay() + ";
1144     case "z":
1145         return "this.getDayOfYear() + ";
1146     case "W":
1147         return "this.getWeekOfYear() + ";
1148     case "F":
1149         return "Date.monthNames[this.getMonth()] + ";
1150     case "m":
1151         return "String.leftPad(this.getMonth() + 1, 2, '0') + ";
1152     case "M":
1153         return "Date.monthNames[this.getMonth()].substring(0, 3) + ";
1154     case "n":
1155         return "(this.getMonth() + 1) + ";
1156     case "t":
1157         return "this.getDaysInMonth() + ";
1158     case "L":
1159         return "(this.isLeapYear() ? 1 : 0) + ";
1160     case "Y":
1161         return "this.getFullYear() + ";
1162     case "y":
1163         return "('' + this.getFullYear()).substring(2, 4) + ";
1164     case "a":
1165         return "(this.getHours() < 12 ? 'am' : 'pm') + ";
1166     case "A":
1167         return "(this.getHours() < 12 ? 'AM' : 'PM') + ";
1168     case "g":
1169         return "((this.getHours() % 12) ? this.getHours() % 12 : 12) + ";
1170     case "G":
1171         return "this.getHours() + ";
1172     case "h":
1173         return "String.leftPad((this.getHours() % 12) ? this.getHours() % 12 : 12, 2, '0') + ";
1174     case "H":
1175         return "String.leftPad(this.getHours(), 2, '0') + ";
1176     case "i":
1177         return "String.leftPad(this.getMinutes(), 2, '0') + ";
1178     case "s":
1179         return "String.leftPad(this.getSeconds(), 2, '0') + ";
1180     case "O":
1181         return "this.getGMTOffset() + ";
1182     case "P":
1183         return "this.getGMTColonOffset() + ";
1184     case "T":
1185         return "this.getTimezone() + ";
1186     case "Z":
1187         return "(this.getTimezoneOffset() * -60) + ";
1188     default:
1189         return "'" + String.escape(character) + "' + ";
1190     }
1191 };
1192
1193 /**
1194  * Parses the passed string using the specified format. Note that this function expects dates in normal calendar
1195  * format, meaning that months are 1-based (1 = January) and not zero-based like in JavaScript dates.  Any part of
1196  * the date format that is not specified will default to the current date value for that part.  Time parts can also
1197  * be specified, but default to 0.  Keep in mind that the input date string must precisely match the specified format
1198  * string or the parse operation will fail.
1199  * Example Usage:
1200 <pre><code>
1201 //dt = Fri May 25 2007 (current date)
1202 var dt = new Date();
1203
1204 //dt = Thu May 25 2006 (today's month/day in 2006)
1205 dt = Date.parseDate("2006", "Y");
1206
1207 //dt = Sun Jan 15 2006 (all date parts specified)
1208 dt = Date.parseDate("2006-1-15", "Y-m-d");
1209
1210 //dt = Sun Jan 15 2006 15:20:01 GMT-0600 (CST)
1211 dt = Date.parseDate("2006-1-15 3:20:01 PM", "Y-m-d h:i:s A" );
1212 </code></pre>
1213  * @param {String} input The unparsed date as a string
1214  * @param {String} format The format the date is in
1215  * @return {Date} The parsed date
1216  * @static
1217  */
1218 Date.parseDate = function(input, format) {
1219     if (Date.parseFunctions[format] == null) {
1220         Date.createParser(format);
1221     }
1222     var func = Date.parseFunctions[format];
1223     return Date[func](input);
1224 };
1225 /**
1226  * @private
1227  */
1228 Date.createParser = function(format) {
1229     var funcName = "parse" + Date.parseFunctions.count++;
1230     var regexNum = Date.parseRegexes.length;
1231     var currentGroup = 1;
1232     Date.parseFunctions[format] = funcName;
1233
1234     var code = "Date." + funcName + " = function(input){\n"
1235         + "var y = -1, m = -1, d = -1, h = -1, i = -1, s = -1, o, z, v;\n"
1236         + "var d = new Date();\n"
1237         + "y = d.getFullYear();\n"
1238         + "m = d.getMonth();\n"
1239         + "d = d.getDate();\n"
1240         + "var results = input.match(Date.parseRegexes[" + regexNum + "]);\n"
1241         + "if (results && results.length > 0) {";
1242     var regex = "";
1243
1244     var special = false;
1245     var ch = '';
1246     for (var i = 0; i < format.length; ++i) {
1247         ch = format.charAt(i);
1248         if (!special && ch == "\\") {
1249             special = true;
1250         }
1251         else if (special) {
1252             special = false;
1253             regex += String.escape(ch);
1254         }
1255         else {
1256             var obj = Date.formatCodeToRegex(ch, currentGroup);
1257             currentGroup += obj.g;
1258             regex += obj.s;
1259             if (obj.g && obj.c) {
1260                 code += obj.c;
1261             }
1262         }
1263     }
1264
1265     code += "if (y >= 0 && m >= 0 && d > 0 && h >= 0 && i >= 0 && s >= 0)\n"
1266         + "{v = new Date(y, m, d, h, i, s);}\n"
1267         + "else if (y >= 0 && m >= 0 && d > 0 && h >= 0 && i >= 0)\n"
1268         + "{v = new Date(y, m, d, h, i);}\n"
1269         + "else if (y >= 0 && m >= 0 && d > 0 && h >= 0)\n"
1270         + "{v = new Date(y, m, d, h);}\n"
1271         + "else if (y >= 0 && m >= 0 && d > 0)\n"
1272         + "{v = new Date(y, m, d);}\n"
1273         + "else if (y >= 0 && m >= 0)\n"
1274         + "{v = new Date(y, m);}\n"
1275         + "else if (y >= 0)\n"
1276         + "{v = new Date(y);}\n"
1277         + "}return (v && (z || o))?\n" // favour UTC offset over GMT offset
1278         + "    ((z)? v.add(Date.SECOND, (v.getTimezoneOffset() * 60) + (z*1)) :\n" // reset to UTC, then add offset
1279         + "        v.add(Date.HOUR, (v.getGMTOffset() / 100) + (o / -100))) : v\n" // reset to GMT, then add offset
1280         + ";}";
1281
1282     Date.parseRegexes[regexNum] = new RegExp("^" + regex + "$");
1283     /** eval:var:zzzzzzzzzzzzz */
1284     eval(code);
1285 };
1286
1287 // private
1288 Date.formatCodeToRegex = function(character, currentGroup) {
1289     switch (character) {
1290     case "D":
1291         return {g:0,
1292         c:null,
1293         s:"(?:Sun|Mon|Tue|Wed|Thu|Fri|Sat)"};
1294     case "j":
1295         return {g:1,
1296             c:"d = parseInt(results[" + currentGroup + "], 10);\n",
1297             s:"(\\d{1,2})"}; // day of month without leading zeroes
1298     case "d":
1299         return {g:1,
1300             c:"d = parseInt(results[" + currentGroup + "], 10);\n",
1301             s:"(\\d{2})"}; // day of month with leading zeroes
1302     case "l":
1303         return {g:0,
1304             c:null,
1305             s:"(?:" + Date.dayNames.join("|") + ")"};
1306     case "S":
1307         return {g:0,
1308             c:null,
1309             s:"(?:st|nd|rd|th)"};
1310     case "w":
1311         return {g:0,
1312             c:null,
1313             s:"\\d"};
1314     case "z":
1315         return {g:0,
1316             c:null,
1317             s:"(?:\\d{1,3})"};
1318     case "W":
1319         return {g:0,
1320             c:null,
1321             s:"(?:\\d{2})"};
1322     case "F":
1323         return {g:1,
1324             c:"m = parseInt(Date.monthNumbers[results[" + currentGroup + "].substring(0, 3)], 10);\n",
1325             s:"(" + Date.monthNames.join("|") + ")"};
1326     case "M":
1327         return {g:1,
1328             c:"m = parseInt(Date.monthNumbers[results[" + currentGroup + "]], 10);\n",
1329             s:"(Jan|Feb|Mar|Apr|May|Jun|Jul|Aug|Sep|Oct|Nov|Dec)"};
1330     case "n":
1331         return {g:1,
1332             c:"m = parseInt(results[" + currentGroup + "], 10) - 1;\n",
1333             s:"(\\d{1,2})"}; // Numeric representation of a month, without leading zeros
1334     case "m":
1335         return {g:1,
1336             c:"m = parseInt(results[" + currentGroup + "], 10) - 1;\n",
1337             s:"(\\d{2})"}; // Numeric representation of a month, with leading zeros
1338     case "t":
1339         return {g:0,
1340             c:null,
1341             s:"\\d{1,2}"};
1342     case "L":
1343         return {g:0,
1344             c:null,
1345             s:"(?:1|0)"};
1346     case "Y":
1347         return {g:1,
1348             c:"y = parseInt(results[" + currentGroup + "], 10);\n",
1349             s:"(\\d{4})"};
1350     case "y":
1351         return {g:1,
1352             c:"var ty = parseInt(results[" + currentGroup + "], 10);\n"
1353                 + "y = ty > Date.y2kYear ? 1900 + ty : 2000 + ty;\n",
1354             s:"(\\d{1,2})"};
1355     case "a":
1356         return {g:1,
1357             c:"if (results[" + currentGroup + "] == 'am') {\n"
1358                 + "if (h == 12) { h = 0; }\n"
1359                 + "} else { if (h < 12) { h += 12; }}",
1360             s:"(am|pm)"};
1361     case "A":
1362         return {g:1,
1363             c:"if (results[" + currentGroup + "] == 'AM') {\n"
1364                 + "if (h == 12) { h = 0; }\n"
1365                 + "} else { if (h < 12) { h += 12; }}",
1366             s:"(AM|PM)"};
1367     case "g":
1368     case "G":
1369         return {g:1,
1370             c:"h = parseInt(results[" + currentGroup + "], 10);\n",
1371             s:"(\\d{1,2})"}; // 12/24-hr format  format of an hour without leading zeroes
1372     case "h":
1373     case "H":
1374         return {g:1,
1375             c:"h = parseInt(results[" + currentGroup + "], 10);\n",
1376             s:"(\\d{2})"}; //  12/24-hr format  format of an hour with leading zeroes
1377     case "i":
1378         return {g:1,
1379             c:"i = parseInt(results[" + currentGroup + "], 10);\n",
1380             s:"(\\d{2})"};
1381     case "s":
1382         return {g:1,
1383             c:"s = parseInt(results[" + currentGroup + "], 10);\n",
1384             s:"(\\d{2})"};
1385     case "O":
1386         return {g:1,
1387             c:[
1388                 "o = results[", currentGroup, "];\n",
1389                 "var sn = o.substring(0,1);\n", // get + / - sign
1390                 "var hr = o.substring(1,3)*1 + Math.floor(o.substring(3,5) / 60);\n", // get hours (performs minutes-to-hour conversion also)
1391                 "var mn = o.substring(3,5) % 60;\n", // get minutes
1392                 "o = ((-12 <= (hr*60 + mn)/60) && ((hr*60 + mn)/60 <= 14))?\n", // -12hrs <= GMT offset <= 14hrs
1393                 "    (sn + String.leftPad(hr, 2, 0) + String.leftPad(mn, 2, 0)) : null;\n"
1394             ].join(""),
1395             s:"([+\-]\\d{2,4})"};
1396     
1397     
1398     case "P":
1399         return {g:1,
1400                 c:[
1401                    "o = results[", currentGroup, "];\n",
1402                    "var sn = o.substring(0,1);\n",
1403                    "var hr = o.substring(1,3)*1 + Math.floor(o.substring(4,6) / 60);\n",
1404                    "var mn = o.substring(4,6) % 60;\n",
1405                    "o = ((-12 <= (hr*60 + mn)/60) && ((hr*60 + mn)/60 <= 14))?\n",
1406                         "    (sn + String.leftPad(hr, 2, 0) + String.leftPad(mn, 2, 0)) : null;\n"
1407             ].join(""),
1408             s:"([+\-]\\d{4})"};
1409     case "T":
1410         return {g:0,
1411             c:null,
1412             s:"[A-Z]{1,4}"}; // timezone abbrev. may be between 1 - 4 chars
1413     case "Z":
1414         return {g:1,
1415             c:"z = results[" + currentGroup + "];\n" // -43200 <= UTC offset <= 50400
1416                   + "z = (-43200 <= z*1 && z*1 <= 50400)? z : null;\n",
1417             s:"([+\-]?\\d{1,5})"}; // leading '+' sign is optional for UTC offset
1418     default:
1419         return {g:0,
1420             c:null,
1421             s:String.escape(character)};
1422     }
1423 };
1424
1425 /**
1426  * Get the timezone abbreviation of the current date (equivalent to the format specifier 'T').
1427  * @return {String} The abbreviated timezone name (e.g. 'CST')
1428  */
1429 Date.prototype.getTimezone = function() {
1430     return this.toString().replace(/^.*? ([A-Z]{1,4})[\-+][0-9]{4} .*$/, "$1");
1431 };
1432
1433 /**
1434  * Get the offset from GMT of the current date (equivalent to the format specifier 'O').
1435  * @return {String} The 4-character offset string prefixed with + or - (e.g. '-0600')
1436  */
1437 Date.prototype.getGMTOffset = function() {
1438     return (this.getTimezoneOffset() > 0 ? "-" : "+")
1439         + String.leftPad(Math.abs(Math.floor(this.getTimezoneOffset() / 60)), 2, "0")
1440         + String.leftPad(this.getTimezoneOffset() % 60, 2, "0");
1441 };
1442
1443 /**
1444  * Get the offset from GMT of the current date (equivalent to the format specifier 'P').
1445  * @return {String} 2-characters representing hours and 2-characters representing minutes
1446  * seperated by a colon and prefixed with + or - (e.g. '-06:00')
1447  */
1448 Date.prototype.getGMTColonOffset = function() {
1449         return (this.getTimezoneOffset() > 0 ? "-" : "+")
1450                 + String.leftPad(Math.abs(Math.floor(this.getTimezoneOffset() / 60)), 2, "0")
1451                 + ":"
1452                 + String.leftPad(this.getTimezoneOffset() %60, 2, "0");
1453 }
1454
1455 /**
1456  * Get the numeric day number of the year, adjusted for leap year.
1457  * @return {Number} 0 through 364 (365 in leap years)
1458  */
1459 Date.prototype.getDayOfYear = function() {
1460     var num = 0;
1461     Date.daysInMonth[1] = this.isLeapYear() ? 29 : 28;
1462     for (var i = 0; i < this.getMonth(); ++i) {
1463         num += Date.daysInMonth[i];
1464     }
1465     return num + this.getDate() - 1;
1466 };
1467
1468 /**
1469  * Get the string representation of the numeric week number of the year
1470  * (equivalent to the format specifier 'W').
1471  * @return {String} '00' through '52'
1472  */
1473 Date.prototype.getWeekOfYear = function() {
1474     // Skip to Thursday of this week
1475     var now = this.getDayOfYear() + (4 - this.getDay());
1476     // Find the first Thursday of the year
1477     var jan1 = new Date(this.getFullYear(), 0, 1);
1478     var then = (7 - jan1.getDay() + 4);
1479     return String.leftPad(((now - then) / 7) + 1, 2, "0");
1480 };
1481
1482 /**
1483  * Whether or not the current date is in a leap year.
1484  * @return {Boolean} True if the current date is in a leap year, else false
1485  */
1486 Date.prototype.isLeapYear = function() {
1487     var year = this.getFullYear();
1488     return ((year & 3) == 0 && (year % 100 || (year % 400 == 0 && year)));
1489 };
1490
1491 /**
1492  * Get the first day of the current month, adjusted for leap year.  The returned value
1493  * is the numeric day index within the week (0-6) which can be used in conjunction with
1494  * the {@link #monthNames} array to retrieve the textual day name.
1495  * Example:
1496  *<pre><code>
1497 var dt = new Date('1/10/2007');
1498 document.write(Date.dayNames[dt.getFirstDayOfMonth()]); //output: 'Monday'
1499 </code></pre>
1500  * @return {Number} The day number (0-6)
1501  */
1502 Date.prototype.getFirstDayOfMonth = function() {
1503     var day = (this.getDay() - (this.getDate() - 1)) % 7;
1504     return (day < 0) ? (day + 7) : day;
1505 };
1506
1507 /**
1508  * Get the last day of the current month, adjusted for leap year.  The returned value
1509  * is the numeric day index within the week (0-6) which can be used in conjunction with
1510  * the {@link #monthNames} array to retrieve the textual day name.
1511  * Example:
1512  *<pre><code>
1513 var dt = new Date('1/10/2007');
1514 document.write(Date.dayNames[dt.getLastDayOfMonth()]); //output: 'Wednesday'
1515 </code></pre>
1516  * @return {Number} The day number (0-6)
1517  */
1518 Date.prototype.getLastDayOfMonth = function() {
1519     var day = (this.getDay() + (Date.daysInMonth[this.getMonth()] - this.getDate())) % 7;
1520     return (day < 0) ? (day + 7) : day;
1521 };
1522
1523
1524 /**
1525  * Get the first date of this date's month
1526  * @return {Date}
1527  */
1528 Date.prototype.getFirstDateOfMonth = function() {
1529     return new Date(this.getFullYear(), this.getMonth(), 1);
1530 };
1531
1532 /**
1533  * Get the last date of this date's month
1534  * @return {Date}
1535  */
1536 Date.prototype.getLastDateOfMonth = function() {
1537     return new Date(this.getFullYear(), this.getMonth(), this.getDaysInMonth());
1538 };
1539 /**
1540  * Get the number of days in the current month, adjusted for leap year.
1541  * @return {Number} The number of days in the month
1542  */
1543 Date.prototype.getDaysInMonth = function() {
1544     Date.daysInMonth[1] = this.isLeapYear() ? 29 : 28;
1545     return Date.daysInMonth[this.getMonth()];
1546 };
1547
1548 /**
1549  * Get the English ordinal suffix of the current day (equivalent to the format specifier 'S').
1550  * @return {String} 'st, 'nd', 'rd' or 'th'
1551  */
1552 Date.prototype.getSuffix = function() {
1553     switch (this.getDate()) {
1554         case 1:
1555         case 21:
1556         case 31:
1557             return "st";
1558         case 2:
1559         case 22:
1560             return "nd";
1561         case 3:
1562         case 23:
1563             return "rd";
1564         default:
1565             return "th";
1566     }
1567 };
1568
1569 // private
1570 Date.daysInMonth = [31,28,31,30,31,30,31,31,30,31,30,31];
1571
1572 /**
1573  * An array of textual month names.
1574  * Override these values for international dates, for example...
1575  * Date.monthNames = ['JanInYourLang', 'FebInYourLang', ...];
1576  * @type Array
1577  * @static
1578  */
1579 Date.monthNames =
1580    ["January",
1581     "February",
1582     "March",
1583     "April",
1584     "May",
1585     "June",
1586     "July",
1587     "August",
1588     "September",
1589     "October",
1590     "November",
1591     "December"];
1592
1593 /**
1594  * An array of textual day names.
1595  * Override these values for international dates, for example...
1596  * Date.dayNames = ['SundayInYourLang', 'MondayInYourLang', ...];
1597  * @type Array
1598  * @static
1599  */
1600 Date.dayNames =
1601    ["Sunday",
1602     "Monday",
1603     "Tuesday",
1604     "Wednesday",
1605     "Thursday",
1606     "Friday",
1607     "Saturday"];
1608
1609 // private
1610 Date.y2kYear = 50;
1611 // private
1612 Date.monthNumbers = {
1613     Jan:0,
1614     Feb:1,
1615     Mar:2,
1616     Apr:3,
1617     May:4,
1618     Jun:5,
1619     Jul:6,
1620     Aug:7,
1621     Sep:8,
1622     Oct:9,
1623     Nov:10,
1624     Dec:11};
1625
1626 /**
1627  * Creates and returns a new Date instance with the exact same date value as the called instance.
1628  * Dates are copied and passed by reference, so if a copied date variable is modified later, the original
1629  * variable will also be changed.  When the intention is to create a new variable that will not
1630  * modify the original instance, you should create a clone.
1631  *
1632  * Example of correctly cloning a date:
1633  * <pre><code>
1634 //wrong way:
1635 var orig = new Date('10/1/2006');
1636 var copy = orig;
1637 copy.setDate(5);
1638 document.write(orig);  //returns 'Thu Oct 05 2006'!
1639
1640 //correct way:
1641 var orig = new Date('10/1/2006');
1642 var copy = orig.clone();
1643 copy.setDate(5);
1644 document.write(orig);  //returns 'Thu Oct 01 2006'
1645 </code></pre>
1646  * @return {Date} The new Date instance
1647  */
1648 Date.prototype.clone = function() {
1649         return new Date(this.getTime());
1650 };
1651
1652 /**
1653  * Clears any time information from this date
1654  @param {Boolean} clone true to create a clone of this date, clear the time and return it
1655  @return {Date} this or the clone
1656  */
1657 Date.prototype.clearTime = function(clone){
1658     if(clone){
1659         return this.clone().clearTime();
1660     }
1661     this.setHours(0);
1662     this.setMinutes(0);
1663     this.setSeconds(0);
1664     this.setMilliseconds(0);
1665     return this;
1666 };
1667
1668 // private
1669 // safari setMonth is broken
1670 if(Roo.isSafari){
1671     Date.brokenSetMonth = Date.prototype.setMonth;
1672         Date.prototype.setMonth = function(num){
1673                 if(num <= -1){
1674                         var n = Math.ceil(-num);
1675                         var back_year = Math.ceil(n/12);
1676                         var month = (n % 12) ? 12 - n % 12 : 0 ;
1677                         this.setFullYear(this.getFullYear() - back_year);
1678                         return Date.brokenSetMonth.call(this, month);
1679                 } else {
1680                         return Date.brokenSetMonth.apply(this, arguments);
1681                 }
1682         };
1683 }
1684
1685 /** Date interval constant 
1686 * @static 
1687 * @type String */
1688 Date.MILLI = "ms";
1689 /** Date interval constant 
1690 * @static 
1691 * @type String */
1692 Date.SECOND = "s";
1693 /** Date interval constant 
1694 * @static 
1695 * @type String */
1696 Date.MINUTE = "mi";
1697 /** Date interval constant 
1698 * @static 
1699 * @type String */
1700 Date.HOUR = "h";
1701 /** Date interval constant 
1702 * @static 
1703 * @type String */
1704 Date.DAY = "d";
1705 /** Date interval constant 
1706 * @static 
1707 * @type String */
1708 Date.MONTH = "mo";
1709 /** Date interval constant 
1710 * @static 
1711 * @type String */
1712 Date.YEAR = "y";
1713
1714 /**
1715  * Provides a convenient method of performing basic date arithmetic.  This method
1716  * does not modify the Date instance being called - it creates and returns
1717  * a new Date instance containing the resulting date value.
1718  *
1719  * Examples:
1720  * <pre><code>
1721 //Basic usage:
1722 var dt = new Date('10/29/2006').add(Date.DAY, 5);
1723 document.write(dt); //returns 'Fri Oct 06 2006 00:00:00'
1724
1725 //Negative values will subtract correctly:
1726 var dt2 = new Date('10/1/2006').add(Date.DAY, -5);
1727 document.write(dt2); //returns 'Tue Sep 26 2006 00:00:00'
1728
1729 //You can even chain several calls together in one line!
1730 var dt3 = new Date('10/1/2006').add(Date.DAY, 5).add(Date.HOUR, 8).add(Date.MINUTE, -30);
1731 document.write(dt3); //returns 'Fri Oct 06 2006 07:30:00'
1732  </code></pre>
1733  *
1734  * @param {String} interval   A valid date interval enum value
1735  * @param {Number} value      The amount to add to the current date
1736  * @return {Date} The new Date instance
1737  */
1738 Date.prototype.add = function(interval, value){
1739   var d = this.clone();
1740   if (!interval || value === 0) return d;
1741   switch(interval.toLowerCase()){
1742     case Date.MILLI:
1743       d.setMilliseconds(this.getMilliseconds() + value);
1744       break;
1745     case Date.SECOND:
1746       d.setSeconds(this.getSeconds() + value);
1747       break;
1748     case Date.MINUTE:
1749       d.setMinutes(this.getMinutes() + value);
1750       break;
1751     case Date.HOUR:
1752       d.setHours(this.getHours() + value);
1753       break;
1754     case Date.DAY:
1755       d.setDate(this.getDate() + value);
1756       break;
1757     case Date.MONTH:
1758       var day = this.getDate();
1759       if(day > 28){
1760           day = Math.min(day, this.getFirstDateOfMonth().add('mo', value).getLastDateOfMonth().getDate());
1761       }
1762       d.setDate(day);
1763       d.setMonth(this.getMonth() + value);
1764       break;
1765     case Date.YEAR:
1766       d.setFullYear(this.getFullYear() + value);
1767       break;
1768   }
1769   return d;
1770 };
1771 /*
1772  * Based on:
1773  * Ext JS Library 1.1.1
1774  * Copyright(c) 2006-2007, Ext JS, LLC.
1775  *
1776  * Originally Released Under LGPL - original licence link has changed is not relivant.
1777  *
1778  * Fork - LGPL
1779  * <script type="text/javascript">
1780  */
1781
1782 /**
1783  * @class Roo.lib.Dom
1784  * @static
1785  * 
1786  * Dom utils (from YIU afaik)
1787  * 
1788  **/
1789 Roo.lib.Dom = {
1790     /**
1791      * Get the view width
1792      * @param {Boolean} full True will get the full document, otherwise it's the view width
1793      * @return {Number} The width
1794      */
1795      
1796     getViewWidth : function(full) {
1797         return full ? this.getDocumentWidth() : this.getViewportWidth();
1798     },
1799     /**
1800      * Get the view height
1801      * @param {Boolean} full True will get the full document, otherwise it's the view height
1802      * @return {Number} The height
1803      */
1804     getViewHeight : function(full) {
1805         return full ? this.getDocumentHeight() : this.getViewportHeight();
1806     },
1807
1808     getDocumentHeight: function() {
1809         var scrollHeight = (document.compatMode != "CSS1Compat") ? document.body.scrollHeight : document.documentElement.scrollHeight;
1810         return Math.max(scrollHeight, this.getViewportHeight());
1811     },
1812
1813     getDocumentWidth: function() {
1814         var scrollWidth = (document.compatMode != "CSS1Compat") ? document.body.scrollWidth : document.documentElement.scrollWidth;
1815         return Math.max(scrollWidth, this.getViewportWidth());
1816     },
1817
1818     getViewportHeight: function() {
1819         var height = self.innerHeight;
1820         var mode = document.compatMode;
1821
1822         if ((mode || Roo.isIE) && !Roo.isOpera) {
1823             height = (mode == "CSS1Compat") ?
1824                      document.documentElement.clientHeight :
1825                      document.body.clientHeight;
1826         }
1827
1828         return height;
1829     },
1830
1831     getViewportWidth: function() {
1832         var width = self.innerWidth;
1833         var mode = document.compatMode;
1834
1835         if (mode || Roo.isIE) {
1836             width = (mode == "CSS1Compat") ?
1837                     document.documentElement.clientWidth :
1838                     document.body.clientWidth;
1839         }
1840         return width;
1841     },
1842
1843     isAncestor : function(p, c) {
1844         p = Roo.getDom(p);
1845         c = Roo.getDom(c);
1846         if (!p || !c) {
1847             return false;
1848         }
1849
1850         if (p.contains && !Roo.isSafari) {
1851             return p.contains(c);
1852         } else if (p.compareDocumentPosition) {
1853             return !!(p.compareDocumentPosition(c) & 16);
1854         } else {
1855             var parent = c.parentNode;
1856             while (parent) {
1857                 if (parent == p) {
1858                     return true;
1859                 }
1860                 else if (!parent.tagName || parent.tagName.toUpperCase() == "HTML") {
1861                     return false;
1862                 }
1863                 parent = parent.parentNode;
1864             }
1865             return false;
1866         }
1867     },
1868
1869     getRegion : function(el) {
1870         return Roo.lib.Region.getRegion(el);
1871     },
1872
1873     getY : function(el) {
1874         return this.getXY(el)[1];
1875     },
1876
1877     getX : function(el) {
1878         return this.getXY(el)[0];
1879     },
1880
1881     getXY : function(el) {
1882         var p, pe, b, scroll, bd = document.body;
1883         el = Roo.getDom(el);
1884         var fly = Roo.lib.AnimBase.fly;
1885         if (el.getBoundingClientRect) {
1886             b = el.getBoundingClientRect();
1887             scroll = fly(document).getScroll();
1888             return [b.left + scroll.left, b.top + scroll.top];
1889         }
1890         var x = 0, y = 0;
1891
1892         p = el;
1893
1894         var hasAbsolute = fly(el).getStyle("position") == "absolute";
1895
1896         while (p) {
1897
1898             x += p.offsetLeft;
1899             y += p.offsetTop;
1900
1901             if (!hasAbsolute && fly(p).getStyle("position") == "absolute") {
1902                 hasAbsolute = true;
1903             }
1904
1905             if (Roo.isGecko) {
1906                 pe = fly(p);
1907
1908                 var bt = parseInt(pe.getStyle("borderTopWidth"), 10) || 0;
1909                 var bl = parseInt(pe.getStyle("borderLeftWidth"), 10) || 0;
1910
1911
1912                 x += bl;
1913                 y += bt;
1914
1915
1916                 if (p != el && pe.getStyle('overflow') != 'visible') {
1917                     x += bl;
1918                     y += bt;
1919                 }
1920             }
1921             p = p.offsetParent;
1922         }
1923
1924         if (Roo.isSafari && hasAbsolute) {
1925             x -= bd.offsetLeft;
1926             y -= bd.offsetTop;
1927         }
1928
1929         if (Roo.isGecko && !hasAbsolute) {
1930             var dbd = fly(bd);
1931             x += parseInt(dbd.getStyle("borderLeftWidth"), 10) || 0;
1932             y += parseInt(dbd.getStyle("borderTopWidth"), 10) || 0;
1933         }
1934
1935         p = el.parentNode;
1936         while (p && p != bd) {
1937             if (!Roo.isOpera || (p.tagName != 'TR' && fly(p).getStyle("display") != "inline")) {
1938                 x -= p.scrollLeft;
1939                 y -= p.scrollTop;
1940             }
1941             p = p.parentNode;
1942         }
1943         return [x, y];
1944     },
1945  
1946   
1947
1948
1949     setXY : function(el, xy) {
1950         el = Roo.fly(el, '_setXY');
1951         el.position();
1952         var pts = el.translatePoints(xy);
1953         if (xy[0] !== false) {
1954             el.dom.style.left = pts.left + "px";
1955         }
1956         if (xy[1] !== false) {
1957             el.dom.style.top = pts.top + "px";
1958         }
1959     },
1960
1961     setX : function(el, x) {
1962         this.setXY(el, [x, false]);
1963     },
1964
1965     setY : function(el, y) {
1966         this.setXY(el, [false, y]);
1967     }
1968 };
1969 /*
1970  * Portions of this file are based on pieces of Yahoo User Interface Library
1971  * Copyright (c) 2007, Yahoo! Inc. All rights reserved.
1972  * YUI licensed under the BSD License:
1973  * http://developer.yahoo.net/yui/license.txt
1974  * <script type="text/javascript">
1975  *
1976  */
1977
1978 Roo.lib.Event = function() {
1979     var loadComplete = false;
1980     var listeners = [];
1981     var unloadListeners = [];
1982     var retryCount = 0;
1983     var onAvailStack = [];
1984     var counter = 0;
1985     var lastError = null;
1986
1987     return {
1988         POLL_RETRYS: 200,
1989         POLL_INTERVAL: 20,
1990         EL: 0,
1991         TYPE: 1,
1992         FN: 2,
1993         WFN: 3,
1994         OBJ: 3,
1995         ADJ_SCOPE: 4,
1996         _interval: null,
1997
1998         startInterval: function() {
1999             if (!this._interval) {
2000                 var self = this;
2001                 var callback = function() {
2002                     self._tryPreloadAttach();
2003                 };
2004                 this._interval = setInterval(callback, this.POLL_INTERVAL);
2005
2006             }
2007         },
2008
2009         onAvailable: function(p_id, p_fn, p_obj, p_override) {
2010             onAvailStack.push({ id:         p_id,
2011                 fn:         p_fn,
2012                 obj:        p_obj,
2013                 override:   p_override,
2014                 checkReady: false    });
2015
2016             retryCount = this.POLL_RETRYS;
2017             this.startInterval();
2018         },
2019
2020
2021         addListener: function(el, eventName, fn) {
2022             el = Roo.getDom(el);
2023             if (!el || !fn) {
2024                 return false;
2025             }
2026
2027             if ("unload" == eventName) {
2028                 unloadListeners[unloadListeners.length] =
2029                 [el, eventName, fn];
2030                 return true;
2031             }
2032
2033             var wrappedFn = function(e) {
2034                 return fn(Roo.lib.Event.getEvent(e));
2035             };
2036
2037             var li = [el, eventName, fn, wrappedFn];
2038
2039             var index = listeners.length;
2040             listeners[index] = li;
2041
2042             this.doAdd(el, eventName, wrappedFn, false);
2043             return true;
2044
2045         },
2046
2047
2048         removeListener: function(el, eventName, fn) {
2049             var i, len;
2050
2051             el = Roo.getDom(el);
2052
2053             if(!fn) {
2054                 return this.purgeElement(el, false, eventName);
2055             }
2056
2057
2058             if ("unload" == eventName) {
2059
2060                 for (i = 0,len = unloadListeners.length; i < len; i++) {
2061                     var li = unloadListeners[i];
2062                     if (li &&
2063                         li[0] == el &&
2064                         li[1] == eventName &&
2065                         li[2] == fn) {
2066                         unloadListeners.splice(i, 1);
2067                         return true;
2068                     }
2069                 }
2070
2071                 return false;
2072             }
2073
2074             var cacheItem = null;
2075
2076
2077             var index = arguments[3];
2078
2079             if ("undefined" == typeof index) {
2080                 index = this._getCacheIndex(el, eventName, fn);
2081             }
2082
2083             if (index >= 0) {
2084                 cacheItem = listeners[index];
2085             }
2086
2087             if (!el || !cacheItem) {
2088                 return false;
2089             }
2090
2091             this.doRemove(el, eventName, cacheItem[this.WFN], false);
2092
2093             delete listeners[index][this.WFN];
2094             delete listeners[index][this.FN];
2095             listeners.splice(index, 1);
2096
2097             return true;
2098
2099         },
2100
2101
2102         getTarget: function(ev, resolveTextNode) {
2103             ev = ev.browserEvent || ev;
2104             ev = ev.touches ? (ev.touches[0] || ev.changedTouches[0] || ev )  : ev;
2105             var t = ev.target || ev.srcElement;
2106             return this.resolveTextNode(t);
2107         },
2108
2109
2110         resolveTextNode: function(node) {
2111             if (Roo.isSafari && node && 3 == node.nodeType) {
2112                 return node.parentNode;
2113             } else {
2114                 return node;
2115             }
2116         },
2117
2118
2119         getPageX: function(ev) {
2120             ev = ev.browserEvent || ev;
2121             ev = ev.touches ? (ev.touches[0] || ev.changedTouches[0] || ev )  : ev;
2122             var x = ev.pageX;
2123             if (!x && 0 !== x) {
2124                 x = ev.clientX || 0;
2125
2126                 if (Roo.isIE) {
2127                     x += this.getScroll()[1];
2128                 }
2129             }
2130
2131             return x;
2132         },
2133
2134
2135         getPageY: function(ev) {
2136             ev = ev.browserEvent || ev;
2137             ev = ev.touches ? (ev.touches[0] || ev.changedTouches[0] || ev )  : ev;
2138             var y = ev.pageY;
2139             if (!y && 0 !== y) {
2140                 y = ev.clientY || 0;
2141
2142                 if (Roo.isIE) {
2143                     y += this.getScroll()[0];
2144                 }
2145             }
2146
2147
2148             return y;
2149         },
2150
2151
2152         getXY: function(ev) {
2153             ev = ev.browserEvent || ev;
2154             ev = ev.touches ? (ev.touches[0] || ev.changedTouches[0] || ev )  : ev;
2155             return [this.getPageX(ev), this.getPageY(ev)];
2156         },
2157
2158
2159         getRelatedTarget: function(ev) {
2160             ev = ev.browserEvent || ev;
2161             ev = ev.touches ? (ev.touches[0] || ev.changedTouches[0] || ev )  : ev;
2162             var t = ev.relatedTarget;
2163             if (!t) {
2164                 if (ev.type == "mouseout") {
2165                     t = ev.toElement;
2166                 } else if (ev.type == "mouseover") {
2167                     t = ev.fromElement;
2168                 }
2169             }
2170
2171             return this.resolveTextNode(t);
2172         },
2173
2174
2175         getTime: function(ev) {
2176             ev = ev.browserEvent || ev;
2177             ev = ev.touches ? (ev.touches[0] || ev.changedTouches[0] || ev )  : ev;
2178             if (!ev.time) {
2179                 var t = new Date().getTime();
2180                 try {
2181                     ev.time = t;
2182                 } catch(ex) {
2183                     this.lastError = ex;
2184                     return t;
2185                 }
2186             }
2187
2188             return ev.time;
2189         },
2190
2191
2192         stopEvent: function(ev) {
2193             this.stopPropagation(ev);
2194             this.preventDefault(ev);
2195         },
2196
2197
2198         stopPropagation: function(ev) {
2199             ev = ev.browserEvent || ev;
2200             if (ev.stopPropagation) {
2201                 ev.stopPropagation();
2202             } else {
2203                 ev.cancelBubble = true;
2204             }
2205         },
2206
2207
2208         preventDefault: function(ev) {
2209             ev = ev.browserEvent || ev;
2210             if(ev.preventDefault) {
2211                 ev.preventDefault();
2212             } else {
2213                 ev.returnValue = false;
2214             }
2215         },
2216
2217
2218         getEvent: function(e) {
2219             var ev = e || window.event;
2220             if (!ev) {
2221                 var c = this.getEvent.caller;
2222                 while (c) {
2223                     ev = c.arguments[0];
2224                     if (ev && Event == ev.constructor) {
2225                         break;
2226                     }
2227                     c = c.caller;
2228                 }
2229             }
2230             return ev;
2231         },
2232
2233
2234         getCharCode: function(ev) {
2235             ev = ev.browserEvent || ev;
2236             return ev.charCode || ev.keyCode || 0;
2237         },
2238
2239
2240         _getCacheIndex: function(el, eventName, fn) {
2241             for (var i = 0,len = listeners.length; i < len; ++i) {
2242                 var li = listeners[i];
2243                 if (li &&
2244                     li[this.FN] == fn &&
2245                     li[this.EL] == el &&
2246                     li[this.TYPE] == eventName) {
2247                     return i;
2248                 }
2249             }
2250
2251             return -1;
2252         },
2253
2254
2255         elCache: {},
2256
2257
2258         getEl: function(id) {
2259             return document.getElementById(id);
2260         },
2261
2262
2263         clearCache: function() {
2264         },
2265
2266
2267         _load: function(e) {
2268             loadComplete = true;
2269             var EU = Roo.lib.Event;
2270
2271
2272             if (Roo.isIE) {
2273                 EU.doRemove(window, "load", EU._load);
2274             }
2275         },
2276
2277
2278         _tryPreloadAttach: function() {
2279
2280             if (this.locked) {
2281                 return false;
2282             }
2283
2284             this.locked = true;
2285
2286
2287             var tryAgain = !loadComplete;
2288             if (!tryAgain) {
2289                 tryAgain = (retryCount > 0);
2290             }
2291
2292
2293             var notAvail = [];
2294             for (var i = 0,len = onAvailStack.length; i < len; ++i) {
2295                 var item = onAvailStack[i];
2296                 if (item) {
2297                     var el = this.getEl(item.id);
2298
2299                     if (el) {
2300                         if (!item.checkReady ||
2301                             loadComplete ||
2302                             el.nextSibling ||
2303                             (document && document.body)) {
2304
2305                             var scope = el;
2306                             if (item.override) {
2307                                 if (item.override === true) {
2308                                     scope = item.obj;
2309                                 } else {
2310                                     scope = item.override;
2311                                 }
2312                             }
2313                             item.fn.call(scope, item.obj);
2314                             onAvailStack[i] = null;
2315                         }
2316                     } else {
2317                         notAvail.push(item);
2318                     }
2319                 }
2320             }
2321
2322             retryCount = (notAvail.length === 0) ? 0 : retryCount - 1;
2323
2324             if (tryAgain) {
2325
2326                 this.startInterval();
2327             } else {
2328                 clearInterval(this._interval);
2329                 this._interval = null;
2330             }
2331
2332             this.locked = false;
2333
2334             return true;
2335
2336         },
2337
2338
2339         purgeElement: function(el, recurse, eventName) {
2340             var elListeners = this.getListeners(el, eventName);
2341             if (elListeners) {
2342                 for (var i = 0,len = elListeners.length; i < len; ++i) {
2343                     var l = elListeners[i];
2344                     this.removeListener(el, l.type, l.fn);
2345                 }
2346             }
2347
2348             if (recurse && el && el.childNodes) {
2349                 for (i = 0,len = el.childNodes.length; i < len; ++i) {
2350                     this.purgeElement(el.childNodes[i], recurse, eventName);
2351                 }
2352             }
2353         },
2354
2355
2356         getListeners: function(el, eventName) {
2357             var results = [], searchLists;
2358             if (!eventName) {
2359                 searchLists = [listeners, unloadListeners];
2360             } else if (eventName == "unload") {
2361                 searchLists = [unloadListeners];
2362             } else {
2363                 searchLists = [listeners];
2364             }
2365
2366             for (var j = 0; j < searchLists.length; ++j) {
2367                 var searchList = searchLists[j];
2368                 if (searchList && searchList.length > 0) {
2369                     for (var i = 0,len = searchList.length; i < len; ++i) {
2370                         var l = searchList[i];
2371                         if (l && l[this.EL] === el &&
2372                             (!eventName || eventName === l[this.TYPE])) {
2373                             results.push({
2374                                 type:   l[this.TYPE],
2375                                 fn:     l[this.FN],
2376                                 obj:    l[this.OBJ],
2377                                 adjust: l[this.ADJ_SCOPE],
2378                                 index:  i
2379                             });
2380                         }
2381                     }
2382                 }
2383             }
2384
2385             return (results.length) ? results : null;
2386         },
2387
2388
2389         _unload: function(e) {
2390
2391             var EU = Roo.lib.Event, i, j, l, len, index;
2392
2393             for (i = 0,len = unloadListeners.length; i < len; ++i) {
2394                 l = unloadListeners[i];
2395                 if (l) {
2396                     var scope = window;
2397                     if (l[EU.ADJ_SCOPE]) {
2398                         if (l[EU.ADJ_SCOPE] === true) {
2399                             scope = l[EU.OBJ];
2400                         } else {
2401                             scope = l[EU.ADJ_SCOPE];
2402                         }
2403                     }
2404                     l[EU.FN].call(scope, EU.getEvent(e), l[EU.OBJ]);
2405                     unloadListeners[i] = null;
2406                     l = null;
2407                     scope = null;
2408                 }
2409             }
2410
2411             unloadListeners = null;
2412
2413             if (listeners && listeners.length > 0) {
2414                 j = listeners.length;
2415                 while (j) {
2416                     index = j - 1;
2417                     l = listeners[index];
2418                     if (l) {
2419                         EU.removeListener(l[EU.EL], l[EU.TYPE],
2420                                 l[EU.FN], index);
2421                     }
2422                     j = j - 1;
2423                 }
2424                 l = null;
2425
2426                 EU.clearCache();
2427             }
2428
2429             EU.doRemove(window, "unload", EU._unload);
2430
2431         },
2432
2433
2434         getScroll: function() {
2435             var dd = document.documentElement, db = document.body;
2436             if (dd && (dd.scrollTop || dd.scrollLeft)) {
2437                 return [dd.scrollTop, dd.scrollLeft];
2438             } else if (db) {
2439                 return [db.scrollTop, db.scrollLeft];
2440             } else {
2441                 return [0, 0];
2442             }
2443         },
2444
2445
2446         doAdd: function () {
2447             if (window.addEventListener) {
2448                 return function(el, eventName, fn, capture) {
2449                     el.addEventListener(eventName, fn, (capture));
2450                 };
2451             } else if (window.attachEvent) {
2452                 return function(el, eventName, fn, capture) {
2453                     el.attachEvent("on" + eventName, fn);
2454                 };
2455             } else {
2456                 return function() {
2457                 };
2458             }
2459         }(),
2460
2461
2462         doRemove: function() {
2463             if (window.removeEventListener) {
2464                 return function (el, eventName, fn, capture) {
2465                     el.removeEventListener(eventName, fn, (capture));
2466                 };
2467             } else if (window.detachEvent) {
2468                 return function (el, eventName, fn) {
2469                     el.detachEvent("on" + eventName, fn);
2470                 };
2471             } else {
2472                 return function() {
2473                 };
2474             }
2475         }()
2476     };
2477     
2478 }();
2479 (function() {     
2480    
2481     var E = Roo.lib.Event;
2482     E.on = E.addListener;
2483     E.un = E.removeListener;
2484
2485     if (document && document.body) {
2486         E._load();
2487     } else {
2488         E.doAdd(window, "load", E._load);
2489     }
2490     E.doAdd(window, "unload", E._unload);
2491     E._tryPreloadAttach();
2492 })();
2493
2494 /*
2495  * Portions of this file are based on pieces of Yahoo User Interface Library
2496  * Copyright (c) 2007, Yahoo! Inc. All rights reserved.
2497  * YUI licensed under the BSD License:
2498  * http://developer.yahoo.net/yui/license.txt
2499  * <script type="text/javascript">
2500  *
2501  */
2502
2503 (function() {
2504     /**
2505      * @class Roo.lib.Ajax
2506      *
2507      */
2508     Roo.lib.Ajax = {
2509         /**
2510          * @static 
2511          */
2512         request : function(method, uri, cb, data, options) {
2513             if(options){
2514                 var hs = options.headers;
2515                 if(hs){
2516                     for(var h in hs){
2517                         if(hs.hasOwnProperty(h)){
2518                             this.initHeader(h, hs[h], false);
2519                         }
2520                     }
2521                 }
2522                 if(options.xmlData){
2523                     this.initHeader('Content-Type', 'text/xml', false);
2524                     method = 'POST';
2525                     data = options.xmlData;
2526                 }
2527             }
2528
2529             return this.asyncRequest(method, uri, cb, data);
2530         },
2531
2532         serializeForm : function(form) {
2533             if(typeof form == 'string') {
2534                 form = (document.getElementById(form) || document.forms[form]);
2535             }
2536
2537             var el, name, val, disabled, data = '', hasSubmit = false;
2538             for (var i = 0; i < form.elements.length; i++) {
2539                 el = form.elements[i];
2540                 disabled = form.elements[i].disabled;
2541                 name = form.elements[i].name;
2542                 val = form.elements[i].value;
2543
2544                 if (!disabled && name){
2545                     switch (el.type)
2546                             {
2547                         case 'select-one':
2548                         case 'select-multiple':
2549                             for (var j = 0; j < el.options.length; j++) {
2550                                 if (el.options[j].selected) {
2551                                     if (Roo.isIE) {
2552                                         data += Roo.encodeURIComponent(name) + '=' + Roo.encodeURIComponent(el.options[j].attributes['value'].specified ? el.options[j].value : el.options[j].text) + '&';
2553                                     }
2554                                     else {
2555                                         data += Roo.encodeURIComponent(name) + '=' + Roo.encodeURIComponent(el.options[j].hasAttribute('value') ? el.options[j].value : el.options[j].text) + '&';
2556                                     }
2557                                 }
2558                             }
2559                             break;
2560                         case 'radio':
2561                         case 'checkbox':
2562                             if (el.checked) {
2563                                 data += Roo.encodeURIComponent(name) + '=' + Roo.encodeURIComponent(val) + '&';
2564                             }
2565                             break;
2566                         case 'file':
2567
2568                         case undefined:
2569
2570                         case 'reset':
2571
2572                         case 'button':
2573
2574                             break;
2575                         case 'submit':
2576                             if(hasSubmit == false) {
2577                                 data += Roo.encodeURIComponent(name) + '=' + Roo.encodeURIComponent(val) + '&';
2578                                 hasSubmit = true;
2579                             }
2580                             break;
2581                         default:
2582                             data += Roo.encodeURIComponent(name) + '=' + Roo.encodeURIComponent(val) + '&';
2583                             break;
2584                     }
2585                 }
2586             }
2587             data = data.substr(0, data.length - 1);
2588             return data;
2589         },
2590
2591         headers:{},
2592
2593         hasHeaders:false,
2594
2595         useDefaultHeader:true,
2596
2597         defaultPostHeader:'application/x-www-form-urlencoded',
2598
2599         useDefaultXhrHeader:true,
2600
2601         defaultXhrHeader:'XMLHttpRequest',
2602
2603         hasDefaultHeaders:true,
2604
2605         defaultHeaders:{},
2606
2607         poll:{},
2608
2609         timeout:{},
2610
2611         pollInterval:50,
2612
2613         transactionId:0,
2614
2615         setProgId:function(id)
2616         {
2617             this.activeX.unshift(id);
2618         },
2619
2620         setDefaultPostHeader:function(b)
2621         {
2622             this.useDefaultHeader = b;
2623         },
2624
2625         setDefaultXhrHeader:function(b)
2626         {
2627             this.useDefaultXhrHeader = b;
2628         },
2629
2630         setPollingInterval:function(i)
2631         {
2632             if (typeof i == 'number' && isFinite(i)) {
2633                 this.pollInterval = i;
2634             }
2635         },
2636
2637         createXhrObject:function(transactionId)
2638         {
2639             var obj,http;
2640             try
2641             {
2642
2643                 http = new XMLHttpRequest();
2644
2645                 obj = { conn:http, tId:transactionId };
2646             }
2647             catch(e)
2648             {
2649                 for (var i = 0; i < this.activeX.length; ++i) {
2650                     try
2651                     {
2652
2653                         http = new ActiveXObject(this.activeX[i]);
2654
2655                         obj = { conn:http, tId:transactionId };
2656                         break;
2657                     }
2658                     catch(e) {
2659                     }
2660                 }
2661             }
2662             finally
2663             {
2664                 return obj;
2665             }
2666         },
2667
2668         getConnectionObject:function()
2669         {
2670             var o;
2671             var tId = this.transactionId;
2672
2673             try
2674             {
2675                 o = this.createXhrObject(tId);
2676                 if (o) {
2677                     this.transactionId++;
2678                 }
2679             }
2680             catch(e) {
2681             }
2682             finally
2683             {
2684                 return o;
2685             }
2686         },
2687
2688         asyncRequest:function(method, uri, callback, postData)
2689         {
2690             var o = this.getConnectionObject();
2691
2692             if (!o) {
2693                 return null;
2694             }
2695             else {
2696                 o.conn.open(method, uri, true);
2697
2698                 if (this.useDefaultXhrHeader) {
2699                     if (!this.defaultHeaders['X-Requested-With']) {
2700                         this.initHeader('X-Requested-With', this.defaultXhrHeader, true);
2701                     }
2702                 }
2703
2704                 if(postData && this.useDefaultHeader){
2705                     this.initHeader('Content-Type', this.defaultPostHeader);
2706                 }
2707
2708                  if (this.hasDefaultHeaders || this.hasHeaders) {
2709                     this.setHeader(o);
2710                 }
2711
2712                 this.handleReadyState(o, callback);
2713                 o.conn.send(postData || null);
2714
2715                 return o;
2716             }
2717         },
2718
2719         handleReadyState:function(o, callback)
2720         {
2721             var oConn = this;
2722
2723             if (callback && callback.timeout) {
2724                 
2725                 this.timeout[o.tId] = window.setTimeout(function() {
2726                     oConn.abort(o, callback, true);
2727                 }, callback.timeout);
2728             }
2729
2730             this.poll[o.tId] = window.setInterval(
2731                     function() {
2732                         if (o.conn && o.conn.readyState == 4) {
2733                             window.clearInterval(oConn.poll[o.tId]);
2734                             delete oConn.poll[o.tId];
2735
2736                             if(callback && callback.timeout) {
2737                                 window.clearTimeout(oConn.timeout[o.tId]);
2738                                 delete oConn.timeout[o.tId];
2739                             }
2740
2741                             oConn.handleTransactionResponse(o, callback);
2742                         }
2743                     }
2744                     , this.pollInterval);
2745         },
2746
2747         handleTransactionResponse:function(o, callback, isAbort)
2748         {
2749
2750             if (!callback) {
2751                 this.releaseObject(o);
2752                 return;
2753             }
2754
2755             var httpStatus, responseObject;
2756
2757             try
2758             {
2759                 if (o.conn.status !== undefined && o.conn.status != 0) {
2760                     httpStatus = o.conn.status;
2761                 }
2762                 else {
2763                     httpStatus = 13030;
2764                 }
2765             }
2766             catch(e) {
2767
2768
2769                 httpStatus = 13030;
2770             }
2771
2772             if (httpStatus >= 200 && httpStatus < 300) {
2773                 responseObject = this.createResponseObject(o, callback.argument);
2774                 if (callback.success) {
2775                     if (!callback.scope) {
2776                         callback.success(responseObject);
2777                     }
2778                     else {
2779
2780
2781                         callback.success.apply(callback.scope, [responseObject]);
2782                     }
2783                 }
2784             }
2785             else {
2786                 switch (httpStatus) {
2787
2788                     case 12002:
2789                     case 12029:
2790                     case 12030:
2791                     case 12031:
2792                     case 12152:
2793                     case 13030:
2794                         responseObject = this.createExceptionObject(o.tId, callback.argument, (isAbort ? isAbort : false));
2795                         if (callback.failure) {
2796                             if (!callback.scope) {
2797                                 callback.failure(responseObject);
2798                             }
2799                             else {
2800                                 callback.failure.apply(callback.scope, [responseObject]);
2801                             }
2802                         }
2803                         break;
2804                     default:
2805                         responseObject = this.createResponseObject(o, callback.argument);
2806                         if (callback.failure) {
2807                             if (!callback.scope) {
2808                                 callback.failure(responseObject);
2809                             }
2810                             else {
2811                                 callback.failure.apply(callback.scope, [responseObject]);
2812                             }
2813                         }
2814                 }
2815             }
2816
2817             this.releaseObject(o);
2818             responseObject = null;
2819         },
2820
2821         createResponseObject:function(o, callbackArg)
2822         {
2823             var obj = {};
2824             var headerObj = {};
2825
2826             try
2827             {
2828                 var headerStr = o.conn.getAllResponseHeaders();
2829                 var header = headerStr.split('\n');
2830                 for (var i = 0; i < header.length; i++) {
2831                     var delimitPos = header[i].indexOf(':');
2832                     if (delimitPos != -1) {
2833                         headerObj[header[i].substring(0, delimitPos)] = header[i].substring(delimitPos + 2);
2834                     }
2835                 }
2836             }
2837             catch(e) {
2838             }
2839
2840             obj.tId = o.tId;
2841             obj.status = o.conn.status;
2842             obj.statusText = o.conn.statusText;
2843             obj.getResponseHeader = headerObj;
2844             obj.getAllResponseHeaders = headerStr;
2845             obj.responseText = o.conn.responseText;
2846             obj.responseXML = o.conn.responseXML;
2847
2848             if (typeof callbackArg !== undefined) {
2849                 obj.argument = callbackArg;
2850             }
2851
2852             return obj;
2853         },
2854
2855         createExceptionObject:function(tId, callbackArg, isAbort)
2856         {
2857             var COMM_CODE = 0;
2858             var COMM_ERROR = 'communication failure';
2859             var ABORT_CODE = -1;
2860             var ABORT_ERROR = 'transaction aborted';
2861
2862             var obj = {};
2863
2864             obj.tId = tId;
2865             if (isAbort) {
2866                 obj.status = ABORT_CODE;
2867                 obj.statusText = ABORT_ERROR;
2868             }
2869             else {
2870                 obj.status = COMM_CODE;
2871                 obj.statusText = COMM_ERROR;
2872             }
2873
2874             if (callbackArg) {
2875                 obj.argument = callbackArg;
2876             }
2877
2878             return obj;
2879         },
2880
2881         initHeader:function(label, value, isDefault)
2882         {
2883             var headerObj = (isDefault) ? this.defaultHeaders : this.headers;
2884
2885             if (headerObj[label] === undefined) {
2886                 headerObj[label] = value;
2887             }
2888             else {
2889
2890
2891                 headerObj[label] = value + "," + headerObj[label];
2892             }
2893
2894             if (isDefault) {
2895                 this.hasDefaultHeaders = true;
2896             }
2897             else {
2898                 this.hasHeaders = true;
2899             }
2900         },
2901
2902
2903         setHeader:function(o)
2904         {
2905             if (this.hasDefaultHeaders) {
2906                 for (var prop in this.defaultHeaders) {
2907                     if (this.defaultHeaders.hasOwnProperty(prop)) {
2908                         o.conn.setRequestHeader(prop, this.defaultHeaders[prop]);
2909                     }
2910                 }
2911             }
2912
2913             if (this.hasHeaders) {
2914                 for (var prop in this.headers) {
2915                     if (this.headers.hasOwnProperty(prop)) {
2916                         o.conn.setRequestHeader(prop, this.headers[prop]);
2917                     }
2918                 }
2919                 this.headers = {};
2920                 this.hasHeaders = false;
2921             }
2922         },
2923
2924         resetDefaultHeaders:function() {
2925             delete this.defaultHeaders;
2926             this.defaultHeaders = {};
2927             this.hasDefaultHeaders = false;
2928         },
2929
2930         abort:function(o, callback, isTimeout)
2931         {
2932             if(this.isCallInProgress(o)) {
2933                 o.conn.abort();
2934                 window.clearInterval(this.poll[o.tId]);
2935                 delete this.poll[o.tId];
2936                 if (isTimeout) {
2937                     delete this.timeout[o.tId];
2938                 }
2939
2940                 this.handleTransactionResponse(o, callback, true);
2941
2942                 return true;
2943             }
2944             else {
2945                 return false;
2946             }
2947         },
2948
2949
2950         isCallInProgress:function(o)
2951         {
2952             if (o && o.conn) {
2953                 return o.conn.readyState != 4 && o.conn.readyState != 0;
2954             }
2955             else {
2956
2957                 return false;
2958             }
2959         },
2960
2961
2962         releaseObject:function(o)
2963         {
2964
2965             o.conn = null;
2966
2967             o = null;
2968         },
2969
2970         activeX:[
2971         'MSXML2.XMLHTTP.3.0',
2972         'MSXML2.XMLHTTP',
2973         'Microsoft.XMLHTTP'
2974         ]
2975
2976
2977     };
2978 })();/*
2979  * Portions of this file are based on pieces of Yahoo User Interface Library
2980  * Copyright (c) 2007, Yahoo! Inc. All rights reserved.
2981  * YUI licensed under the BSD License:
2982  * http://developer.yahoo.net/yui/license.txt
2983  * <script type="text/javascript">
2984  *
2985  */
2986
2987 Roo.lib.Region = function(t, r, b, l) {
2988     this.top = t;
2989     this[1] = t;
2990     this.right = r;
2991     this.bottom = b;
2992     this.left = l;
2993     this[0] = l;
2994 };
2995
2996
2997 Roo.lib.Region.prototype = {
2998     contains : function(region) {
2999         return ( region.left >= this.left &&
3000                  region.right <= this.right &&
3001                  region.top >= this.top &&
3002                  region.bottom <= this.bottom    );
3003
3004     },
3005
3006     getArea : function() {
3007         return ( (this.bottom - this.top) * (this.right - this.left) );
3008     },
3009
3010     intersect : function(region) {
3011         var t = Math.max(this.top, region.top);
3012         var r = Math.min(this.right, region.right);
3013         var b = Math.min(this.bottom, region.bottom);
3014         var l = Math.max(this.left, region.left);
3015
3016         if (b >= t && r >= l) {
3017             return new Roo.lib.Region(t, r, b, l);
3018         } else {
3019             return null;
3020         }
3021     },
3022     union : function(region) {
3023         var t = Math.min(this.top, region.top);
3024         var r = Math.max(this.right, region.right);
3025         var b = Math.max(this.bottom, region.bottom);
3026         var l = Math.min(this.left, region.left);
3027
3028         return new Roo.lib.Region(t, r, b, l);
3029     },
3030
3031     adjust : function(t, l, b, r) {
3032         this.top += t;
3033         this.left += l;
3034         this.right += r;
3035         this.bottom += b;
3036         return this;
3037     }
3038 };
3039
3040 Roo.lib.Region.getRegion = function(el) {
3041     var p = Roo.lib.Dom.getXY(el);
3042
3043     var t = p[1];
3044     var r = p[0] + el.offsetWidth;
3045     var b = p[1] + el.offsetHeight;
3046     var l = p[0];
3047
3048     return new Roo.lib.Region(t, r, b, l);
3049 };
3050 /*
3051  * Portions of this file are based on pieces of Yahoo User Interface Library
3052  * Copyright (c) 2007, Yahoo! Inc. All rights reserved.
3053  * YUI licensed under the BSD License:
3054  * http://developer.yahoo.net/yui/license.txt
3055  * <script type="text/javascript">
3056  *
3057  */
3058 //@@dep Roo.lib.Region
3059
3060
3061 Roo.lib.Point = function(x, y) {
3062     if (x instanceof Array) {
3063         y = x[1];
3064         x = x[0];
3065     }
3066     this.x = this.right = this.left = this[0] = x;
3067     this.y = this.top = this.bottom = this[1] = y;
3068 };
3069
3070 Roo.lib.Point.prototype = new Roo.lib.Region();
3071 /*
3072  * Portions of this file are based on pieces of Yahoo User Interface Library
3073  * Copyright (c) 2007, Yahoo! Inc. All rights reserved.
3074  * YUI licensed under the BSD License:
3075  * http://developer.yahoo.net/yui/license.txt
3076  * <script type="text/javascript">
3077  *
3078  */
3079  
3080 (function() {   
3081
3082     Roo.lib.Anim = {
3083         scroll : function(el, args, duration, easing, cb, scope) {
3084             this.run(el, args, duration, easing, cb, scope, Roo.lib.Scroll);
3085         },
3086
3087         motion : function(el, args, duration, easing, cb, scope) {
3088             this.run(el, args, duration, easing, cb, scope, Roo.lib.Motion);
3089         },
3090
3091         color : function(el, args, duration, easing, cb, scope) {
3092             this.run(el, args, duration, easing, cb, scope, Roo.lib.ColorAnim);
3093         },
3094
3095         run : function(el, args, duration, easing, cb, scope, type) {
3096             type = type || Roo.lib.AnimBase;
3097             if (typeof easing == "string") {
3098                 easing = Roo.lib.Easing[easing];
3099             }
3100             var anim = new type(el, args, duration, easing);
3101             anim.animateX(function() {
3102                 Roo.callback(cb, scope);
3103             });
3104             return anim;
3105         }
3106     };
3107 })();/*
3108  * Portions of this file are based on pieces of Yahoo User Interface Library
3109  * Copyright (c) 2007, Yahoo! Inc. All rights reserved.
3110  * YUI licensed under the BSD License:
3111  * http://developer.yahoo.net/yui/license.txt
3112  * <script type="text/javascript">
3113  *
3114  */
3115
3116 (function() {    
3117     var libFlyweight;
3118     
3119     function fly(el) {
3120         if (!libFlyweight) {
3121             libFlyweight = new Roo.Element.Flyweight();
3122         }
3123         libFlyweight.dom = el;
3124         return libFlyweight;
3125     }
3126
3127     // since this uses fly! - it cant be in DOM (which does not have fly yet..)
3128     
3129    
3130     
3131     Roo.lib.AnimBase = function(el, attributes, duration, method) {
3132         if (el) {
3133             this.init(el, attributes, duration, method);
3134         }
3135     };
3136
3137     Roo.lib.AnimBase.fly = fly;
3138     
3139     
3140     
3141     Roo.lib.AnimBase.prototype = {
3142
3143         toString: function() {
3144             var el = this.getEl();
3145             var id = el.id || el.tagName;
3146             return ("Anim " + id);
3147         },
3148
3149         patterns: {
3150             noNegatives:        /width|height|opacity|padding/i,
3151             offsetAttribute:  /^((width|height)|(top|left))$/,
3152             defaultUnit:        /width|height|top$|bottom$|left$|right$/i,
3153             offsetUnit:         /\d+(em|%|en|ex|pt|in|cm|mm|pc)$/i
3154         },
3155
3156
3157         doMethod: function(attr, start, end) {
3158             return this.method(this.currentFrame, start, end - start, this.totalFrames);
3159         },
3160
3161
3162         setAttribute: function(attr, val, unit) {
3163             if (this.patterns.noNegatives.test(attr)) {
3164                 val = (val > 0) ? val : 0;
3165             }
3166
3167             Roo.fly(this.getEl(), '_anim').setStyle(attr, val + unit);
3168         },
3169
3170
3171         getAttribute: function(attr) {
3172             var el = this.getEl();
3173             var val = fly(el).getStyle(attr);
3174
3175             if (val !== 'auto' && !this.patterns.offsetUnit.test(val)) {
3176                 return parseFloat(val);
3177             }
3178
3179             var a = this.patterns.offsetAttribute.exec(attr) || [];
3180             var pos = !!( a[3] );
3181             var box = !!( a[2] );
3182
3183
3184             if (box || (fly(el).getStyle('position') == 'absolute' && pos)) {
3185                 val = el['offset' + a[0].charAt(0).toUpperCase() + a[0].substr(1)];
3186             } else {
3187                 val = 0;
3188             }
3189
3190             return val;
3191         },
3192
3193
3194         getDefaultUnit: function(attr) {
3195             if (this.patterns.defaultUnit.test(attr)) {
3196                 return 'px';
3197             }
3198
3199             return '';
3200         },
3201
3202         animateX : function(callback, scope) {
3203             var f = function() {
3204                 this.onComplete.removeListener(f);
3205                 if (typeof callback == "function") {
3206                     callback.call(scope || this, this);
3207                 }
3208             };
3209             this.onComplete.addListener(f, this);
3210             this.animate();
3211         },
3212
3213
3214         setRuntimeAttribute: function(attr) {
3215             var start;
3216             var end;
3217             var attributes = this.attributes;
3218
3219             this.runtimeAttributes[attr] = {};
3220
3221             var isset = function(prop) {
3222                 return (typeof prop !== 'undefined');
3223             };
3224
3225             if (!isset(attributes[attr]['to']) && !isset(attributes[attr]['by'])) {
3226                 return false;
3227             }
3228
3229             start = ( isset(attributes[attr]['from']) ) ? attributes[attr]['from'] : this.getAttribute(attr);
3230
3231
3232             if (isset(attributes[attr]['to'])) {
3233                 end = attributes[attr]['to'];
3234             } else if (isset(attributes[attr]['by'])) {
3235                 if (start.constructor == Array) {
3236                     end = [];
3237                     for (var i = 0, len = start.length; i < len; ++i) {
3238                         end[i] = start[i] + attributes[attr]['by'][i];
3239                     }
3240                 } else {
3241                     end = start + attributes[attr]['by'];
3242                 }
3243             }
3244
3245             this.runtimeAttributes[attr].start = start;
3246             this.runtimeAttributes[attr].end = end;
3247
3248
3249             this.runtimeAttributes[attr].unit = ( isset(attributes[attr].unit) ) ? attributes[attr]['unit'] : this.getDefaultUnit(attr);
3250         },
3251
3252
3253         init: function(el, attributes, duration, method) {
3254
3255             var isAnimated = false;
3256
3257
3258             var startTime = null;
3259
3260
3261             var actualFrames = 0;
3262
3263
3264             el = Roo.getDom(el);
3265
3266
3267             this.attributes = attributes || {};
3268
3269
3270             this.duration = duration || 1;
3271
3272
3273             this.method = method || Roo.lib.Easing.easeNone;
3274
3275
3276             this.useSeconds = true;
3277
3278
3279             this.currentFrame = 0;
3280
3281
3282             this.totalFrames = Roo.lib.AnimMgr.fps;
3283
3284
3285             this.getEl = function() {
3286                 return el;
3287             };
3288
3289
3290             this.isAnimated = function() {
3291                 return isAnimated;
3292             };
3293
3294
3295             this.getStartTime = function() {
3296                 return startTime;
3297             };
3298
3299             this.runtimeAttributes = {};
3300
3301
3302             this.animate = function() {
3303                 if (this.isAnimated()) {
3304                     return false;
3305                 }
3306
3307                 this.currentFrame = 0;
3308
3309                 this.totalFrames = ( this.useSeconds ) ? Math.ceil(Roo.lib.AnimMgr.fps * this.duration) : this.duration;
3310
3311                 Roo.lib.AnimMgr.registerElement(this);
3312             };
3313
3314
3315             this.stop = function(finish) {
3316                 if (finish) {
3317                     this.currentFrame = this.totalFrames;
3318                     this._onTween.fire();
3319                 }
3320                 Roo.lib.AnimMgr.stop(this);
3321             };
3322
3323             var onStart = function() {
3324                 this.onStart.fire();
3325
3326                 this.runtimeAttributes = {};
3327                 for (var attr in this.attributes) {
3328                     this.setRuntimeAttribute(attr);
3329                 }
3330
3331                 isAnimated = true;
3332                 actualFrames = 0;
3333                 startTime = new Date();
3334             };
3335
3336
3337             var onTween = function() {
3338                 var data = {
3339                     duration: new Date() - this.getStartTime(),
3340                     currentFrame: this.currentFrame
3341                 };
3342
3343                 data.toString = function() {
3344                     return (
3345                             'duration: ' + data.duration +
3346                             ', currentFrame: ' + data.currentFrame
3347                             );
3348                 };
3349
3350                 this.onTween.fire(data);
3351
3352                 var runtimeAttributes = this.runtimeAttributes;
3353
3354                 for (var attr in runtimeAttributes) {
3355                     this.setAttribute(attr, this.doMethod(attr, runtimeAttributes[attr].start, runtimeAttributes[attr].end), runtimeAttributes[attr].unit);
3356                 }
3357
3358                 actualFrames += 1;
3359             };
3360
3361             var onComplete = function() {
3362                 var actual_duration = (new Date() - startTime) / 1000 ;
3363
3364                 var data = {
3365                     duration: actual_duration,
3366                     frames: actualFrames,
3367                     fps: actualFrames / actual_duration
3368                 };
3369
3370                 data.toString = function() {
3371                     return (
3372                             'duration: ' + data.duration +
3373                             ', frames: ' + data.frames +
3374                             ', fps: ' + data.fps
3375                             );
3376                 };
3377
3378                 isAnimated = false;
3379                 actualFrames = 0;
3380                 this.onComplete.fire(data);
3381             };
3382
3383
3384             this._onStart = new Roo.util.Event(this);
3385             this.onStart = new Roo.util.Event(this);
3386             this.onTween = new Roo.util.Event(this);
3387             this._onTween = new Roo.util.Event(this);
3388             this.onComplete = new Roo.util.Event(this);
3389             this._onComplete = new Roo.util.Event(this);
3390             this._onStart.addListener(onStart);
3391             this._onTween.addListener(onTween);
3392             this._onComplete.addListener(onComplete);
3393         }
3394     };
3395 })();
3396 /*
3397  * Portions of this file are based on pieces of Yahoo User Interface Library
3398  * Copyright (c) 2007, Yahoo! Inc. All rights reserved.
3399  * YUI licensed under the BSD License:
3400  * http://developer.yahoo.net/yui/license.txt
3401  * <script type="text/javascript">
3402  *
3403  */
3404
3405 Roo.lib.AnimMgr = new function() {
3406
3407     var thread = null;
3408
3409
3410     var queue = [];
3411
3412
3413     var tweenCount = 0;
3414
3415
3416     this.fps = 1000;
3417
3418
3419     this.delay = 1;
3420
3421
3422     this.registerElement = function(tween) {
3423         queue[queue.length] = tween;
3424         tweenCount += 1;
3425         tween._onStart.fire();
3426         this.start();
3427     };
3428
3429
3430     this.unRegister = function(tween, index) {
3431         tween._onComplete.fire();
3432         index = index || getIndex(tween);
3433         if (index != -1) {
3434             queue.splice(index, 1);
3435         }
3436
3437         tweenCount -= 1;
3438         if (tweenCount <= 0) {
3439             this.stop();
3440         }
3441     };
3442
3443
3444     this.start = function() {
3445         if (thread === null) {
3446             thread = setInterval(this.run, this.delay);
3447         }
3448     };
3449
3450
3451     this.stop = function(tween) {
3452         if (!tween) {
3453             clearInterval(thread);
3454
3455             for (var i = 0, len = queue.length; i < len; ++i) {
3456                 if (queue[0].isAnimated()) {
3457                     this.unRegister(queue[0], 0);
3458                 }
3459             }
3460
3461             queue = [];
3462             thread = null;
3463             tweenCount = 0;
3464         }
3465         else {
3466             this.unRegister(tween);
3467         }
3468     };
3469
3470
3471     this.run = function() {
3472         for (var i = 0, len = queue.length; i < len; ++i) {
3473             var tween = queue[i];
3474             if (!tween || !tween.isAnimated()) {
3475                 continue;
3476             }
3477
3478             if (tween.currentFrame < tween.totalFrames || tween.totalFrames === null)
3479             {
3480                 tween.currentFrame += 1;
3481
3482                 if (tween.useSeconds) {
3483                     correctFrame(tween);
3484                 }
3485                 tween._onTween.fire();
3486             }
3487             else {
3488                 Roo.lib.AnimMgr.stop(tween, i);
3489             }
3490         }
3491     };
3492
3493     var getIndex = function(anim) {
3494         for (var i = 0, len = queue.length; i < len; ++i) {
3495             if (queue[i] == anim) {
3496                 return i;
3497             }
3498         }
3499         return -1;
3500     };
3501
3502
3503     var correctFrame = function(tween) {
3504         var frames = tween.totalFrames;
3505         var frame = tween.currentFrame;
3506         var expected = (tween.currentFrame * tween.duration * 1000 / tween.totalFrames);
3507         var elapsed = (new Date() - tween.getStartTime());
3508         var tweak = 0;
3509
3510         if (elapsed < tween.duration * 1000) {
3511             tweak = Math.round((elapsed / expected - 1) * tween.currentFrame);
3512         } else {
3513             tweak = frames - (frame + 1);
3514         }
3515         if (tweak > 0 && isFinite(tweak)) {
3516             if (tween.currentFrame + tweak >= frames) {
3517                 tweak = frames - (frame + 1);
3518             }
3519
3520             tween.currentFrame += tweak;
3521         }
3522     };
3523 };
3524
3525     /*
3526  * Portions of this file are based on pieces of Yahoo User Interface Library
3527  * Copyright (c) 2007, Yahoo! Inc. All rights reserved.
3528  * YUI licensed under the BSD License:
3529  * http://developer.yahoo.net/yui/license.txt
3530  * <script type="text/javascript">
3531  *
3532  */
3533 Roo.lib.Bezier = new function() {
3534
3535         this.getPosition = function(points, t) {
3536             var n = points.length;
3537             var tmp = [];
3538
3539             for (var i = 0; i < n; ++i) {
3540                 tmp[i] = [points[i][0], points[i][1]];
3541             }
3542
3543             for (var j = 1; j < n; ++j) {
3544                 for (i = 0; i < n - j; ++i) {
3545                     tmp[i][0] = (1 - t) * tmp[i][0] + t * tmp[parseInt(i + 1, 10)][0];
3546                     tmp[i][1] = (1 - t) * tmp[i][1] + t * tmp[parseInt(i + 1, 10)][1];
3547                 }
3548             }
3549
3550             return [ tmp[0][0], tmp[0][1] ];
3551
3552         };
3553     };/*
3554  * Portions of this file are based on pieces of Yahoo User Interface Library
3555  * Copyright (c) 2007, Yahoo! Inc. All rights reserved.
3556  * YUI licensed under the BSD License:
3557  * http://developer.yahoo.net/yui/license.txt
3558  * <script type="text/javascript">
3559  *
3560  */
3561 (function() {
3562
3563     Roo.lib.ColorAnim = function(el, attributes, duration, method) {
3564         Roo.lib.ColorAnim.superclass.constructor.call(this, el, attributes, duration, method);
3565     };
3566
3567     Roo.extend(Roo.lib.ColorAnim, Roo.lib.AnimBase);
3568
3569     var fly = Roo.lib.AnimBase.fly;
3570     var Y = Roo.lib;
3571     var superclass = Y.ColorAnim.superclass;
3572     var proto = Y.ColorAnim.prototype;
3573
3574     proto.toString = function() {
3575         var el = this.getEl();
3576         var id = el.id || el.tagName;
3577         return ("ColorAnim " + id);
3578     };
3579
3580     proto.patterns.color = /color$/i;
3581     proto.patterns.rgb = /^rgb\(([0-9]+)\s*,\s*([0-9]+)\s*,\s*([0-9]+)\)$/i;
3582     proto.patterns.hex = /^#?([0-9A-F]{2})([0-9A-F]{2})([0-9A-F]{2})$/i;
3583     proto.patterns.hex3 = /^#?([0-9A-F]{1})([0-9A-F]{1})([0-9A-F]{1})$/i;
3584     proto.patterns.transparent = /^transparent|rgba\(0, 0, 0, 0\)$/;
3585
3586
3587     proto.parseColor = function(s) {
3588         if (s.length == 3) {
3589             return s;
3590         }
3591
3592         var c = this.patterns.hex.exec(s);
3593         if (c && c.length == 4) {
3594             return [ parseInt(c[1], 16), parseInt(c[2], 16), parseInt(c[3], 16) ];
3595         }
3596
3597         c = this.patterns.rgb.exec(s);
3598         if (c && c.length == 4) {
3599             return [ parseInt(c[1], 10), parseInt(c[2], 10), parseInt(c[3], 10) ];
3600         }
3601
3602         c = this.patterns.hex3.exec(s);
3603         if (c && c.length == 4) {
3604             return [ parseInt(c[1] + c[1], 16), parseInt(c[2] + c[2], 16), parseInt(c[3] + c[3], 16) ];
3605         }
3606
3607         return null;
3608     };
3609     // since this uses fly! - it cant be in ColorAnim (which does not have fly yet..)
3610     proto.getAttribute = function(attr) {
3611         var el = this.getEl();
3612         if (this.patterns.color.test(attr)) {
3613             var val = fly(el).getStyle(attr);
3614
3615             if (this.patterns.transparent.test(val)) {
3616                 var parent = el.parentNode;
3617                 val = fly(parent).getStyle(attr);
3618
3619                 while (parent && this.patterns.transparent.test(val)) {
3620                     parent = parent.parentNode;
3621                     val = fly(parent).getStyle(attr);
3622                     if (parent.tagName.toUpperCase() == 'HTML') {
3623                         val = '#fff';
3624                     }
3625                 }
3626             }
3627         } else {
3628             val = superclass.getAttribute.call(this, attr);
3629         }
3630
3631         return val;
3632     };
3633     proto.getAttribute = function(attr) {
3634         var el = this.getEl();
3635         if (this.patterns.color.test(attr)) {
3636             var val = fly(el).getStyle(attr);
3637
3638             if (this.patterns.transparent.test(val)) {
3639                 var parent = el.parentNode;
3640                 val = fly(parent).getStyle(attr);
3641
3642                 while (parent && this.patterns.transparent.test(val)) {
3643                     parent = parent.parentNode;
3644                     val = fly(parent).getStyle(attr);
3645                     if (parent.tagName.toUpperCase() == 'HTML') {
3646                         val = '#fff';
3647                     }
3648                 }
3649             }
3650         } else {
3651             val = superclass.getAttribute.call(this, attr);
3652         }
3653
3654         return val;
3655     };
3656
3657     proto.doMethod = function(attr, start, end) {
3658         var val;
3659
3660         if (this.patterns.color.test(attr)) {
3661             val = [];
3662             for (var i = 0, len = start.length; i < len; ++i) {
3663                 val[i] = superclass.doMethod.call(this, attr, start[i], end[i]);
3664             }
3665
3666             val = 'rgb(' + Math.floor(val[0]) + ',' + Math.floor(val[1]) + ',' + Math.floor(val[2]) + ')';
3667         }
3668         else {
3669             val = superclass.doMethod.call(this, attr, start, end);
3670         }
3671
3672         return val;
3673     };
3674
3675     proto.setRuntimeAttribute = function(attr) {
3676         superclass.setRuntimeAttribute.call(this, attr);
3677
3678         if (this.patterns.color.test(attr)) {
3679             var attributes = this.attributes;
3680             var start = this.parseColor(this.runtimeAttributes[attr].start);
3681             var end = this.parseColor(this.runtimeAttributes[attr].end);
3682
3683             if (typeof attributes[attr]['to'] === 'undefined' && typeof attributes[attr]['by'] !== 'undefined') {
3684                 end = this.parseColor(attributes[attr].by);
3685
3686                 for (var i = 0, len = start.length; i < len; ++i) {
3687                     end[i] = start[i] + end[i];
3688                 }
3689             }
3690
3691             this.runtimeAttributes[attr].start = start;
3692             this.runtimeAttributes[attr].end = end;
3693         }
3694     };
3695 })();
3696
3697 /*
3698  * Portions of this file are based on pieces of Yahoo User Interface Library
3699  * Copyright (c) 2007, Yahoo! Inc. All rights reserved.
3700  * YUI licensed under the BSD License:
3701  * http://developer.yahoo.net/yui/license.txt
3702  * <script type="text/javascript">
3703  *
3704  */
3705 Roo.lib.Easing = {
3706
3707
3708     easeNone: function (t, b, c, d) {
3709         return c * t / d + b;
3710     },
3711
3712
3713     easeIn: function (t, b, c, d) {
3714         return c * (t /= d) * t + b;
3715     },
3716
3717
3718     easeOut: function (t, b, c, d) {
3719         return -c * (t /= d) * (t - 2) + b;
3720     },
3721
3722
3723     easeBoth: function (t, b, c, d) {
3724         if ((t /= d / 2) < 1) {
3725             return c / 2 * t * t + b;
3726         }
3727
3728         return -c / 2 * ((--t) * (t - 2) - 1) + b;
3729     },
3730
3731
3732     easeInStrong: function (t, b, c, d) {
3733         return c * (t /= d) * t * t * t + b;
3734     },
3735
3736
3737     easeOutStrong: function (t, b, c, d) {
3738         return -c * ((t = t / d - 1) * t * t * t - 1) + b;
3739     },
3740
3741
3742     easeBothStrong: function (t, b, c, d) {
3743         if ((t /= d / 2) < 1) {
3744             return c / 2 * t * t * t * t + b;
3745         }
3746
3747         return -c / 2 * ((t -= 2) * t * t * t - 2) + b;
3748     },
3749
3750
3751
3752     elasticIn: function (t, b, c, d, a, p) {
3753         if (t == 0) {
3754             return b;
3755         }
3756         if ((t /= d) == 1) {
3757             return b + c;
3758         }
3759         if (!p) {
3760             p = d * .3;
3761         }
3762
3763         if (!a || a < Math.abs(c)) {
3764             a = c;
3765             var s = p / 4;
3766         }
3767         else {
3768             var s = p / (2 * Math.PI) * Math.asin(c / a);
3769         }
3770
3771         return -(a * Math.pow(2, 10 * (t -= 1)) * Math.sin((t * d - s) * (2 * Math.PI) / p)) + b;
3772     },
3773
3774
3775     elasticOut: function (t, b, c, d, a, p) {
3776         if (t == 0) {
3777             return b;
3778         }
3779         if ((t /= d) == 1) {
3780             return b + c;
3781         }
3782         if (!p) {
3783             p = d * .3;
3784         }
3785
3786         if (!a || a < Math.abs(c)) {
3787             a = c;
3788             var s = p / 4;
3789         }
3790         else {
3791             var s = p / (2 * Math.PI) * Math.asin(c / a);
3792         }
3793
3794         return a * Math.pow(2, -10 * t) * Math.sin((t * d - s) * (2 * Math.PI) / p) + c + b;
3795     },
3796
3797
3798     elasticBoth: function (t, b, c, d, a, p) {
3799         if (t == 0) {
3800             return b;
3801         }
3802
3803         if ((t /= d / 2) == 2) {
3804             return b + c;
3805         }
3806
3807         if (!p) {
3808             p = d * (.3 * 1.5);
3809         }
3810
3811         if (!a || a < Math.abs(c)) {
3812             a = c;
3813             var s = p / 4;
3814         }
3815         else {
3816             var s = p / (2 * Math.PI) * Math.asin(c / a);
3817         }
3818
3819         if (t < 1) {
3820             return -.5 * (a * Math.pow(2, 10 * (t -= 1)) *
3821                           Math.sin((t * d - s) * (2 * Math.PI) / p)) + b;
3822         }
3823         return a * Math.pow(2, -10 * (t -= 1)) *
3824                Math.sin((t * d - s) * (2 * Math.PI) / p) * .5 + c + b;
3825     },
3826
3827
3828
3829     backIn: function (t, b, c, d, s) {
3830         if (typeof s == 'undefined') {
3831             s = 1.70158;
3832         }
3833         return c * (t /= d) * t * ((s + 1) * t - s) + b;
3834     },
3835
3836
3837     backOut: function (t, b, c, d, s) {
3838         if (typeof s == 'undefined') {
3839             s = 1.70158;
3840         }
3841         return c * ((t = t / d - 1) * t * ((s + 1) * t + s) + 1) + b;
3842     },
3843
3844
3845     backBoth: function (t, b, c, d, s) {
3846         if (typeof s == 'undefined') {
3847             s = 1.70158;
3848         }
3849
3850         if ((t /= d / 2 ) < 1) {
3851             return c / 2 * (t * t * (((s *= (1.525)) + 1) * t - s)) + b;
3852         }
3853         return c / 2 * ((t -= 2) * t * (((s *= (1.525)) + 1) * t + s) + 2) + b;
3854     },
3855
3856
3857     bounceIn: function (t, b, c, d) {
3858         return c - Roo.lib.Easing.bounceOut(d - t, 0, c, d) + b;
3859     },
3860
3861
3862     bounceOut: function (t, b, c, d) {
3863         if ((t /= d) < (1 / 2.75)) {
3864             return c * (7.5625 * t * t) + b;
3865         } else if (t < (2 / 2.75)) {
3866             return c * (7.5625 * (t -= (1.5 / 2.75)) * t + .75) + b;
3867         } else if (t < (2.5 / 2.75)) {
3868             return c * (7.5625 * (t -= (2.25 / 2.75)) * t + .9375) + b;
3869         }
3870         return c * (7.5625 * (t -= (2.625 / 2.75)) * t + .984375) + b;
3871     },
3872
3873
3874     bounceBoth: function (t, b, c, d) {
3875         if (t < d / 2) {
3876             return Roo.lib.Easing.bounceIn(t * 2, 0, c, d) * .5 + b;
3877         }
3878         return Roo.lib.Easing.bounceOut(t * 2 - d, 0, c, d) * .5 + c * .5 + b;
3879     }
3880 };/*
3881  * Portions of this file are based on pieces of Yahoo User Interface Library
3882  * Copyright (c) 2007, Yahoo! Inc. All rights reserved.
3883  * YUI licensed under the BSD License:
3884  * http://developer.yahoo.net/yui/license.txt
3885  * <script type="text/javascript">
3886  *
3887  */
3888     (function() {
3889         Roo.lib.Motion = function(el, attributes, duration, method) {
3890             if (el) {
3891                 Roo.lib.Motion.superclass.constructor.call(this, el, attributes, duration, method);
3892             }
3893         };
3894
3895         Roo.extend(Roo.lib.Motion, Roo.lib.ColorAnim);
3896
3897
3898         var Y = Roo.lib;
3899         var superclass = Y.Motion.superclass;
3900         var proto = Y.Motion.prototype;
3901
3902         proto.toString = function() {
3903             var el = this.getEl();
3904             var id = el.id || el.tagName;
3905             return ("Motion " + id);
3906         };
3907
3908         proto.patterns.points = /^points$/i;
3909
3910         proto.setAttribute = function(attr, val, unit) {
3911             if (this.patterns.points.test(attr)) {
3912                 unit = unit || 'px';
3913                 superclass.setAttribute.call(this, 'left', val[0], unit);
3914                 superclass.setAttribute.call(this, 'top', val[1], unit);
3915             } else {
3916                 superclass.setAttribute.call(this, attr, val, unit);
3917             }
3918         };
3919
3920         proto.getAttribute = function(attr) {
3921             if (this.patterns.points.test(attr)) {
3922                 var val = [
3923                         superclass.getAttribute.call(this, 'left'),
3924                         superclass.getAttribute.call(this, 'top')
3925                         ];
3926             } else {
3927                 val = superclass.getAttribute.call(this, attr);
3928             }
3929
3930             return val;
3931         };
3932
3933         proto.doMethod = function(attr, start, end) {
3934             var val = null;
3935
3936             if (this.patterns.points.test(attr)) {
3937                 var t = this.method(this.currentFrame, 0, 100, this.totalFrames) / 100;
3938                 val = Y.Bezier.getPosition(this.runtimeAttributes[attr], t);
3939             } else {
3940                 val = superclass.doMethod.call(this, attr, start, end);
3941             }
3942             return val;
3943         };
3944
3945         proto.setRuntimeAttribute = function(attr) {
3946             if (this.patterns.points.test(attr)) {
3947                 var el = this.getEl();
3948                 var attributes = this.attributes;
3949                 var start;
3950                 var control = attributes['points']['control'] || [];
3951                 var end;
3952                 var i, len;
3953
3954                 if (control.length > 0 && !(control[0] instanceof Array)) {
3955                     control = [control];
3956                 } else {
3957                     var tmp = [];
3958                     for (i = 0,len = control.length; i < len; ++i) {
3959                         tmp[i] = control[i];
3960                     }
3961                     control = tmp;
3962                 }
3963
3964                 Roo.fly(el).position();
3965
3966                 if (isset(attributes['points']['from'])) {
3967                     Roo.lib.Dom.setXY(el, attributes['points']['from']);
3968                 }
3969                 else {
3970                     Roo.lib.Dom.setXY(el, Roo.lib.Dom.getXY(el));
3971                 }
3972
3973                 start = this.getAttribute('points');
3974
3975
3976                 if (isset(attributes['points']['to'])) {
3977                     end = translateValues.call(this, attributes['points']['to'], start);
3978
3979                     var pageXY = Roo.lib.Dom.getXY(this.getEl());
3980                     for (i = 0,len = control.length; i < len; ++i) {
3981                         control[i] = translateValues.call(this, control[i], start);
3982                     }
3983
3984
3985                 } else if (isset(attributes['points']['by'])) {
3986                     end = [ start[0] + attributes['points']['by'][0], start[1] + attributes['points']['by'][1] ];
3987
3988                     for (i = 0,len = control.length; i < len; ++i) {
3989                         control[i] = [ start[0] + control[i][0], start[1] + control[i][1] ];
3990                     }
3991                 }
3992
3993                 this.runtimeAttributes[attr] = [start];
3994
3995                 if (control.length > 0) {
3996                     this.runtimeAttributes[attr] = this.runtimeAttributes[attr].concat(control);
3997                 }
3998
3999                 this.runtimeAttributes[attr][this.runtimeAttributes[attr].length] = end;
4000             }
4001             else {
4002                 superclass.setRuntimeAttribute.call(this, attr);
4003             }
4004         };
4005
4006         var translateValues = function(val, start) {
4007             var pageXY = Roo.lib.Dom.getXY(this.getEl());
4008             val = [ val[0] - pageXY[0] + start[0], val[1] - pageXY[1] + start[1] ];
4009
4010             return val;
4011         };
4012
4013         var isset = function(prop) {
4014             return (typeof prop !== 'undefined');
4015         };
4016     })();
4017 /*
4018  * Portions of this file are based on pieces of Yahoo User Interface Library
4019  * Copyright (c) 2007, Yahoo! Inc. All rights reserved.
4020  * YUI licensed under the BSD License:
4021  * http://developer.yahoo.net/yui/license.txt
4022  * <script type="text/javascript">
4023  *
4024  */
4025     (function() {
4026         Roo.lib.Scroll = function(el, attributes, duration, method) {
4027             if (el) {
4028                 Roo.lib.Scroll.superclass.constructor.call(this, el, attributes, duration, method);
4029             }
4030         };
4031
4032         Roo.extend(Roo.lib.Scroll, Roo.lib.ColorAnim);
4033
4034
4035         var Y = Roo.lib;
4036         var superclass = Y.Scroll.superclass;
4037         var proto = Y.Scroll.prototype;
4038
4039         proto.toString = function() {
4040             var el = this.getEl();
4041             var id = el.id || el.tagName;
4042             return ("Scroll " + id);
4043         };
4044
4045         proto.doMethod = function(attr, start, end) {
4046             var val = null;
4047
4048             if (attr == 'scroll') {
4049                 val = [
4050                         this.method(this.currentFrame, start[0], end[0] - start[0], this.totalFrames),
4051                         this.method(this.currentFrame, start[1], end[1] - start[1], this.totalFrames)
4052                         ];
4053
4054             } else {
4055                 val = superclass.doMethod.call(this, attr, start, end);
4056             }
4057             return val;
4058         };
4059
4060         proto.getAttribute = function(attr) {
4061             var val = null;
4062             var el = this.getEl();
4063
4064             if (attr == 'scroll') {
4065                 val = [ el.scrollLeft, el.scrollTop ];
4066             } else {
4067                 val = superclass.getAttribute.call(this, attr);
4068             }
4069
4070             return val;
4071         };
4072
4073         proto.setAttribute = function(attr, val, unit) {
4074             var el = this.getEl();
4075
4076             if (attr == 'scroll') {
4077                 el.scrollLeft = val[0];
4078                 el.scrollTop = val[1];
4079             } else {
4080                 superclass.setAttribute.call(this, attr, val, unit);
4081             }
4082         };
4083     })();
4084 /*
4085  * Based on:
4086  * Ext JS Library 1.1.1
4087  * Copyright(c) 2006-2007, Ext JS, LLC.
4088  *
4089  * Originally Released Under LGPL - original licence link has changed is not relivant.
4090  *
4091  * Fork - LGPL
4092  * <script type="text/javascript">
4093  */
4094
4095
4096 // nasty IE9 hack - what a pile of crap that is..
4097
4098  if (typeof Range != "undefined" && typeof Range.prototype.createContextualFragment == "undefined") {
4099     Range.prototype.createContextualFragment = function (html) {
4100         var doc = window.document;
4101         var container = doc.createElement("div");
4102         container.innerHTML = html;
4103         var frag = doc.createDocumentFragment(), n;
4104         while ((n = container.firstChild)) {
4105             frag.appendChild(n);
4106         }
4107         return frag;
4108     };
4109 }
4110
4111 /**
4112  * @class Roo.DomHelper
4113  * Utility class for working with DOM and/or Templates. It transparently supports using HTML fragments or DOM.
4114  * For more information see <a href="http://web.archive.org/web/20071221063734/http://www.jackslocum.com/blog/2006/10/06/domhelper-create-elements-using-dom-html-fragments-or-templates/">this blog post with examples</a>.
4115  * @singleton
4116  */
4117 Roo.DomHelper = function(){
4118     var tempTableEl = null;
4119     var emptyTags = /^(?:br|frame|hr|img|input|link|meta|range|spacer|wbr|area|param|col)$/i;
4120     var tableRe = /^table|tbody|tr|td$/i;
4121     var xmlns = {};
4122     // build as innerHTML where available
4123     /** @ignore */
4124     var createHtml = function(o){
4125         if(typeof o == 'string'){
4126             return o;
4127         }
4128         var b = "";
4129         if(!o.tag){
4130             o.tag = "div";
4131         }
4132         b += "<" + o.tag;
4133         for(var attr in o){
4134             if(attr == "tag" || attr == "children" || attr == "cn" || attr == "html" || typeof o[attr] == "function") continue;
4135             if(attr == "style"){
4136                 var s = o["style"];
4137                 if(typeof s == "function"){
4138                     s = s.call();
4139                 }
4140                 if(typeof s == "string"){
4141                     b += ' style="' + s + '"';
4142                 }else if(typeof s == "object"){
4143                     b += ' style="';
4144                     for(var key in s){
4145                         if(typeof s[key] != "function"){
4146                             b += key + ":" + s[key] + ";";
4147                         }
4148                     }
4149                     b += '"';
4150                 }
4151             }else{
4152                 if(attr == "cls"){
4153                     b += ' class="' + o["cls"] + '"';
4154                 }else if(attr == "htmlFor"){
4155                     b += ' for="' + o["htmlFor"] + '"';
4156                 }else{
4157                     b += " " + attr + '="' + o[attr] + '"';
4158                 }
4159             }
4160         }
4161         if(emptyTags.test(o.tag)){
4162             b += "/>";
4163         }else{
4164             b += ">";
4165             var cn = o.children || o.cn;
4166             if(cn){
4167                 //http://bugs.kde.org/show_bug.cgi?id=71506
4168                 if((cn instanceof Array) || (Roo.isSafari && typeof(cn.join) == "function")){
4169                     for(var i = 0, len = cn.length; i < len; i++) {
4170                         b += createHtml(cn[i], b);
4171                     }
4172                 }else{
4173                     b += createHtml(cn, b);
4174                 }
4175             }
4176             if(o.html){
4177                 b += o.html;
4178             }
4179             b += "</" + o.tag + ">";
4180         }
4181         return b;
4182     };
4183
4184     // build as dom
4185     /** @ignore */
4186     var createDom = function(o, parentNode){
4187          
4188         // defininition craeted..
4189         var ns = false;
4190         if (o.ns && o.ns != 'html') {
4191                
4192             if (o.xmlns && typeof(xmlns[o.ns]) == 'undefined') {
4193                 xmlns[o.ns] = o.xmlns;
4194                 ns = o.xmlns;
4195             }
4196             if (typeof(xmlns[o.ns]) == 'undefined') {
4197                 console.log("Trying to create namespace element " + o.ns + ", however no xmlns was sent to builder previously");
4198             }
4199             ns = xmlns[o.ns];
4200         }
4201         
4202         
4203         if (typeof(o) == 'string') {
4204             return parentNode.appendChild(document.createTextNode(o));
4205         }
4206         o.tag = o.tag || div;
4207         if (o.ns && Roo.isIE) {
4208             ns = false;
4209             o.tag = o.ns + ':' + o.tag;
4210             
4211         }
4212         var el = ns ? document.createElementNS( ns, o.tag||'div') :  document.createElement(o.tag||'div');
4213         var useSet = el.setAttribute ? true : false; // In IE some elements don't have setAttribute
4214         for(var attr in o){
4215             
4216             if(attr == "tag" || attr == "ns" ||attr == "xmlns" ||attr == "children" || attr == "cn" || attr == "html" || 
4217                     attr == "style" || typeof o[attr] == "function") continue;
4218                     
4219             if(attr=="cls" && Roo.isIE){
4220                 el.className = o["cls"];
4221             }else{
4222                 if(useSet) el.setAttribute(attr=="cls" ? 'class' : attr, o[attr]);
4223                 else el[attr] = o[attr];
4224             }
4225         }
4226         Roo.DomHelper.applyStyles(el, o.style);
4227         var cn = o.children || o.cn;
4228         if(cn){
4229             //http://bugs.kde.org/show_bug.cgi?id=71506
4230              if((cn instanceof Array) || (Roo.isSafari && typeof(cn.join) == "function")){
4231                 for(var i = 0, len = cn.length; i < len; i++) {
4232                     createDom(cn[i], el);
4233                 }
4234             }else{
4235                 createDom(cn, el);
4236             }
4237         }
4238         if(o.html){
4239             el.innerHTML = o.html;
4240         }
4241         if(parentNode){
4242            parentNode.appendChild(el);
4243         }
4244         return el;
4245     };
4246
4247     var ieTable = function(depth, s, h, e){
4248         tempTableEl.innerHTML = [s, h, e].join('');
4249         var i = -1, el = tempTableEl;
4250         while(++i < depth){
4251             el = el.firstChild;
4252         }
4253         return el;
4254     };
4255
4256     // kill repeat to save bytes
4257     var ts = '<table>',
4258         te = '</table>',
4259         tbs = ts+'<tbody>',
4260         tbe = '</tbody>'+te,
4261         trs = tbs + '<tr>',
4262         tre = '</tr>'+tbe;
4263
4264     /**
4265      * @ignore
4266      * Nasty code for IE's broken table implementation
4267      */
4268     var insertIntoTable = function(tag, where, el, html){
4269         if(!tempTableEl){
4270             tempTableEl = document.createElement('div');
4271         }
4272         var node;
4273         var before = null;
4274         if(tag == 'td'){
4275             if(where == 'afterbegin' || where == 'beforeend'){ // INTO a TD
4276                 return;
4277             }
4278             if(where == 'beforebegin'){
4279                 before = el;
4280                 el = el.parentNode;
4281             } else{
4282                 before = el.nextSibling;
4283                 el = el.parentNode;
4284             }
4285             node = ieTable(4, trs, html, tre);
4286         }
4287         else if(tag == 'tr'){
4288             if(where == 'beforebegin'){
4289                 before = el;
4290                 el = el.parentNode;
4291                 node = ieTable(3, tbs, html, tbe);
4292             } else if(where == 'afterend'){
4293                 before = el.nextSibling;
4294                 el = el.parentNode;
4295                 node = ieTable(3, tbs, html, tbe);
4296             } else{ // INTO a TR
4297                 if(where == 'afterbegin'){
4298                     before = el.firstChild;
4299                 }
4300                 node = ieTable(4, trs, html, tre);
4301             }
4302         } else if(tag == 'tbody'){
4303             if(where == 'beforebegin'){
4304                 before = el;
4305                 el = el.parentNode;
4306                 node = ieTable(2, ts, html, te);
4307             } else if(where == 'afterend'){
4308                 before = el.nextSibling;
4309                 el = el.parentNode;
4310                 node = ieTable(2, ts, html, te);
4311             } else{
4312                 if(where == 'afterbegin'){
4313                     before = el.firstChild;
4314                 }
4315                 node = ieTable(3, tbs, html, tbe);
4316             }
4317         } else{ // TABLE
4318             if(where == 'beforebegin' || where == 'afterend'){ // OUTSIDE the table
4319                 return;
4320             }
4321             if(where == 'afterbegin'){
4322                 before = el.firstChild;
4323             }
4324             node = ieTable(2, ts, html, te);
4325         }
4326         el.insertBefore(node, before);
4327         return node;
4328     };
4329
4330     return {
4331     /** True to force the use of DOM instead of html fragments @type Boolean */
4332     useDom : false,
4333
4334     /**
4335      * Returns the markup for the passed Element(s) config
4336      * @param {Object} o The Dom object spec (and children)
4337      * @return {String}
4338      */
4339     markup : function(o){
4340         return createHtml(o);
4341     },
4342
4343     /**
4344      * Applies a style specification to an element
4345      * @param {String/HTMLElement} el The element to apply styles to
4346      * @param {String/Object/Function} styles A style specification string eg "width:100px", or object in the form {width:"100px"}, or
4347      * a function which returns such a specification.
4348      */
4349     applyStyles : function(el, styles){
4350         if(styles){
4351            el = Roo.fly(el);
4352            if(typeof styles == "string"){
4353                var re = /\s?([a-z\-]*)\:\s?([^;]*);?/gi;
4354                var matches;
4355                while ((matches = re.exec(styles)) != null){
4356                    el.setStyle(matches[1], matches[2]);
4357                }
4358            }else if (typeof styles == "object"){
4359                for (var style in styles){
4360                   el.setStyle(style, styles[style]);
4361                }
4362            }else if (typeof styles == "function"){
4363                 Roo.DomHelper.applyStyles(el, styles.call());
4364            }
4365         }
4366     },
4367
4368     /**
4369      * Inserts an HTML fragment into the Dom
4370      * @param {String} where Where to insert the html in relation to el - beforeBegin, afterBegin, beforeEnd, afterEnd.
4371      * @param {HTMLElement} el The context element
4372      * @param {String} html The HTML fragmenet
4373      * @return {HTMLElement} The new node
4374      */
4375     insertHtml : function(where, el, html){
4376         where = where.toLowerCase();
4377         if(el.insertAdjacentHTML){
4378             if(tableRe.test(el.tagName)){
4379                 var rs;
4380                 if(rs = insertIntoTable(el.tagName.toLowerCase(), where, el, html)){
4381                     return rs;
4382                 }
4383             }
4384             switch(where){
4385                 case "beforebegin":
4386                     el.insertAdjacentHTML('BeforeBegin', html);
4387                     return el.previousSibling;
4388                 case "afterbegin":
4389                     el.insertAdjacentHTML('AfterBegin', html);
4390                     return el.firstChild;
4391                 case "beforeend":
4392                     el.insertAdjacentHTML('BeforeEnd', html);
4393                     return el.lastChild;
4394                 case "afterend":
4395                     el.insertAdjacentHTML('AfterEnd', html);
4396                     return el.nextSibling;
4397             }
4398             throw 'Illegal insertion point -> "' + where + '"';
4399         }
4400         var range = el.ownerDocument.createRange();
4401         var frag;
4402         switch(where){
4403              case "beforebegin":
4404                 range.setStartBefore(el);
4405                 frag = range.createContextualFragment(html);
4406                 el.parentNode.insertBefore(frag, el);
4407                 return el.previousSibling;
4408              case "afterbegin":
4409                 if(el.firstChild){
4410                     range.setStartBefore(el.firstChild);
4411                     frag = range.createContextualFragment(html);
4412                     el.insertBefore(frag, el.firstChild);
4413                     return el.firstChild;
4414                 }else{
4415                     el.innerHTML = html;
4416                     return el.firstChild;
4417                 }
4418             case "beforeend":
4419                 if(el.lastChild){
4420                     range.setStartAfter(el.lastChild);
4421                     frag = range.createContextualFragment(html);
4422                     el.appendChild(frag);
4423                     return el.lastChild;
4424                 }else{
4425                     el.innerHTML = html;
4426                     return el.lastChild;
4427                 }
4428             case "afterend":
4429                 range.setStartAfter(el);
4430                 frag = range.createContextualFragment(html);
4431                 el.parentNode.insertBefore(frag, el.nextSibling);
4432                 return el.nextSibling;
4433             }
4434             throw 'Illegal insertion point -> "' + where + '"';
4435     },
4436
4437     /**
4438      * Creates new Dom element(s) and inserts them before el
4439      * @param {String/HTMLElement/Element} el The context element
4440      * @param {Object/String} o The Dom object spec (and children) or raw HTML blob
4441      * @param {Boolean} returnElement (optional) true to return a Roo.Element
4442      * @return {HTMLElement/Roo.Element} The new node
4443      */
4444     insertBefore : function(el, o, returnElement){
4445         return this.doInsert(el, o, returnElement, "beforeBegin");
4446     },
4447
4448     /**
4449      * Creates new Dom element(s) and inserts them after el
4450      * @param {String/HTMLElement/Element} el The context element
4451      * @param {Object} o The Dom object spec (and children)
4452      * @param {Boolean} returnElement (optional) true to return a Roo.Element
4453      * @return {HTMLElement/Roo.Element} The new node
4454      */
4455     insertAfter : function(el, o, returnElement){
4456         return this.doInsert(el, o, returnElement, "afterEnd", "nextSibling");
4457     },
4458
4459     /**
4460      * Creates new Dom element(s) and inserts them as the first child of el
4461      * @param {String/HTMLElement/Element} el The context element
4462      * @param {Object/String} o The Dom object spec (and children) or raw HTML blob
4463      * @param {Boolean} returnElement (optional) true to return a Roo.Element
4464      * @return {HTMLElement/Roo.Element} The new node
4465      */
4466     insertFirst : function(el, o, returnElement){
4467         return this.doInsert(el, o, returnElement, "afterBegin");
4468     },
4469
4470     // private
4471     doInsert : function(el, o, returnElement, pos, sibling){
4472         el = Roo.getDom(el);
4473         var newNode;
4474         if(this.useDom || o.ns){
4475             newNode = createDom(o, null);
4476             el.parentNode.insertBefore(newNode, sibling ? el[sibling] : el);
4477         }else{
4478             var html = createHtml(o);
4479             newNode = this.insertHtml(pos, el, html);
4480         }
4481         return returnElement ? Roo.get(newNode, true) : newNode;
4482     },
4483
4484     /**
4485      * Creates new Dom element(s) and appends them to el
4486      * @param {String/HTMLElement/Element} el The context element
4487      * @param {Object/String} o The Dom object spec (and children) or raw HTML blob
4488      * @param {Boolean} returnElement (optional) true to return a Roo.Element
4489      * @return {HTMLElement/Roo.Element} The new node
4490      */
4491     append : function(el, o, returnElement){
4492         el = Roo.getDom(el);
4493         var newNode;
4494         if(this.useDom || o.ns){
4495             newNode = createDom(o, null);
4496             el.appendChild(newNode);
4497         }else{
4498             var html = createHtml(o);
4499             newNode = this.insertHtml("beforeEnd", el, html);
4500         }
4501         return returnElement ? Roo.get(newNode, true) : newNode;
4502     },
4503
4504     /**
4505      * Creates new Dom element(s) and overwrites the contents of el with them
4506      * @param {String/HTMLElement/Element} el The context element
4507      * @param {Object/String} o The Dom object spec (and children) or raw HTML blob
4508      * @param {Boolean} returnElement (optional) true to return a Roo.Element
4509      * @return {HTMLElement/Roo.Element} The new node
4510      */
4511     overwrite : function(el, o, returnElement){
4512         el = Roo.getDom(el);
4513         if (o.ns) {
4514           
4515             while (el.childNodes.length) {
4516                 el.removeChild(el.firstChild);
4517             }
4518             createDom(o, el);
4519         } else {
4520             el.innerHTML = createHtml(o);   
4521         }
4522         
4523         return returnElement ? Roo.get(el.firstChild, true) : el.firstChild;
4524     },
4525
4526     /**
4527      * Creates a new Roo.DomHelper.Template from the Dom object spec
4528      * @param {Object} o The Dom object spec (and children)
4529      * @return {Roo.DomHelper.Template} The new template
4530      */
4531     createTemplate : function(o){
4532         var html = createHtml(o);
4533         return new Roo.Template(html);
4534     }
4535     };
4536 }();
4537 /*
4538  * Based on:
4539  * Ext JS Library 1.1.1
4540  * Copyright(c) 2006-2007, Ext JS, LLC.
4541  *
4542  * Originally Released Under LGPL - original licence link has changed is not relivant.
4543  *
4544  * Fork - LGPL
4545  * <script type="text/javascript">
4546  */
4547  
4548 /**
4549 * @class Roo.Template
4550 * Represents an HTML fragment template. Templates can be precompiled for greater performance.
4551 * For a list of available format functions, see {@link Roo.util.Format}.<br />
4552 * Usage:
4553 <pre><code>
4554 var t = new Roo.Template({
4555     html :  '&lt;div name="{id}"&gt;' + 
4556         '&lt;span class="{cls}"&gt;{name:trim} {someval:this.myformat}{value:ellipsis(10)}&lt;/span&gt;' +
4557         '&lt;/div&gt;',
4558     myformat: function (value, allValues) {
4559         return 'XX' + value;
4560     }
4561 });
4562 t.append('some-element', {id: 'myid', cls: 'myclass', name: 'foo', value: 'bar'});
4563 </code></pre>
4564 * For more information see this blog post with examples:
4565 *  <a href="http://www.cnitblog.com/seeyeah/archive/2011/12/30/38728.html/">DomHelper
4566      - Create Elements using DOM, HTML fragments and Templates</a>. 
4567 * @constructor
4568 * @param {Object} cfg - Configuration object.
4569 */
4570 Roo.Template = function(cfg){
4571     // BC!
4572     if(cfg instanceof Array){
4573         cfg = cfg.join("");
4574     }else if(arguments.length > 1){
4575         cfg = Array.prototype.join.call(arguments, "");
4576     }
4577     
4578     
4579     if (typeof(cfg) == 'object') {
4580         Roo.apply(this,cfg)
4581     } else {
4582         // bc
4583         this.html = cfg;
4584     }
4585     if (this.url) {
4586         this.load();
4587     }
4588     
4589 };
4590 Roo.Template.prototype = {
4591     
4592     /**
4593      * @cfg {String} url  The Url to load the template from. beware if you are loading from a url, the data may not be ready if you use it instantly..
4594      *                    it should be fixed so that template is observable...
4595      */
4596     url : false,
4597     /**
4598      * @cfg {String} html  The HTML fragment or an array of fragments to join("") or multiple arguments to join("")
4599      */
4600     html : '',
4601     /**
4602      * Returns an HTML fragment of this template with the specified values applied.
4603      * @param {Object} values The template values. Can be an array if your params are numeric (i.e. {0}) or an object (i.e. {foo: 'bar'})
4604      * @return {String} The HTML fragment
4605      */
4606     applyTemplate : function(values){
4607         try {
4608            
4609             if(this.compiled){
4610                 return this.compiled(values);
4611             }
4612             var useF = this.disableFormats !== true;
4613             var fm = Roo.util.Format, tpl = this;
4614             var fn = function(m, name, format, args){
4615                 if(format && useF){
4616                     if(format.substr(0, 5) == "this."){
4617                         return tpl.call(format.substr(5), values[name], values);
4618                     }else{
4619                         if(args){
4620                             // quoted values are required for strings in compiled templates, 
4621                             // but for non compiled we need to strip them
4622                             // quoted reversed for jsmin
4623                             var re = /^\s*['"](.*)["']\s*$/;
4624                             args = args.split(',');
4625                             for(var i = 0, len = args.length; i < len; i++){
4626                                 args[i] = args[i].replace(re, "$1");
4627                             }
4628                             args = [values[name]].concat(args);
4629                         }else{
4630                             args = [values[name]];
4631                         }
4632                         return fm[format].apply(fm, args);
4633                     }
4634                 }else{
4635                     return values[name] !== undefined ? values[name] : "";
4636                 }
4637             };
4638             return this.html.replace(this.re, fn);
4639         } catch (e) {
4640             Roo.log(e);
4641             throw e;
4642         }
4643          
4644     },
4645     
4646     loading : false,
4647       
4648     load : function ()
4649     {
4650          
4651         if (this.loading) {
4652             return;
4653         }
4654         var _t = this;
4655         
4656         this.loading = true;
4657         this.compiled = false;
4658         
4659         var cx = new Roo.data.Connection();
4660         cx.request({
4661             url : this.url,
4662             method : 'GET',
4663             success : function (response) {
4664                 _t.loading = false;
4665                 _t.html = response.responseText;
4666                 _t.url = false;
4667                 _t.compile();
4668              },
4669             failure : function(response) {
4670                 Roo.log("Template failed to load from " + _t.url);
4671                 _t.loading = false;
4672             }
4673         });
4674     },
4675
4676     /**
4677      * Sets the HTML used as the template and optionally compiles it.
4678      * @param {String} html
4679      * @param {Boolean} compile (optional) True to compile the template (defaults to undefined)
4680      * @return {Roo.Template} this
4681      */
4682     set : function(html, compile){
4683         this.html = html;
4684         this.compiled = null;
4685         if(compile){
4686             this.compile();
4687         }
4688         return this;
4689     },
4690     
4691     /**
4692      * True to disable format functions (defaults to false)
4693      * @type Boolean
4694      */
4695     disableFormats : false,
4696     
4697     /**
4698     * The regular expression used to match template variables 
4699     * @type RegExp
4700     * @property 
4701     */
4702     re : /\{([\w-]+)(?:\:([\w\.]*)(?:\((.*?)?\))?)?\}/g,
4703     
4704     /**
4705      * Compiles the template into an internal function, eliminating the RegEx overhead.
4706      * @return {Roo.Template} this
4707      */
4708     compile : function(){
4709         var fm = Roo.util.Format;
4710         var useF = this.disableFormats !== true;
4711         var sep = Roo.isGecko ? "+" : ",";
4712         var fn = function(m, name, format, args){
4713             if(format && useF){
4714                 args = args ? ',' + args : "";
4715                 if(format.substr(0, 5) != "this."){
4716                     format = "fm." + format + '(';
4717                 }else{
4718                     format = 'this.call("'+ format.substr(5) + '", ';
4719                     args = ", values";
4720                 }
4721             }else{
4722                 args= ''; format = "(values['" + name + "'] == undefined ? '' : ";
4723             }
4724             return "'"+ sep + format + "values['" + name + "']" + args + ")"+sep+"'";
4725         };
4726         var body;
4727         // branched to use + in gecko and [].join() in others
4728         if(Roo.isGecko){
4729             body = "this.compiled = function(values){ return '" +
4730                    this.html.replace(/\\/g, '\\\\').replace(/(\r\n|\n)/g, '\\n').replace(/'/g, "\\'").replace(this.re, fn) +
4731                     "';};";
4732         }else{
4733             body = ["this.compiled = function(values){ return ['"];
4734             body.push(this.html.replace(/\\/g, '\\\\').replace(/(\r\n|\n)/g, '\\n').replace(/'/g, "\\'").replace(this.re, fn));
4735             body.push("'].join('');};");
4736             body = body.join('');
4737         }
4738         /**
4739          * eval:var:values
4740          * eval:var:fm
4741          */
4742         eval(body);
4743         return this;
4744     },
4745     
4746     // private function used to call members
4747     call : function(fnName, value, allValues){
4748         return this[fnName](value, allValues);
4749     },
4750     
4751     /**
4752      * Applies the supplied values to the template and inserts the new node(s) as the first child of el.
4753      * @param {String/HTMLElement/Roo.Element} el The context element
4754      * @param {Object} values The template values. Can be an array if your params are numeric (i.e. {0}) or an object (i.e. {foo: 'bar'})
4755      * @param {Boolean} returnElement (optional) true to return a Roo.Element (defaults to undefined)
4756      * @return {HTMLElement/Roo.Element} The new node or Element
4757      */
4758     insertFirst: function(el, values, returnElement){
4759         return this.doInsert('afterBegin', el, values, returnElement);
4760     },
4761
4762     /**
4763      * Applies the supplied values to the template and inserts the new node(s) before el.
4764      * @param {String/HTMLElement/Roo.Element} el The context element
4765      * @param {Object} values The template values. Can be an array if your params are numeric (i.e. {0}) or an object (i.e. {foo: 'bar'})
4766      * @param {Boolean} returnElement (optional) true to return a Roo.Element (defaults to undefined)
4767      * @return {HTMLElement/Roo.Element} The new node or Element
4768      */
4769     insertBefore: function(el, values, returnElement){
4770         return this.doInsert('beforeBegin', el, values, returnElement);
4771     },
4772
4773     /**
4774      * Applies the supplied values to the template and inserts the new node(s) after el.
4775      * @param {String/HTMLElement/Roo.Element} el The context element
4776      * @param {Object} values The template values. Can be an array if your params are numeric (i.e. {0}) or an object (i.e. {foo: 'bar'})
4777      * @param {Boolean} returnElement (optional) true to return a Roo.Element (defaults to undefined)
4778      * @return {HTMLElement/Roo.Element} The new node or Element
4779      */
4780     insertAfter : function(el, values, returnElement){
4781         return this.doInsert('afterEnd', el, values, returnElement);
4782     },
4783     
4784     /**
4785      * Applies the supplied values to the template and appends the new node(s) to el.
4786      * @param {String/HTMLElement/Roo.Element} el The context element
4787      * @param {Object} values The template values. Can be an array if your params are numeric (i.e. {0}) or an object (i.e. {foo: 'bar'})
4788      * @param {Boolean} returnElement (optional) true to return a Roo.Element (defaults to undefined)
4789      * @return {HTMLElement/Roo.Element} The new node or Element
4790      */
4791     append : function(el, values, returnElement){
4792         return this.doInsert('beforeEnd', el, values, returnElement);
4793     },
4794
4795     doInsert : function(where, el, values, returnEl){
4796         el = Roo.getDom(el);
4797         var newNode = Roo.DomHelper.insertHtml(where, el, this.applyTemplate(values));
4798         return returnEl ? Roo.get(newNode, true) : newNode;
4799     },
4800
4801     /**
4802      * Applies the supplied values to the template and overwrites the content of el with the new node(s).
4803      * @param {String/HTMLElement/Roo.Element} el The context element
4804      * @param {Object} values The template values. Can be an array if your params are numeric (i.e. {0}) or an object (i.e. {foo: 'bar'})
4805      * @param {Boolean} returnElement (optional) true to return a Roo.Element (defaults to undefined)
4806      * @return {HTMLElement/Roo.Element} The new node or Element
4807      */
4808     overwrite : function(el, values, returnElement){
4809         el = Roo.getDom(el);
4810         el.innerHTML = this.applyTemplate(values);
4811         return returnElement ? Roo.get(el.firstChild, true) : el.firstChild;
4812     }
4813 };
4814 /**
4815  * Alias for {@link #applyTemplate}
4816  * @method
4817  */
4818 Roo.Template.prototype.apply = Roo.Template.prototype.applyTemplate;
4819
4820 // backwards compat
4821 Roo.DomHelper.Template = Roo.Template;
4822
4823 /**
4824  * Creates a template from the passed element's value (<i>display:none</i> textarea, preferred) or innerHTML.
4825  * @param {String/HTMLElement} el A DOM element or its id
4826  * @returns {Roo.Template} The created template
4827  * @static
4828  */
4829 Roo.Template.from = function(el){
4830     el = Roo.getDom(el);
4831     return new Roo.Template(el.value || el.innerHTML);
4832 };/*
4833  * Based on:
4834  * Ext JS Library 1.1.1
4835  * Copyright(c) 2006-2007, Ext JS, LLC.
4836  *
4837  * Originally Released Under LGPL - original licence link has changed is not relivant.
4838  *
4839  * Fork - LGPL
4840  * <script type="text/javascript">
4841  */
4842  
4843
4844 /*
4845  * This is code is also distributed under MIT license for use
4846  * with jQuery and prototype JavaScript libraries.
4847  */
4848 /**
4849  * @class Roo.DomQuery
4850 Provides high performance selector/xpath processing by compiling queries into reusable functions. New pseudo classes and matchers can be plugged. It works on HTML and XML documents (if a content node is passed in).
4851 <p>
4852 DomQuery supports most of the <a href="http://www.w3.org/TR/2005/WD-css3-selectors-20051215/">CSS3 selectors spec</a>, along with some custom selectors and basic XPath.</p>
4853
4854 <p>
4855 All selectors, attribute filters and pseudos below can be combined infinitely in any order. For example "div.foo:nth-child(odd)[@foo=bar].bar:first" would be a perfectly valid selector. Node filters are processed in the order in which they appear, which allows you to optimize your queries for your document structure.
4856 </p>
4857 <h4>Element Selectors:</h4>
4858 <ul class="list">
4859     <li> <b>*</b> any element</li>
4860     <li> <b>E</b> an element with the tag E</li>
4861     <li> <b>E F</b> All descendent elements of E that have the tag F</li>
4862     <li> <b>E > F</b> or <b>E/F</b> all direct children elements of E that have the tag F</li>
4863     <li> <b>E + F</b> all elements with the tag F that are immediately preceded by an element with the tag E</li>
4864     <li> <b>E ~ F</b> all elements with the tag F that are preceded by a sibling element with the tag E</li>
4865 </ul>
4866 <h4>Attribute Selectors:</h4>
4867 <p>The use of @ and quotes are optional. For example, div[@foo='bar'] is also a valid attribute selector.</p>
4868 <ul class="list">
4869     <li> <b>E[foo]</b> has an attribute "foo"</li>
4870     <li> <b>E[foo=bar]</b> has an attribute "foo" that equals "bar"</li>
4871     <li> <b>E[foo^=bar]</b> has an attribute "foo" that starts with "bar"</li>
4872     <li> <b>E[foo$=bar]</b> has an attribute "foo" that ends with "bar"</li>
4873     <li> <b>E[foo*=bar]</b> has an attribute "foo" that contains the substring "bar"</li>
4874     <li> <b>E[foo%=2]</b> has an attribute "foo" that is evenly divisible by 2</li>
4875     <li> <b>E[foo!=bar]</b> has an attribute "foo" that does not equal "bar"</li>
4876 </ul>
4877 <h4>Pseudo Classes:</h4>
4878 <ul class="list">
4879     <li> <b>E:first-child</b> E is the first child of its parent</li>
4880     <li> <b>E:last-child</b> E is the last child of its parent</li>
4881     <li> <b>E:nth-child(<i>n</i>)</b> E is the <i>n</i>th child of its parent (1 based as per the spec)</li>
4882     <li> <b>E:nth-child(odd)</b> E is an odd child of its parent</li>
4883     <li> <b>E:nth-child(even)</b> E is an even child of its parent</li>
4884     <li> <b>E:only-child</b> E is the only child of its parent</li>
4885     <li> <b>E:checked</b> E is an element that is has a checked attribute that is true (e.g. a radio or checkbox) </li>
4886     <li> <b>E:first</b> the first E in the resultset</li>
4887     <li> <b>E:last</b> the last E in the resultset</li>
4888     <li> <b>E:nth(<i>n</i>)</b> the <i>n</i>th E in the resultset (1 based)</li>
4889     <li> <b>E:odd</b> shortcut for :nth-child(odd)</li>
4890     <li> <b>E:even</b> shortcut for :nth-child(even)</li>
4891     <li> <b>E:contains(foo)</b> E's innerHTML contains the substring "foo"</li>
4892     <li> <b>E:nodeValue(foo)</b> E contains a textNode with a nodeValue that equals "foo"</li>
4893     <li> <b>E:not(S)</b> an E element that does not match simple selector S</li>
4894     <li> <b>E:has(S)</b> an E element that has a descendent that matches simple selector S</li>
4895     <li> <b>E:next(S)</b> an E element whose next sibling matches simple selector S</li>
4896     <li> <b>E:prev(S)</b> an E element whose previous sibling matches simple selector S</li>
4897 </ul>
4898 <h4>CSS Value Selectors:</h4>
4899 <ul class="list">
4900     <li> <b>E{display=none}</b> css value "display" that equals "none"</li>
4901     <li> <b>E{display^=none}</b> css value "display" that starts with "none"</li>
4902     <li> <b>E{display$=none}</b> css value "display" that ends with "none"</li>
4903     <li> <b>E{display*=none}</b> css value "display" that contains the substring "none"</li>
4904     <li> <b>E{display%=2}</b> css value "display" that is evenly divisible by 2</li>
4905     <li> <b>E{display!=none}</b> css value "display" that does not equal "none"</li>
4906 </ul>
4907  * @singleton
4908  */
4909 Roo.DomQuery = function(){
4910     var cache = {}, simpleCache = {}, valueCache = {};
4911     var nonSpace = /\S/;
4912     var trimRe = /^\s+|\s+$/g;
4913     var tplRe = /\{(\d+)\}/g;
4914     var modeRe = /^(\s?[\/>+~]\s?|\s|$)/;
4915     var tagTokenRe = /^(#)?([\w-\*]+)/;
4916     var nthRe = /(\d*)n\+?(\d*)/, nthRe2 = /\D/;
4917
4918     function child(p, index){
4919         var i = 0;
4920         var n = p.firstChild;
4921         while(n){
4922             if(n.nodeType == 1){
4923                if(++i == index){
4924                    return n;
4925                }
4926             }
4927             n = n.nextSibling;
4928         }
4929         return null;
4930     };
4931
4932     function next(n){
4933         while((n = n.nextSibling) && n.nodeType != 1);
4934         return n;
4935     };
4936
4937     function prev(n){
4938         while((n = n.previousSibling) && n.nodeType != 1);
4939         return n;
4940     };
4941
4942     function children(d){
4943         var n = d.firstChild, ni = -1;
4944             while(n){
4945                 var nx = n.nextSibling;
4946                 if(n.nodeType == 3 && !nonSpace.test(n.nodeValue)){
4947                     d.removeChild(n);
4948                 }else{
4949                     n.nodeIndex = ++ni;
4950                 }
4951                 n = nx;
4952             }
4953             return this;
4954         };
4955
4956     function byClassName(c, a, v){
4957         if(!v){
4958             return c;
4959         }
4960         var r = [], ri = -1, cn;
4961         for(var i = 0, ci; ci = c[i]; i++){
4962             if((' '+ci.className+' ').indexOf(v) != -1){
4963                 r[++ri] = ci;
4964             }
4965         }
4966         return r;
4967     };
4968
4969     function attrValue(n, attr){
4970         if(!n.tagName && typeof n.length != "undefined"){
4971             n = n[0];
4972         }
4973         if(!n){
4974             return null;
4975         }
4976         if(attr == "for"){
4977             return n.htmlFor;
4978         }
4979         if(attr == "class" || attr == "className"){
4980             return n.className;
4981         }
4982         return n.getAttribute(attr) || n[attr];
4983
4984     };
4985
4986     function getNodes(ns, mode, tagName){
4987         var result = [], ri = -1, cs;
4988         if(!ns){
4989             return result;
4990         }
4991         tagName = tagName || "*";
4992         if(typeof ns.getElementsByTagName != "undefined"){
4993             ns = [ns];
4994         }
4995         if(!mode){
4996             for(var i = 0, ni; ni = ns[i]; i++){
4997                 cs = ni.getElementsByTagName(tagName);
4998                 for(var j = 0, ci; ci = cs[j]; j++){
4999                     result[++ri] = ci;
5000                 }
5001             }
5002         }else if(mode == "/" || mode == ">"){
5003             var utag = tagName.toUpperCase();
5004             for(var i = 0, ni, cn; ni = ns[i]; i++){
5005                 cn = ni.children || ni.childNodes;
5006                 for(var j = 0, cj; cj = cn[j]; j++){
5007                     if(cj.nodeName == utag || cj.nodeName == tagName  || tagName == '*'){
5008                         result[++ri] = cj;
5009                     }
5010                 }
5011             }
5012         }else if(mode == "+"){
5013             var utag = tagName.toUpperCase();
5014             for(var i = 0, n; n = ns[i]; i++){
5015                 while((n = n.nextSibling) && n.nodeType != 1);
5016                 if(n && (n.nodeName == utag || n.nodeName == tagName || tagName == '*')){
5017                     result[++ri] = n;
5018                 }
5019             }
5020         }else if(mode == "~"){
5021             for(var i = 0, n; n = ns[i]; i++){
5022                 while((n = n.nextSibling) && (n.nodeType != 1 || (tagName == '*' || n.tagName.toLowerCase()!=tagName)));
5023                 if(n){
5024                     result[++ri] = n;
5025                 }
5026             }
5027         }
5028         return result;
5029     };
5030
5031     function concat(a, b){
5032         if(b.slice){
5033             return a.concat(b);
5034         }
5035         for(var i = 0, l = b.length; i < l; i++){
5036             a[a.length] = b[i];
5037         }
5038         return a;
5039     }
5040
5041     function byTag(cs, tagName){
5042         if(cs.tagName || cs == document){
5043             cs = [cs];
5044         }
5045         if(!tagName){
5046             return cs;
5047         }
5048         var r = [], ri = -1;
5049         tagName = tagName.toLowerCase();
5050         for(var i = 0, ci; ci = cs[i]; i++){
5051             if(ci.nodeType == 1 && ci.tagName.toLowerCase()==tagName){
5052                 r[++ri] = ci;
5053             }
5054         }
5055         return r;
5056     };
5057
5058     function byId(cs, attr, id){
5059         if(cs.tagName || cs == document){
5060             cs = [cs];
5061         }
5062         if(!id){
5063             return cs;
5064         }
5065         var r = [], ri = -1;
5066         for(var i = 0,ci; ci = cs[i]; i++){
5067             if(ci && ci.id == id){
5068                 r[++ri] = ci;
5069                 return r;
5070             }
5071         }
5072         return r;
5073     };
5074
5075     function byAttribute(cs, attr, value, op, custom){
5076         var r = [], ri = -1, st = custom=="{";
5077         var f = Roo.DomQuery.operators[op];
5078         for(var i = 0, ci; ci = cs[i]; i++){
5079             var a;
5080             if(st){
5081                 a = Roo.DomQuery.getStyle(ci, attr);
5082             }
5083             else if(attr == "class" || attr == "className"){
5084                 a = ci.className;
5085             }else if(attr == "for"){
5086                 a = ci.htmlFor;
5087             }else if(attr == "href"){
5088                 a = ci.getAttribute("href", 2);
5089             }else{
5090                 a = ci.getAttribute(attr);
5091             }
5092             if((f && f(a, value)) || (!f && a)){
5093                 r[++ri] = ci;
5094             }
5095         }
5096         return r;
5097     };
5098
5099     function byPseudo(cs, name, value){
5100         return Roo.DomQuery.pseudos[name](cs, value);
5101     };
5102
5103     // This is for IE MSXML which does not support expandos.
5104     // IE runs the same speed using setAttribute, however FF slows way down
5105     // and Safari completely fails so they need to continue to use expandos.
5106     var isIE = window.ActiveXObject ? true : false;
5107
5108     // this eval is stop the compressor from
5109     // renaming the variable to something shorter
5110     
5111     /** eval:var:batch */
5112     var batch = 30803; 
5113
5114     var key = 30803;
5115
5116     function nodupIEXml(cs){
5117         var d = ++key;
5118         cs[0].setAttribute("_nodup", d);
5119         var r = [cs[0]];
5120         for(var i = 1, len = cs.length; i < len; i++){
5121             var c = cs[i];
5122             if(!c.getAttribute("_nodup") != d){
5123                 c.setAttribute("_nodup", d);
5124                 r[r.length] = c;
5125             }
5126         }
5127         for(var i = 0, len = cs.length; i < len; i++){
5128             cs[i].removeAttribute("_nodup");
5129         }
5130         return r;
5131     }
5132
5133     function nodup(cs){
5134         if(!cs){
5135             return [];
5136         }
5137         var len = cs.length, c, i, r = cs, cj, ri = -1;
5138         if(!len || typeof cs.nodeType != "undefined" || len == 1){
5139             return cs;
5140         }
5141         if(isIE && typeof cs[0].selectSingleNode != "undefined"){
5142             return nodupIEXml(cs);
5143         }
5144         var d = ++key;
5145         cs[0]._nodup = d;
5146         for(i = 1; c = cs[i]; i++){
5147             if(c._nodup != d){
5148                 c._nodup = d;
5149             }else{
5150                 r = [];
5151                 for(var j = 0; j < i; j++){
5152                     r[++ri] = cs[j];
5153                 }
5154                 for(j = i+1; cj = cs[j]; j++){
5155                     if(cj._nodup != d){
5156                         cj._nodup = d;
5157                         r[++ri] = cj;
5158                     }
5159                 }
5160                 return r;
5161             }
5162         }
5163         return r;
5164     }
5165
5166     function quickDiffIEXml(c1, c2){
5167         var d = ++key;
5168         for(var i = 0, len = c1.length; i < len; i++){
5169             c1[i].setAttribute("_qdiff", d);
5170         }
5171         var r = [];
5172         for(var i = 0, len = c2.length; i < len; i++){
5173             if(c2[i].getAttribute("_qdiff") != d){
5174                 r[r.length] = c2[i];
5175             }
5176         }
5177         for(var i = 0, len = c1.length; i < len; i++){
5178            c1[i].removeAttribute("_qdiff");
5179         }
5180         return r;
5181     }
5182
5183     function quickDiff(c1, c2){
5184         var len1 = c1.length;
5185         if(!len1){
5186             return c2;
5187         }
5188         if(isIE && c1[0].selectSingleNode){
5189             return quickDiffIEXml(c1, c2);
5190         }
5191         var d = ++key;
5192         for(var i = 0; i < len1; i++){
5193             c1[i]._qdiff = d;
5194         }
5195         var r = [];
5196         for(var i = 0, len = c2.length; i < len; i++){
5197             if(c2[i]._qdiff != d){
5198                 r[r.length] = c2[i];
5199             }
5200         }
5201         return r;
5202     }
5203
5204     function quickId(ns, mode, root, id){
5205         if(ns == root){
5206            var d = root.ownerDocument || root;
5207            return d.getElementById(id);
5208         }
5209         ns = getNodes(ns, mode, "*");
5210         return byId(ns, null, id);
5211     }
5212
5213     return {
5214         getStyle : function(el, name){
5215             return Roo.fly(el).getStyle(name);
5216         },
5217         /**
5218          * Compiles a selector/xpath query into a reusable function. The returned function
5219          * takes one parameter "root" (optional), which is the context node from where the query should start.
5220          * @param {String} selector The selector/xpath query
5221          * @param {String} type (optional) Either "select" (the default) or "simple" for a simple selector match
5222          * @return {Function}
5223          */
5224         compile : function(path, type){
5225             type = type || "select";
5226             
5227             var fn = ["var f = function(root){\n var mode; ++batch; var n = root || document;\n"];
5228             var q = path, mode, lq;
5229             var tk = Roo.DomQuery.matchers;
5230             var tklen = tk.length;
5231             var mm;
5232
5233             // accept leading mode switch
5234             var lmode = q.match(modeRe);
5235             if(lmode && lmode[1]){
5236                 fn[fn.length] = 'mode="'+lmode[1].replace(trimRe, "")+'";';
5237                 q = q.replace(lmode[1], "");
5238             }
5239             // strip leading slashes
5240             while(path.substr(0, 1)=="/"){
5241                 path = path.substr(1);
5242             }
5243
5244             while(q && lq != q){
5245                 lq = q;
5246                 var tm = q.match(tagTokenRe);
5247                 if(type == "select"){
5248                     if(tm){
5249                         if(tm[1] == "#"){
5250                             fn[fn.length] = 'n = quickId(n, mode, root, "'+tm[2]+'");';
5251                         }else{
5252                             fn[fn.length] = 'n = getNodes(n, mode, "'+tm[2]+'");';
5253                         }
5254                         q = q.replace(tm[0], "");
5255                     }else if(q.substr(0, 1) != '@'){
5256                         fn[fn.length] = 'n = getNodes(n, mode, "*");';
5257                     }
5258                 }else{
5259                     if(tm){
5260                         if(tm[1] == "#"){
5261                             fn[fn.length] = 'n = byId(n, null, "'+tm[2]+'");';
5262                         }else{
5263                             fn[fn.length] = 'n = byTag(n, "'+tm[2]+'");';
5264                         }
5265                         q = q.replace(tm[0], "");
5266                     }
5267                 }
5268                 while(!(mm = q.match(modeRe))){
5269                     var matched = false;
5270                     for(var j = 0; j < tklen; j++){
5271                         var t = tk[j];
5272                         var m = q.match(t.re);
5273                         if(m){
5274                             fn[fn.length] = t.select.replace(tplRe, function(x, i){
5275                                                     return m[i];
5276                                                 });
5277                             q = q.replace(m[0], "");
5278                             matched = true;
5279                             break;
5280                         }
5281                     }
5282                     // prevent infinite loop on bad selector
5283                     if(!matched){
5284                         throw 'Error parsing selector, parsing failed at "' + q + '"';
5285                     }
5286                 }
5287                 if(mm[1]){
5288                     fn[fn.length] = 'mode="'+mm[1].replace(trimRe, "")+'";';
5289                     q = q.replace(mm[1], "");
5290                 }
5291             }
5292             fn[fn.length] = "return nodup(n);\n}";
5293             
5294              /** 
5295               * list of variables that need from compression as they are used by eval.
5296              *  eval:var:batch 
5297              *  eval:var:nodup
5298              *  eval:var:byTag
5299              *  eval:var:ById
5300              *  eval:var:getNodes
5301              *  eval:var:quickId
5302              *  eval:var:mode
5303              *  eval:var:root
5304              *  eval:var:n
5305              *  eval:var:byClassName
5306              *  eval:var:byPseudo
5307              *  eval:var:byAttribute
5308              *  eval:var:attrValue
5309              * 
5310              **/ 
5311             eval(fn.join(""));
5312             return f;
5313         },
5314
5315         /**
5316          * Selects a group of elements.
5317          * @param {String} selector The selector/xpath query (can be a comma separated list of selectors)
5318          * @param {Node} root (optional) The start of the query (defaults to document).
5319          * @return {Array}
5320          */
5321         select : function(path, root, type){
5322             if(!root || root == document){
5323                 root = document;
5324             }
5325             if(typeof root == "string"){
5326                 root = document.getElementById(root);
5327             }
5328             var paths = path.split(",");
5329             var results = [];
5330             for(var i = 0, len = paths.length; i < len; i++){
5331                 var p = paths[i].replace(trimRe, "");
5332                 if(!cache[p]){
5333                     cache[p] = Roo.DomQuery.compile(p);
5334                     if(!cache[p]){
5335                         throw p + " is not a valid selector";
5336                     }
5337                 }
5338                 var result = cache[p](root);
5339                 if(result && result != document){
5340                     results = results.concat(result);
5341                 }
5342             }
5343             if(paths.length > 1){
5344                 return nodup(results);
5345             }
5346             return results;
5347         },
5348
5349         /**
5350          * Selects a single element.
5351          * @param {String} selector The selector/xpath query
5352          * @param {Node} root (optional) The start of the query (defaults to document).
5353          * @return {Element}
5354          */
5355         selectNode : function(path, root){
5356             return Roo.DomQuery.select(path, root)[0];
5357         },
5358
5359         /**
5360          * Selects the value of a node, optionally replacing null with the defaultValue.
5361          * @param {String} selector The selector/xpath query
5362          * @param {Node} root (optional) The start of the query (defaults to document).
5363          * @param {String} defaultValue
5364          */
5365         selectValue : function(path, root, defaultValue){
5366             path = path.replace(trimRe, "");
5367             if(!valueCache[path]){
5368                 valueCache[path] = Roo.DomQuery.compile(path, "select");
5369             }
5370             var n = valueCache[path](root);
5371             n = n[0] ? n[0] : n;
5372             var v = (n && n.firstChild ? n.firstChild.nodeValue : null);
5373             return ((v === null||v === undefined||v==='') ? defaultValue : v);
5374         },
5375
5376         /**
5377          * Selects the value of a node, parsing integers and floats.
5378          * @param {String} selector The selector/xpath query
5379          * @param {Node} root (optional) The start of the query (defaults to document).
5380          * @param {Number} defaultValue
5381          * @return {Number}
5382          */
5383         selectNumber : function(path, root, defaultValue){
5384             var v = Roo.DomQuery.selectValue(path, root, defaultValue || 0);
5385             return parseFloat(v);
5386         },
5387
5388         /**
5389          * Returns true if the passed element(s) match the passed simple selector (e.g. div.some-class or span:first-child)
5390          * @param {String/HTMLElement/Array} el An element id, element or array of elements
5391          * @param {String} selector The simple selector to test
5392          * @return {Boolean}
5393          */
5394         is : function(el, ss){
5395             if(typeof el == "string"){
5396                 el = document.getElementById(el);
5397             }
5398             var isArray = (el instanceof Array);
5399             var result = Roo.DomQuery.filter(isArray ? el : [el], ss);
5400             return isArray ? (result.length == el.length) : (result.length > 0);
5401         },
5402
5403         /**
5404          * Filters an array of elements to only include matches of a simple selector (e.g. div.some-class or span:first-child)
5405          * @param {Array} el An array of elements to filter
5406          * @param {String} selector The simple selector to test
5407          * @param {Boolean} nonMatches If true, it returns the elements that DON'T match
5408          * the selector instead of the ones that match
5409          * @return {Array}
5410          */
5411         filter : function(els, ss, nonMatches){
5412             ss = ss.replace(trimRe, "");
5413             if(!simpleCache[ss]){
5414                 simpleCache[ss] = Roo.DomQuery.compile(ss, "simple");
5415             }
5416             var result = simpleCache[ss](els);
5417             return nonMatches ? quickDiff(result, els) : result;
5418         },
5419
5420         /**
5421          * Collection of matching regular expressions and code snippets.
5422          */
5423         matchers : [{
5424                 re: /^\.([\w-]+)/,
5425                 select: 'n = byClassName(n, null, " {1} ");'
5426             }, {
5427                 re: /^\:([\w-]+)(?:\(((?:[^\s>\/]*|.*?))\))?/,
5428                 select: 'n = byPseudo(n, "{1}", "{2}");'
5429             },{
5430                 re: /^(?:([\[\{])(?:@)?([\w-]+)\s?(?:(=|.=)\s?['"]?(.*?)["']?)?[\]\}])/,
5431                 select: 'n = byAttribute(n, "{2}", "{4}", "{3}", "{1}");'
5432             }, {
5433                 re: /^#([\w-]+)/,
5434                 select: 'n = byId(n, null, "{1}");'
5435             },{
5436                 re: /^@([\w-]+)/,
5437                 select: 'return {firstChild:{nodeValue:attrValue(n, "{1}")}};'
5438             }
5439         ],
5440
5441         /**
5442          * Collection of operator comparison functions. The default operators are =, !=, ^=, $=, *=, %=, |= and ~=.
5443          * New operators can be added as long as the match the format <i>c</i>= where <i>c</i> is any character other than space, &gt; &lt;.
5444          */
5445         operators : {
5446             "=" : function(a, v){
5447                 return a == v;
5448             },
5449             "!=" : function(a, v){
5450                 return a != v;
5451             },
5452             "^=" : function(a, v){
5453                 return a && a.substr(0, v.length) == v;
5454             },
5455             "$=" : function(a, v){
5456                 return a && a.substr(a.length-v.length) == v;
5457             },
5458             "*=" : function(a, v){
5459                 return a && a.indexOf(v) !== -1;
5460             },
5461             "%=" : function(a, v){
5462                 return (a % v) == 0;
5463             },
5464             "|=" : function(a, v){
5465                 return a && (a == v || a.substr(0, v.length+1) == v+'-');
5466             },
5467             "~=" : function(a, v){
5468                 return a && (' '+a+' ').indexOf(' '+v+' ') != -1;
5469             }
5470         },
5471
5472         /**
5473          * Collection of "pseudo class" processors. Each processor is passed the current nodeset (array)
5474          * and the argument (if any) supplied in the selector.
5475          */
5476         pseudos : {
5477             "first-child" : function(c){
5478                 var r = [], ri = -1, n;
5479                 for(var i = 0, ci; ci = n = c[i]; i++){
5480                     while((n = n.previousSibling) && n.nodeType != 1);
5481                     if(!n){
5482                         r[++ri] = ci;
5483                     }
5484                 }
5485                 return r;
5486             },
5487
5488             "last-child" : function(c){
5489                 var r = [], ri = -1, n;
5490                 for(var i = 0, ci; ci = n = c[i]; i++){
5491                     while((n = n.nextSibling) && n.nodeType != 1);
5492                     if(!n){
5493                         r[++ri] = ci;
5494                     }
5495                 }
5496                 return r;
5497             },
5498
5499             "nth-child" : function(c, a) {
5500                 var r = [], ri = -1;
5501                 var m = nthRe.exec(a == "even" && "2n" || a == "odd" && "2n+1" || !nthRe2.test(a) && "n+" + a || a);
5502                 var f = (m[1] || 1) - 0, l = m[2] - 0;
5503                 for(var i = 0, n; n = c[i]; i++){
5504                     var pn = n.parentNode;
5505                     if (batch != pn._batch) {
5506                         var j = 0;
5507                         for(var cn = pn.firstChild; cn; cn = cn.nextSibling){
5508                             if(cn.nodeType == 1){
5509                                cn.nodeIndex = ++j;
5510                             }
5511                         }
5512                         pn._batch = batch;
5513                     }
5514                     if (f == 1) {
5515                         if (l == 0 || n.nodeIndex == l){
5516                             r[++ri] = n;
5517                         }
5518                     } else if ((n.nodeIndex + l) % f == 0){
5519                         r[++ri] = n;
5520                     }
5521                 }
5522
5523                 return r;
5524             },
5525
5526             "only-child" : function(c){
5527                 var r = [], ri = -1;;
5528                 for(var i = 0, ci; ci = c[i]; i++){
5529                     if(!prev(ci) && !next(ci)){
5530                         r[++ri] = ci;
5531                     }
5532                 }
5533                 return r;
5534             },
5535
5536             "empty" : function(c){
5537                 var r = [], ri = -1;
5538                 for(var i = 0, ci; ci = c[i]; i++){
5539                     var cns = ci.childNodes, j = 0, cn, empty = true;
5540                     while(cn = cns[j]){
5541                         ++j;
5542                         if(cn.nodeType == 1 || cn.nodeType == 3){
5543                             empty = false;
5544                             break;
5545                         }
5546                     }
5547                     if(empty){
5548                         r[++ri] = ci;
5549                     }
5550                 }
5551                 return r;
5552             },
5553
5554             "contains" : function(c, v){
5555                 var r = [], ri = -1;
5556                 for(var i = 0, ci; ci = c[i]; i++){
5557                     if((ci.textContent||ci.innerText||'').indexOf(v) != -1){
5558                         r[++ri] = ci;
5559                     }
5560                 }
5561                 return r;
5562             },
5563
5564             "nodeValue" : function(c, v){
5565                 var r = [], ri = -1;
5566                 for(var i = 0, ci; ci = c[i]; i++){
5567                     if(ci.firstChild && ci.firstChild.nodeValue == v){
5568                         r[++ri] = ci;
5569                     }
5570                 }
5571                 return r;
5572             },
5573
5574             "checked" : function(c){
5575                 var r = [], ri = -1;
5576                 for(var i = 0, ci; ci = c[i]; i++){
5577                     if(ci.checked == true){
5578                         r[++ri] = ci;
5579                     }
5580                 }
5581                 return r;
5582             },
5583
5584             "not" : function(c, ss){
5585                 return Roo.DomQuery.filter(c, ss, true);
5586             },
5587
5588             "odd" : function(c){
5589                 return this["nth-child"](c, "odd");
5590             },
5591
5592             "even" : function(c){
5593                 return this["nth-child"](c, "even");
5594             },
5595
5596             "nth" : function(c, a){
5597                 return c[a-1] || [];
5598             },
5599
5600             "first" : function(c){
5601                 return c[0] || [];
5602             },
5603
5604             "last" : function(c){
5605                 return c[c.length-1] || [];
5606             },
5607
5608             "has" : function(c, ss){
5609                 var s = Roo.DomQuery.select;
5610                 var r = [], ri = -1;
5611                 for(var i = 0, ci; ci = c[i]; i++){
5612                     if(s(ss, ci).length > 0){
5613                         r[++ri] = ci;
5614                     }
5615                 }
5616                 return r;
5617             },
5618
5619             "next" : function(c, ss){
5620                 var is = Roo.DomQuery.is;
5621                 var r = [], ri = -1;
5622                 for(var i = 0, ci; ci = c[i]; i++){
5623                     var n = next(ci);
5624                     if(n && is(n, ss)){
5625                         r[++ri] = ci;
5626                     }
5627                 }
5628                 return r;
5629             },
5630
5631             "prev" : function(c, ss){
5632                 var is = Roo.DomQuery.is;
5633                 var r = [], ri = -1;
5634                 for(var i = 0, ci; ci = c[i]; i++){
5635                     var n = prev(ci);
5636                     if(n && is(n, ss)){
5637                         r[++ri] = ci;
5638                     }
5639                 }
5640                 return r;
5641             }
5642         }
5643     };
5644 }();
5645
5646 /**
5647  * Selects an array of DOM nodes by CSS/XPath selector. Shorthand of {@link Roo.DomQuery#select}
5648  * @param {String} path The selector/xpath query
5649  * @param {Node} root (optional) The start of the query (defaults to document).
5650  * @return {Array}
5651  * @member Roo
5652  * @method query
5653  */
5654 Roo.query = Roo.DomQuery.select;
5655 /*
5656  * Based on:
5657  * Ext JS Library 1.1.1
5658  * Copyright(c) 2006-2007, Ext JS, LLC.
5659  *
5660  * Originally Released Under LGPL - original licence link has changed is not relivant.
5661  *
5662  * Fork - LGPL
5663  * <script type="text/javascript">
5664  */
5665
5666 /**
5667  * @class Roo.util.Observable
5668  * Base class that provides a common interface for publishing events. Subclasses are expected to
5669  * to have a property "events" with all the events defined.<br>
5670  * For example:
5671  * <pre><code>
5672  Employee = function(name){
5673     this.name = name;
5674     this.addEvents({
5675         "fired" : true,
5676         "quit" : true
5677     });
5678  }
5679  Roo.extend(Employee, Roo.util.Observable);
5680 </code></pre>
5681  * @param {Object} config properties to use (incuding events / listeners)
5682  */
5683
5684 Roo.util.Observable = function(cfg){
5685     
5686     cfg = cfg|| {};
5687     this.addEvents(cfg.events || {});
5688     if (cfg.events) {
5689         delete cfg.events; // make sure
5690     }
5691      
5692     Roo.apply(this, cfg);
5693     
5694     if(this.listeners){
5695         this.on(this.listeners);
5696         delete this.listeners;
5697     }
5698 };
5699 Roo.util.Observable.prototype = {
5700     /** 
5701  * @cfg {Object} listeners  list of events and functions to call for this object, 
5702  * For example :
5703  * <pre><code>
5704     listeners :  { 
5705        'click' : function(e) {
5706            ..... 
5707         } ,
5708         .... 
5709     } 
5710   </code></pre>
5711  */
5712     
5713     
5714     /**
5715      * Fires the specified event with the passed parameters (minus the event name).
5716      * @param {String} eventName
5717      * @param {Object...} args Variable number of parameters are passed to handlers
5718      * @return {Boolean} returns false if any of the handlers return false otherwise it returns true
5719      */
5720     fireEvent : function(){
5721         var ce = this.events[arguments[0].toLowerCase()];
5722         if(typeof ce == "object"){
5723             return ce.fire.apply(ce, Array.prototype.slice.call(arguments, 1));
5724         }else{
5725             return true;
5726         }
5727     },
5728
5729     // private
5730     filterOptRe : /^(?:scope|delay|buffer|single)$/,
5731
5732     /**
5733      * Appends an event handler to this component
5734      * @param {String}   eventName The type of event to listen for
5735      * @param {Function} handler The method the event invokes
5736      * @param {Object}   scope (optional) The scope in which to execute the handler
5737      * function. The handler function's "this" context.
5738      * @param {Object}   options (optional) An object containing handler configuration
5739      * properties. This may contain any of the following properties:<ul>
5740      * <li>scope {Object} The scope in which to execute the handler function. The handler function's "this" context.</li>
5741      * <li>delay {Number} The number of milliseconds to delay the invocation of the handler after te event fires.</li>
5742      * <li>single {Boolean} True to add a handler to handle just the next firing of the event, and then remove itself.</li>
5743      * <li>buffer {Number} Causes the handler to be scheduled to run in an {@link Roo.util.DelayedTask} delayed
5744      * by the specified number of milliseconds. If the event fires again within that time, the original
5745      * handler is <em>not</em> invoked, but the new handler is scheduled in its place.</li>
5746      * </ul><br>
5747      * <p>
5748      * <b>Combining Options</b><br>
5749      * Using the options argument, it is possible to combine different types of listeners:<br>
5750      * <br>
5751      * A normalized, delayed, one-time listener that auto stops the event and passes a custom argument (forumId)
5752                 <pre><code>
5753                 el.on('click', this.onClick, this, {
5754                         single: true,
5755                 delay: 100,
5756                 forumId: 4
5757                 });
5758                 </code></pre>
5759      * <p>
5760      * <b>Attaching multiple handlers in 1 call</b><br>
5761      * The method also allows for a single argument to be passed which is a config object containing properties
5762      * which specify multiple handlers.
5763      * <pre><code>
5764                 el.on({
5765                         'click': {
5766                         fn: this.onClick,
5767                         scope: this,
5768                         delay: 100
5769                 }, 
5770                 'mouseover': {
5771                         fn: this.onMouseOver,
5772                         scope: this
5773                 },
5774                 'mouseout': {
5775                         fn: this.onMouseOut,
5776                         scope: this
5777                 }
5778                 });
5779                 </code></pre>
5780      * <p>
5781      * Or a shorthand syntax which passes the same scope object to all handlers:
5782         <pre><code>
5783                 el.on({
5784                         'click': this.onClick,
5785                 'mouseover': this.onMouseOver,
5786                 'mouseout': this.onMouseOut,
5787                 scope: this
5788                 });
5789                 </code></pre>
5790      */
5791     addListener : function(eventName, fn, scope, o){
5792         if(typeof eventName == "object"){
5793             o = eventName;
5794             for(var e in o){
5795                 if(this.filterOptRe.test(e)){
5796                     continue;
5797                 }
5798                 if(typeof o[e] == "function"){
5799                     // shared options
5800                     this.addListener(e, o[e], o.scope,  o);
5801                 }else{
5802                     // individual options
5803                     this.addListener(e, o[e].fn, o[e].scope, o[e]);
5804                 }
5805             }
5806             return;
5807         }
5808         o = (!o || typeof o == "boolean") ? {} : o;
5809         eventName = eventName.toLowerCase();
5810         var ce = this.events[eventName] || true;
5811         if(typeof ce == "boolean"){
5812             ce = new Roo.util.Event(this, eventName);
5813             this.events[eventName] = ce;
5814         }
5815         ce.addListener(fn, scope, o);
5816     },
5817
5818     /**
5819      * Removes a listener
5820      * @param {String}   eventName     The type of event to listen for
5821      * @param {Function} handler        The handler to remove
5822      * @param {Object}   scope  (optional) The scope (this object) for the handler
5823      */
5824     removeListener : function(eventName, fn, scope){
5825         var ce = this.events[eventName.toLowerCase()];
5826         if(typeof ce == "object"){
5827             ce.removeListener(fn, scope);
5828         }
5829     },
5830
5831     /**
5832      * Removes all listeners for this object
5833      */
5834     purgeListeners : function(){
5835         for(var evt in this.events){
5836             if(typeof this.events[evt] == "object"){
5837                  this.events[evt].clearListeners();
5838             }
5839         }
5840     },
5841
5842     relayEvents : function(o, events){
5843         var createHandler = function(ename){
5844             return function(){
5845                 return this.fireEvent.apply(this, Roo.combine(ename, Array.prototype.slice.call(arguments, 0)));
5846             };
5847         };
5848         for(var i = 0, len = events.length; i < len; i++){
5849             var ename = events[i];
5850             if(!this.events[ename]){ this.events[ename] = true; };
5851             o.on(ename, createHandler(ename), this);
5852         }
5853     },
5854
5855     /**
5856      * Used to define events on this Observable
5857      * @param {Object} object The object with the events defined
5858      */
5859     addEvents : function(o){
5860         if(!this.events){
5861             this.events = {};
5862         }
5863         Roo.applyIf(this.events, o);
5864     },
5865
5866     /**
5867      * Checks to see if this object has any listeners for a specified event
5868      * @param {String} eventName The name of the event to check for
5869      * @return {Boolean} True if the event is being listened for, else false
5870      */
5871     hasListener : function(eventName){
5872         var e = this.events[eventName];
5873         return typeof e == "object" && e.listeners.length > 0;
5874     }
5875 };
5876 /**
5877  * Appends an event handler to this element (shorthand for addListener)
5878  * @param {String}   eventName     The type of event to listen for
5879  * @param {Function} handler        The method the event invokes
5880  * @param {Object}   scope (optional) The scope in which to execute the handler
5881  * function. The handler function's "this" context.
5882  * @param {Object}   options  (optional)
5883  * @method
5884  */
5885 Roo.util.Observable.prototype.on = Roo.util.Observable.prototype.addListener;
5886 /**
5887  * Removes a listener (shorthand for removeListener)
5888  * @param {String}   eventName     The type of event to listen for
5889  * @param {Function} handler        The handler to remove
5890  * @param {Object}   scope  (optional) The scope (this object) for the handler
5891  * @method
5892  */
5893 Roo.util.Observable.prototype.un = Roo.util.Observable.prototype.removeListener;
5894
5895 /**
5896  * Starts capture on the specified Observable. All events will be passed
5897  * to the supplied function with the event name + standard signature of the event
5898  * <b>before</b> the event is fired. If the supplied function returns false,
5899  * the event will not fire.
5900  * @param {Observable} o The Observable to capture
5901  * @param {Function} fn The function to call
5902  * @param {Object} scope (optional) The scope (this object) for the fn
5903  * @static
5904  */
5905 Roo.util.Observable.capture = function(o, fn, scope){
5906     o.fireEvent = o.fireEvent.createInterceptor(fn, scope);
5907 };
5908
5909 /**
5910  * Removes <b>all</b> added captures from the Observable.
5911  * @param {Observable} o The Observable to release
5912  * @static
5913  */
5914 Roo.util.Observable.releaseCapture = function(o){
5915     o.fireEvent = Roo.util.Observable.prototype.fireEvent;
5916 };
5917
5918 (function(){
5919
5920     var createBuffered = function(h, o, scope){
5921         var task = new Roo.util.DelayedTask();
5922         return function(){
5923             task.delay(o.buffer, h, scope, Array.prototype.slice.call(arguments, 0));
5924         };
5925     };
5926
5927     var createSingle = function(h, e, fn, scope){
5928         return function(){
5929             e.removeListener(fn, scope);
5930             return h.apply(scope, arguments);
5931         };
5932     };
5933
5934     var createDelayed = function(h, o, scope){
5935         return function(){
5936             var args = Array.prototype.slice.call(arguments, 0);
5937             setTimeout(function(){
5938                 h.apply(scope, args);
5939             }, o.delay || 10);
5940         };
5941     };
5942
5943     Roo.util.Event = function(obj, name){
5944         this.name = name;
5945         this.obj = obj;
5946         this.listeners = [];
5947     };
5948
5949     Roo.util.Event.prototype = {
5950         addListener : function(fn, scope, options){
5951             var o = options || {};
5952             scope = scope || this.obj;
5953             if(!this.isListening(fn, scope)){
5954                 var l = {fn: fn, scope: scope, options: o};
5955                 var h = fn;
5956                 if(o.delay){
5957                     h = createDelayed(h, o, scope);
5958                 }
5959                 if(o.single){
5960                     h = createSingle(h, this, fn, scope);
5961                 }
5962                 if(o.buffer){
5963                     h = createBuffered(h, o, scope);
5964                 }
5965                 l.fireFn = h;
5966                 if(!this.firing){ // if we are currently firing this event, don't disturb the listener loop
5967                     this.listeners.push(l);
5968                 }else{
5969                     this.listeners = this.listeners.slice(0);
5970                     this.listeners.push(l);
5971                 }
5972             }
5973         },
5974
5975         findListener : function(fn, scope){
5976             scope = scope || this.obj;
5977             var ls = this.listeners;
5978             for(var i = 0, len = ls.length; i < len; i++){
5979                 var l = ls[i];
5980                 if(l.fn == fn && l.scope == scope){
5981                     return i;
5982                 }
5983             }
5984             return -1;
5985         },
5986
5987         isListening : function(fn, scope){
5988             return this.findListener(fn, scope) != -1;
5989         },
5990
5991         removeListener : function(fn, scope){
5992             var index;
5993             if((index = this.findListener(fn, scope)) != -1){
5994                 if(!this.firing){
5995                     this.listeners.splice(index, 1);
5996                 }else{
5997                     this.listeners = this.listeners.slice(0);
5998                     this.listeners.splice(index, 1);
5999                 }
6000                 return true;
6001             }
6002             return false;
6003         },
6004
6005         clearListeners : function(){
6006             this.listeners = [];
6007         },
6008
6009         fire : function(){
6010             var ls = this.listeners, scope, len = ls.length;
6011             if(len > 0){
6012                 this.firing = true;
6013                 var args = Array.prototype.slice.call(arguments, 0);
6014                 for(var i = 0; i < len; i++){
6015                     var l = ls[i];
6016                     if(l.fireFn.apply(l.scope||this.obj||window, arguments) === false){
6017                         this.firing = false;
6018                         return false;
6019                     }
6020                 }
6021                 this.firing = false;
6022             }
6023             return true;
6024         }
6025     };
6026 })();/*
6027  * Based on:
6028  * Ext JS Library 1.1.1
6029  * Copyright(c) 2006-2007, Ext JS, LLC.
6030  *
6031  * Originally Released Under LGPL - original licence link has changed is not relivant.
6032  *
6033  * Fork - LGPL
6034  * <script type="text/javascript">
6035  */
6036
6037 /**
6038  * @class Roo.EventManager
6039  * Registers event handlers that want to receive a normalized EventObject instead of the standard browser event and provides 
6040  * several useful events directly.
6041  * See {@link Roo.EventObject} for more details on normalized event objects.
6042  * @singleton
6043  */
6044 Roo.EventManager = function(){
6045     var docReadyEvent, docReadyProcId, docReadyState = false;
6046     var resizeEvent, resizeTask, textEvent, textSize;
6047     var E = Roo.lib.Event;
6048     var D = Roo.lib.Dom;
6049
6050     
6051     
6052
6053     var fireDocReady = function(){
6054         if(!docReadyState){
6055             docReadyState = true;
6056             Roo.isReady = true;
6057             if(docReadyProcId){
6058                 clearInterval(docReadyProcId);
6059             }
6060             if(Roo.isGecko || Roo.isOpera) {
6061                 document.removeEventListener("DOMContentLoaded", fireDocReady, false);
6062             }
6063             if(Roo.isIE){
6064                 var defer = document.getElementById("ie-deferred-loader");
6065                 if(defer){
6066                     defer.onreadystatechange = null;
6067                     defer.parentNode.removeChild(defer);
6068                 }
6069             }
6070             if(docReadyEvent){
6071                 docReadyEvent.fire();
6072                 docReadyEvent.clearListeners();
6073             }
6074         }
6075     };
6076     
6077     var initDocReady = function(){
6078         docReadyEvent = new Roo.util.Event();
6079         if(Roo.isGecko || Roo.isOpera) {
6080             document.addEventListener("DOMContentLoaded", fireDocReady, false);
6081         }else if(Roo.isIE){
6082             document.write("<s"+'cript id="ie-deferred-loader" defer="defer" src="/'+'/:"></s'+"cript>");
6083             var defer = document.getElementById("ie-deferred-loader");
6084             defer.onreadystatechange = function(){
6085                 if(this.readyState == "complete"){
6086                     fireDocReady();
6087                 }
6088             };
6089         }else if(Roo.isSafari){ 
6090             docReadyProcId = setInterval(function(){
6091                 var rs = document.readyState;
6092                 if(rs == "complete") {
6093                     fireDocReady();     
6094                  }
6095             }, 10);
6096         }
6097         // no matter what, make sure it fires on load
6098         E.on(window, "load", fireDocReady);
6099     };
6100
6101     var createBuffered = function(h, o){
6102         var task = new Roo.util.DelayedTask(h);
6103         return function(e){
6104             // create new event object impl so new events don't wipe out properties
6105             e = new Roo.EventObjectImpl(e);
6106             task.delay(o.buffer, h, null, [e]);
6107         };
6108     };
6109
6110     var createSingle = function(h, el, ename, fn){
6111         return function(e){
6112             Roo.EventManager.removeListener(el, ename, fn);
6113             h(e);
6114         };
6115     };
6116
6117     var createDelayed = function(h, o){
6118         return function(e){
6119             // create new event object impl so new events don't wipe out properties
6120             e = new Roo.EventObjectImpl(e);
6121             setTimeout(function(){
6122                 h(e);
6123             }, o.delay || 10);
6124         };
6125     };
6126     var transitionEndVal = false;
6127     
6128     var transitionEnd = function()
6129     {
6130         if (transitionEndVal) {
6131             return transitionEndVal;
6132         }
6133         var el = document.createElement('div');
6134
6135         var transEndEventNames = {
6136             WebkitTransition : 'webkitTransitionEnd',
6137             MozTransition    : 'transitionend',
6138             OTransition      : 'oTransitionEnd otransitionend',
6139             transition       : 'transitionend'
6140         };
6141     
6142         for (var name in transEndEventNames) {
6143             if (el.style[name] !== undefined) {
6144                 transitionEndVal = transEndEventNames[name];
6145                 return  transitionEndVal ;
6146             }
6147         }
6148     }
6149     
6150
6151     var listen = function(element, ename, opt, fn, scope){
6152         var o = (!opt || typeof opt == "boolean") ? {} : opt;
6153         fn = fn || o.fn; scope = scope || o.scope;
6154         var el = Roo.getDom(element);
6155         
6156         
6157         if(!el){
6158             throw "Error listening for \"" + ename + '\". Element "' + element + '" doesn\'t exist.';
6159         }
6160         
6161         if (ename == 'transitionend') {
6162             ename = transitionEnd();
6163         }
6164         var h = function(e){
6165             e = Roo.EventObject.setEvent(e);
6166             var t;
6167             if(o.delegate){
6168                 t = e.getTarget(o.delegate, el);
6169                 if(!t){
6170                     return;
6171                 }
6172             }else{
6173                 t = e.target;
6174             }
6175             if(o.stopEvent === true){
6176                 e.stopEvent();
6177             }
6178             if(o.preventDefault === true){
6179                e.preventDefault();
6180             }
6181             if(o.stopPropagation === true){
6182                 e.stopPropagation();
6183             }
6184
6185             if(o.normalized === false){
6186                 e = e.browserEvent;
6187             }
6188
6189             fn.call(scope || el, e, t, o);
6190         };
6191         if(o.delay){
6192             h = createDelayed(h, o);
6193         }
6194         if(o.single){
6195             h = createSingle(h, el, ename, fn);
6196         }
6197         if(o.buffer){
6198             h = createBuffered(h, o);
6199         }
6200         fn._handlers = fn._handlers || [];
6201         
6202         
6203         fn._handlers.push([Roo.id(el), ename, h]);
6204         
6205         
6206          
6207         E.on(el, ename, h);
6208         if(ename == "mousewheel" && el.addEventListener){ // workaround for jQuery
6209             el.addEventListener("DOMMouseScroll", h, false);
6210             E.on(window, 'unload', function(){
6211                 el.removeEventListener("DOMMouseScroll", h, false);
6212             });
6213         }
6214         if(ename == "mousedown" && el == document){ // fix stopped mousedowns on the document
6215             Roo.EventManager.stoppedMouseDownEvent.addListener(h);
6216         }
6217         return h;
6218     };
6219
6220     var stopListening = function(el, ename, fn){
6221         var id = Roo.id(el), hds = fn._handlers, hd = fn;
6222         if(hds){
6223             for(var i = 0, len = hds.length; i < len; i++){
6224                 var h = hds[i];
6225                 if(h[0] == id && h[1] == ename){
6226                     hd = h[2];
6227                     hds.splice(i, 1);
6228                     break;
6229                 }
6230             }
6231         }
6232         E.un(el, ename, hd);
6233         el = Roo.getDom(el);
6234         if(ename == "mousewheel" && el.addEventListener){
6235             el.removeEventListener("DOMMouseScroll", hd, false);
6236         }
6237         if(ename == "mousedown" && el == document){ // fix stopped mousedowns on the document
6238             Roo.EventManager.stoppedMouseDownEvent.removeListener(hd);
6239         }
6240     };
6241
6242     var propRe = /^(?:scope|delay|buffer|single|stopEvent|preventDefault|stopPropagation|normalized|args|delegate)$/;
6243     
6244     var pub = {
6245         
6246         
6247         /** 
6248          * Fix for doc tools
6249          * @scope Roo.EventManager
6250          */
6251         
6252         
6253         /** 
6254          * This is no longer needed and is deprecated. Places a simple wrapper around an event handler to override the browser event
6255          * object with a Roo.EventObject
6256          * @param {Function} fn        The method the event invokes
6257          * @param {Object}   scope    An object that becomes the scope of the handler
6258          * @param {boolean}  override If true, the obj passed in becomes
6259          *                             the execution scope of the listener
6260          * @return {Function} The wrapped function
6261          * @deprecated
6262          */
6263         wrap : function(fn, scope, override){
6264             return function(e){
6265                 Roo.EventObject.setEvent(e);
6266                 fn.call(override ? scope || window : window, Roo.EventObject, scope);
6267             };
6268         },
6269         
6270         /**
6271      * Appends an event handler to an element (shorthand for addListener)
6272      * @param {String/HTMLElement}   element        The html element or id to assign the
6273      * @param {String}   eventName The type of event to listen for
6274      * @param {Function} handler The method the event invokes
6275      * @param {Object}   scope (optional) The scope in which to execute the handler
6276      * function. The handler function's "this" context.
6277      * @param {Object}   options (optional) An object containing handler configuration
6278      * properties. This may contain any of the following properties:<ul>
6279      * <li>scope {Object} The scope in which to execute the handler function. The handler function's "this" context.</li>
6280      * <li>delegate {String} A simple selector to filter the target or look for a descendant of the target</li>
6281      * <li>stopEvent {Boolean} True to stop the event. That is stop propagation, and prevent the default action.</li>
6282      * <li>preventDefault {Boolean} True to prevent the default action</li>
6283      * <li>stopPropagation {Boolean} True to prevent event propagation</li>
6284      * <li>normalized {Boolean} False to pass a browser event to the handler function instead of an Roo.EventObject</li>
6285      * <li>delay {Number} The number of milliseconds to delay the invocation of the handler after te event fires.</li>
6286      * <li>single {Boolean} True to add a handler to handle just the next firing of the event, and then remove itself.</li>
6287      * <li>buffer {Number} Causes the handler to be scheduled to run in an {@link Roo.util.DelayedTask} delayed
6288      * by the specified number of milliseconds. If the event fires again within that time, the original
6289      * handler is <em>not</em> invoked, but the new handler is scheduled in its place.</li>
6290      * </ul><br>
6291      * <p>
6292      * <b>Combining Options</b><br>
6293      * Using the options argument, it is possible to combine different types of listeners:<br>
6294      * <br>
6295      * A normalized, delayed, one-time listener that auto stops the event and passes a custom argument (forumId)<div style="margin: 5px 20px 20px;">
6296      * Code:<pre><code>
6297 el.on('click', this.onClick, this, {
6298     single: true,
6299     delay: 100,
6300     stopEvent : true,
6301     forumId: 4
6302 });</code></pre>
6303      * <p>
6304      * <b>Attaching multiple handlers in 1 call</b><br>
6305       * The method also allows for a single argument to be passed which is a config object containing properties
6306      * which specify multiple handlers.
6307      * <p>
6308      * Code:<pre><code>
6309 el.on({
6310     'click' : {
6311         fn: this.onClick
6312         scope: this,
6313         delay: 100
6314     },
6315     'mouseover' : {
6316         fn: this.onMouseOver
6317         scope: this
6318     },
6319     'mouseout' : {
6320         fn: this.onMouseOut
6321         scope: this
6322     }
6323 });</code></pre>
6324      * <p>
6325      * Or a shorthand syntax:<br>
6326      * Code:<pre><code>
6327 el.on({
6328     'click' : this.onClick,
6329     'mouseover' : this.onMouseOver,
6330     'mouseout' : this.onMouseOut
6331     scope: this
6332 });</code></pre>
6333      */
6334         addListener : function(element, eventName, fn, scope, options){
6335             if(typeof eventName == "object"){
6336                 var o = eventName;
6337                 for(var e in o){
6338                     if(propRe.test(e)){
6339                         continue;
6340                     }
6341                     if(typeof o[e] == "function"){
6342                         // shared options
6343                         listen(element, e, o, o[e], o.scope);
6344                     }else{
6345                         // individual options
6346                         listen(element, e, o[e]);
6347                     }
6348                 }
6349                 return;
6350             }
6351             return listen(element, eventName, options, fn, scope);
6352         },
6353         
6354         /**
6355          * Removes an event handler
6356          *
6357          * @param {String/HTMLElement}   element        The id or html element to remove the 
6358          *                             event from
6359          * @param {String}   eventName     The type of event
6360          * @param {Function} fn
6361          * @return {Boolean} True if a listener was actually removed
6362          */
6363         removeListener : function(element, eventName, fn){
6364             return stopListening(element, eventName, fn);
6365         },
6366         
6367         /**
6368          * Fires when the document is ready (before onload and before images are loaded). Can be 
6369          * accessed shorthanded Roo.onReady().
6370          * @param {Function} fn        The method the event invokes
6371          * @param {Object}   scope    An  object that becomes the scope of the handler
6372          * @param {boolean}  options
6373          */
6374         onDocumentReady : function(fn, scope, options){
6375             if(docReadyState){ // if it already fired
6376                 docReadyEvent.addListener(fn, scope, options);
6377                 docReadyEvent.fire();
6378                 docReadyEvent.clearListeners();
6379                 return;
6380             }
6381             if(!docReadyEvent){
6382                 initDocReady();
6383             }
6384             docReadyEvent.addListener(fn, scope, options);
6385         },
6386         
6387         /**
6388          * Fires when the window is resized and provides resize event buffering (50 milliseconds), passes new viewport width and height to handlers.
6389          * @param {Function} fn        The method the event invokes
6390          * @param {Object}   scope    An object that becomes the scope of the handler
6391          * @param {boolean}  options
6392          */
6393         onWindowResize : function(fn, scope, options){
6394             if(!resizeEvent){
6395                 resizeEvent = new Roo.util.Event();
6396                 resizeTask = new Roo.util.DelayedTask(function(){
6397                     resizeEvent.fire(D.getViewWidth(), D.getViewHeight());
6398                 });
6399                 E.on(window, "resize", function(){
6400                     if(Roo.isIE){
6401                         resizeTask.delay(50);
6402                     }else{
6403                         resizeEvent.fire(D.getViewWidth(), D.getViewHeight());
6404                     }
6405                 });
6406             }
6407             resizeEvent.addListener(fn, scope, options);
6408         },
6409
6410         /**
6411          * Fires when the user changes the active text size. Handler gets called with 2 params, the old size and the new size.
6412          * @param {Function} fn        The method the event invokes
6413          * @param {Object}   scope    An object that becomes the scope of the handler
6414          * @param {boolean}  options
6415          */
6416         onTextResize : function(fn, scope, options){
6417             if(!textEvent){
6418                 textEvent = new Roo.util.Event();
6419                 var textEl = new Roo.Element(document.createElement('div'));
6420                 textEl.dom.className = 'x-text-resize';
6421                 textEl.dom.innerHTML = 'X';
6422                 textEl.appendTo(document.body);
6423                 textSize = textEl.dom.offsetHeight;
6424                 setInterval(function(){
6425                     if(textEl.dom.offsetHeight != textSize){
6426                         textEvent.fire(textSize, textSize = textEl.dom.offsetHeight);
6427                     }
6428                 }, this.textResizeInterval);
6429             }
6430             textEvent.addListener(fn, scope, options);
6431         },
6432
6433         /**
6434          * Removes the passed window resize listener.
6435          * @param {Function} fn        The method the event invokes
6436          * @param {Object}   scope    The scope of handler
6437          */
6438         removeResizeListener : function(fn, scope){
6439             if(resizeEvent){
6440                 resizeEvent.removeListener(fn, scope);
6441             }
6442         },
6443
6444         // private
6445         fireResize : function(){
6446             if(resizeEvent){
6447                 resizeEvent.fire(D.getViewWidth(), D.getViewHeight());
6448             }   
6449         },
6450         /**
6451          * Url used for onDocumentReady with using SSL (defaults to Roo.SSL_SECURE_URL)
6452          */
6453         ieDeferSrc : false,
6454         /**
6455          * The frequency, in milliseconds, to check for text resize events (defaults to 50)
6456          */
6457         textResizeInterval : 50
6458     };
6459     
6460     /**
6461      * Fix for doc tools
6462      * @scopeAlias pub=Roo.EventManager
6463      */
6464     
6465      /**
6466      * Appends an event handler to an element (shorthand for addListener)
6467      * @param {String/HTMLElement}   element        The html element or id to assign the
6468      * @param {String}   eventName The type of event to listen for
6469      * @param {Function} handler The method the event invokes
6470      * @param {Object}   scope (optional) The scope in which to execute the handler
6471      * function. The handler function's "this" context.
6472      * @param {Object}   options (optional) An object containing handler configuration
6473      * properties. This may contain any of the following properties:<ul>
6474      * <li>scope {Object} The scope in which to execute the handler function. The handler function's "this" context.</li>
6475      * <li>delegate {String} A simple selector to filter the target or look for a descendant of the target</li>
6476      * <li>stopEvent {Boolean} True to stop the event. That is stop propagation, and prevent the default action.</li>
6477      * <li>preventDefault {Boolean} True to prevent the default action</li>
6478      * <li>stopPropagation {Boolean} True to prevent event propagation</li>
6479      * <li>normalized {Boolean} False to pass a browser event to the handler function instead of an Roo.EventObject</li>
6480      * <li>delay {Number} The number of milliseconds to delay the invocation of the handler after te event fires.</li>
6481      * <li>single {Boolean} True to add a handler to handle just the next firing of the event, and then remove itself.</li>
6482      * <li>buffer {Number} Causes the handler to be scheduled to run in an {@link Roo.util.DelayedTask} delayed
6483      * by the specified number of milliseconds. If the event fires again within that time, the original
6484      * handler is <em>not</em> invoked, but the new handler is scheduled in its place.</li>
6485      * </ul><br>
6486      * <p>
6487      * <b>Combining Options</b><br>
6488      * Using the options argument, it is possible to combine different types of listeners:<br>
6489      * <br>
6490      * A normalized, delayed, one-time listener that auto stops the event and passes a custom argument (forumId)<div style="margin: 5px 20px 20px;">
6491      * Code:<pre><code>
6492 el.on('click', this.onClick, this, {
6493     single: true,
6494     delay: 100,
6495     stopEvent : true,
6496     forumId: 4
6497 });</code></pre>
6498      * <p>
6499      * <b>Attaching multiple handlers in 1 call</b><br>
6500       * The method also allows for a single argument to be passed which is a config object containing properties
6501      * which specify multiple handlers.
6502      * <p>
6503      * Code:<pre><code>
6504 el.on({
6505     'click' : {
6506         fn: this.onClick
6507         scope: this,
6508         delay: 100
6509     },
6510     'mouseover' : {
6511         fn: this.onMouseOver
6512         scope: this
6513     },
6514     'mouseout' : {
6515         fn: this.onMouseOut
6516         scope: this
6517     }
6518 });</code></pre>
6519      * <p>
6520      * Or a shorthand syntax:<br>
6521      * Code:<pre><code>
6522 el.on({
6523     'click' : this.onClick,
6524     'mouseover' : this.onMouseOver,
6525     'mouseout' : this.onMouseOut
6526     scope: this
6527 });</code></pre>
6528      */
6529     pub.on = pub.addListener;
6530     pub.un = pub.removeListener;
6531
6532     pub.stoppedMouseDownEvent = new Roo.util.Event();
6533     return pub;
6534 }();
6535 /**
6536   * Fires when the document is ready (before onload and before images are loaded).  Shorthand of {@link Roo.EventManager#onDocumentReady}.
6537   * @param {Function} fn        The method the event invokes
6538   * @param {Object}   scope    An  object that becomes the scope of the handler
6539   * @param {boolean}  override If true, the obj passed in becomes
6540   *                             the execution scope of the listener
6541   * @member Roo
6542   * @method onReady
6543  */
6544 Roo.onReady = Roo.EventManager.onDocumentReady;
6545
6546 Roo.onReady(function(){
6547     var bd = Roo.get(document.body);
6548     if(!bd){ return; }
6549
6550     var cls = [
6551             Roo.isIE ? "roo-ie"
6552             : Roo.isGecko ? "roo-gecko"
6553             : Roo.isOpera ? "roo-opera"
6554             : Roo.isSafari ? "roo-safari" : ""];
6555
6556     if(Roo.isMac){
6557         cls.push("roo-mac");
6558     }
6559     if(Roo.isLinux){
6560         cls.push("roo-linux");
6561     }
6562     if(Roo.isBorderBox){
6563         cls.push('roo-border-box');
6564     }
6565     if(Roo.isStrict){ // add to the parent to allow for selectors like ".ext-strict .ext-ie"
6566         var p = bd.dom.parentNode;
6567         if(p){
6568             p.className += ' roo-strict';
6569         }
6570     }
6571     bd.addClass(cls.join(' '));
6572 });
6573
6574 /**
6575  * @class Roo.EventObject
6576  * EventObject exposes the Yahoo! UI Event functionality directly on the object
6577  * passed to your event handler. It exists mostly for convenience. It also fixes the annoying null checks automatically to cleanup your code 
6578  * Example:
6579  * <pre><code>
6580  function handleClick(e){ // e is not a standard event object, it is a Roo.EventObject
6581     e.preventDefault();
6582     var target = e.getTarget();
6583     ...
6584  }
6585  var myDiv = Roo.get("myDiv");
6586  myDiv.on("click", handleClick);
6587  //or
6588  Roo.EventManager.on("myDiv", 'click', handleClick);
6589  Roo.EventManager.addListener("myDiv", 'click', handleClick);
6590  </code></pre>
6591  * @singleton
6592  */
6593 Roo.EventObject = function(){
6594     
6595     var E = Roo.lib.Event;
6596     
6597     // safari keypress events for special keys return bad keycodes
6598     var safariKeys = {
6599         63234 : 37, // left
6600         63235 : 39, // right
6601         63232 : 38, // up
6602         63233 : 40, // down
6603         63276 : 33, // page up
6604         63277 : 34, // page down
6605         63272 : 46, // delete
6606         63273 : 36, // home
6607         63275 : 35  // end
6608     };
6609
6610     // normalize button clicks
6611     var btnMap = Roo.isIE ? {1:0,4:1,2:2} :
6612                 (Roo.isSafari ? {1:0,2:1,3:2} : {0:0,1:1,2:2});
6613
6614     Roo.EventObjectImpl = function(e){
6615         if(e){
6616             this.setEvent(e.browserEvent || e);
6617         }
6618     };
6619     Roo.EventObjectImpl.prototype = {
6620         /**
6621          * Used to fix doc tools.
6622          * @scope Roo.EventObject.prototype
6623          */
6624             
6625
6626         
6627         
6628         /** The normal browser event */
6629         browserEvent : null,
6630         /** The button pressed in a mouse event */
6631         button : -1,
6632         /** True if the shift key was down during the event */
6633         shiftKey : false,
6634         /** True if the control key was down during the event */
6635         ctrlKey : false,
6636         /** True if the alt key was down during the event */
6637         altKey : false,
6638
6639         /** Key constant 
6640         * @type Number */
6641         BACKSPACE : 8,
6642         /** Key constant 
6643         * @type Number */
6644         TAB : 9,
6645         /** Key constant 
6646         * @type Number */
6647         RETURN : 13,
6648         /** Key constant 
6649         * @type Number */
6650         ENTER : 13,
6651         /** Key constant 
6652         * @type Number */
6653         SHIFT : 16,
6654         /** Key constant 
6655         * @type Number */
6656         CONTROL : 17,
6657         /** Key constant 
6658         * @type Number */
6659         ESC : 27,
6660         /** Key constant 
6661         * @type Number */
6662         SPACE : 32,
6663         /** Key constant 
6664         * @type Number */
6665         PAGEUP : 33,
6666         /** Key constant 
6667         * @type Number */
6668         PAGEDOWN : 34,
6669         /** Key constant 
6670         * @type Number */
6671         END : 35,
6672         /** Key constant 
6673         * @type Number */
6674         HOME : 36,
6675         /** Key constant 
6676         * @type Number */
6677         LEFT : 37,
6678         /** Key constant 
6679         * @type Number */
6680         UP : 38,
6681         /** Key constant 
6682         * @type Number */
6683         RIGHT : 39,
6684         /** Key constant 
6685         * @type Number */
6686         DOWN : 40,
6687         /** Key constant 
6688         * @type Number */
6689         DELETE : 46,
6690         /** Key constant 
6691         * @type Number */
6692         F5 : 116,
6693
6694            /** @private */
6695         setEvent : function(e){
6696             if(e == this || (e && e.browserEvent)){ // already wrapped
6697                 return e;
6698             }
6699             this.browserEvent = e;
6700             if(e){
6701                 // normalize buttons
6702                 this.button = e.button ? btnMap[e.button] : (e.which ? e.which-1 : -1);
6703                 if(e.type == 'click' && this.button == -1){
6704                     this.button = 0;
6705                 }
6706                 this.type = e.type;
6707                 this.shiftKey = e.shiftKey;
6708                 // mac metaKey behaves like ctrlKey
6709                 this.ctrlKey = e.ctrlKey || e.metaKey;
6710                 this.altKey = e.altKey;
6711                 // in getKey these will be normalized for the mac
6712                 this.keyCode = e.keyCode;
6713                 // keyup warnings on firefox.
6714                 this.charCode = (e.type == 'keyup' || e.type == 'keydown') ? 0 : e.charCode;
6715                 // cache the target for the delayed and or buffered events
6716                 this.target = E.getTarget(e);
6717                 // same for XY
6718                 this.xy = E.getXY(e);
6719             }else{
6720                 this.button = -1;
6721                 this.shiftKey = false;
6722                 this.ctrlKey = false;
6723                 this.altKey = false;
6724                 this.keyCode = 0;
6725                 this.charCode =0;
6726                 this.target = null;
6727                 this.xy = [0, 0];
6728             }
6729             return this;
6730         },
6731
6732         /**
6733          * Stop the event (preventDefault and stopPropagation)
6734          */
6735         stopEvent : function(){
6736             if(this.browserEvent){
6737                 if(this.browserEvent.type == 'mousedown'){
6738                     Roo.EventManager.stoppedMouseDownEvent.fire(this);
6739                 }
6740                 E.stopEvent(this.browserEvent);
6741             }
6742         },
6743
6744         /**
6745          * Prevents the browsers default handling of the event.
6746          */
6747         preventDefault : function(){
6748             if(this.browserEvent){
6749                 E.preventDefault(this.browserEvent);
6750             }
6751         },
6752
6753         /** @private */
6754         isNavKeyPress : function(){
6755             var k = this.keyCode;
6756             k = Roo.isSafari ? (safariKeys[k] || k) : k;
6757             return (k >= 33 && k <= 40) || k == this.RETURN || k == this.TAB || k == this.ESC;
6758         },
6759
6760         isSpecialKey : function(){
6761             var k = this.keyCode;
6762             return (this.type == 'keypress' && this.ctrlKey) || k == 9 || k == 13  || k == 40 || k == 27 ||
6763             (k == 16) || (k == 17) ||
6764             (k >= 18 && k <= 20) ||
6765             (k >= 33 && k <= 35) ||
6766             (k >= 36 && k <= 39) ||
6767             (k >= 44 && k <= 45);
6768         },
6769         /**
6770          * Cancels bubbling of the event.
6771          */
6772         stopPropagation : function(){
6773             if(this.browserEvent){
6774                 if(this.type == 'mousedown'){
6775                     Roo.EventManager.stoppedMouseDownEvent.fire(this);
6776                 }
6777                 E.stopPropagation(this.browserEvent);
6778             }
6779         },
6780
6781         /**
6782          * Gets the key code for the event.
6783          * @return {Number}
6784          */
6785         getCharCode : function(){
6786             return this.charCode || this.keyCode;
6787         },
6788
6789         /**
6790          * Returns a normalized keyCode for the event.
6791          * @return {Number} The key code
6792          */
6793         getKey : function(){
6794             var k = this.keyCode || this.charCode;
6795             return Roo.isSafari ? (safariKeys[k] || k) : k;
6796         },
6797
6798         /**
6799          * Gets the x coordinate of the event.
6800          * @return {Number}
6801          */
6802         getPageX : function(){
6803             return this.xy[0];
6804         },
6805
6806         /**
6807          * Gets the y coordinate of the event.
6808          * @return {Number}
6809          */
6810         getPageY : function(){
6811             return this.xy[1];
6812         },
6813
6814         /**
6815          * Gets the time of the event.
6816          * @return {Number}
6817          */
6818         getTime : function(){
6819             if(this.browserEvent){
6820                 return E.getTime(this.browserEvent);
6821             }
6822             return null;
6823         },
6824
6825         /**
6826          * Gets the page coordinates of the event.
6827          * @return {Array} The xy values like [x, y]
6828          */
6829         getXY : function(){
6830             return this.xy;
6831         },
6832
6833         /**
6834          * Gets the target for the event.
6835          * @param {String} selector (optional) A simple selector to filter the target or look for an ancestor of the target
6836          * @param {Number/String/HTMLElement/Element} maxDepth (optional) The max depth to
6837                 search as a number or element (defaults to 10 || document.body)
6838          * @param {Boolean} returnEl (optional) True to return a Roo.Element object instead of DOM node
6839          * @return {HTMLelement}
6840          */
6841         getTarget : function(selector, maxDepth, returnEl){
6842             return selector ? Roo.fly(this.target).findParent(selector, maxDepth, returnEl) : this.target;
6843         },
6844         /**
6845          * Gets the related target.
6846          * @return {HTMLElement}
6847          */
6848         getRelatedTarget : function(){
6849             if(this.browserEvent){
6850                 return E.getRelatedTarget(this.browserEvent);
6851             }
6852             return null;
6853         },
6854
6855         /**
6856          * Normalizes mouse wheel delta across browsers
6857          * @return {Number} The delta
6858          */
6859         getWheelDelta : function(){
6860             var e = this.browserEvent;
6861             var delta = 0;
6862             if(e.wheelDelta){ /* IE/Opera. */
6863                 delta = e.wheelDelta/120;
6864             }else if(e.detail){ /* Mozilla case. */
6865                 delta = -e.detail/3;
6866             }
6867             return delta;
6868         },
6869
6870         /**
6871          * Returns true if the control, meta, shift or alt key was pressed during this event.
6872          * @return {Boolean}
6873          */
6874         hasModifier : function(){
6875             return !!((this.ctrlKey || this.altKey) || this.shiftKey);
6876         },
6877
6878         /**
6879          * Returns true if the target of this event equals el or is a child of el
6880          * @param {String/HTMLElement/Element} el
6881          * @param {Boolean} related (optional) true to test if the related target is within el instead of the target
6882          * @return {Boolean}
6883          */
6884         within : function(el, related){
6885             var t = this[related ? "getRelatedTarget" : "getTarget"]();
6886             return t && Roo.fly(el).contains(t);
6887         },
6888
6889         getPoint : function(){
6890             return new Roo.lib.Point(this.xy[0], this.xy[1]);
6891         }
6892     };
6893
6894     return new Roo.EventObjectImpl();
6895 }();
6896             
6897     /*
6898  * Based on:
6899  * Ext JS Library 1.1.1
6900  * Copyright(c) 2006-2007, Ext JS, LLC.
6901  *
6902  * Originally Released Under LGPL - original licence link has changed is not relivant.
6903  *
6904  * Fork - LGPL
6905  * <script type="text/javascript">
6906  */
6907
6908  
6909 // was in Composite Element!??!?!
6910  
6911 (function(){
6912     var D = Roo.lib.Dom;
6913     var E = Roo.lib.Event;
6914     var A = Roo.lib.Anim;
6915
6916     // local style camelizing for speed
6917     var propCache = {};
6918     var camelRe = /(-[a-z])/gi;
6919     var camelFn = function(m, a){ return a.charAt(1).toUpperCase(); };
6920     var view = document.defaultView;
6921
6922 /**
6923  * @class Roo.Element
6924  * Represents an Element in the DOM.<br><br>
6925  * Usage:<br>
6926 <pre><code>
6927 var el = Roo.get("my-div");
6928
6929 // or with getEl
6930 var el = getEl("my-div");
6931
6932 // or with a DOM element
6933 var el = Roo.get(myDivElement);
6934 </code></pre>
6935  * Using Roo.get() or getEl() instead of calling the constructor directly ensures you get the same object
6936  * each call instead of constructing a new one.<br><br>
6937  * <b>Animations</b><br />
6938  * Many of the functions for manipulating an element have an optional "animate" parameter. The animate parameter
6939  * should either be a boolean (true) or an object literal with animation options. The animation options are:
6940 <pre>
6941 Option    Default   Description
6942 --------- --------  ---------------------------------------------
6943 duration  .35       The duration of the animation in seconds
6944 easing    easeOut   The YUI easing method
6945 callback  none      A function to execute when the anim completes
6946 scope     this      The scope (this) of the callback function
6947 </pre>
6948 * Also, the Anim object being used for the animation will be set on your options object as "anim", which allows you to stop or
6949 * manipulate the animation. Here's an example:
6950 <pre><code>
6951 var el = Roo.get("my-div");
6952
6953 // no animation
6954 el.setWidth(100);
6955
6956 // default animation
6957 el.setWidth(100, true);
6958
6959 // animation with some options set
6960 el.setWidth(100, {
6961     duration: 1,
6962     callback: this.foo,
6963     scope: this
6964 });
6965
6966 // using the "anim" property to get the Anim object
6967 var opt = {
6968     duration: 1,
6969     callback: this.foo,
6970     scope: this
6971 };
6972 el.setWidth(100, opt);
6973 ...
6974 if(opt.anim.isAnimated()){
6975     opt.anim.stop();
6976 }
6977 </code></pre>
6978 * <b> Composite (Collections of) Elements</b><br />
6979  * For working with collections of Elements, see <a href="Roo.CompositeElement.html">Roo.CompositeElement</a>
6980  * @constructor Create a new Element directly.
6981  * @param {String/HTMLElement} element
6982  * @param {Boolean} forceNew (optional) By default the constructor checks to see if there is already an instance of this element in the cache and if there is it returns the same instance. This will skip that check (useful for extending this class).
6983  */
6984     Roo.Element = function(element, forceNew){
6985         var dom = typeof element == "string" ?
6986                 document.getElementById(element) : element;
6987         if(!dom){ // invalid id/element
6988             return null;
6989         }
6990         var id = dom.id;
6991         if(forceNew !== true && id && Roo.Element.cache[id]){ // element object already exists
6992             return Roo.Element.cache[id];
6993         }
6994
6995         /**
6996          * The DOM element
6997          * @type HTMLElement
6998          */
6999         this.dom = dom;
7000
7001         /**
7002          * The DOM element ID
7003          * @type String
7004          */
7005         this.id = id || Roo.id(dom);
7006     };
7007
7008     var El = Roo.Element;
7009
7010     El.prototype = {
7011         /**
7012          * The element's default display mode  (defaults to "")
7013          * @type String
7014          */
7015         originalDisplay : "",
7016
7017         visibilityMode : 1,
7018         /**
7019          * The default unit to append to CSS values where a unit isn't provided (defaults to px).
7020          * @type String
7021          */
7022         defaultUnit : "px",
7023         /**
7024          * Sets the element's visibility mode. When setVisible() is called it
7025          * will use this to determine whether to set the visibility or the display property.
7026          * @param visMode Element.VISIBILITY or Element.DISPLAY
7027          * @return {Roo.Element} this
7028          */
7029         setVisibilityMode : function(visMode){
7030             this.visibilityMode = visMode;
7031             return this;
7032         },
7033         /**
7034          * Convenience method for setVisibilityMode(Element.DISPLAY)
7035          * @param {String} display (optional) What to set display to when visible
7036          * @return {Roo.Element} this
7037          */
7038         enableDisplayMode : function(display){
7039             this.setVisibilityMode(El.DISPLAY);
7040             if(typeof display != "undefined") this.originalDisplay = display;
7041             return this;
7042         },
7043
7044         /**
7045          * Looks at this node and then at parent nodes for a match of the passed simple selector (e.g. div.some-class or span:first-child)
7046          * @param {String} selector The simple selector to test
7047          * @param {Number/String/HTMLElement/Element} maxDepth (optional) The max depth to
7048                 search as a number or element (defaults to 10 || document.body)
7049          * @param {Boolean} returnEl (optional) True to return a Roo.Element object instead of DOM node
7050          * @return {HTMLElement} The matching DOM node (or null if no match was found)
7051          */
7052         findParent : function(simpleSelector, maxDepth, returnEl){
7053             var p = this.dom, b = document.body, depth = 0, dq = Roo.DomQuery, stopEl;
7054             maxDepth = maxDepth || 50;
7055             if(typeof maxDepth != "number"){
7056                 stopEl = Roo.getDom(maxDepth);
7057                 maxDepth = 10;
7058             }
7059             while(p && p.nodeType == 1 && depth < maxDepth && p != b && p != stopEl){
7060                 if(dq.is(p, simpleSelector)){
7061                     return returnEl ? Roo.get(p) : p;
7062                 }
7063                 depth++;
7064                 p = p.parentNode;
7065             }
7066             return null;
7067         },
7068
7069
7070         /**
7071          * Looks at parent nodes for a match of the passed simple selector (e.g. div.some-class or span:first-child)
7072          * @param {String} selector The simple selector to test
7073          * @param {Number/String/HTMLElement/Element} maxDepth (optional) The max depth to
7074                 search as a number or element (defaults to 10 || document.body)
7075          * @param {Boolean} returnEl (optional) True to return a Roo.Element object instead of DOM node
7076          * @return {HTMLElement} The matching DOM node (or null if no match was found)
7077          */
7078         findParentNode : function(simpleSelector, maxDepth, returnEl){
7079             var p = Roo.fly(this.dom.parentNode, '_internal');
7080             return p ? p.findParent(simpleSelector, maxDepth, returnEl) : null;
7081         },
7082
7083         /**
7084          * Walks up the dom looking for a parent node that matches the passed simple selector (e.g. div.some-class or span:first-child).
7085          * This is a shortcut for findParentNode() that always returns an Roo.Element.
7086          * @param {String} selector The simple selector to test
7087          * @param {Number/String/HTMLElement/Element} maxDepth (optional) The max depth to
7088                 search as a number or element (defaults to 10 || document.body)
7089          * @return {Roo.Element} The matching DOM node (or null if no match was found)
7090          */
7091         up : function(simpleSelector, maxDepth){
7092             return this.findParentNode(simpleSelector, maxDepth, true);
7093         },
7094
7095
7096
7097         /**
7098          * Returns true if this element matches the passed simple selector (e.g. div.some-class or span:first-child)
7099          * @param {String} selector The simple selector to test
7100          * @return {Boolean} True if this element matches the selector, else false
7101          */
7102         is : function(simpleSelector){
7103             return Roo.DomQuery.is(this.dom, simpleSelector);
7104         },
7105
7106         /**
7107          * Perform animation on this element.
7108          * @param {Object} args The YUI animation control args
7109          * @param {Float} duration (optional) How long the animation lasts in seconds (defaults to .35)
7110          * @param {Function} onComplete (optional) Function to call when animation completes
7111          * @param {String} easing (optional) Easing method to use (defaults to 'easeOut')
7112          * @param {String} animType (optional) 'run' is the default. Can also be 'color', 'motion', or 'scroll'
7113          * @return {Roo.Element} this
7114          */
7115         animate : function(args, duration, onComplete, easing, animType){
7116             this.anim(args, {duration: duration, callback: onComplete, easing: easing}, animType);
7117             return this;
7118         },
7119
7120         /*
7121          * @private Internal animation call
7122          */
7123         anim : function(args, opt, animType, defaultDur, defaultEase, cb){
7124             animType = animType || 'run';
7125             opt = opt || {};
7126             var anim = Roo.lib.Anim[animType](
7127                 this.dom, args,
7128                 (opt.duration || defaultDur) || .35,
7129                 (opt.easing || defaultEase) || 'easeOut',
7130                 function(){
7131                     Roo.callback(cb, this);
7132                     Roo.callback(opt.callback, opt.scope || this, [this, opt]);
7133                 },
7134                 this
7135             );
7136             opt.anim = anim;
7137             return anim;
7138         },
7139
7140         // private legacy anim prep
7141         preanim : function(a, i){
7142             return !a[i] ? false : (typeof a[i] == "object" ? a[i]: {duration: a[i+1], callback: a[i+2], easing: a[i+3]});
7143         },
7144
7145         /**
7146          * Removes worthless text nodes
7147          * @param {Boolean} forceReclean (optional) By default the element
7148          * keeps track if it has been cleaned already so
7149          * you can call this over and over. However, if you update the element and
7150          * need to force a reclean, you can pass true.
7151          */
7152         clean : function(forceReclean){
7153             if(this.isCleaned && forceReclean !== true){
7154                 return this;
7155             }
7156             var ns = /\S/;
7157             var d = this.dom, n = d.firstChild, ni = -1;
7158             while(n){
7159                 var nx = n.nextSibling;
7160                 if(n.nodeType == 3 && !ns.test(n.nodeValue)){
7161                     d.removeChild(n);
7162                 }else{
7163                     n.nodeIndex = ++ni;
7164                 }
7165                 n = nx;
7166             }
7167             this.isCleaned = true;
7168             return this;
7169         },
7170
7171         // private
7172         calcOffsetsTo : function(el){
7173             el = Roo.get(el);
7174             var d = el.dom;
7175             var restorePos = false;
7176             if(el.getStyle('position') == 'static'){
7177                 el.position('relative');
7178                 restorePos = true;
7179             }
7180             var x = 0, y =0;
7181             var op = this.dom;
7182             while(op && op != d && op.tagName != 'HTML'){
7183                 x+= op.offsetLeft;
7184                 y+= op.offsetTop;
7185                 op = op.offsetParent;
7186             }
7187             if(restorePos){
7188                 el.position('static');
7189             }
7190             return [x, y];
7191         },
7192
7193         /**
7194          * Scrolls this element into view within the passed container.
7195          * @param {String/HTMLElement/Element} container (optional) The container element to scroll (defaults to document.body)
7196          * @param {Boolean} hscroll (optional) False to disable horizontal scroll (defaults to true)
7197          * @return {Roo.Element} this
7198          */
7199         scrollIntoView : function(container, hscroll){
7200             var c = Roo.getDom(container) || document.body;
7201             var el = this.dom;
7202
7203             var o = this.calcOffsetsTo(c),
7204                 l = o[0],
7205                 t = o[1],
7206                 b = t+el.offsetHeight,
7207                 r = l+el.offsetWidth;
7208
7209             var ch = c.clientHeight;
7210             var ct = parseInt(c.scrollTop, 10);
7211             var cl = parseInt(c.scrollLeft, 10);
7212             var cb = ct + ch;
7213             var cr = cl + c.clientWidth;
7214
7215             if(t < ct){
7216                 c.scrollTop = t;
7217             }else if(b > cb){
7218                 c.scrollTop = b-ch;
7219             }
7220
7221             if(hscroll !== false){
7222                 if(l < cl){
7223                     c.scrollLeft = l;
7224                 }else if(r > cr){
7225                     c.scrollLeft = r-c.clientWidth;
7226                 }
7227             }
7228             return this;
7229         },
7230
7231         // private
7232         scrollChildIntoView : function(child, hscroll){
7233             Roo.fly(child, '_scrollChildIntoView').scrollIntoView(this, hscroll);
7234         },
7235
7236         /**
7237          * Measures the element's content height and updates height to match. Note: this function uses setTimeout so
7238          * the new height may not be available immediately.
7239          * @param {Boolean} animate (optional) Animate the transition (defaults to false)
7240          * @param {Float} duration (optional) Length of the animation in seconds (defaults to .35)
7241          * @param {Function} onComplete (optional) Function to call when animation completes
7242          * @param {String} easing (optional) Easing method to use (defaults to easeOut)
7243          * @return {Roo.Element} this
7244          */
7245         autoHeight : function(animate, duration, onComplete, easing){
7246             var oldHeight = this.getHeight();
7247             this.clip();
7248             this.setHeight(1); // force clipping
7249             setTimeout(function(){
7250                 var height = parseInt(this.dom.scrollHeight, 10); // parseInt for Safari
7251                 if(!animate){
7252                     this.setHeight(height);
7253                     this.unclip();
7254                     if(typeof onComplete == "function"){
7255                         onComplete();
7256                     }
7257                 }else{
7258                     this.setHeight(oldHeight); // restore original height
7259                     this.setHeight(height, animate, duration, function(){
7260                         this.unclip();
7261                         if(typeof onComplete == "function") onComplete();
7262                     }.createDelegate(this), easing);
7263                 }
7264             }.createDelegate(this), 0);
7265             return this;
7266         },
7267
7268         /**
7269          * Returns true if this element is an ancestor of the passed element
7270          * @param {HTMLElement/String} el The element to check
7271          * @return {Boolean} True if this element is an ancestor of el, else false
7272          */
7273         contains : function(el){
7274             if(!el){return false;}
7275             return D.isAncestor(this.dom, el.dom ? el.dom : el);
7276         },
7277
7278         /**
7279          * Checks whether the element is currently visible using both visibility and display properties.
7280          * @param {Boolean} deep (optional) True to walk the dom and see if parent elements are hidden (defaults to false)
7281          * @return {Boolean} True if the element is currently visible, else false
7282          */
7283         isVisible : function(deep) {
7284             var vis = !(this.getStyle("visibility") == "hidden" || this.getStyle("display") == "none");
7285             if(deep !== true || !vis){
7286                 return vis;
7287             }
7288             var p = this.dom.parentNode;
7289             while(p && p.tagName.toLowerCase() != "body"){
7290                 if(!Roo.fly(p, '_isVisible').isVisible()){
7291                     return false;
7292                 }
7293                 p = p.parentNode;
7294             }
7295             return true;
7296         },
7297
7298         /**
7299          * Creates a {@link Roo.CompositeElement} for child nodes based on the passed CSS selector (the selector should not contain an id).
7300          * @param {String} selector The CSS selector
7301          * @param {Boolean} unique (optional) True to create a unique Roo.Element for each child (defaults to false, which creates a single shared flyweight object)
7302          * @return {CompositeElement/CompositeElementLite} The composite element
7303          */
7304         select : function(selector, unique){
7305             return El.select(selector, unique, this.dom);
7306         },
7307
7308         /**
7309          * Selects child nodes based on the passed CSS selector (the selector should not contain an id).
7310          * @param {String} selector The CSS selector
7311          * @return {Array} An array of the matched nodes
7312          */
7313         query : function(selector, unique){
7314             return Roo.DomQuery.select(selector, this.dom);
7315         },
7316
7317         /**
7318          * Selects a single child at any depth below this element based on the passed CSS selector (the selector should not contain an id).
7319          * @param {String} selector The CSS selector
7320          * @param {Boolean} returnDom (optional) True to return the DOM node instead of Roo.Element (defaults to false)
7321          * @return {HTMLElement/Roo.Element} The child Roo.Element (or DOM node if returnDom = true)
7322          */
7323         child : function(selector, returnDom){
7324             var n = Roo.DomQuery.selectNode(selector, this.dom);
7325             return returnDom ? n : Roo.get(n);
7326         },
7327
7328         /**
7329          * Selects a single *direct* child based on the passed CSS selector (the selector should not contain an id).
7330          * @param {String} selector The CSS selector
7331          * @param {Boolean} returnDom (optional) True to return the DOM node instead of Roo.Element (defaults to false)
7332          * @return {HTMLElement/Roo.Element} The child Roo.Element (or DOM node if returnDom = true)
7333          */
7334         down : function(selector, returnDom){
7335             var n = Roo.DomQuery.selectNode(" > " + selector, this.dom);
7336             return returnDom ? n : Roo.get(n);
7337         },
7338
7339         /**
7340          * Initializes a {@link Roo.dd.DD} drag drop object for this element.
7341          * @param {String} group The group the DD object is member of
7342          * @param {Object} config The DD config object
7343          * @param {Object} overrides An object containing methods to override/implement on the DD object
7344          * @return {Roo.dd.DD} The DD object
7345          */
7346         initDD : function(group, config, overrides){
7347             var dd = new Roo.dd.DD(Roo.id(this.dom), group, config);
7348             return Roo.apply(dd, overrides);
7349         },
7350
7351         /**
7352          * Initializes a {@link Roo.dd.DDProxy} object for this element.
7353          * @param {String} group The group the DDProxy object is member of
7354          * @param {Object} config The DDProxy config object
7355          * @param {Object} overrides An object containing methods to override/implement on the DDProxy object
7356          * @return {Roo.dd.DDProxy} The DDProxy object
7357          */
7358         initDDProxy : function(group, config, overrides){
7359             var dd = new Roo.dd.DDProxy(Roo.id(this.dom), group, config);
7360             return Roo.apply(dd, overrides);
7361         },
7362
7363         /**
7364          * Initializes a {@link Roo.dd.DDTarget} object for this element.
7365          * @param {String} group The group the DDTarget object is member of
7366          * @param {Object} config The DDTarget config object
7367          * @param {Object} overrides An object containing methods to override/implement on the DDTarget object
7368          * @return {Roo.dd.DDTarget} The DDTarget object
7369          */
7370         initDDTarget : function(group, config, overrides){
7371             var dd = new Roo.dd.DDTarget(Roo.id(this.dom), group, config);
7372             return Roo.apply(dd, overrides);
7373         },
7374
7375         /**
7376          * Sets the visibility of the element (see details). If the visibilityMode is set to Element.DISPLAY, it will use
7377          * the display property to hide the element, otherwise it uses visibility. The default is to hide and show using the visibility property.
7378          * @param {Boolean} visible Whether the element is visible
7379          * @param {Boolean/Object} animate (optional) True for the default animation, or a standard Element animation config object
7380          * @return {Roo.Element} this
7381          */
7382          setVisible : function(visible, animate){
7383             if(!animate || !A){
7384                 if(this.visibilityMode == El.DISPLAY){
7385                     this.setDisplayed(visible);
7386                 }else{
7387                     this.fixDisplay();
7388                     this.dom.style.visibility = visible ? "visible" : "hidden";
7389                 }
7390             }else{
7391                 // closure for composites
7392                 var dom = this.dom;
7393                 var visMode = this.visibilityMode;
7394                 if(visible){
7395                     this.setOpacity(.01);
7396                     this.setVisible(true);
7397                 }
7398                 this.anim({opacity: { to: (visible?1:0) }},
7399                       this.preanim(arguments, 1),
7400                       null, .35, 'easeIn', function(){
7401                          if(!visible){
7402                              if(visMode == El.DISPLAY){
7403                                  dom.style.display = "none";
7404                              }else{
7405                                  dom.style.visibility = "hidden";
7406                              }
7407                              Roo.get(dom).setOpacity(1);
7408                          }
7409                      });
7410             }
7411             return this;
7412         },
7413
7414         /**
7415          * Returns true if display is not "none"
7416          * @return {Boolean}
7417          */
7418         isDisplayed : function() {
7419             return this.getStyle("display") != "none";
7420         },
7421
7422         /**
7423          * Toggles the element's visibility or display, depending on visibility mode.
7424          * @param {Boolean/Object} animate (optional) True for the default animation, or a standard Element animation config object
7425          * @return {Roo.Element} this
7426          */
7427         toggle : function(animate){
7428             this.setVisible(!this.isVisible(), this.preanim(arguments, 0));
7429             return this;
7430         },
7431
7432         /**
7433          * Sets the CSS display property. Uses originalDisplay if the specified value is a boolean true.
7434          * @param {Boolean} value Boolean value to display the element using its default display, or a string to set the display directly
7435          * @return {Roo.Element} this
7436          */
7437         setDisplayed : function(value) {
7438             if(typeof value == "boolean"){
7439                value = value ? this.originalDisplay : "none";
7440             }
7441             this.setStyle("display", value);
7442             return this;
7443         },
7444
7445         /**
7446          * Tries to focus the element. Any exceptions are caught and ignored.
7447          * @return {Roo.Element} this
7448          */
7449         focus : function() {
7450             try{
7451                 this.dom.focus();
7452             }catch(e){}
7453             return this;
7454         },
7455
7456         /**
7457          * Tries to blur the element. Any exceptions are caught and ignored.
7458          * @return {Roo.Element} this
7459          */
7460         blur : function() {
7461             try{
7462                 this.dom.blur();
7463             }catch(e){}
7464             return this;
7465         },
7466
7467         /**
7468          * Adds one or more CSS classes to the element. Duplicate classes are automatically filtered out.
7469          * @param {String/Array} className The CSS class to add, or an array of classes
7470          * @return {Roo.Element} this
7471          */
7472         addClass : function(className){
7473             if(className instanceof Array){
7474                 for(var i = 0, len = className.length; i < len; i++) {
7475                     this.addClass(className[i]);
7476                 }
7477             }else{
7478                 if(className && !this.hasClass(className)){
7479                     this.dom.className = this.dom.className + " " + className;
7480                 }
7481             }
7482             return this;
7483         },
7484
7485         /**
7486          * Adds one or more CSS classes to this element and removes the same class(es) from all siblings.
7487          * @param {String/Array} className The CSS class to add, or an array of classes
7488          * @return {Roo.Element} this
7489          */
7490         radioClass : function(className){
7491             var siblings = this.dom.parentNode.childNodes;
7492             for(var i = 0; i < siblings.length; i++) {
7493                 var s = siblings[i];
7494                 if(s.nodeType == 1){
7495                     Roo.get(s).removeClass(className);
7496                 }
7497             }
7498             this.addClass(className);
7499             return this;
7500         },
7501
7502         /**
7503          * Removes one or more CSS classes from the element.
7504          * @param {String/Array} className The CSS class to remove, or an array of classes
7505          * @return {Roo.Element} this
7506          */
7507         removeClass : function(className){
7508             if(!className || !this.dom.className){
7509                 return this;
7510             }
7511             if(className instanceof Array){
7512                 for(var i = 0, len = className.length; i < len; i++) {
7513                     this.removeClass(className[i]);
7514                 }
7515             }else{
7516                 if(this.hasClass(className)){
7517                     var re = this.classReCache[className];
7518                     if (!re) {
7519                        re = new RegExp('(?:^|\\s+)' + className + '(?:\\s+|$)', "g");
7520                        this.classReCache[className] = re;
7521                     }
7522                     this.dom.className =
7523                         this.dom.className.replace(re, " ");
7524                 }
7525             }
7526             return this;
7527         },
7528
7529         // private
7530         classReCache: {},
7531
7532         /**
7533          * Toggles the specified CSS class on this element (removes it if it already exists, otherwise adds it).
7534          * @param {String} className The CSS class to toggle
7535          * @return {Roo.Element} this
7536          */
7537         toggleClass : function(className){
7538             if(this.hasClass(className)){
7539                 this.removeClass(className);
7540             }else{
7541                 this.addClass(className);
7542             }
7543             return this;
7544         },
7545
7546         /**
7547          * Checks if the specified CSS class exists on this element's DOM node.
7548          * @param {String} className The CSS class to check for
7549          * @return {Boolean} True if the class exists, else false
7550          */
7551         hasClass : function(className){
7552             return className && (' '+this.dom.className+' ').indexOf(' '+className+' ') != -1;
7553         },
7554
7555         /**
7556          * Replaces a CSS class on the element with another.  If the old name does not exist, the new name will simply be added.
7557          * @param {String} oldClassName The CSS class to replace
7558          * @param {String} newClassName The replacement CSS class
7559          * @return {Roo.Element} this
7560          */
7561         replaceClass : function(oldClassName, newClassName){
7562             this.removeClass(oldClassName);
7563             this.addClass(newClassName);
7564             return this;
7565         },
7566
7567         /**
7568          * Returns an object with properties matching the styles requested.
7569          * For example, el.getStyles('color', 'font-size', 'width') might return
7570          * {'color': '#FFFFFF', 'font-size': '13px', 'width': '100px'}.
7571          * @param {String} style1 A style name
7572          * @param {String} style2 A style name
7573          * @param {String} etc.
7574          * @return {Object} The style object
7575          */
7576         getStyles : function(){
7577             var a = arguments, len = a.length, r = {};
7578             for(var i = 0; i < len; i++){
7579                 r[a[i]] = this.getStyle(a[i]);
7580             }
7581             return r;
7582         },
7583
7584         /**
7585          * Normalizes currentStyle and computedStyle. This is not YUI getStyle, it is an optimised version.
7586          * @param {String} property The style property whose value is returned.
7587          * @return {String} The current value of the style property for this element.
7588          */
7589         getStyle : function(){
7590             return view && view.getComputedStyle ?
7591                 function(prop){
7592                     var el = this.dom, v, cs, camel;
7593                     if(prop == 'float'){
7594                         prop = "cssFloat";
7595                     }
7596                     if(el.style && (v = el.style[prop])){
7597                         return v;
7598                     }
7599                     if(cs = view.getComputedStyle(el, "")){
7600                         if(!(camel = propCache[prop])){
7601                             camel = propCache[prop] = prop.replace(camelRe, camelFn);
7602                         }
7603                         return cs[camel];
7604                     }
7605                     return null;
7606                 } :
7607                 function(prop){
7608                     var el = this.dom, v, cs, camel;
7609                     if(prop == 'opacity'){
7610                         if(typeof el.style.filter == 'string'){
7611                             var m = el.style.filter.match(/alpha\(opacity=(.*)\)/i);
7612                             if(m){
7613                                 var fv = parseFloat(m[1]);
7614                                 if(!isNaN(fv)){
7615                                     return fv ? fv / 100 : 0;
7616                                 }
7617                             }
7618                         }
7619                         return 1;
7620                     }else if(prop == 'float'){
7621                         prop = "styleFloat";
7622                     }
7623                     if(!(camel = propCache[prop])){
7624                         camel = propCache[prop] = prop.replace(camelRe, camelFn);
7625                     }
7626                     if(v = el.style[camel]){
7627                         return v;
7628                     }
7629                     if(cs = el.currentStyle){
7630                         return cs[camel];
7631                     }
7632                     return null;
7633                 };
7634         }(),
7635
7636         /**
7637          * Wrapper for setting style properties, also takes single object parameter of multiple styles.
7638          * @param {String/Object} property The style property to be set, or an object of multiple styles.
7639          * @param {String} value (optional) The value to apply to the given property, or null if an object was passed.
7640          * @return {Roo.Element} this
7641          */
7642         setStyle : function(prop, value){
7643             if(typeof prop == "string"){
7644                 
7645                 if (prop == 'float') {
7646                     this.setStyle(Roo.isIE ? 'styleFloat'  : 'cssFloat', value);
7647                     return this;
7648                 }
7649                 
7650                 var camel;
7651                 if(!(camel = propCache[prop])){
7652                     camel = propCache[prop] = prop.replace(camelRe, camelFn);
7653                 }
7654                 
7655                 if(camel == 'opacity') {
7656                     this.setOpacity(value);
7657                 }else{
7658                     this.dom.style[camel] = value;
7659                 }
7660             }else{
7661                 for(var style in prop){
7662                     if(typeof prop[style] != "function"){
7663                        this.setStyle(style, prop[style]);
7664                     }
7665                 }
7666             }
7667             return this;
7668         },
7669
7670         /**
7671          * More flexible version of {@link #setStyle} for setting style properties.
7672          * @param {String/Object/Function} styles A style specification string, e.g. "width:100px", or object in the form {width:"100px"}, or
7673          * a function which returns such a specification.
7674          * @return {Roo.Element} this
7675          */
7676         applyStyles : function(style){
7677             Roo.DomHelper.applyStyles(this.dom, style);
7678             return this;
7679         },
7680
7681         /**
7682           * Gets the current X position of the element based on page coordinates.  Element must be part of the DOM tree to have page coordinates (display:none or elements not appended return false).
7683           * @return {Number} The X position of the element
7684           */
7685         getX : function(){
7686             return D.getX(this.dom);
7687         },
7688
7689         /**
7690           * Gets the current Y position of the element based on page coordinates.  Element must be part of the DOM tree to have page coordinates (display:none or elements not appended return false).
7691           * @return {Number} The Y position of the element
7692           */
7693         getY : function(){
7694             return D.getY(this.dom);
7695         },
7696
7697         /**
7698           * Gets the current position of the element based on page coordinates.  Element must be part of the DOM tree to have page coordinates (display:none or elements not appended return false).
7699           * @return {Array} The XY position of the element
7700           */
7701         getXY : function(){
7702             return D.getXY(this.dom);
7703         },
7704
7705         /**
7706          * Sets the X position of the element based on page coordinates.  Element must be part of the DOM tree to have page coordinates (display:none or elements not appended return false).
7707          * @param {Number} The X position of the element
7708          * @param {Boolean/Object} animate (optional) True for the default animation, or a standard Element animation config object
7709          * @return {Roo.Element} this
7710          */
7711         setX : function(x, animate){
7712             if(!animate || !A){
7713                 D.setX(this.dom, x);
7714             }else{
7715                 this.setXY([x, this.getY()], this.preanim(arguments, 1));
7716             }
7717             return this;
7718         },
7719
7720         /**
7721          * Sets the Y position of the element based on page coordinates.  Element must be part of the DOM tree to have page coordinates (display:none or elements not appended return false).
7722          * @param {Number} The Y position of the element
7723          * @param {Boolean/Object} animate (optional) True for the default animation, or a standard Element animation config object
7724          * @return {Roo.Element} this
7725          */
7726         setY : function(y, animate){
7727             if(!animate || !A){
7728                 D.setY(this.dom, y);
7729             }else{
7730                 this.setXY([this.getX(), y], this.preanim(arguments, 1));
7731             }
7732             return this;
7733         },
7734
7735         /**
7736          * Sets the element's left position directly using CSS style (instead of {@link #setX}).
7737          * @param {String} left The left CSS property value
7738          * @return {Roo.Element} this
7739          */
7740         setLeft : function(left){
7741             this.setStyle("left", this.addUnits(left));
7742             return this;
7743         },
7744
7745         /**
7746          * Sets the element's top position directly using CSS style (instead of {@link #setY}).
7747          * @param {String} top The top CSS property value
7748          * @return {Roo.Element} this
7749          */
7750         setTop : function(top){
7751             this.setStyle("top", this.addUnits(top));
7752             return this;
7753         },
7754
7755         /**
7756          * Sets the element's CSS right style.
7757          * @param {String} right The right CSS property value
7758          * @return {Roo.Element} this
7759          */
7760         setRight : function(right){
7761             this.setStyle("right", this.addUnits(right));
7762             return this;
7763         },
7764
7765         /**
7766          * Sets the element's CSS bottom style.
7767          * @param {String} bottom The bottom CSS property value
7768          * @return {Roo.Element} this
7769          */
7770         setBottom : function(bottom){
7771             this.setStyle("bottom", this.addUnits(bottom));
7772             return this;
7773         },
7774
7775         /**
7776          * Sets the position of the element in page coordinates, regardless of how the element is positioned.
7777          * The element must be part of the DOM tree to have page coordinates (display:none or elements not appended return false).
7778          * @param {Array} pos Contains X & Y [x, y] values for new position (coordinates are page-based)
7779          * @param {Boolean/Object} animate (optional) True for the default animation, or a standard Element animation config object
7780          * @return {Roo.Element} this
7781          */
7782         setXY : function(pos, animate){
7783             if(!animate || !A){
7784                 D.setXY(this.dom, pos);
7785             }else{
7786                 this.anim({points: {to: pos}}, this.preanim(arguments, 1), 'motion');
7787             }
7788             return this;
7789         },
7790
7791         /**
7792          * Sets the position of the element in page coordinates, regardless of how the element is positioned.
7793          * The element must be part of the DOM tree to have page coordinates (display:none or elements not appended return false).
7794          * @param {Number} x X value for new position (coordinates are page-based)
7795          * @param {Number} y Y value for new position (coordinates are page-based)
7796          * @param {Boolean/Object} animate (optional) True for the default animation, or a standard Element animation config object
7797          * @return {Roo.Element} this
7798          */
7799         setLocation : function(x, y, animate){
7800             this.setXY([x, y], this.preanim(arguments, 2));
7801             return this;
7802         },
7803
7804         /**
7805          * Sets the position of the element in page coordinates, regardless of how the element is positioned.
7806          * The element must be part of the DOM tree to have page coordinates (display:none or elements not appended return false).
7807          * @param {Number} x X value for new position (coordinates are page-based)
7808          * @param {Number} y Y value for new position (coordinates are page-based)
7809          * @param {Boolean/Object} animate (optional) True for the default animation, or a standard Element animation config object
7810          * @return {Roo.Element} this
7811          */
7812         moveTo : function(x, y, animate){
7813             this.setXY([x, y], this.preanim(arguments, 2));
7814             return this;
7815         },
7816
7817         /**
7818          * Returns the region of the given element.
7819          * The element must be part of the DOM tree to have a region (display:none or elements not appended return false).
7820          * @return {Region} A Roo.lib.Region containing "top, left, bottom, right" member data.
7821          */
7822         getRegion : function(){
7823             return D.getRegion(this.dom);
7824         },
7825
7826         /**
7827          * Returns the offset height of the element
7828          * @param {Boolean} contentHeight (optional) true to get the height minus borders and padding
7829          * @return {Number} The element's height
7830          */
7831         getHeight : function(contentHeight){
7832             var h = this.dom.offsetHeight || 0;
7833             return contentHeight !== true ? h : h-this.getBorderWidth("tb")-this.getPadding("tb");
7834         },
7835
7836         /**
7837          * Returns the offset width of the element
7838          * @param {Boolean} contentWidth (optional) true to get the width minus borders and padding
7839          * @return {Number} The element's width
7840          */
7841         getWidth : function(contentWidth){
7842             var w = this.dom.offsetWidth || 0;
7843             return contentWidth !== true ? w : w-this.getBorderWidth("lr")-this.getPadding("lr");
7844         },
7845
7846         /**
7847          * Returns either the offsetHeight or the height of this element based on CSS height adjusted by padding or borders
7848          * when needed to simulate offsetHeight when offsets aren't available. This may not work on display:none elements
7849          * if a height has not been set using CSS.
7850          * @return {Number}
7851          */
7852         getComputedHeight : function(){
7853             var h = Math.max(this.dom.offsetHeight, this.dom.clientHeight);
7854             if(!h){
7855                 h = parseInt(this.getStyle('height'), 10) || 0;
7856                 if(!this.isBorderBox()){
7857                     h += this.getFrameWidth('tb');
7858                 }
7859             }
7860             return h;
7861         },
7862
7863         /**
7864          * Returns either the offsetWidth or the width of this element based on CSS width adjusted by padding or borders
7865          * when needed to simulate offsetWidth when offsets aren't available. This may not work on display:none elements
7866          * if a width has not been set using CSS.
7867          * @return {Number}
7868          */
7869         getComputedWidth : function(){
7870             var w = Math.max(this.dom.offsetWidth, this.dom.clientWidth);
7871             if(!w){
7872                 w = parseInt(this.getStyle('width'), 10) || 0;
7873                 if(!this.isBorderBox()){
7874                     w += this.getFrameWidth('lr');
7875                 }
7876             }
7877             return w;
7878         },
7879
7880         /**
7881          * Returns the size of the element.
7882          * @param {Boolean} contentSize (optional) true to get the width/size minus borders and padding
7883          * @return {Object} An object containing the element's size {width: (element width), height: (element height)}
7884          */
7885         getSize : function(contentSize){
7886             return {width: this.getWidth(contentSize), height: this.getHeight(contentSize)};
7887         },
7888
7889         /**
7890          * Returns the width and height of the viewport.
7891          * @return {Object} An object containing the viewport's size {width: (viewport width), height: (viewport height)}
7892          */
7893         getViewSize : function(){
7894             var d = this.dom, doc = document, aw = 0, ah = 0;
7895             if(d == doc || d == doc.body){
7896                 return {width : D.getViewWidth(), height: D.getViewHeight()};
7897             }else{
7898                 return {
7899                     width : d.clientWidth,
7900                     height: d.clientHeight
7901                 };
7902             }
7903         },
7904
7905         /**
7906          * Returns the value of the "value" attribute
7907          * @param {Boolean} asNumber true to parse the value as a number
7908          * @return {String/Number}
7909          */
7910         getValue : function(asNumber){
7911             return asNumber ? parseInt(this.dom.value, 10) : this.dom.value;
7912         },
7913
7914         // private
7915         adjustWidth : function(width){
7916             if(typeof width == "number"){
7917                 if(this.autoBoxAdjust && !this.isBorderBox()){
7918                    width -= (this.getBorderWidth("lr") + this.getPadding("lr"));
7919                 }
7920                 if(width < 0){
7921                     width = 0;
7922                 }
7923             }
7924             return width;
7925         },
7926
7927         // private
7928         adjustHeight : function(height){
7929             if(typeof height == "number"){
7930                if(this.autoBoxAdjust && !this.isBorderBox()){
7931                    height -= (this.getBorderWidth("tb") + this.getPadding("tb"));
7932                }
7933                if(height < 0){
7934                    height = 0;
7935                }
7936             }
7937             return height;
7938         },
7939
7940         /**
7941          * Set the width of the element
7942          * @param {Number} width The new width
7943          * @param {Boolean/Object} animate (optional) true for the default animation or a standard Element animation config object
7944          * @return {Roo.Element} this
7945          */
7946         setWidth : function(width, animate){
7947             width = this.adjustWidth(width);
7948             if(!animate || !A){
7949                 this.dom.style.width = this.addUnits(width);
7950             }else{
7951                 this.anim({width: {to: width}}, this.preanim(arguments, 1));
7952             }
7953             return this;
7954         },
7955
7956         /**
7957          * Set the height of the element
7958          * @param {Number} height The new height
7959          * @param {Boolean/Object} animate (optional) true for the default animation or a standard Element animation config object
7960          * @return {Roo.Element} this
7961          */
7962          setHeight : function(height, animate){
7963             height = this.adjustHeight(height);
7964             if(!animate || !A){
7965                 this.dom.style.height = this.addUnits(height);
7966             }else{
7967                 this.anim({height: {to: height}}, this.preanim(arguments, 1));
7968             }
7969             return this;
7970         },
7971
7972         /**
7973          * Set the size of the element. If animation is true, both width an height will be animated concurrently.
7974          * @param {Number} width The new width
7975          * @param {Number} height The new height
7976          * @param {Boolean/Object} animate (optional) true for the default animation or a standard Element animation config object
7977          * @return {Roo.Element} this
7978          */
7979          setSize : function(width, height, animate){
7980             if(typeof width == "object"){ // in case of object from getSize()
7981                 height = width.height; width = width.width;
7982             }
7983             width = this.adjustWidth(width); height = this.adjustHeight(height);
7984             if(!animate || !A){
7985                 this.dom.style.width = this.addUnits(width);
7986                 this.dom.style.height = this.addUnits(height);
7987             }else{
7988                 this.anim({width: {to: width}, height: {to: height}}, this.preanim(arguments, 2));
7989             }
7990             return this;
7991         },
7992
7993         /**
7994          * Sets the element's position and size in one shot. If animation is true then width, height, x and y will be animated concurrently.
7995          * @param {Number} x X value for new position (coordinates are page-based)
7996          * @param {Number} y Y value for new position (coordinates are page-based)
7997          * @param {Number} width The new width
7998          * @param {Number} height The new height
7999          * @param {Boolean/Object} animate (optional) true for the default animation or a standard Element animation config object
8000          * @return {Roo.Element} this
8001          */
8002         setBounds : function(x, y, width, height, animate){
8003             if(!animate || !A){
8004                 this.setSize(width, height);
8005                 this.setLocation(x, y);
8006             }else{
8007                 width = this.adjustWidth(width); height = this.adjustHeight(height);
8008                 this.anim({points: {to: [x, y]}, width: {to: width}, height: {to: height}},
8009                               this.preanim(arguments, 4), 'motion');
8010             }
8011             return this;
8012         },
8013
8014         /**
8015          * Sets the element's position and size the the specified region. If animation is true then width, height, x and y will be animated concurrently.
8016          * @param {Roo.lib.Region} region The region to fill
8017          * @param {Boolean/Object} animate (optional) true for the default animation or a standard Element animation config object
8018          * @return {Roo.Element} this
8019          */
8020         setRegion : function(region, animate){
8021             this.setBounds(region.left, region.top, region.right-region.left, region.bottom-region.top, this.preanim(arguments, 1));
8022             return this;
8023         },
8024
8025         /**
8026          * Appends an event handler
8027          *
8028          * @param {String}   eventName     The type of event to append
8029          * @param {Function} fn        The method the event invokes
8030          * @param {Object} scope       (optional) The scope (this object) of the fn
8031          * @param {Object}   options   (optional)An object with standard {@link Roo.EventManager#addListener} options
8032          */
8033         addListener : function(eventName, fn, scope, options){
8034             if (this.dom) {
8035                 Roo.EventManager.on(this.dom,  eventName, fn, scope || this, options);
8036             }
8037         },
8038
8039         /**
8040          * Removes an event handler from this element
8041          * @param {String} eventName the type of event to remove
8042          * @param {Function} fn the method the event invokes
8043          * @return {Roo.Element} this
8044          */
8045         removeListener : function(eventName, fn){
8046             Roo.EventManager.removeListener(this.dom,  eventName, fn);
8047             return this;
8048         },
8049
8050         /**
8051          * Removes all previous added listeners from this element
8052          * @return {Roo.Element} this
8053          */
8054         removeAllListeners : function(){
8055             E.purgeElement(this.dom);
8056             return this;
8057         },
8058
8059         relayEvent : function(eventName, observable){
8060             this.on(eventName, function(e){
8061                 observable.fireEvent(eventName, e);
8062             });
8063         },
8064
8065         /**
8066          * Set the opacity of the element
8067          * @param {Float} opacity The new opacity. 0 = transparent, .5 = 50% visibile, 1 = fully visible, etc
8068          * @param {Boolean/Object} animate (optional) true for the default animation or a standard Element animation config object
8069          * @return {Roo.Element} this
8070          */
8071          setOpacity : function(opacity, animate){
8072             if(!animate || !A){
8073                 var s = this.dom.style;
8074                 if(Roo.isIE){
8075                     s.zoom = 1;
8076                     s.filter = (s.filter || '').replace(/alpha\([^\)]*\)/gi,"") +
8077                                (opacity == 1 ? "" : "alpha(opacity=" + opacity * 100 + ")");
8078                 }else{
8079                     s.opacity = opacity;
8080                 }
8081             }else{
8082                 this.anim({opacity: {to: opacity}}, this.preanim(arguments, 1), null, .35, 'easeIn');
8083             }
8084             return this;
8085         },
8086
8087         /**
8088          * Gets the left X coordinate
8089          * @param {Boolean} local True to get the local css position instead of page coordinate
8090          * @return {Number}
8091          */
8092         getLeft : function(local){
8093             if(!local){
8094                 return this.getX();
8095             }else{
8096                 return parseInt(this.getStyle("left"), 10) || 0;
8097             }
8098         },
8099
8100         /**
8101          * Gets the right X coordinate of the element (element X position + element width)
8102          * @param {Boolean} local True to get the local css position instead of page coordinate
8103          * @return {Number}
8104          */
8105         getRight : function(local){
8106             if(!local){
8107                 return this.getX() + this.getWidth();
8108             }else{
8109                 return (this.getLeft(true) + this.getWidth()) || 0;
8110             }
8111         },
8112
8113         /**
8114          * Gets the top Y coordinate
8115          * @param {Boolean} local True to get the local css position instead of page coordinate
8116          * @return {Number}
8117          */
8118         getTop : function(local) {
8119             if(!local){
8120                 return this.getY();
8121             }else{
8122                 return parseInt(this.getStyle("top"), 10) || 0;
8123             }
8124         },
8125
8126         /**
8127          * Gets the bottom Y coordinate of the element (element Y position + element height)
8128          * @param {Boolean} local True to get the local css position instead of page coordinate
8129          * @return {Number}
8130          */
8131         getBottom : function(local){
8132             if(!local){
8133                 return this.getY() + this.getHeight();
8134             }else{
8135                 return (this.getTop(true) + this.getHeight()) || 0;
8136             }
8137         },
8138
8139         /**
8140         * Initializes positioning on this element. If a desired position is not passed, it will make the
8141         * the element positioned relative IF it is not already positioned.
8142         * @param {String} pos (optional) Positioning to use "relative", "absolute" or "fixed"
8143         * @param {Number} zIndex (optional) The zIndex to apply
8144         * @param {Number} x (optional) Set the page X position
8145         * @param {Number} y (optional) Set the page Y position
8146         */
8147         position : function(pos, zIndex, x, y){
8148             if(!pos){
8149                if(this.getStyle('position') == 'static'){
8150                    this.setStyle('position', 'relative');
8151                }
8152             }else{
8153                 this.setStyle("position", pos);
8154             }
8155             if(zIndex){
8156                 this.setStyle("z-index", zIndex);
8157             }
8158             if(x !== undefined && y !== undefined){
8159                 this.setXY([x, y]);
8160             }else if(x !== undefined){
8161                 this.setX(x);
8162             }else if(y !== undefined){
8163                 this.setY(y);
8164             }
8165         },
8166
8167         /**
8168         * Clear positioning back to the default when the document was loaded
8169         * @param {String} value (optional) The value to use for the left,right,top,bottom, defaults to '' (empty string). You could use 'auto'.
8170         * @return {Roo.Element} this
8171          */
8172         clearPositioning : function(value){
8173             value = value ||'';
8174             this.setStyle({
8175                 "left": value,
8176                 "right": value,
8177                 "top": value,
8178                 "bottom": value,
8179                 "z-index": "",
8180                 "position" : "static"
8181             });
8182             return this;
8183         },
8184
8185         /**
8186         * Gets an object with all CSS positioning properties. Useful along with setPostioning to get
8187         * snapshot before performing an update and then restoring the element.
8188         * @return {Object}
8189         */
8190         getPositioning : function(){
8191             var l = this.getStyle("left");
8192             var t = this.getStyle("top");
8193             return {
8194                 "position" : this.getStyle("position"),
8195                 "left" : l,
8196                 "right" : l ? "" : this.getStyle("right"),
8197                 "top" : t,
8198                 "bottom" : t ? "" : this.getStyle("bottom"),
8199                 "z-index" : this.getStyle("z-index")
8200             };
8201         },
8202
8203         /**
8204          * Gets the width of the border(s) for the specified side(s)
8205          * @param {String} side Can be t, l, r, b or any combination of those to add multiple values. For example,
8206          * passing lr would get the border (l)eft width + the border (r)ight width.
8207          * @return {Number} The width of the sides passed added together
8208          */
8209         getBorderWidth : function(side){
8210             return this.addStyles(side, El.borders);
8211         },
8212
8213         /**
8214          * Gets the width of the padding(s) for the specified side(s)
8215          * @param {String} side Can be t, l, r, b or any combination of those to add multiple values. For example,
8216          * passing lr would get the padding (l)eft + the padding (r)ight.
8217          * @return {Number} The padding of the sides passed added together
8218          */
8219         getPadding : function(side){
8220             return this.addStyles(side, El.paddings);
8221         },
8222
8223         /**
8224         * Set positioning with an object returned by getPositioning().
8225         * @param {Object} posCfg
8226         * @return {Roo.Element} this
8227          */
8228         setPositioning : function(pc){
8229             this.applyStyles(pc);
8230             if(pc.right == "auto"){
8231                 this.dom.style.right = "";
8232             }
8233             if(pc.bottom == "auto"){
8234                 this.dom.style.bottom = "";
8235             }
8236             return this;
8237         },
8238
8239         // private
8240         fixDisplay : function(){
8241             if(this.getStyle("display") == "none"){
8242                 this.setStyle("visibility", "hidden");
8243                 this.setStyle("display", this.originalDisplay); // first try reverting to default
8244                 if(this.getStyle("display") == "none"){ // if that fails, default to block
8245                     this.setStyle("display", "block");
8246                 }
8247             }
8248         },
8249
8250         /**
8251          * Quick set left and top adding default units
8252          * @param {String} left The left CSS property value
8253          * @param {String} top The top CSS property value
8254          * @return {Roo.Element} this
8255          */
8256          setLeftTop : function(left, top){
8257             this.dom.style.left = this.addUnits(left);
8258             this.dom.style.top = this.addUnits(top);
8259             return this;
8260         },
8261
8262         /**
8263          * Move this element relative to its current position.
8264          * @param {String} direction Possible values are: "l","left" - "r","right" - "t","top","up" - "b","bottom","down".
8265          * @param {Number} distance How far to move the element in pixels
8266          * @param {Boolean/Object} animate (optional) true for the default animation or a standard Element animation config object
8267          * @return {Roo.Element} this
8268          */
8269          move : function(direction, distance, animate){
8270             var xy = this.getXY();
8271             direction = direction.toLowerCase();
8272             switch(direction){
8273                 case "l":
8274                 case "left":
8275                     this.moveTo(xy[0]-distance, xy[1], this.preanim(arguments, 2));
8276                     break;
8277                case "r":
8278                case "right":
8279                     this.moveTo(xy[0]+distance, xy[1], this.preanim(arguments, 2));
8280                     break;
8281                case "t":
8282                case "top":
8283                case "up":
8284                     this.moveTo(xy[0], xy[1]-distance, this.preanim(arguments, 2));
8285                     break;
8286                case "b":
8287                case "bottom":
8288                case "down":
8289                     this.moveTo(xy[0], xy[1]+distance, this.preanim(arguments, 2));
8290                     break;
8291             }
8292             return this;
8293         },
8294
8295         /**
8296          *  Store the current overflow setting and clip overflow on the element - use {@link #unclip} to remove
8297          * @return {Roo.Element} this
8298          */
8299         clip : function(){
8300             if(!this.isClipped){
8301                this.isClipped = true;
8302                this.originalClip = {
8303                    "o": this.getStyle("overflow"),
8304                    "x": this.getStyle("overflow-x"),
8305                    "y": this.getStyle("overflow-y")
8306                };
8307                this.setStyle("overflow", "hidden");
8308                this.setStyle("overflow-x", "hidden");
8309                this.setStyle("overflow-y", "hidden");
8310             }
8311             return this;
8312         },
8313
8314         /**
8315          *  Return clipping (overflow) to original clipping before clip() was called
8316          * @return {Roo.Element} this
8317          */
8318         unclip : function(){
8319             if(this.isClipped){
8320                 this.isClipped = false;
8321                 var o = this.originalClip;
8322                 if(o.o){this.setStyle("overflow", o.o);}
8323                 if(o.x){this.setStyle("overflow-x", o.x);}
8324                 if(o.y){this.setStyle("overflow-y", o.y);}
8325             }
8326             return this;
8327         },
8328
8329
8330         /**
8331          * Gets the x,y coordinates specified by the anchor position on the element.
8332          * @param {String} anchor (optional) The specified anchor position (defaults to "c").  See {@link #alignTo} for details on supported anchor positions.
8333          * @param {Object} size (optional) An object containing the size to use for calculating anchor position
8334          *                       {width: (target width), height: (target height)} (defaults to the element's current size)
8335          * @param {Boolean} local (optional) True to get the local (element top/left-relative) anchor position instead of page coordinates
8336          * @return {Array} [x, y] An array containing the element's x and y coordinates
8337          */
8338         getAnchorXY : function(anchor, local, s){
8339             //Passing a different size is useful for pre-calculating anchors,
8340             //especially for anchored animations that change the el size.
8341
8342             var w, h, vp = false;
8343             if(!s){
8344                 var d = this.dom;
8345                 if(d == document.body || d == document){
8346                     vp = true;
8347                     w = D.getViewWidth(); h = D.getViewHeight();
8348                 }else{
8349                     w = this.getWidth(); h = this.getHeight();
8350                 }
8351             }else{
8352                 w = s.width;  h = s.height;
8353             }
8354             var x = 0, y = 0, r = Math.round;
8355             switch((anchor || "tl").toLowerCase()){
8356                 case "c":
8357                     x = r(w*.5);
8358                     y = r(h*.5);
8359                 break;
8360                 case "t":
8361                     x = r(w*.5);
8362                     y = 0;
8363                 break;
8364                 case "l":
8365                     x = 0;
8366                     y = r(h*.5);
8367                 break;
8368                 case "r":
8369                     x = w;
8370                     y = r(h*.5);
8371                 break;
8372                 case "b":
8373                     x = r(w*.5);
8374                     y = h;
8375                 break;
8376                 case "tl":
8377                     x = 0;
8378                     y = 0;
8379                 break;
8380                 case "bl":
8381                     x = 0;
8382                     y = h;
8383                 break;
8384                 case "br":
8385                     x = w;
8386                     y = h;
8387                 break;
8388                 case "tr":
8389                     x = w;
8390                     y = 0;
8391                 break;
8392             }
8393             if(local === true){
8394                 return [x, y];
8395             }
8396             if(vp){
8397                 var sc = this.getScroll();
8398                 return [x + sc.left, y + sc.top];
8399             }
8400             //Add the element's offset xy
8401             var o = this.getXY();
8402             return [x+o[0], y+o[1]];
8403         },
8404
8405         /**
8406          * Gets the x,y coordinates to align this element with another element. See {@link #alignTo} for more info on the
8407          * supported position values.
8408          * @param {String/HTMLElement/Roo.Element} element The element to align to.
8409          * @param {String} position The position to align to.
8410          * @param {Array} offsets (optional) Offset the positioning by [x, y]
8411          * @return {Array} [x, y]
8412          */
8413         getAlignToXY : function(el, p, o){
8414             el = Roo.get(el);
8415             var d = this.dom;
8416             if(!el.dom){
8417                 throw "Element.alignTo with an element that doesn't exist";
8418             }
8419             var c = false; //constrain to viewport
8420             var p1 = "", p2 = "";
8421             o = o || [0,0];
8422
8423             if(!p){
8424                 p = "tl-bl";
8425             }else if(p == "?"){
8426                 p = "tl-bl?";
8427             }else if(p.indexOf("-") == -1){
8428                 p = "tl-" + p;
8429             }
8430             p = p.toLowerCase();
8431             var m = p.match(/^([a-z]+)-([a-z]+)(\?)?$/);
8432             if(!m){
8433                throw "Element.alignTo with an invalid alignment " + p;
8434             }
8435             p1 = m[1]; p2 = m[2]; c = !!m[3];
8436
8437             //Subtract the aligned el's internal xy from the target's offset xy
8438             //plus custom offset to get the aligned el's new offset xy
8439             var a1 = this.getAnchorXY(p1, true);
8440             var a2 = el.getAnchorXY(p2, false);
8441             var x = a2[0] - a1[0] + o[0];
8442             var y = a2[1] - a1[1] + o[1];
8443             if(c){
8444                 //constrain the aligned el to viewport if necessary
8445                 var w = this.getWidth(), h = this.getHeight(), r = el.getRegion();
8446                 // 5px of margin for ie
8447                 var dw = D.getViewWidth()-5, dh = D.getViewHeight()-5;
8448
8449                 //If we are at a viewport boundary and the aligned el is anchored on a target border that is
8450                 //perpendicular to the vp border, allow the aligned el to slide on that border,
8451                 //otherwise swap the aligned el to the opposite border of the target.
8452                 var p1y = p1.charAt(0), p1x = p1.charAt(p1.length-1);
8453                var p2y = p2.charAt(0), p2x = p2.charAt(p2.length-1);
8454                var swapY = ((p1y=="t" && p2y=="b") || (p1y=="b" && p2y=="t"));
8455                var swapX = ((p1x=="r" && p2x=="l") || (p1x=="l" && p2x=="r"));
8456
8457                var doc = document;
8458                var scrollX = (doc.documentElement.scrollLeft || doc.body.scrollLeft || 0)+5;
8459                var scrollY = (doc.documentElement.scrollTop || doc.body.scrollTop || 0)+5;
8460
8461                if((x+w) > dw + scrollX){
8462                     x = swapX ? r.left-w : dw+scrollX-w;
8463                 }
8464                if(x < scrollX){
8465                    x = swapX ? r.right : scrollX;
8466                }
8467                if((y+h) > dh + scrollY){
8468                     y = swapY ? r.top-h : dh+scrollY-h;
8469                 }
8470                if (y < scrollY){
8471                    y = swapY ? r.bottom : scrollY;
8472                }
8473             }
8474             return [x,y];
8475         },
8476
8477         // private
8478         getConstrainToXY : function(){
8479             var os = {top:0, left:0, bottom:0, right: 0};
8480
8481             return function(el, local, offsets, proposedXY){
8482                 el = Roo.get(el);
8483                 offsets = offsets ? Roo.applyIf(offsets, os) : os;
8484
8485                 var vw, vh, vx = 0, vy = 0;
8486                 if(el.dom == document.body || el.dom == document){
8487                     vw = Roo.lib.Dom.getViewWidth();
8488                     vh = Roo.lib.Dom.getViewHeight();
8489                 }else{
8490                     vw = el.dom.clientWidth;
8491                     vh = el.dom.clientHeight;
8492                     if(!local){
8493                         var vxy = el.getXY();
8494                         vx = vxy[0];
8495                         vy = vxy[1];
8496                     }
8497                 }
8498
8499                 var s = el.getScroll();
8500
8501                 vx += offsets.left + s.left;
8502                 vy += offsets.top + s.top;
8503
8504                 vw -= offsets.right;
8505                 vh -= offsets.bottom;
8506
8507                 var vr = vx+vw;
8508                 var vb = vy+vh;
8509
8510                 var xy = proposedXY || (!local ? this.getXY() : [this.getLeft(true), this.getTop(true)]);
8511                 var x = xy[0], y = xy[1];
8512                 var w = this.dom.offsetWidth, h = this.dom.offsetHeight;
8513
8514                 // only move it if it needs it
8515                 var moved = false;
8516
8517                 // first validate right/bottom
8518                 if((x + w) > vr){
8519                     x = vr - w;
8520                     moved = true;
8521                 }
8522                 if((y + h) > vb){
8523                     y = vb - h;
8524                     moved = true;
8525                 }
8526                 // then make sure top/left isn't negative
8527                 if(x < vx){
8528                     x = vx;
8529                     moved = true;
8530                 }
8531                 if(y < vy){
8532                     y = vy;
8533                     moved = true;
8534                 }
8535                 return moved ? [x, y] : false;
8536             };
8537         }(),
8538
8539         // private
8540         adjustForConstraints : function(xy, parent, offsets){
8541             return this.getConstrainToXY(parent || document, false, offsets, xy) ||  xy;
8542         },
8543
8544         /**
8545          * Aligns this element with another element relative to the specified anchor points. If the other element is the
8546          * document it aligns it to the viewport.
8547          * The position parameter is optional, and can be specified in any one of the following formats:
8548          * <ul>
8549          *   <li><b>Blank</b>: Defaults to aligning the element's top-left corner to the target's bottom-left corner ("tl-bl").</li>
8550          *   <li><b>One anchor (deprecated)</b>: The passed anchor position is used as the target element's anchor point.
8551          *       The element being aligned will position its top-left corner (tl) to that point.  <i>This method has been
8552          *       deprecated in favor of the newer two anchor syntax below</i>.</li>
8553          *   <li><b>Two anchors</b>: If two values from the table below are passed separated by a dash, the first value is used as the
8554          *       element's anchor point, and the second value is used as the target's anchor point.</li>
8555          * </ul>
8556          * In addition to the anchor points, the position parameter also supports the "?" character.  If "?" is passed at the end of
8557          * the position string, the element will attempt to align as specified, but the position will be adjusted to constrain to
8558          * the viewport if necessary.  Note that the element being aligned might be swapped to align to a different position than
8559          * that specified in order to enforce the viewport constraints.
8560          * Following are all of the supported anchor positions:
8561     <pre>
8562     Value  Description
8563     -----  -----------------------------
8564     tl     The top left corner (default)
8565     t      The center of the top edge
8566     tr     The top right corner
8567     l      The center of the left edge
8568     c      In the center of the element
8569     r      The center of the right edge
8570     bl     The bottom left corner
8571     b      The center of the bottom edge
8572     br     The bottom right corner
8573     </pre>
8574     Example Usage:
8575     <pre><code>
8576     // align el to other-el using the default positioning ("tl-bl", non-constrained)
8577     el.alignTo("other-el");
8578
8579     // align the top left corner of el with the top right corner of other-el (constrained to viewport)
8580     el.alignTo("other-el", "tr?");
8581
8582     // align the bottom right corner of el with the center left edge of other-el
8583     el.alignTo("other-el", "br-l?");
8584
8585     // align the center of el with the bottom left corner of other-el and
8586     // adjust the x position by -6 pixels (and the y position by 0)
8587     el.alignTo("other-el", "c-bl", [-6, 0]);
8588     </code></pre>
8589          * @param {String/HTMLElement/Roo.Element} element The element to align to.
8590          * @param {String} position The position to align to.
8591          * @param {Array} offsets (optional) Offset the positioning by [x, y]
8592          * @param {Boolean/Object} animate (optional) true for the default animation or a standard Element animation config object
8593          * @return {Roo.Element} this
8594          */
8595         alignTo : function(element, position, offsets, animate){
8596             var xy = this.getAlignToXY(element, position, offsets);
8597             this.setXY(xy, this.preanim(arguments, 3));
8598             return this;
8599         },
8600
8601         /**
8602          * Anchors an element to another element and realigns it when the window is resized.
8603          * @param {String/HTMLElement/Roo.Element} element The element to align to.
8604          * @param {String} position The position to align to.
8605          * @param {Array} offsets (optional) Offset the positioning by [x, y]
8606          * @param {Boolean/Object} animate (optional) True for the default animation or a standard Element animation config object
8607          * @param {Boolean/Number} monitorScroll (optional) True to monitor body scroll and reposition. If this parameter
8608          * is a number, it is used as the buffer delay (defaults to 50ms).
8609          * @param {Function} callback The function to call after the animation finishes
8610          * @return {Roo.Element} this
8611          */
8612         anchorTo : function(el, alignment, offsets, animate, monitorScroll, callback){
8613             var action = function(){
8614                 this.alignTo(el, alignment, offsets, animate);
8615                 Roo.callback(callback, this);
8616             };
8617             Roo.EventManager.onWindowResize(action, this);
8618             var tm = typeof monitorScroll;
8619             if(tm != 'undefined'){
8620                 Roo.EventManager.on(window, 'scroll', action, this,
8621                     {buffer: tm == 'number' ? monitorScroll : 50});
8622             }
8623             action.call(this); // align immediately
8624             return this;
8625         },
8626         /**
8627          * Clears any opacity settings from this element. Required in some cases for IE.
8628          * @return {Roo.Element} this
8629          */
8630         clearOpacity : function(){
8631             if (window.ActiveXObject) {
8632                 if(typeof this.dom.style.filter == 'string' && (/alpha/i).test(this.dom.style.filter)){
8633                     this.dom.style.filter = "";
8634                 }
8635             } else {
8636                 this.dom.style.opacity = "";
8637                 this.dom.style["-moz-opacity"] = "";
8638                 this.dom.style["-khtml-opacity"] = "";
8639             }
8640             return this;
8641         },
8642
8643         /**
8644          * Hide this element - Uses display mode to determine whether to use "display" or "visibility". See {@link #setVisible}.
8645          * @param {Boolean/Object} animate (optional) true for the default animation or a standard Element animation config object
8646          * @return {Roo.Element} this
8647          */
8648         hide : function(animate){
8649             this.setVisible(false, this.preanim(arguments, 0));
8650             return this;
8651         },
8652
8653         /**
8654         * Show this element - Uses display mode to determine whether to use "display" or "visibility". See {@link #setVisible}.
8655         * @param {Boolean/Object} animate (optional) true for the default animation or a standard Element animation config object
8656          * @return {Roo.Element} this
8657          */
8658         show : function(animate){
8659             this.setVisible(true, this.preanim(arguments, 0));
8660             return this;
8661         },
8662
8663         /**
8664          * @private Test if size has a unit, otherwise appends the default
8665          */
8666         addUnits : function(size){
8667             return Roo.Element.addUnits(size, this.defaultUnit);
8668         },
8669
8670         /**
8671          * Temporarily enables offsets (width,height,x,y) for an element with display:none, use endMeasure() when done.
8672          * @return {Roo.Element} this
8673          */
8674         beginMeasure : function(){
8675             var el = this.dom;
8676             if(el.offsetWidth || el.offsetHeight){
8677                 return this; // offsets work already
8678             }
8679             var changed = [];
8680             var p = this.dom, b = document.body; // start with this element
8681             while((!el.offsetWidth && !el.offsetHeight) && p && p.tagName && p != b){
8682                 var pe = Roo.get(p);
8683                 if(pe.getStyle('display') == 'none'){
8684                     changed.push({el: p, visibility: pe.getStyle("visibility")});
8685                     p.style.visibility = "hidden";
8686                     p.style.display = "block";
8687                 }
8688                 p = p.parentNode;
8689             }
8690             this._measureChanged = changed;
8691             return this;
8692
8693         },
8694
8695         /**
8696          * Restores displays to before beginMeasure was called
8697          * @return {Roo.Element} this
8698          */
8699         endMeasure : function(){
8700             var changed = this._measureChanged;
8701             if(changed){
8702                 for(var i = 0, len = changed.length; i < len; i++) {
8703                     var r = changed[i];
8704                     r.el.style.visibility = r.visibility;
8705                     r.el.style.display = "none";
8706                 }
8707                 this._measureChanged = null;
8708             }
8709             return this;
8710         },
8711
8712         /**
8713         * Update the innerHTML of this element, optionally searching for and processing scripts
8714         * @param {String} html The new HTML
8715         * @param {Boolean} loadScripts (optional) true to look for and process scripts
8716         * @param {Function} callback For async script loading you can be noticed when the update completes
8717         * @return {Roo.Element} this
8718          */
8719         update : function(html, loadScripts, callback){
8720             if(typeof html == "undefined"){
8721                 html = "";
8722             }
8723             if(loadScripts !== true){
8724                 this.dom.innerHTML = html;
8725                 if(typeof callback == "function"){
8726                     callback();
8727                 }
8728                 return this;
8729             }
8730             var id = Roo.id();
8731             var dom = this.dom;
8732
8733             html += '<span id="' + id + '"></span>';
8734
8735             E.onAvailable(id, function(){
8736                 var hd = document.getElementsByTagName("head")[0];
8737                 var re = /(?:<script([^>]*)?>)((\n|\r|.)*?)(?:<\/script>)/ig;
8738                 var srcRe = /\ssrc=([\'\"])(.*?)\1/i;
8739                 var typeRe = /\stype=([\'\"])(.*?)\1/i;
8740
8741                 var match;
8742                 while(match = re.exec(html)){
8743                     var attrs = match[1];
8744                     var srcMatch = attrs ? attrs.match(srcRe) : false;
8745                     if(srcMatch && srcMatch[2]){
8746                        var s = document.createElement("script");
8747                        s.src = srcMatch[2];
8748                        var typeMatch = attrs.match(typeRe);
8749                        if(typeMatch && typeMatch[2]){
8750                            s.type = typeMatch[2];
8751                        }
8752                        hd.appendChild(s);
8753                     }else if(match[2] && match[2].length > 0){
8754                         if(window.execScript) {
8755                            window.execScript(match[2]);
8756                         } else {
8757                             /**
8758                              * eval:var:id
8759                              * eval:var:dom
8760                              * eval:var:html
8761                              * 
8762                              */
8763                            window.eval(match[2]);
8764                         }
8765                     }
8766                 }
8767                 var el = document.getElementById(id);
8768                 if(el){el.parentNode.removeChild(el);}
8769                 if(typeof callback == "function"){
8770                     callback();
8771                 }
8772             });
8773             dom.innerHTML = html.replace(/(?:<script.*?>)((\n|\r|.)*?)(?:<\/script>)/ig, "");
8774             return this;
8775         },
8776
8777         /**
8778          * Direct access to the UpdateManager update() method (takes the same parameters).
8779          * @param {String/Function} url The url for this request or a function to call to get the url
8780          * @param {String/Object} params (optional) The parameters to pass as either a url encoded string "param1=1&amp;param2=2" or an object {param1: 1, param2: 2}
8781          * @param {Function} callback (optional) Callback when transaction is complete - called with signature (oElement, bSuccess)
8782          * @param {Boolean} discardUrl (optional) By default when you execute an update the defaultUrl is changed to the last used url. If true, it will not store the url.
8783          * @return {Roo.Element} this
8784          */
8785         load : function(){
8786             var um = this.getUpdateManager();
8787             um.update.apply(um, arguments);
8788             return this;
8789         },
8790
8791         /**
8792         * Gets this element's UpdateManager
8793         * @return {Roo.UpdateManager} The UpdateManager
8794         */
8795         getUpdateManager : function(){
8796             if(!this.updateManager){
8797                 this.updateManager = new Roo.UpdateManager(this);
8798             }
8799             return this.updateManager;
8800         },
8801
8802         /**
8803          * Disables text selection for this element (normalized across browsers)
8804          * @return {Roo.Element} this
8805          */
8806         unselectable : function(){
8807             this.dom.unselectable = "on";
8808             this.swallowEvent("selectstart", true);
8809             this.applyStyles("-moz-user-select:none;-khtml-user-select:none;");
8810             this.addClass("x-unselectable");
8811             return this;
8812         },
8813
8814         /**
8815         * Calculates the x, y to center this element on the screen
8816         * @return {Array} The x, y values [x, y]
8817         */
8818         getCenterXY : function(){
8819             return this.getAlignToXY(document, 'c-c');
8820         },
8821
8822         /**
8823         * Centers the Element in either the viewport, or another Element.
8824         * @param {String/HTMLElement/Roo.Element} centerIn (optional) The element in which to center the element.
8825         */
8826         center : function(centerIn){
8827             this.alignTo(centerIn || document, 'c-c');
8828             return this;
8829         },
8830
8831         /**
8832          * Tests various css rules/browsers to determine if this element uses a border box
8833          * @return {Boolean}
8834          */
8835         isBorderBox : function(){
8836             return noBoxAdjust[this.dom.tagName.toLowerCase()] || Roo.isBorderBox;
8837         },
8838
8839         /**
8840          * Return a box {x, y, width, height} that can be used to set another elements
8841          * size/location to match this element.
8842          * @param {Boolean} contentBox (optional) If true a box for the content of the element is returned.
8843          * @param {Boolean} local (optional) If true the element's left and top are returned instead of page x/y.
8844          * @return {Object} box An object in the format {x, y, width, height}
8845          */
8846         getBox : function(contentBox, local){
8847             var xy;
8848             if(!local){
8849                 xy = this.getXY();
8850             }else{
8851                 var left = parseInt(this.getStyle("left"), 10) || 0;
8852                 var top = parseInt(this.getStyle("top"), 10) || 0;
8853                 xy = [left, top];
8854             }
8855             var el = this.dom, w = el.offsetWidth, h = el.offsetHeight, bx;
8856             if(!contentBox){
8857                 bx = {x: xy[0], y: xy[1], 0: xy[0], 1: xy[1], width: w, height: h};
8858             }else{
8859                 var l = this.getBorderWidth("l")+this.getPadding("l");
8860                 var r = this.getBorderWidth("r")+this.getPadding("r");
8861                 var t = this.getBorderWidth("t")+this.getPadding("t");
8862                 var b = this.getBorderWidth("b")+this.getPadding("b");
8863                 bx = {x: xy[0]+l, y: xy[1]+t, 0: xy[0]+l, 1: xy[1]+t, width: w-(l+r), height: h-(t+b)};
8864             }
8865             bx.right = bx.x + bx.width;
8866             bx.bottom = bx.y + bx.height;
8867             return bx;
8868         },
8869
8870         /**
8871          * Returns the sum width of the padding and borders for the passed "sides". See getBorderWidth()
8872          for more information about the sides.
8873          * @param {String} sides
8874          * @return {Number}
8875          */
8876         getFrameWidth : function(sides, onlyContentBox){
8877             return onlyContentBox && Roo.isBorderBox ? 0 : (this.getPadding(sides) + this.getBorderWidth(sides));
8878         },
8879
8880         /**
8881          * Sets the element's box. Use getBox() on another element to get a box obj. If animate is true then width, height, x and y will be animated concurrently.
8882          * @param {Object} box The box to fill {x, y, width, height}
8883          * @param {Boolean} adjust (optional) Whether to adjust for box-model issues automatically
8884          * @param {Boolean/Object} animate (optional) true for the default animation or a standard Element animation config object
8885          * @return {Roo.Element} this
8886          */
8887         setBox : function(box, adjust, animate){
8888             var w = box.width, h = box.height;
8889             if((adjust && !this.autoBoxAdjust) && !this.isBorderBox()){
8890                w -= (this.getBorderWidth("lr") + this.getPadding("lr"));
8891                h -= (this.getBorderWidth("tb") + this.getPadding("tb"));
8892             }
8893             this.setBounds(box.x, box.y, w, h, this.preanim(arguments, 2));
8894             return this;
8895         },
8896
8897         /**
8898          * Forces the browser to repaint this element
8899          * @return {Roo.Element} this
8900          */
8901          repaint : function(){
8902             var dom = this.dom;
8903             this.addClass("x-repaint");
8904             setTimeout(function(){
8905                 Roo.get(dom).removeClass("x-repaint");
8906             }, 1);
8907             return this;
8908         },
8909
8910         /**
8911          * Returns an object with properties top, left, right and bottom representing the margins of this element unless sides is passed,
8912          * then it returns the calculated width of the sides (see getPadding)
8913          * @param {String} sides (optional) Any combination of l, r, t, b to get the sum of those sides
8914          * @return {Object/Number}
8915          */
8916         getMargins : function(side){
8917             if(!side){
8918                 return {
8919                     top: parseInt(this.getStyle("margin-top"), 10) || 0,
8920                     left: parseInt(this.getStyle("margin-left"), 10) || 0,
8921                     bottom: parseInt(this.getStyle("margin-bottom"), 10) || 0,
8922                     right: parseInt(this.getStyle("margin-right"), 10) || 0
8923                 };
8924             }else{
8925                 return this.addStyles(side, El.margins);
8926              }
8927         },
8928
8929         // private
8930         addStyles : function(sides, styles){
8931             var val = 0, v, w;
8932             for(var i = 0, len = sides.length; i < len; i++){
8933                 v = this.getStyle(styles[sides.charAt(i)]);
8934                 if(v){
8935                      w = parseInt(v, 10);
8936                      if(w){ val += w; }
8937                 }
8938             }
8939             return val;
8940         },
8941
8942         /**
8943          * Creates a proxy element of this element
8944          * @param {String/Object} config The class name of the proxy element or a DomHelper config object
8945          * @param {String/HTMLElement} renderTo (optional) The element or element id to render the proxy to (defaults to document.body)
8946          * @param {Boolean} matchBox (optional) True to align and size the proxy to this element now (defaults to false)
8947          * @return {Roo.Element} The new proxy element
8948          */
8949         createProxy : function(config, renderTo, matchBox){
8950             if(renderTo){
8951                 renderTo = Roo.getDom(renderTo);
8952             }else{
8953                 renderTo = document.body;
8954             }
8955             config = typeof config == "object" ?
8956                 config : {tag : "div", cls: config};
8957             var proxy = Roo.DomHelper.append(renderTo, config, true);
8958             if(matchBox){
8959                proxy.setBox(this.getBox());
8960             }
8961             return proxy;
8962         },
8963
8964         /**
8965          * Puts a mask over this element to disable user interaction. Requires core.css.
8966          * This method can only be applied to elements which accept child nodes.
8967          * @param {String} msg (optional) A message to display in the mask
8968          * @param {String} msgCls (optional) A css class to apply to the msg element
8969          * @return {Element} The mask  element
8970          */
8971         mask : function(msg, msgCls)
8972         {
8973             if(this.getStyle("position") == "static" && this.dom.tagName !== 'BODY'){
8974                 this.setStyle("position", "relative");
8975             }
8976             if(!this._mask){
8977                 this._mask = Roo.DomHelper.append(this.dom, {cls:"roo-el-mask"}, true);
8978             }
8979             this.addClass("x-masked");
8980             this._mask.setDisplayed(true);
8981             
8982             // we wander
8983             var z = 0;
8984             var dom = this.dom
8985             while (dom && dom.style) {
8986                 if (!isNaN(parseInt(dom.style.zIndex))) {
8987                     z = Math.max(z, parseInt(dom.style.zIndex));
8988                 }
8989                 dom = dom.parentNode;
8990             }
8991             // if we are masking the body - then it hides everything..
8992             if (this.dom == document.body) {
8993                 z = 1000000;
8994                 this._mask.setWidth(Roo.lib.Dom.getDocumentWidth());
8995                 this._mask.setHeight(Roo.lib.Dom.getDocumentHeight());
8996             }
8997            
8998             if(typeof msg == 'string'){
8999                 if(!this._maskMsg){
9000                     this._maskMsg = Roo.DomHelper.append(this.dom, {cls:"roo-el-mask-msg", cn:{tag:'div'}}, true);
9001                 }
9002                 var mm = this._maskMsg;
9003                 mm.dom.className = msgCls ? "roo-el-mask-msg " + msgCls : "roo-el-mask-msg";
9004                 mm.dom.firstChild.innerHTML = msg;
9005                 mm.setDisplayed(true);
9006                 mm.center(this);
9007                 mm.setStyle('z-index', z + 102);
9008             }
9009             if(Roo.isIE && !(Roo.isIE7 && Roo.isStrict) && this.getStyle('height') == 'auto'){ // ie will not expand full height automatically
9010                 this._mask.setHeight(this.getHeight());
9011             }
9012             this._mask.setStyle('z-index', z + 100);
9013             
9014             return this._mask;
9015         },
9016
9017         /**
9018          * Removes a previously applied mask. If removeEl is true the mask overlay is destroyed, otherwise
9019          * it is cached for reuse.
9020          */
9021         unmask : function(removeEl){
9022             if(this._mask){
9023                 if(removeEl === true){
9024                     this._mask.remove();
9025                     delete this._mask;
9026                     if(this._maskMsg){
9027                         this._maskMsg.remove();
9028                         delete this._maskMsg;
9029                     }
9030                 }else{
9031                     this._mask.setDisplayed(false);
9032                     if(this._maskMsg){
9033                         this._maskMsg.setDisplayed(false);
9034                     }
9035                 }
9036             }
9037             this.removeClass("x-masked");
9038         },
9039
9040         /**
9041          * Returns true if this element is masked
9042          * @return {Boolean}
9043          */
9044         isMasked : function(){
9045             return this._mask && this._mask.isVisible();
9046         },
9047
9048         /**
9049          * Creates an iframe shim for this element to keep selects and other windowed objects from
9050          * showing through.
9051          * @return {Roo.Element} The new shim element
9052          */
9053         createShim : function(){
9054             var el = document.createElement('iframe');
9055             el.frameBorder = 'no';
9056             el.className = 'roo-shim';
9057             if(Roo.isIE && Roo.isSecure){
9058                 el.src = Roo.SSL_SECURE_URL;
9059             }
9060             var shim = Roo.get(this.dom.parentNode.insertBefore(el, this.dom));
9061             shim.autoBoxAdjust = false;
9062             return shim;
9063         },
9064
9065         /**
9066          * Removes this element from the DOM and deletes it from the cache
9067          */
9068         remove : function(){
9069             if(this.dom.parentNode){
9070                 this.dom.parentNode.removeChild(this.dom);
9071             }
9072             delete El.cache[this.dom.id];
9073         },
9074
9075         /**
9076          * Sets up event handlers to add and remove a css class when the mouse is over this element
9077          * @param {String} className
9078          * @param {Boolean} preventFlicker (optional) If set to true, it prevents flickering by filtering
9079          * mouseout events for children elements
9080          * @return {Roo.Element} this
9081          */
9082         addClassOnOver : function(className, preventFlicker){
9083             this.on("mouseover", function(){
9084                 Roo.fly(this, '_internal').addClass(className);
9085             }, this.dom);
9086             var removeFn = function(e){
9087                 if(preventFlicker !== true || !e.within(this, true)){
9088                     Roo.fly(this, '_internal').removeClass(className);
9089                 }
9090             };
9091             this.on("mouseout", removeFn, this.dom);
9092             return this;
9093         },
9094
9095         /**
9096          * Sets up event handlers to add and remove a css class when this element has the focus
9097          * @param {String} className
9098          * @return {Roo.Element} this
9099          */
9100         addClassOnFocus : function(className){
9101             this.on("focus", function(){
9102                 Roo.fly(this, '_internal').addClass(className);
9103             }, this.dom);
9104             this.on("blur", function(){
9105                 Roo.fly(this, '_internal').removeClass(className);
9106             }, this.dom);
9107             return this;
9108         },
9109         /**
9110          * Sets up event handlers to add and remove a css class when the mouse is down and then up on this element (a click effect)
9111          * @param {String} className
9112          * @return {Roo.Element} this
9113          */
9114         addClassOnClick : function(className){
9115             var dom = this.dom;
9116             this.on("mousedown", function(){
9117                 Roo.fly(dom, '_internal').addClass(className);
9118                 var d = Roo.get(document);
9119                 var fn = function(){
9120                     Roo.fly(dom, '_internal').removeClass(className);
9121                     d.removeListener("mouseup", fn);
9122                 };
9123                 d.on("mouseup", fn);
9124             });
9125             return this;
9126         },
9127
9128         /**
9129          * Stops the specified event from bubbling and optionally prevents the default action
9130          * @param {String} eventName
9131          * @param {Boolean} preventDefault (optional) true to prevent the default action too
9132          * @return {Roo.Element} this
9133          */
9134         swallowEvent : function(eventName, preventDefault){
9135             var fn = function(e){
9136                 e.stopPropagation();
9137                 if(preventDefault){
9138                     e.preventDefault();
9139                 }
9140             };
9141             if(eventName instanceof Array){
9142                 for(var i = 0, len = eventName.length; i < len; i++){
9143                      this.on(eventName[i], fn);
9144                 }
9145                 return this;
9146             }
9147             this.on(eventName, fn);
9148             return this;
9149         },
9150
9151         /**
9152          * @private
9153          */
9154       fitToParentDelegate : Roo.emptyFn, // keep a reference to the fitToParent delegate
9155
9156         /**
9157          * Sizes this element to its parent element's dimensions performing
9158          * neccessary box adjustments.
9159          * @param {Boolean} monitorResize (optional) If true maintains the fit when the browser window is resized.
9160          * @param {String/HTMLElment/Element} targetParent (optional) The target parent, default to the parentNode.
9161          * @return {Roo.Element} this
9162          */
9163         fitToParent : function(monitorResize, targetParent) {
9164           Roo.EventManager.removeResizeListener(this.fitToParentDelegate); // always remove previous fitToParent delegate from onWindowResize
9165           this.fitToParentDelegate = Roo.emptyFn; // remove reference to previous delegate
9166           if (monitorResize === true && !this.dom.parentNode) { // check if this Element still exists
9167             return;
9168           }
9169           var p = Roo.get(targetParent || this.dom.parentNode);
9170           this.setSize(p.getComputedWidth() - p.getFrameWidth('lr'), p.getComputedHeight() - p.getFrameWidth('tb'));
9171           if (monitorResize === true) {
9172             this.fitToParentDelegate = this.fitToParent.createDelegate(this, [true, targetParent]);
9173             Roo.EventManager.onWindowResize(this.fitToParentDelegate);
9174           }
9175           return this;
9176         },
9177
9178         /**
9179          * Gets the next sibling, skipping text nodes
9180          * @return {HTMLElement} The next sibling or null
9181          */
9182         getNextSibling : function(){
9183             var n = this.dom.nextSibling;
9184             while(n && n.nodeType != 1){
9185                 n = n.nextSibling;
9186             }
9187             return n;
9188         },
9189
9190         /**
9191          * Gets the previous sibling, skipping text nodes
9192          * @return {HTMLElement} The previous sibling or null
9193          */
9194         getPrevSibling : function(){
9195             var n = this.dom.previousSibling;
9196             while(n && n.nodeType != 1){
9197                 n = n.previousSibling;
9198             }
9199             return n;
9200         },
9201
9202
9203         /**
9204          * Appends the passed element(s) to this element
9205          * @param {String/HTMLElement/Array/Element/CompositeElement} el
9206          * @return {Roo.Element} this
9207          */
9208         appendChild: function(el){
9209             el = Roo.get(el);
9210             el.appendTo(this);
9211             return this;
9212         },
9213
9214         /**
9215          * Creates the passed DomHelper config and appends it to this element or optionally inserts it before the passed child element.
9216          * @param {Object} config DomHelper element config object.  If no tag is specified (e.g., {tag:'input'}) then a div will be
9217          * automatically generated with the specified attributes.
9218          * @param {HTMLElement} insertBefore (optional) a child element of this element
9219          * @param {Boolean} returnDom (optional) true to return the dom node instead of creating an Element
9220          * @return {Roo.Element} The new child element
9221          */
9222         createChild: function(config, insertBefore, returnDom){
9223             config = config || {tag:'div'};
9224             if(insertBefore){
9225                 return Roo.DomHelper.insertBefore(insertBefore, config, returnDom !== true);
9226             }
9227             return Roo.DomHelper[!this.dom.firstChild ? 'overwrite' : 'append'](this.dom, config,  returnDom !== true);
9228         },
9229
9230         /**
9231          * Appends this element to the passed element
9232          * @param {String/HTMLElement/Element} el The new parent element
9233          * @return {Roo.Element} this
9234          */
9235         appendTo: function(el){
9236             el = Roo.getDom(el);
9237             el.appendChild(this.dom);
9238             return this;
9239         },
9240
9241         /**
9242          * Inserts this element before the passed element in the DOM
9243          * @param {String/HTMLElement/Element} el The element to insert before
9244          * @return {Roo.Element} this
9245          */
9246         insertBefore: function(el){
9247             el = Roo.getDom(el);
9248             el.parentNode.insertBefore(this.dom, el);
9249             return this;
9250         },
9251
9252         /**
9253          * Inserts this element after the passed element in the DOM
9254          * @param {String/HTMLElement/Element} el The element to insert after
9255          * @return {Roo.Element} this
9256          */
9257         insertAfter: function(el){
9258             el = Roo.getDom(el);
9259             el.parentNode.insertBefore(this.dom, el.nextSibling);
9260             return this;
9261         },
9262
9263         /**
9264          * Inserts (or creates) an element (or DomHelper config) as the first child of the this element
9265          * @param {String/HTMLElement/Element/Object} el The id or element to insert or a DomHelper config to create and insert
9266          * @return {Roo.Element} The new child
9267          */
9268         insertFirst: function(el, returnDom){
9269             el = el || {};
9270             if(typeof el == 'object' && !el.nodeType){ // dh config
9271                 return this.createChild(el, this.dom.firstChild, returnDom);
9272             }else{
9273                 el = Roo.getDom(el);
9274                 this.dom.insertBefore(el, this.dom.firstChild);
9275                 return !returnDom ? Roo.get(el) : el;
9276             }
9277         },
9278
9279         /**
9280          * Inserts (or creates) the passed element (or DomHelper config) as a sibling of this element
9281          * @param {String/HTMLElement/Element/Object} el The id or element to insert or a DomHelper config to create and insert
9282          * @param {String} where (optional) 'before' or 'after' defaults to before
9283          * @param {Boolean} returnDom (optional) True to return the raw DOM element instead of Roo.Element
9284          * @return {Roo.Element} the inserted Element
9285          */
9286         insertSibling: function(el, where, returnDom){
9287             where = where ? where.toLowerCase() : 'before';
9288             el = el || {};
9289             var rt, refNode = where == 'before' ? this.dom : this.dom.nextSibling;
9290
9291             if(typeof el == 'object' && !el.nodeType){ // dh config
9292                 if(where == 'after' && !this.dom.nextSibling){
9293                     rt = Roo.DomHelper.append(this.dom.parentNode, el, !returnDom);
9294                 }else{
9295                     rt = Roo.DomHelper[where == 'after' ? 'insertAfter' : 'insertBefore'](this.dom, el, !returnDom);
9296                 }
9297
9298             }else{
9299                 rt = this.dom.parentNode.insertBefore(Roo.getDom(el),
9300                             where == 'before' ? this.dom : this.dom.nextSibling);
9301                 if(!returnDom){
9302                     rt = Roo.get(rt);
9303                 }
9304             }
9305             return rt;
9306         },
9307
9308         /**
9309          * Creates and wraps this element with another element
9310          * @param {Object} config (optional) DomHelper element config object for the wrapper element or null for an empty div
9311          * @param {Boolean} returnDom (optional) True to return the raw DOM element instead of Roo.Element
9312          * @return {HTMLElement/Element} The newly created wrapper element
9313          */
9314         wrap: function(config, returnDom){
9315             if(!config){
9316                 config = {tag: "div"};
9317             }
9318             var newEl = Roo.DomHelper.insertBefore(this.dom, config, !returnDom);
9319             newEl.dom ? newEl.dom.appendChild(this.dom) : newEl.appendChild(this.dom);
9320             return newEl;
9321         },
9322
9323         /**
9324          * Replaces the passed element with this element
9325          * @param {String/HTMLElement/Element} el The element to replace
9326          * @return {Roo.Element} this
9327          */
9328         replace: function(el){
9329             el = Roo.get(el);
9330             this.insertBefore(el);
9331             el.remove();
9332             return this;
9333         },
9334
9335         /**
9336          * Inserts an html fragment into this element
9337          * @param {String} where Where to insert the html in relation to the this element - beforeBegin, afterBegin, beforeEnd, afterEnd.
9338          * @param {String} html The HTML fragment
9339          * @param {Boolean} returnEl True to return an Roo.Element
9340          * @return {HTMLElement/Roo.Element} The inserted node (or nearest related if more than 1 inserted)
9341          */
9342         insertHtml : function(where, html, returnEl){
9343             var el = Roo.DomHelper.insertHtml(where, this.dom, html);
9344             return returnEl ? Roo.get(el) : el;
9345         },
9346
9347         /**
9348          * Sets the passed attributes as attributes of this element (a style attribute can be a string, object or function)
9349          * @param {Object} o The object with the attributes
9350          * @param {Boolean} useSet (optional) false to override the default setAttribute to use expandos.
9351          * @return {Roo.Element} this
9352          */
9353         set : function(o, useSet){
9354             var el = this.dom;
9355             useSet = typeof useSet == 'undefined' ? (el.setAttribute ? true : false) : useSet;
9356             for(var attr in o){
9357                 if(attr == "style" || typeof o[attr] == "function") continue;
9358                 if(attr=="cls"){
9359                     el.className = o["cls"];
9360                 }else{
9361                     if(useSet) el.setAttribute(attr, o[attr]);
9362                     else el[attr] = o[attr];
9363                 }
9364             }
9365             if(o.style){
9366                 Roo.DomHelper.applyStyles(el, o.style);
9367             }
9368             return this;
9369         },
9370
9371         /**
9372          * Convenience method for constructing a KeyMap
9373          * @param {Number/Array/Object/String} key Either a string with the keys to listen for, the numeric key code, array of key codes or an object with the following options:
9374          *                                  {key: (number or array), shift: (true/false), ctrl: (true/false), alt: (true/false)}
9375          * @param {Function} fn The function to call
9376          * @param {Object} scope (optional) The scope of the function
9377          * @return {Roo.KeyMap} The KeyMap created
9378          */
9379         addKeyListener : function(key, fn, scope){
9380             var config;
9381             if(typeof key != "object" || key instanceof Array){
9382                 config = {
9383                     key: key,
9384                     fn: fn,
9385                     scope: scope
9386                 };
9387             }else{
9388                 config = {
9389                     key : key.key,
9390                     shift : key.shift,
9391                     ctrl : key.ctrl,
9392                     alt : key.alt,
9393                     fn: fn,
9394                     scope: scope
9395                 };
9396             }
9397             return new Roo.KeyMap(this, config);
9398         },
9399
9400         /**
9401          * Creates a KeyMap for this element
9402          * @param {Object} config The KeyMap config. See {@link Roo.KeyMap} for more details
9403          * @return {Roo.KeyMap} The KeyMap created
9404          */
9405         addKeyMap : function(config){
9406             return new Roo.KeyMap(this, config);
9407         },
9408
9409         /**
9410          * Returns true if this element is scrollable.
9411          * @return {Boolean}
9412          */
9413          isScrollable : function(){
9414             var dom = this.dom;
9415             return dom.scrollHeight > dom.clientHeight || dom.scrollWidth > dom.clientWidth;
9416         },
9417
9418         /**
9419          * Scrolls this element the specified scroll point. It does NOT do bounds checking so if you scroll to a weird value it will try to do it. For auto bounds checking, use scroll().
9420          * @param {String} side Either "left" for scrollLeft values or "top" for scrollTop values.
9421          * @param {Number} value The new scroll value
9422          * @param {Boolean/Object} animate (optional) true for the default animation or a standard Element animation config object
9423          * @return {Element} this
9424          */
9425
9426         scrollTo : function(side, value, animate){
9427             var prop = side.toLowerCase() == "left" ? "scrollLeft" : "scrollTop";
9428             if(!animate || !A){
9429                 this.dom[prop] = value;
9430             }else{
9431                 var to = prop == "scrollLeft" ? [value, this.dom.scrollTop] : [this.dom.scrollLeft, value];
9432                 this.anim({scroll: {"to": to}}, this.preanim(arguments, 2), 'scroll');
9433             }
9434             return this;
9435         },
9436
9437         /**
9438          * Scrolls this element the specified direction. Does bounds checking to make sure the scroll is
9439          * within this element's scrollable range.
9440          * @param {String} direction Possible values are: "l","left" - "r","right" - "t","top","up" - "b","bottom","down".
9441          * @param {Number} distance How far to scroll the element in pixels
9442          * @param {Boolean/Object} animate (optional) true for the default animation or a standard Element animation config object
9443          * @return {Boolean} Returns true if a scroll was triggered or false if the element
9444          * was scrolled as far as it could go.
9445          */
9446          scroll : function(direction, distance, animate){
9447              if(!this.isScrollable()){
9448                  return;
9449              }
9450              var el = this.dom;
9451              var l = el.scrollLeft, t = el.scrollTop;
9452              var w = el.scrollWidth, h = el.scrollHeight;
9453              var cw = el.clientWidth, ch = el.clientHeight;
9454              direction = direction.toLowerCase();
9455              var scrolled = false;
9456              var a = this.preanim(arguments, 2);
9457              switch(direction){
9458                  case "l":
9459                  case "left":
9460                      if(w - l > cw){
9461                          var v = Math.min(l + distance, w-cw);
9462                          this.scrollTo("left", v, a);
9463                          scrolled = true;
9464                      }
9465                      break;
9466                 case "r":
9467                 case "right":
9468                      if(l > 0){
9469                          var v = Math.max(l - distance, 0);
9470                          this.scrollTo("left", v, a);
9471                          scrolled = true;
9472                      }
9473                      break;
9474                 case "t":
9475                 case "top":
9476                 case "up":
9477                      if(t > 0){
9478                          var v = Math.max(t - distance, 0);
9479                          this.scrollTo("top", v, a);
9480                          scrolled = true;
9481                      }
9482                      break;
9483                 case "b":
9484                 case "bottom":
9485                 case "down":
9486                      if(h - t > ch){
9487                          var v = Math.min(t + distance, h-ch);
9488                          this.scrollTo("top", v, a);
9489                          scrolled = true;
9490                      }
9491                      break;
9492              }
9493              return scrolled;
9494         },
9495
9496         /**
9497          * Translates the passed page coordinates into left/top css values for this element
9498          * @param {Number/Array} x The page x or an array containing [x, y]
9499          * @param {Number} y The page y
9500          * @return {Object} An object with left and top properties. e.g. {left: (value), top: (value)}
9501          */
9502         translatePoints : function(x, y){
9503             if(typeof x == 'object' || x instanceof Array){
9504                 y = x[1]; x = x[0];
9505             }
9506             var p = this.getStyle('position');
9507             var o = this.getXY();
9508
9509             var l = parseInt(this.getStyle('left'), 10);
9510             var t = parseInt(this.getStyle('top'), 10);
9511
9512             if(isNaN(l)){
9513                 l = (p == "relative") ? 0 : this.dom.offsetLeft;
9514             }
9515             if(isNaN(t)){
9516                 t = (p == "relative") ? 0 : this.dom.offsetTop;
9517             }
9518
9519             return {left: (x - o[0] + l), top: (y - o[1] + t)};
9520         },
9521
9522         /**
9523          * Returns the current scroll position of the element.
9524          * @return {Object} An object containing the scroll position in the format {left: (scrollLeft), top: (scrollTop)}
9525          */
9526         getScroll : function(){
9527             var d = this.dom, doc = document;
9528             if(d == doc || d == doc.body){
9529                 var l = window.pageXOffset || doc.documentElement.scrollLeft || doc.body.scrollLeft || 0;
9530                 var t = window.pageYOffset || doc.documentElement.scrollTop || doc.body.scrollTop || 0;
9531                 return {left: l, top: t};
9532             }else{
9533                 return {left: d.scrollLeft, top: d.scrollTop};
9534             }
9535         },
9536
9537         /**
9538          * Return the CSS color for the specified CSS attribute. rgb, 3 digit (like #fff) and valid values
9539          * are convert to standard 6 digit hex color.
9540          * @param {String} attr The css attribute
9541          * @param {String} defaultValue The default value to use when a valid color isn't found
9542          * @param {String} prefix (optional) defaults to #. Use an empty string when working with
9543          * YUI color anims.
9544          */
9545         getColor : function(attr, defaultValue, prefix){
9546             var v = this.getStyle(attr);
9547             if(!v || v == "transparent" || v == "inherit") {
9548                 return defaultValue;
9549             }
9550             var color = typeof prefix == "undefined" ? "#" : prefix;
9551             if(v.substr(0, 4) == "rgb("){
9552                 var rvs = v.slice(4, v.length -1).split(",");
9553                 for(var i = 0; i < 3; i++){
9554                     var h = parseInt(rvs[i]).toString(16);
9555                     if(h < 16){
9556                         h = "0" + h;
9557                     }
9558                     color += h;
9559                 }
9560             } else {
9561                 if(v.substr(0, 1) == "#"){
9562                     if(v.length == 4) {
9563                         for(var i = 1; i < 4; i++){
9564                             var c = v.charAt(i);
9565                             color +=  c + c;
9566                         }
9567                     }else if(v.length == 7){
9568                         color += v.substr(1);
9569                     }
9570                 }
9571             }
9572             return(color.length > 5 ? color.toLowerCase() : defaultValue);
9573         },
9574
9575         /**
9576          * Wraps the specified element with a special markup/CSS block that renders by default as a gray container with a
9577          * gradient background, rounded corners and a 4-way shadow.
9578          * @param {String} class (optional) A base CSS class to apply to the containing wrapper element (defaults to 'x-box').
9579          * Note that there are a number of CSS rules that are dependent on this name to make the overall effect work,
9580          * so if you supply an alternate base class, make sure you also supply all of the necessary rules.
9581          * @return {Roo.Element} this
9582          */
9583         boxWrap : function(cls){
9584             cls = cls || 'x-box';
9585             var el = Roo.get(this.insertHtml('beforeBegin', String.format('<div class="{0}">'+El.boxMarkup+'</div>', cls)));
9586             el.child('.'+cls+'-mc').dom.appendChild(this.dom);
9587             return el;
9588         },
9589
9590         /**
9591          * Returns the value of a namespaced attribute from the element's underlying DOM node.
9592          * @param {String} namespace The namespace in which to look for the attribute
9593          * @param {String} name The attribute name
9594          * @return {String} The attribute value
9595          */
9596         getAttributeNS : Roo.isIE ? function(ns, name){
9597             var d = this.dom;
9598             var type = typeof d[ns+":"+name];
9599             if(type != 'undefined' && type != 'unknown'){
9600                 return d[ns+":"+name];
9601             }
9602             return d[name];
9603         } : function(ns, name){
9604             var d = this.dom;
9605             return d.getAttributeNS(ns, name) || d.getAttribute(ns+":"+name) || d.getAttribute(name) || d[name];
9606         },
9607         
9608         
9609         /**
9610          * Sets or Returns the value the dom attribute value
9611          * @param {String|Object} name The attribute name (or object to set multiple attributes)
9612          * @param {String} value (optional) The value to set the attribute to
9613          * @return {String} The attribute value
9614          */
9615         attr : function(name){
9616             if (arguments.length > 1) {
9617                 this.dom.setAttribute(name, arguments[1]);
9618                 return arguments[1];
9619             }
9620             if (typeof(name) == 'object') {
9621                 for(var i in name) {
9622                     this.attr(i, name[i]);
9623                 }
9624                 return name;
9625             }
9626             
9627             
9628             if (!this.dom.hasAttribute(name)) {
9629                 return undefined;
9630             }
9631             return this.dom.getAttribute(name);
9632         }
9633         
9634         
9635         
9636     };
9637
9638     var ep = El.prototype;
9639
9640     /**
9641      * Appends an event handler (Shorthand for addListener)
9642      * @param {String}   eventName     The type of event to append
9643      * @param {Function} fn        The method the event invokes
9644      * @param {Object} scope       (optional) The scope (this object) of the fn
9645      * @param {Object}   options   (optional)An object with standard {@link Roo.EventManager#addListener} options
9646      * @method
9647      */
9648     ep.on = ep.addListener;
9649         // backwards compat
9650     ep.mon = ep.addListener;
9651
9652     /**
9653      * Removes an event handler from this element (shorthand for removeListener)
9654      * @param {String} eventName the type of event to remove
9655      * @param {Function} fn the method the event invokes
9656      * @return {Roo.Element} this
9657      * @method
9658      */
9659     ep.un = ep.removeListener;
9660
9661     /**
9662      * true to automatically adjust width and height settings for box-model issues (default to true)
9663      */
9664     ep.autoBoxAdjust = true;
9665
9666     // private
9667     El.unitPattern = /\d+(px|em|%|en|ex|pt|in|cm|mm|pc)$/i;
9668
9669     // private
9670     El.addUnits = function(v, defaultUnit){
9671         if(v === "" || v == "auto"){
9672             return v;
9673         }
9674         if(v === undefined){
9675             return '';
9676         }
9677         if(typeof v == "number" || !El.unitPattern.test(v)){
9678             return v + (defaultUnit || 'px');
9679         }
9680         return v;
9681     };
9682
9683     // special markup used throughout Roo when box wrapping elements
9684     El.boxMarkup = '<div class="{0}-tl"><div class="{0}-tr"><div class="{0}-tc"></div></div></div><div class="{0}-ml"><div class="{0}-mr"><div class="{0}-mc"></div></div></div><div class="{0}-bl"><div class="{0}-br"><div class="{0}-bc"></div></div></div>';
9685     /**
9686      * Visibility mode constant - Use visibility to hide element
9687      * @static
9688      * @type Number
9689      */
9690     El.VISIBILITY = 1;
9691     /**
9692      * Visibility mode constant - Use display to hide element
9693      * @static
9694      * @type Number
9695      */
9696     El.DISPLAY = 2;
9697
9698     El.borders = {l: "border-left-width", r: "border-right-width", t: "border-top-width", b: "border-bottom-width"};
9699     El.paddings = {l: "padding-left", r: "padding-right", t: "padding-top", b: "padding-bottom"};
9700     El.margins = {l: "margin-left", r: "margin-right", t: "margin-top", b: "margin-bottom"};
9701
9702
9703
9704     /**
9705      * @private
9706      */
9707     El.cache = {};
9708
9709     var docEl;
9710
9711     /**
9712      * Static method to retrieve Element objects. Uses simple caching to consistently return the same object.
9713      * Automatically fixes if an object was recreated with the same id via AJAX or DOM.
9714      * @param {String/HTMLElement/Element} el The id of the node, a DOM Node or an existing Element.
9715      * @return {Element} The Element object
9716      * @static
9717      */
9718     El.get = function(el){
9719         var ex, elm, id;
9720         if(!el){ return null; }
9721         if(typeof el == "string"){ // element id
9722             if(!(elm = document.getElementById(el))){
9723                 return null;
9724             }
9725             if(ex = El.cache[el]){
9726                 ex.dom = elm;
9727             }else{
9728                 ex = El.cache[el] = new El(elm);
9729             }
9730             return ex;
9731         }else if(el.tagName){ // dom element
9732             if(!(id = el.id)){
9733                 id = Roo.id(el);
9734             }
9735             if(ex = El.cache[id]){
9736                 ex.dom = el;
9737             }else{
9738                 ex = El.cache[id] = new El(el);
9739             }
9740             return ex;
9741         }else if(el instanceof El){
9742             if(el != docEl){
9743                 el.dom = document.getElementById(el.id) || el.dom; // refresh dom element in case no longer valid,
9744                                                               // catch case where it hasn't been appended
9745                 El.cache[el.id] = el; // in case it was created directly with Element(), let's cache it
9746             }
9747             return el;
9748         }else if(el.isComposite){
9749             return el;
9750         }else if(el instanceof Array){
9751             return El.select(el);
9752         }else if(el == document){
9753             // create a bogus element object representing the document object
9754             if(!docEl){
9755                 var f = function(){};
9756                 f.prototype = El.prototype;
9757                 docEl = new f();
9758                 docEl.dom = document;
9759             }
9760             return docEl;
9761         }
9762         return null;
9763     };
9764
9765     // private
9766     El.uncache = function(el){
9767         for(var i = 0, a = arguments, len = a.length; i < len; i++) {
9768             if(a[i]){
9769                 delete El.cache[a[i].id || a[i]];
9770             }
9771         }
9772     };
9773
9774     // private
9775     // Garbage collection - uncache elements/purge listeners on orphaned elements
9776     // so we don't hold a reference and cause the browser to retain them
9777     El.garbageCollect = function(){
9778         if(!Roo.enableGarbageCollector){
9779             clearInterval(El.collectorThread);
9780             return;
9781         }
9782         for(var eid in El.cache){
9783             var el = El.cache[eid], d = el.dom;
9784             // -------------------------------------------------------
9785             // Determining what is garbage:
9786             // -------------------------------------------------------
9787             // !d
9788             // dom node is null, definitely garbage
9789             // -------------------------------------------------------
9790             // !d.parentNode
9791             // no parentNode == direct orphan, definitely garbage
9792             // -------------------------------------------------------
9793             // !d.offsetParent && !document.getElementById(eid)
9794             // display none elements have no offsetParent so we will
9795             // also try to look it up by it's id. However, check
9796             // offsetParent first so we don't do unneeded lookups.
9797             // This enables collection of elements that are not orphans
9798             // directly, but somewhere up the line they have an orphan
9799             // parent.
9800             // -------------------------------------------------------
9801             if(!d || !d.parentNode || (!d.offsetParent && !document.getElementById(eid))){
9802                 delete El.cache[eid];
9803                 if(d && Roo.enableListenerCollection){
9804                     E.purgeElement(d);
9805                 }
9806             }
9807         }
9808     }
9809     El.collectorThreadId = setInterval(El.garbageCollect, 30000);
9810
9811
9812     // dom is optional
9813     El.Flyweight = function(dom){
9814         this.dom = dom;
9815     };
9816     El.Flyweight.prototype = El.prototype;
9817
9818     El._flyweights = {};
9819     /**
9820      * Gets the globally shared flyweight Element, with the passed node as the active element. Do not store a reference to this element -
9821      * the dom node can be overwritten by other code.
9822      * @param {String/HTMLElement} el The dom node or id
9823      * @param {String} named (optional) Allows for creation of named reusable flyweights to
9824      *                                  prevent conflicts (e.g. internally Roo uses "_internal")
9825      * @static
9826      * @return {Element} The shared Element object
9827      */
9828     El.fly = function(el, named){
9829         named = named || '_global';
9830         el = Roo.getDom(el);
9831         if(!el){
9832             return null;
9833         }
9834         if(!El._flyweights[named]){
9835             El._flyweights[named] = new El.Flyweight();
9836         }
9837         El._flyweights[named].dom = el;
9838         return El._flyweights[named];
9839     };
9840
9841     /**
9842      * Static method to retrieve Element objects. Uses simple caching to consistently return the same object.
9843      * Automatically fixes if an object was recreated with the same id via AJAX or DOM.
9844      * Shorthand of {@link Roo.Element#get}
9845      * @param {String/HTMLElement/Element} el The id of the node, a DOM Node or an existing Element.
9846      * @return {Element} The Element object
9847      * @member Roo
9848      * @method get
9849      */
9850     Roo.get = El.get;
9851     /**
9852      * Gets the globally shared flyweight Element, with the passed node as the active element. Do not store a reference to this element -
9853      * the dom node can be overwritten by other code.
9854      * Shorthand of {@link Roo.Element#fly}
9855      * @param {String/HTMLElement} el The dom node or id
9856      * @param {String} named (optional) Allows for creation of named reusable flyweights to
9857      *                                  prevent conflicts (e.g. internally Roo uses "_internal")
9858      * @static
9859      * @return {Element} The shared Element object
9860      * @member Roo
9861      * @method fly
9862      */
9863     Roo.fly = El.fly;
9864
9865     // speedy lookup for elements never to box adjust
9866     var noBoxAdjust = Roo.isStrict ? {
9867         select:1
9868     } : {
9869         input:1, select:1, textarea:1
9870     };
9871     if(Roo.isIE || Roo.isGecko){
9872         noBoxAdjust['button'] = 1;
9873     }
9874
9875
9876     Roo.EventManager.on(window, 'unload', function(){
9877         delete El.cache;
9878         delete El._flyweights;
9879     });
9880 })();
9881
9882
9883
9884
9885 if(Roo.DomQuery){
9886     Roo.Element.selectorFunction = Roo.DomQuery.select;
9887 }
9888
9889 Roo.Element.select = function(selector, unique, root){
9890     var els;
9891     if(typeof selector == "string"){
9892         els = Roo.Element.selectorFunction(selector, root);
9893     }else if(selector.length !== undefined){
9894         els = selector;
9895     }else{
9896         throw "Invalid selector";
9897     }
9898     if(unique === true){
9899         return new Roo.CompositeElement(els);
9900     }else{
9901         return new Roo.CompositeElementLite(els);
9902     }
9903 };
9904 /**
9905  * Selects elements based on the passed CSS selector to enable working on them as 1.
9906  * @param {String/Array} selector The CSS selector or an array of elements
9907  * @param {Boolean} unique (optional) true to create a unique Roo.Element for each element (defaults to a shared flyweight object)
9908  * @param {HTMLElement/String} root (optional) The root element of the query or id of the root
9909  * @return {CompositeElementLite/CompositeElement}
9910  * @member Roo
9911  * @method select
9912  */
9913 Roo.select = Roo.Element.select;
9914
9915
9916
9917
9918
9919
9920
9921
9922
9923
9924
9925
9926
9927
9928 /*
9929  * Based on:
9930  * Ext JS Library 1.1.1
9931  * Copyright(c) 2006-2007, Ext JS, LLC.
9932  *
9933  * Originally Released Under LGPL - original licence link has changed is not relivant.
9934  *
9935  * Fork - LGPL
9936  * <script type="text/javascript">
9937  */
9938
9939
9940
9941 //Notifies Element that fx methods are available
9942 Roo.enableFx = true;
9943
9944 /**
9945  * @class Roo.Fx
9946  * <p>A class to provide basic animation and visual effects support.  <b>Note:</b> This class is automatically applied
9947  * to the {@link Roo.Element} interface when included, so all effects calls should be performed via Element.
9948  * Conversely, since the effects are not actually defined in Element, Roo.Fx <b>must</b> be included in order for the 
9949  * Element effects to work.</p><br/>
9950  *
9951  * <p>It is important to note that although the Fx methods and many non-Fx Element methods support "method chaining" in that
9952  * they return the Element object itself as the method return value, it is not always possible to mix the two in a single
9953  * method chain.  The Fx methods use an internal effects queue so that each effect can be properly timed and sequenced.
9954  * Non-Fx methods, on the other hand, have no such internal queueing and will always execute immediately.  For this reason,
9955  * while it may be possible to mix certain Fx and non-Fx method calls in a single chain, it may not always provide the
9956  * expected results and should be done with care.</p><br/>
9957  *
9958  * <p>Motion effects support 8-way anchoring, meaning that you can choose one of 8 different anchor points on the Element
9959  * that will serve as either the start or end point of the animation.  Following are all of the supported anchor positions:</p>
9960 <pre>
9961 Value  Description
9962 -----  -----------------------------
9963 tl     The top left corner
9964 t      The center of the top edge
9965 tr     The top right corner
9966 l      The center of the left edge
9967 r      The center of the right edge
9968 bl     The bottom left corner
9969 b      The center of the bottom edge
9970 br     The bottom right corner
9971 </pre>
9972  * <b>Although some Fx methods accept specific custom config parameters, the ones shown in the Config Options section
9973  * below are common options that can be passed to any Fx method.</b>
9974  * @cfg {Function} callback A function called when the effect is finished
9975  * @cfg {Object} scope The scope of the effect function
9976  * @cfg {String} easing A valid Easing value for the effect
9977  * @cfg {String} afterCls A css class to apply after the effect
9978  * @cfg {Number} duration The length of time (in seconds) that the effect should last
9979  * @cfg {Boolean} remove Whether the Element should be removed from the DOM and destroyed after the effect finishes
9980  * @cfg {Boolean} useDisplay Whether to use the <i>display</i> CSS property instead of <i>visibility</i> when hiding Elements (only applies to 
9981  * effects that end with the element being visually hidden, ignored otherwise)
9982  * @cfg {String/Object/Function} afterStyle A style specification string, e.g. "width:100px", or an object in the form {width:"100px"}, or
9983  * a function which returns such a specification that will be applied to the Element after the effect finishes
9984  * @cfg {Boolean} block Whether the effect should block other effects from queueing while it runs
9985  * @cfg {Boolean} concurrent Whether to allow subsequently-queued effects to run at the same time as the current effect, or to ensure that they run in sequence
9986  * @cfg {Boolean} stopFx Whether subsequent effects should be stopped and removed after the current effect finishes
9987  */
9988 Roo.Fx = {
9989         /**
9990          * Slides the element into view.  An anchor point can be optionally passed to set the point of
9991          * origin for the slide effect.  This function automatically handles wrapping the element with
9992          * a fixed-size container if needed.  See the Fx class overview for valid anchor point options.
9993          * Usage:
9994          *<pre><code>
9995 // default: slide the element in from the top
9996 el.slideIn();
9997
9998 // custom: slide the element in from the right with a 2-second duration
9999 el.slideIn('r', { duration: 2 });
10000
10001 // common config options shown with default values
10002 el.slideIn('t', {
10003     easing: 'easeOut',
10004     duration: .5
10005 });
10006 </code></pre>
10007          * @param {String} anchor (optional) One of the valid Fx anchor positions (defaults to top: 't')
10008          * @param {Object} options (optional) Object literal with any of the Fx config options
10009          * @return {Roo.Element} The Element
10010          */
10011     slideIn : function(anchor, o){
10012         var el = this.getFxEl();
10013         o = o || {};
10014
10015         el.queueFx(o, function(){
10016
10017             anchor = anchor || "t";
10018
10019             // fix display to visibility
10020             this.fixDisplay();
10021
10022             // restore values after effect
10023             var r = this.getFxRestore();
10024             var b = this.getBox();
10025             // fixed size for slide
10026             this.setSize(b);
10027
10028             // wrap if needed
10029             var wrap = this.fxWrap(r.pos, o, "hidden");
10030
10031             var st = this.dom.style;
10032             st.visibility = "visible";
10033             st.position = "absolute";
10034
10035             // clear out temp styles after slide and unwrap
10036             var after = function(){
10037                 el.fxUnwrap(wrap, r.pos, o);
10038                 st.width = r.width;
10039                 st.height = r.height;
10040                 el.afterFx(o);
10041             };
10042             // time to calc the positions
10043             var a, pt = {to: [b.x, b.y]}, bw = {to: b.width}, bh = {to: b.height};
10044
10045             switch(anchor.toLowerCase()){
10046                 case "t":
10047                     wrap.setSize(b.width, 0);
10048                     st.left = st.bottom = "0";
10049                     a = {height: bh};
10050                 break;
10051                 case "l":
10052                     wrap.setSize(0, b.height);
10053                     st.right = st.top = "0";
10054                     a = {width: bw};
10055                 break;
10056                 case "r":
10057                     wrap.setSize(0, b.height);
10058                     wrap.setX(b.right);
10059                     st.left = st.top = "0";
10060                     a = {width: bw, points: pt};
10061                 break;
10062                 case "b":
10063                     wrap.setSize(b.width, 0);
10064                     wrap.setY(b.bottom);
10065                     st.left = st.top = "0";
10066                     a = {height: bh, points: pt};
10067                 break;
10068                 case "tl":
10069                     wrap.setSize(0, 0);
10070                     st.right = st.bottom = "0";
10071                     a = {width: bw, height: bh};
10072                 break;
10073                 case "bl":
10074                     wrap.setSize(0, 0);
10075                     wrap.setY(b.y+b.height);
10076                     st.right = st.top = "0";
10077                     a = {width: bw, height: bh, points: pt};
10078                 break;
10079                 case "br":
10080                     wrap.setSize(0, 0);
10081                     wrap.setXY([b.right, b.bottom]);
10082                     st.left = st.top = "0";
10083                     a = {width: bw, height: bh, points: pt};
10084                 break;
10085                 case "tr":
10086                     wrap.setSize(0, 0);
10087                     wrap.setX(b.x+b.width);
10088                     st.left = st.bottom = "0";
10089                     a = {width: bw, height: bh, points: pt};
10090                 break;
10091             }
10092             this.dom.style.visibility = "visible";
10093             wrap.show();
10094
10095             arguments.callee.anim = wrap.fxanim(a,
10096                 o,
10097                 'motion',
10098                 .5,
10099                 'easeOut', after);
10100         });
10101         return this;
10102     },
10103     
10104         /**
10105          * Slides the element out of view.  An anchor point can be optionally passed to set the end point
10106          * for the slide effect.  When the effect is completed, the element will be hidden (visibility = 
10107          * 'hidden') but block elements will still take up space in the document.  The element must be removed
10108          * from the DOM using the 'remove' config option if desired.  This function automatically handles 
10109          * wrapping the element with a fixed-size container if needed.  See the Fx class overview for valid anchor point options.
10110          * Usage:
10111          *<pre><code>
10112 // default: slide the element out to the top
10113 el.slideOut();
10114
10115 // custom: slide the element out to the right with a 2-second duration
10116 el.slideOut('r', { duration: 2 });
10117
10118 // common config options shown with default values
10119 el.slideOut('t', {
10120     easing: 'easeOut',
10121     duration: .5,
10122     remove: false,
10123     useDisplay: false
10124 });
10125 </code></pre>
10126          * @param {String} anchor (optional) One of the valid Fx anchor positions (defaults to top: 't')
10127          * @param {Object} options (optional) Object literal with any of the Fx config options
10128          * @return {Roo.Element} The Element
10129          */
10130     slideOut : function(anchor, o){
10131         var el = this.getFxEl();
10132         o = o || {};
10133
10134         el.queueFx(o, function(){
10135
10136             anchor = anchor || "t";
10137
10138             // restore values after effect
10139             var r = this.getFxRestore();
10140             
10141             var b = this.getBox();
10142             // fixed size for slide
10143             this.setSize(b);
10144
10145             // wrap if needed
10146             var wrap = this.fxWrap(r.pos, o, "visible");
10147
10148             var st = this.dom.style;
10149             st.visibility = "visible";
10150             st.position = "absolute";
10151
10152             wrap.setSize(b);
10153
10154             var after = function(){
10155                 if(o.useDisplay){
10156                     el.setDisplayed(false);
10157                 }else{
10158                     el.hide();
10159                 }
10160
10161                 el.fxUnwrap(wrap, r.pos, o);
10162
10163                 st.width = r.width;
10164                 st.height = r.height;
10165
10166                 el.afterFx(o);
10167             };
10168
10169             var a, zero = {to: 0};
10170             switch(anchor.toLowerCase()){
10171                 case "t":
10172                     st.left = st.bottom = "0";
10173                     a = {height: zero};
10174                 break;
10175                 case "l":
10176                     st.right = st.top = "0";
10177                     a = {width: zero};
10178                 break;
10179                 case "r":
10180                     st.left = st.top = "0";
10181                     a = {width: zero, points: {to:[b.right, b.y]}};
10182                 break;
10183                 case "b":
10184                     st.left = st.top = "0";
10185                     a = {height: zero, points: {to:[b.x, b.bottom]}};
10186                 break;
10187                 case "tl":
10188                     st.right = st.bottom = "0";
10189                     a = {width: zero, height: zero};
10190                 break;
10191                 case "bl":
10192                     st.right = st.top = "0";
10193                     a = {width: zero, height: zero, points: {to:[b.x, b.bottom]}};
10194                 break;
10195                 case "br":
10196                     st.left = st.top = "0";
10197                     a = {width: zero, height: zero, points: {to:[b.x+b.width, b.bottom]}};
10198                 break;
10199                 case "tr":
10200                     st.left = st.bottom = "0";
10201                     a = {width: zero, height: zero, points: {to:[b.right, b.y]}};
10202                 break;
10203             }
10204
10205             arguments.callee.anim = wrap.fxanim(a,
10206                 o,
10207                 'motion',
10208                 .5,
10209                 "easeOut", after);
10210         });
10211         return this;
10212     },
10213
10214         /**
10215          * Fades the element out while slowly expanding it in all directions.  When the effect is completed, the 
10216          * element will be hidden (visibility = 'hidden') but block elements will still take up space in the document. 
10217          * The element must be removed from the DOM using the 'remove' config option if desired.
10218          * Usage:
10219          *<pre><code>
10220 // default
10221 el.puff();
10222
10223 // common config options shown with default values
10224 el.puff({
10225     easing: 'easeOut',
10226     duration: .5,
10227     remove: false,
10228     useDisplay: false
10229 });
10230 </code></pre>
10231          * @param {Object} options (optional) Object literal with any of the Fx config options
10232          * @return {Roo.Element} The Element
10233          */
10234     puff : function(o){
10235         var el = this.getFxEl();
10236         o = o || {};
10237
10238         el.queueFx(o, function(){
10239             this.clearOpacity();
10240             this.show();
10241
10242             // restore values after effect
10243             var r = this.getFxRestore();
10244             var st = this.dom.style;
10245
10246             var after = function(){
10247                 if(o.useDisplay){
10248                     el.setDisplayed(false);
10249                 }else{
10250                     el.hide();
10251                 }
10252
10253                 el.clearOpacity();
10254
10255                 el.setPositioning(r.pos);
10256                 st.width = r.width;
10257                 st.height = r.height;
10258                 st.fontSize = '';
10259                 el.afterFx(o);
10260             };
10261
10262             var width = this.getWidth();
10263             var height = this.getHeight();
10264
10265             arguments.callee.anim = this.fxanim({
10266                     width : {to: this.adjustWidth(width * 2)},
10267                     height : {to: this.adjustHeight(height * 2)},
10268                     points : {by: [-(width * .5), -(height * .5)]},
10269                     opacity : {to: 0},
10270                     fontSize: {to:200, unit: "%"}
10271                 },
10272                 o,
10273                 'motion',
10274                 .5,
10275                 "easeOut", after);
10276         });
10277         return this;
10278     },
10279
10280         /**
10281          * Blinks the element as if it was clicked and then collapses on its center (similar to switching off a television).
10282          * When the effect is completed, the element will be hidden (visibility = 'hidden') but block elements will still 
10283          * take up space in the document. The element must be removed from the DOM using the 'remove' config option if desired.
10284          * Usage:
10285          *<pre><code>
10286 // default
10287 el.switchOff();
10288
10289 // all config options shown with default values
10290 el.switchOff({
10291     easing: 'easeIn',
10292     duration: .3,
10293     remove: false,
10294     useDisplay: false
10295 });
10296 </code></pre>
10297          * @param {Object} options (optional) Object literal with any of the Fx config options
10298          * @return {Roo.Element} The Element
10299          */
10300     switchOff : function(o){
10301         var el = this.getFxEl();
10302         o = o || {};
10303
10304         el.queueFx(o, function(){
10305             this.clearOpacity();
10306             this.clip();
10307
10308             // restore values after effect
10309             var r = this.getFxRestore();
10310             var st = this.dom.style;
10311
10312             var after = function(){
10313                 if(o.useDisplay){
10314                     el.setDisplayed(false);
10315                 }else{
10316                     el.hide();
10317                 }
10318
10319                 el.clearOpacity();
10320                 el.setPositioning(r.pos);
10321                 st.width = r.width;
10322                 st.height = r.height;
10323
10324                 el.afterFx(o);
10325             };
10326
10327             this.fxanim({opacity:{to:0.3}}, null, null, .1, null, function(){
10328                 this.clearOpacity();
10329                 (function(){
10330                     this.fxanim({
10331                         height:{to:1},
10332                         points:{by:[0, this.getHeight() * .5]}
10333                     }, o, 'motion', 0.3, 'easeIn', after);
10334                 }).defer(100, this);
10335             });
10336         });
10337         return this;
10338     },
10339
10340     /**
10341      * Highlights the Element by setting a color (applies to the background-color by default, but can be
10342      * changed using the "attr" config option) and then fading back to the original color. If no original
10343      * color is available, you should provide the "endColor" config option which will be cleared after the animation.
10344      * Usage:
10345 <pre><code>
10346 // default: highlight background to yellow
10347 el.highlight();
10348
10349 // custom: highlight foreground text to blue for 2 seconds
10350 el.highlight("0000ff", { attr: 'color', duration: 2 });
10351
10352 // common config options shown with default values
10353 el.highlight("ffff9c", {
10354     attr: "background-color", //can be any valid CSS property (attribute) that supports a color value
10355     endColor: (current color) or "ffffff",
10356     easing: 'easeIn',
10357     duration: 1
10358 });
10359 </code></pre>
10360      * @param {String} color (optional) The highlight color. Should be a 6 char hex color without the leading # (defaults to yellow: 'ffff9c')
10361      * @param {Object} options (optional) Object literal with any of the Fx config options
10362      * @return {Roo.Element} The Element
10363      */ 
10364     highlight : function(color, o){
10365         var el = this.getFxEl();
10366         o = o || {};
10367
10368         el.queueFx(o, function(){
10369             color = color || "ffff9c";
10370             attr = o.attr || "backgroundColor";
10371
10372             this.clearOpacity();
10373             this.show();
10374
10375             var origColor = this.getColor(attr);
10376             var restoreColor = this.dom.style[attr];
10377             endColor = (o.endColor || origColor) || "ffffff";
10378
10379             var after = function(){
10380                 el.dom.style[attr] = restoreColor;
10381                 el.afterFx(o);
10382             };
10383
10384             var a = {};
10385             a[attr] = {from: color, to: endColor};
10386             arguments.callee.anim = this.fxanim(a,
10387                 o,
10388                 'color',
10389                 1,
10390                 'easeIn', after);
10391         });
10392         return this;
10393     },
10394
10395    /**
10396     * Shows a ripple of exploding, attenuating borders to draw attention to an Element.
10397     * Usage:
10398 <pre><code>
10399 // default: a single light blue ripple
10400 el.frame();
10401
10402 // custom: 3 red ripples lasting 3 seconds total
10403 el.frame("ff0000", 3, { duration: 3 });
10404
10405 // common config options shown with default values
10406 el.frame("C3DAF9", 1, {
10407     duration: 1 //duration of entire animation (not each individual ripple)
10408     // Note: Easing is not configurable and will be ignored if included
10409 });
10410 </code></pre>
10411     * @param {String} color (optional) The color of the border.  Should be a 6 char hex color without the leading # (defaults to light blue: 'C3DAF9').
10412     * @param {Number} count (optional) The number of ripples to display (defaults to 1)
10413     * @param {Object} options (optional) Object literal with any of the Fx config options
10414     * @return {Roo.Element} The Element
10415     */
10416     frame : function(color, count, o){
10417         var el = this.getFxEl();
10418         o = o || {};
10419
10420         el.queueFx(o, function(){
10421             color = color || "#C3DAF9";
10422             if(color.length == 6){
10423                 color = "#" + color;
10424             }
10425             count = count || 1;
10426             duration = o.duration || 1;
10427             this.show();
10428
10429             var b = this.getBox();
10430             var animFn = function(){
10431                 var proxy = this.createProxy({
10432
10433                      style:{
10434                         visbility:"hidden",
10435                         position:"absolute",
10436                         "z-index":"35000", // yee haw
10437                         border:"0px solid " + color
10438                      }
10439                   });
10440                 var scale = Roo.isBorderBox ? 2 : 1;
10441                 proxy.animate({
10442                     top:{from:b.y, to:b.y - 20},
10443                     left:{from:b.x, to:b.x - 20},
10444                     borderWidth:{from:0, to:10},
10445                     opacity:{from:1, to:0},
10446                     height:{from:b.height, to:(b.height + (20*scale))},
10447                     width:{from:b.width, to:(b.width + (20*scale))}
10448                 }, duration, function(){
10449                     proxy.remove();
10450                 });
10451                 if(--count > 0){
10452                      animFn.defer((duration/2)*1000, this);
10453                 }else{
10454                     el.afterFx(o);
10455                 }
10456             };
10457             animFn.call(this);
10458         });
10459         return this;
10460     },
10461
10462    /**
10463     * Creates a pause before any subsequent queued effects begin.  If there are
10464     * no effects queued after the pause it will have no effect.
10465     * Usage:
10466 <pre><code>
10467 el.pause(1);
10468 </code></pre>
10469     * @param {Number} seconds The length of time to pause (in seconds)
10470     * @return {Roo.Element} The Element
10471     */
10472     pause : function(seconds){
10473         var el = this.getFxEl();
10474         var o = {};
10475
10476         el.queueFx(o, function(){
10477             setTimeout(function(){
10478                 el.afterFx(o);
10479             }, seconds * 1000);
10480         });
10481         return this;
10482     },
10483
10484    /**
10485     * Fade an element in (from transparent to opaque).  The ending opacity can be specified
10486     * using the "endOpacity" config option.
10487     * Usage:
10488 <pre><code>
10489 // default: fade in from opacity 0 to 100%
10490 el.fadeIn();
10491
10492 // custom: fade in from opacity 0 to 75% over 2 seconds
10493 el.fadeIn({ endOpacity: .75, duration: 2});
10494
10495 // common config options shown with default values
10496 el.fadeIn({
10497     endOpacity: 1, //can be any value between 0 and 1 (e.g. .5)
10498     easing: 'easeOut',
10499     duration: .5
10500 });
10501 </code></pre>
10502     * @param {Object} options (optional) Object literal with any of the Fx config options
10503     * @return {Roo.Element} The Element
10504     */
10505     fadeIn : function(o){
10506         var el = this.getFxEl();
10507         o = o || {};
10508         el.queueFx(o, function(){
10509             this.setOpacity(0);
10510             this.fixDisplay();
10511             this.dom.style.visibility = 'visible';
10512             var to = o.endOpacity || 1;
10513             arguments.callee.anim = this.fxanim({opacity:{to:to}},
10514                 o, null, .5, "easeOut", function(){
10515                 if(to == 1){
10516                     this.clearOpacity();
10517                 }
10518                 el.afterFx(o);
10519             });
10520         });
10521         return this;
10522     },
10523
10524    /**
10525     * Fade an element out (from opaque to transparent).  The ending opacity can be specified
10526     * using the "endOpacity" config option.
10527     * Usage:
10528 <pre><code>
10529 // default: fade out from the element's current opacity to 0
10530 el.fadeOut();
10531
10532 // custom: fade out from the element's current opacity to 25% over 2 seconds
10533 el.fadeOut({ endOpacity: .25, duration: 2});
10534
10535 // common config options shown with default values
10536 el.fadeOut({
10537     endOpacity: 0, //can be any value between 0 and 1 (e.g. .5)
10538     easing: 'easeOut',
10539     duration: .5
10540     remove: false,
10541     useDisplay: false
10542 });
10543 </code></pre>
10544     * @param {Object} options (optional) Object literal with any of the Fx config options
10545     * @return {Roo.Element} The Element
10546     */
10547     fadeOut : function(o){
10548         var el = this.getFxEl();
10549         o = o || {};
10550         el.queueFx(o, function(){
10551             arguments.callee.anim = this.fxanim({opacity:{to:o.endOpacity || 0}},
10552                 o, null, .5, "easeOut", function(){
10553                 if(this.visibilityMode == Roo.Element.DISPLAY || o.useDisplay){
10554                      this.dom.style.display = "none";
10555                 }else{
10556                      this.dom.style.visibility = "hidden";
10557                 }
10558                 this.clearOpacity();
10559                 el.afterFx(o);
10560             });
10561         });
10562         return this;
10563     },
10564
10565    /**
10566     * Animates the transition of an element's dimensions from a starting height/width
10567     * to an ending height/width.
10568     * Usage:
10569 <pre><code>
10570 // change height and width to 100x100 pixels
10571 el.scale(100, 100);
10572
10573 // common config options shown with default values.  The height and width will default to
10574 // the element's existing values if passed as null.
10575 el.scale(
10576     [element's width],
10577     [element's height], {
10578     easing: 'easeOut',
10579     duration: .35
10580 });
10581 </code></pre>
10582     * @param {Number} width  The new width (pass undefined to keep the original width)
10583     * @param {Number} height  The new height (pass undefined to keep the original height)
10584     * @param {Object} options (optional) Object literal with any of the Fx config options
10585     * @return {Roo.Element} The Element
10586     */
10587     scale : function(w, h, o){
10588         this.shift(Roo.apply({}, o, {
10589             width: w,
10590             height: h
10591         }));
10592         return this;
10593     },
10594
10595    /**
10596     * Animates the transition of any combination of an element's dimensions, xy position and/or opacity.
10597     * Any of these properties not specified in the config object will not be changed.  This effect 
10598     * requires that at least one new dimension, position or opacity setting must be passed in on
10599     * the config object in order for the function to have any effect.
10600     * Usage:
10601 <pre><code>
10602 // slide the element horizontally to x position 200 while changing the height and opacity
10603 el.shift({ x: 200, height: 50, opacity: .8 });
10604
10605 // common config options shown with default values.
10606 el.shift({
10607     width: [element's width],
10608     height: [element's height],
10609     x: [element's x position],
10610     y: [element's y position],
10611     opacity: [element's opacity],
10612     easing: 'easeOut',
10613     duration: .35
10614 });
10615 </code></pre>
10616     * @param {Object} options  Object literal with any of the Fx config options
10617     * @return {Roo.Element} The Element
10618     */
10619     shift : function(o){
10620         var el = this.getFxEl();
10621         o = o || {};
10622         el.queueFx(o, function(){
10623             var a = {}, w = o.width, h = o.height, x = o.x, y = o.y,  op = o.opacity;
10624             if(w !== undefined){
10625                 a.width = {to: this.adjustWidth(w)};
10626             }
10627             if(h !== undefined){
10628                 a.height = {to: this.adjustHeight(h)};
10629             }
10630             if(x !== undefined || y !== undefined){
10631                 a.points = {to: [
10632                     x !== undefined ? x : this.getX(),
10633                     y !== undefined ? y : this.getY()
10634                 ]};
10635             }
10636             if(op !== undefined){
10637                 a.opacity = {to: op};
10638             }
10639             if(o.xy !== undefined){
10640                 a.points = {to: o.xy};
10641             }
10642             arguments.callee.anim = this.fxanim(a,
10643                 o, 'motion', .35, "easeOut", function(){
10644                 el.afterFx(o);
10645             });
10646         });
10647         return this;
10648     },
10649
10650         /**
10651          * Slides the element while fading it out of view.  An anchor point can be optionally passed to set the 
10652          * ending point of the effect.
10653          * Usage:
10654          *<pre><code>
10655 // default: slide the element downward while fading out
10656 el.ghost();
10657
10658 // custom: slide the element out to the right with a 2-second duration
10659 el.ghost('r', { duration: 2 });
10660
10661 // common config options shown with default values
10662 el.ghost('b', {
10663     easing: 'easeOut',
10664     duration: .5
10665     remove: false,
10666     useDisplay: false
10667 });
10668 </code></pre>
10669          * @param {String} anchor (optional) One of the valid Fx anchor positions (defaults to bottom: 'b')
10670          * @param {Object} options (optional) Object literal with any of the Fx config options
10671          * @return {Roo.Element} The Element
10672          */
10673     ghost : function(anchor, o){
10674         var el = this.getFxEl();
10675         o = o || {};
10676
10677         el.queueFx(o, function(){
10678             anchor = anchor || "b";
10679
10680             // restore values after effect
10681             var r = this.getFxRestore();
10682             var w = this.getWidth(),
10683                 h = this.getHeight();
10684
10685             var st = this.dom.style;
10686
10687             var after = function(){
10688                 if(o.useDisplay){
10689                     el.setDisplayed(false);
10690                 }else{
10691                     el.hide();
10692                 }
10693
10694                 el.clearOpacity();
10695                 el.setPositioning(r.pos);
10696                 st.width = r.width;
10697                 st.height = r.height;
10698
10699                 el.afterFx(o);
10700             };
10701
10702             var a = {opacity: {to: 0}, points: {}}, pt = a.points;
10703             switch(anchor.toLowerCase()){
10704                 case "t":
10705                     pt.by = [0, -h];
10706                 break;
10707                 case "l":
10708                     pt.by = [-w, 0];
10709                 break;
10710                 case "r":
10711                     pt.by = [w, 0];
10712                 break;
10713                 case "b":
10714                     pt.by = [0, h];
10715                 break;
10716                 case "tl":
10717                     pt.by = [-w, -h];
10718                 break;
10719                 case "bl":
10720                     pt.by = [-w, h];
10721                 break;
10722                 case "br":
10723                     pt.by = [w, h];
10724                 break;
10725                 case "tr":
10726                     pt.by = [w, -h];
10727                 break;
10728             }
10729
10730             arguments.callee.anim = this.fxanim(a,
10731                 o,
10732                 'motion',
10733                 .5,
10734                 "easeOut", after);
10735         });
10736         return this;
10737     },
10738
10739         /**
10740          * Ensures that all effects queued after syncFx is called on the element are
10741          * run concurrently.  This is the opposite of {@link #sequenceFx}.
10742          * @return {Roo.Element} The Element
10743          */
10744     syncFx : function(){
10745         this.fxDefaults = Roo.apply(this.fxDefaults || {}, {
10746             block : false,
10747             concurrent : true,
10748             stopFx : false
10749         });
10750         return this;
10751     },
10752
10753         /**
10754          * Ensures that all effects queued after sequenceFx is called on the element are
10755          * run in sequence.  This is the opposite of {@link #syncFx}.
10756          * @return {Roo.Element} The Element
10757          */
10758     sequenceFx : function(){
10759         this.fxDefaults = Roo.apply(this.fxDefaults || {}, {
10760             block : false,
10761             concurrent : false,
10762             stopFx : false
10763         });
10764         return this;
10765     },
10766
10767         /* @private */
10768     nextFx : function(){
10769         var ef = this.fxQueue[0];
10770         if(ef){
10771             ef.call(this);
10772         }
10773     },
10774
10775         /**
10776          * Returns true if the element has any effects actively running or queued, else returns false.
10777          * @return {Boolean} True if element has active effects, else false
10778          */
10779     hasActiveFx : function(){
10780         return this.fxQueue && this.fxQueue[0];
10781     },
10782
10783         /**
10784          * Stops any running effects and clears the element's internal effects queue if it contains
10785          * any additional effects that haven't started yet.
10786          * @return {Roo.Element} The Element
10787          */
10788     stopFx : function(){
10789         if(this.hasActiveFx()){
10790             var cur = this.fxQueue[0];
10791             if(cur && cur.anim && cur.anim.isAnimated()){
10792                 this.fxQueue = [cur]; // clear out others
10793                 cur.anim.stop(true);
10794             }
10795         }
10796         return this;
10797     },
10798
10799         /* @private */
10800     beforeFx : function(o){
10801         if(this.hasActiveFx() && !o.concurrent){
10802            if(o.stopFx){
10803                this.stopFx();
10804                return true;
10805            }
10806            return false;
10807         }
10808         return true;
10809     },
10810
10811         /**
10812          * Returns true if the element is currently blocking so that no other effect can be queued
10813          * until this effect is finished, else returns false if blocking is not set.  This is commonly
10814          * used to ensure that an effect initiated by a user action runs to completion prior to the
10815          * same effect being restarted (e.g., firing only one effect even if the user clicks several times).
10816          * @return {Boolean} True if blocking, else false
10817          */
10818     hasFxBlock : function(){
10819         var q = this.fxQueue;
10820         return q && q[0] && q[0].block;
10821     },
10822
10823         /* @private */
10824     queueFx : function(o, fn){
10825         if(!this.fxQueue){
10826             this.fxQueue = [];
10827         }
10828         if(!this.hasFxBlock()){
10829             Roo.applyIf(o, this.fxDefaults);
10830             if(!o.concurrent){
10831                 var run = this.beforeFx(o);
10832                 fn.block = o.block;
10833                 this.fxQueue.push(fn);
10834                 if(run){
10835                     this.nextFx();
10836                 }
10837             }else{
10838                 fn.call(this);
10839             }
10840         }
10841         return this;
10842     },
10843
10844         /* @private */
10845     fxWrap : function(pos, o, vis){
10846         var wrap;
10847         if(!o.wrap || !(wrap = Roo.get(o.wrap))){
10848             var wrapXY;
10849             if(o.fixPosition){
10850                 wrapXY = this.getXY();
10851             }
10852             var div = document.createElement("div");
10853             div.style.visibility = vis;
10854             wrap = Roo.get(this.dom.parentNode.insertBefore(div, this.dom));
10855             wrap.setPositioning(pos);
10856             if(wrap.getStyle("position") == "static"){
10857                 wrap.position("relative");
10858             }
10859             this.clearPositioning('auto');
10860             wrap.clip();
10861             wrap.dom.appendChild(this.dom);
10862             if(wrapXY){
10863                 wrap.setXY(wrapXY);
10864             }
10865         }
10866         return wrap;
10867     },
10868
10869         /* @private */
10870     fxUnwrap : function(wrap, pos, o){
10871         this.clearPositioning();
10872         this.setPositioning(pos);
10873         if(!o.wrap){
10874             wrap.dom.parentNode.insertBefore(this.dom, wrap.dom);
10875             wrap.remove();
10876         }
10877     },
10878
10879         /* @private */
10880     getFxRestore : function(){
10881         var st = this.dom.style;
10882         return {pos: this.getPositioning(), width: st.width, height : st.height};
10883     },
10884
10885         /* @private */
10886     afterFx : function(o){
10887         if(o.afterStyle){
10888             this.applyStyles(o.afterStyle);
10889         }
10890         if(o.afterCls){
10891             this.addClass(o.afterCls);
10892         }
10893         if(o.remove === true){
10894             this.remove();
10895         }
10896         Roo.callback(o.callback, o.scope, [this]);
10897         if(!o.concurrent){
10898             this.fxQueue.shift();
10899             this.nextFx();
10900         }
10901     },
10902
10903         /* @private */
10904     getFxEl : function(){ // support for composite element fx
10905         return Roo.get(this.dom);
10906     },
10907
10908         /* @private */
10909     fxanim : function(args, opt, animType, defaultDur, defaultEase, cb){
10910         animType = animType || 'run';
10911         opt = opt || {};
10912         var anim = Roo.lib.Anim[animType](
10913             this.dom, args,
10914             (opt.duration || defaultDur) || .35,
10915             (opt.easing || defaultEase) || 'easeOut',
10916             function(){
10917                 Roo.callback(cb, this);
10918             },
10919             this
10920         );
10921         opt.anim = anim;
10922         return anim;
10923     }
10924 };
10925
10926 // backwords compat
10927 Roo.Fx.resize = Roo.Fx.scale;
10928
10929 //When included, Roo.Fx is automatically applied to Element so that all basic
10930 //effects are available directly via the Element API
10931 Roo.apply(Roo.Element.prototype, Roo.Fx);/*
10932  * Based on:
10933  * Ext JS Library 1.1.1
10934  * Copyright(c) 2006-2007, Ext JS, LLC.
10935  *
10936  * Originally Released Under LGPL - original licence link has changed is not relivant.
10937  *
10938  * Fork - LGPL
10939  * <script type="text/javascript">
10940  */
10941
10942
10943 /**
10944  * @class Roo.CompositeElement
10945  * Standard composite class. Creates a Roo.Element for every element in the collection.
10946  * <br><br>
10947  * <b>NOTE: Although they are not listed, this class supports all of the set/update methods of Roo.Element. All Roo.Element
10948  * actions will be performed on all the elements in this collection.</b>
10949  * <br><br>
10950  * All methods return <i>this</i> and can be chained.
10951  <pre><code>
10952  var els = Roo.select("#some-el div.some-class", true);
10953  // or select directly from an existing element
10954  var el = Roo.get('some-el');
10955  el.select('div.some-class', true);
10956
10957  els.setWidth(100); // all elements become 100 width
10958  els.hide(true); // all elements fade out and hide
10959  // or
10960  els.setWidth(100).hide(true);
10961  </code></pre>
10962  */
10963 Roo.CompositeElement = function(els){
10964     this.elements = [];
10965     this.addElements(els);
10966 };
10967 Roo.CompositeElement.prototype = {
10968     isComposite: true,
10969     addElements : function(els){
10970         if(!els) return this;
10971         if(typeof els == "string"){
10972             els = Roo.Element.selectorFunction(els);
10973         }
10974         var yels = this.elements;
10975         var index = yels.length-1;
10976         for(var i = 0, len = els.length; i < len; i++) {
10977                 yels[++index] = Roo.get(els[i]);
10978         }
10979         return this;
10980     },
10981
10982     /**
10983     * Clears this composite and adds the elements returned by the passed selector.
10984     * @param {String/Array} els A string CSS selector, an array of elements or an element
10985     * @return {CompositeElement} this
10986     */
10987     fill : function(els){
10988         this.elements = [];
10989         this.add(els);
10990         return this;
10991     },
10992
10993     /**
10994     * Filters this composite to only elements that match the passed selector.
10995     * @param {String} selector A string CSS selector
10996     * @param {Boolean} inverse return inverse filter (not matches)
10997     * @return {CompositeElement} this
10998     */
10999     filter : function(selector, inverse){
11000         var els = [];
11001         inverse = inverse || false;
11002         this.each(function(el){
11003             var match = inverse ? !el.is(selector) : el.is(selector);
11004             if(match){
11005                 els[els.length] = el.dom;
11006             }
11007         });
11008         this.fill(els);
11009         return this;
11010     },
11011
11012     invoke : function(fn, args){
11013         var els = this.elements;
11014         for(var i = 0, len = els.length; i < len; i++) {
11015                 Roo.Element.prototype[fn].apply(els[i], args);
11016         }
11017         return this;
11018     },
11019     /**
11020     * Adds elements to this composite.
11021     * @param {String/Array} els A string CSS selector, an array of elements or an element
11022     * @return {CompositeElement} this
11023     */
11024     add : function(els){
11025         if(typeof els == "string"){
11026             this.addElements(Roo.Element.selectorFunction(els));
11027         }else if(els.length !== undefined){
11028             this.addElements(els);
11029         }else{
11030             this.addElements([els]);
11031         }
11032         return this;
11033     },
11034     /**
11035     * Calls the passed function passing (el, this, index) for each element in this composite.
11036     * @param {Function} fn The function to call
11037     * @param {Object} scope (optional) The <i>this</i> object (defaults to the element)
11038     * @return {CompositeElement} this
11039     */
11040     each : function(fn, scope){
11041         var els = this.elements;
11042         for(var i = 0, len = els.length; i < len; i++){
11043             if(fn.call(scope || els[i], els[i], this, i) === false) {
11044                 break;
11045             }
11046         }
11047         return this;
11048     },
11049
11050     /**
11051      * Returns the Element object at the specified index
11052      * @param {Number} index
11053      * @return {Roo.Element}
11054      */
11055     item : function(index){
11056         return this.elements[index] || null;
11057     },
11058
11059     /**
11060      * Returns the first Element
11061      * @return {Roo.Element}
11062      */
11063     first : function(){
11064         return this.item(0);
11065     },
11066
11067     /**
11068      * Returns the last Element
11069      * @return {Roo.Element}
11070      */
11071     last : function(){
11072         return this.item(this.elements.length-1);
11073     },
11074
11075     /**
11076      * Returns the number of elements in this composite
11077      * @return Number
11078      */
11079     getCount : function(){
11080         return this.elements.length;
11081     },
11082
11083     /**
11084      * Returns true if this composite contains the passed element
11085      * @return Boolean
11086      */
11087     contains : function(el){
11088         return this.indexOf(el) !== -1;
11089     },
11090
11091     /**
11092      * Returns true if this composite contains the passed element
11093      * @return Boolean
11094      */
11095     indexOf : function(el){
11096         return this.elements.indexOf(Roo.get(el));
11097     },
11098
11099
11100     /**
11101     * Removes the specified element(s).
11102     * @param {Mixed} el The id of an element, the Element itself, the index of the element in this composite
11103     * or an array of any of those.
11104     * @param {Boolean} removeDom (optional) True to also remove the element from the document
11105     * @return {CompositeElement} this
11106     */
11107     removeElement : function(el, removeDom){
11108         if(el instanceof Array){
11109             for(var i = 0, len = el.length; i < len; i++){
11110                 this.removeElement(el[i]);
11111             }
11112             return this;
11113         }
11114         var index = typeof el == 'number' ? el : this.indexOf(el);
11115         if(index !== -1){
11116             if(removeDom){
11117                 var d = this.elements[index];
11118                 if(d.dom){
11119                     d.remove();
11120                 }else{
11121                     d.parentNode.removeChild(d);
11122                 }
11123             }
11124             this.elements.splice(index, 1);
11125         }
11126         return this;
11127     },
11128
11129     /**
11130     * Replaces the specified element with the passed element.
11131     * @param {String/HTMLElement/Element/Number} el The id of an element, the Element itself, the index of the element in this composite
11132     * to replace.
11133     * @param {String/HTMLElement/Element} replacement The id of an element or the Element itself.
11134     * @param {Boolean} domReplace (Optional) True to remove and replace the element in the document too.
11135     * @return {CompositeElement} this
11136     */
11137     replaceElement : function(el, replacement, domReplace){
11138         var index = typeof el == 'number' ? el : this.indexOf(el);
11139         if(index !== -1){
11140             if(domReplace){
11141                 this.elements[index].replaceWith(replacement);
11142             }else{
11143                 this.elements.splice(index, 1, Roo.get(replacement))
11144             }
11145         }
11146         return this;
11147     },
11148
11149     /**
11150      * Removes all elements.
11151      */
11152     clear : function(){
11153         this.elements = [];
11154     }
11155 };
11156 (function(){
11157     Roo.CompositeElement.createCall = function(proto, fnName){
11158         if(!proto[fnName]){
11159             proto[fnName] = function(){
11160                 return this.invoke(fnName, arguments);
11161             };
11162         }
11163     };
11164     for(var fnName in Roo.Element.prototype){
11165         if(typeof Roo.Element.prototype[fnName] == "function"){
11166             Roo.CompositeElement.createCall(Roo.CompositeElement.prototype, fnName);
11167         }
11168     };
11169 })();
11170 /*
11171  * Based on:
11172  * Ext JS Library 1.1.1
11173  * Copyright(c) 2006-2007, Ext JS, LLC.
11174  *
11175  * Originally Released Under LGPL - original licence link has changed is not relivant.
11176  *
11177  * Fork - LGPL
11178  * <script type="text/javascript">
11179  */
11180
11181 /**
11182  * @class Roo.CompositeElementLite
11183  * @extends Roo.CompositeElement
11184  * Flyweight composite class. Reuses the same Roo.Element for element operations.
11185  <pre><code>
11186  var els = Roo.select("#some-el div.some-class");
11187  // or select directly from an existing element
11188  var el = Roo.get('some-el');
11189  el.select('div.some-class');
11190
11191  els.setWidth(100); // all elements become 100 width
11192  els.hide(true); // all elements fade out and hide
11193  // or
11194  els.setWidth(100).hide(true);
11195  </code></pre><br><br>
11196  * <b>NOTE: Although they are not listed, this class supports all of the set/update methods of Roo.Element. All Roo.Element
11197  * actions will be performed on all the elements in this collection.</b>
11198  */
11199 Roo.CompositeElementLite = function(els){
11200     Roo.CompositeElementLite.superclass.constructor.call(this, els);
11201     this.el = new Roo.Element.Flyweight();
11202 };
11203 Roo.extend(Roo.CompositeElementLite, Roo.CompositeElement, {
11204     addElements : function(els){
11205         if(els){
11206             if(els instanceof Array){
11207                 this.elements = this.elements.concat(els);
11208             }else{
11209                 var yels = this.elements;
11210                 var index = yels.length-1;
11211                 for(var i = 0, len = els.length; i < len; i++) {
11212                     yels[++index] = els[i];
11213                 }
11214             }
11215         }
11216         return this;
11217     },
11218     invoke : function(fn, args){
11219         var els = this.elements;
11220         var el = this.el;
11221         for(var i = 0, len = els.length; i < len; i++) {
11222             el.dom = els[i];
11223                 Roo.Element.prototype[fn].apply(el, args);
11224         }
11225         return this;
11226     },
11227     /**
11228      * Returns a flyweight Element of the dom element object at the specified index
11229      * @param {Number} index
11230      * @return {Roo.Element}
11231      */
11232     item : function(index){
11233         if(!this.elements[index]){
11234             return null;
11235         }
11236         this.el.dom = this.elements[index];
11237         return this.el;
11238     },
11239
11240     // fixes scope with flyweight
11241     addListener : function(eventName, handler, scope, opt){
11242         var els = this.elements;
11243         for(var i = 0, len = els.length; i < len; i++) {
11244             Roo.EventManager.on(els[i], eventName, handler, scope || els[i], opt);
11245         }
11246         return this;
11247     },
11248
11249     /**
11250     * Calls the passed function passing (el, this, index) for each element in this composite. <b>The element
11251     * passed is the flyweight (shared) Roo.Element instance, so if you require a
11252     * a reference to the dom node, use el.dom.</b>
11253     * @param {Function} fn The function to call
11254     * @param {Object} scope (optional) The <i>this</i> object (defaults to the element)
11255     * @return {CompositeElement} this
11256     */
11257     each : function(fn, scope){
11258         var els = this.elements;
11259         var el = this.el;
11260         for(var i = 0, len = els.length; i < len; i++){
11261             el.dom = els[i];
11262                 if(fn.call(scope || el, el, this, i) === false){
11263                 break;
11264             }
11265         }
11266         return this;
11267     },
11268
11269     indexOf : function(el){
11270         return this.elements.indexOf(Roo.getDom(el));
11271     },
11272
11273     replaceElement : function(el, replacement, domReplace){
11274         var index = typeof el == 'number' ? el : this.indexOf(el);
11275         if(index !== -1){
11276             replacement = Roo.getDom(replacement);
11277             if(domReplace){
11278                 var d = this.elements[index];
11279                 d.parentNode.insertBefore(replacement, d);
11280                 d.parentNode.removeChild(d);
11281             }
11282             this.elements.splice(index, 1, replacement);
11283         }
11284         return this;
11285     }
11286 });
11287 Roo.CompositeElementLite.prototype.on = Roo.CompositeElementLite.prototype.addListener;
11288
11289 /*
11290  * Based on:
11291  * Ext JS Library 1.1.1
11292  * Copyright(c) 2006-2007, Ext JS, LLC.
11293  *
11294  * Originally Released Under LGPL - original licence link has changed is not relivant.
11295  *
11296  * Fork - LGPL
11297  * <script type="text/javascript">
11298  */
11299
11300  
11301
11302 /**
11303  * @class Roo.data.Connection
11304  * @extends Roo.util.Observable
11305  * The class encapsulates a connection to the page's originating domain, allowing requests to be made
11306  * either to a configured URL, or to a URL specified at request time.<br><br>
11307  * <p>
11308  * Requests made by this class are asynchronous, and will return immediately. No data from
11309  * the server will be available to the statement immediately following the {@link #request} call.
11310  * To process returned data, use a callback in the request options object, or an event listener.</p><br>
11311  * <p>
11312  * Note: If you are doing a file upload, you will not get a normal response object sent back to
11313  * your callback or event handler.  Since the upload is handled via in IFRAME, there is no XMLHttpRequest.
11314  * The response object is created using the innerHTML of the IFRAME's document as the responseText
11315  * property and, if present, the IFRAME's XML document as the responseXML property.</p><br>
11316  * This means that a valid XML or HTML document must be returned. If JSON data is required, it is suggested
11317  * that it be placed either inside a &lt;textarea> in an HTML document and retrieved from the responseText
11318  * using a regex, or inside a CDATA section in an XML document and retrieved from the responseXML using
11319  * standard DOM methods.
11320  * @constructor
11321  * @param {Object} config a configuration object.
11322  */
11323 Roo.data.Connection = function(config){
11324     Roo.apply(this, config);
11325     this.addEvents({
11326         /**
11327          * @event beforerequest
11328          * Fires before a network request is made to retrieve a data object.
11329          * @param {Connection} conn This Connection object.
11330          * @param {Object} options The options config object passed to the {@link #request} method.
11331          */
11332         "beforerequest" : true,
11333         /**
11334          * @event requestcomplete
11335          * Fires if the request was successfully completed.
11336          * @param {Connection} conn This Connection object.
11337          * @param {Object} response The XHR object containing the response data.
11338          * See {@link http://www.w3.org/TR/XMLHttpRequest/} for details.
11339          * @param {Object} options The options config object passed to the {@link #request} method.
11340          */
11341         "requestcomplete" : true,
11342         /**
11343          * @event requestexception
11344          * Fires if an error HTTP status was returned from the server.
11345          * See {@link http://www.w3.org/Protocols/rfc2616/rfc2616-sec10.html} for details of HTTP status codes.
11346          * @param {Connection} conn This Connection object.
11347          * @param {Object} response The XHR object containing the response data.
11348          * See {@link http://www.w3.org/TR/XMLHttpRequest/} for details.
11349          * @param {Object} options The options config object passed to the {@link #request} method.
11350          */
11351         "requestexception" : true
11352     });
11353     Roo.data.Connection.superclass.constructor.call(this);
11354 };
11355
11356 Roo.extend(Roo.data.Connection, Roo.util.Observable, {
11357     /**
11358      * @cfg {String} url (Optional) The default URL to be used for requests to the server. (defaults to undefined)
11359      */
11360     /**
11361      * @cfg {Object} extraParams (Optional) An object containing properties which are used as
11362      * extra parameters to each request made by this object. (defaults to undefined)
11363      */
11364     /**
11365      * @cfg {Object} defaultHeaders (Optional) An object containing request headers which are added
11366      *  to each request made by this object. (defaults to undefined)
11367      */
11368     /**
11369      * @cfg {String} method (Optional) The default HTTP method to be used for requests. (defaults to undefined; if not set but parms are present will use POST, otherwise GET)
11370      */
11371     /**
11372      * @cfg {Number} timeout (Optional) The timeout in milliseconds to be used for requests. (defaults to 30000)
11373      */
11374     timeout : 30000,
11375     /**
11376      * @cfg {Boolean} autoAbort (Optional) Whether this request should abort any pending requests. (defaults to false)
11377      * @type Boolean
11378      */
11379     autoAbort:false,
11380
11381     /**
11382      * @cfg {Boolean} disableCaching (Optional) True to add a unique cache-buster param to GET requests. (defaults to true)
11383      * @type Boolean
11384      */
11385     disableCaching: true,
11386
11387     /**
11388      * Sends an HTTP request to a remote server.
11389      * @param {Object} options An object which may contain the following properties:<ul>
11390      * <li><b>url</b> {String} (Optional) The URL to which to send the request. Defaults to configured URL</li>
11391      * <li><b>params</b> {Object/String/Function} (Optional) An object containing properties which are used as parameters to the
11392      * request, a url encoded string or a function to call to get either.</li>
11393      * <li><b>method</b> {String} (Optional) The HTTP method to use for the request. Defaults to the configured method, or
11394      * if no method was configured, "GET" if no parameters are being sent, and "POST" if parameters are being sent.</li>
11395      * <li><b>callback</b> {Function} (Optional) The function to be called upon receipt of the HTTP response.
11396      * The callback is called regardless of success or failure and is passed the following parameters:<ul>
11397      * <li>options {Object} The parameter to the request call.</li>
11398      * <li>success {Boolean} True if the request succeeded.</li>
11399      * <li>response {Object} The XMLHttpRequest object containing the response data.</li>
11400      * </ul></li>
11401      * <li><b>success</b> {Function} (Optional) The function to be called upon success of the request.
11402      * The callback is passed the following parameters:<ul>
11403      * <li>response {Object} The XMLHttpRequest object containing the response data.</li>
11404      * <li>options {Object} The parameter to the request call.</li>
11405      * </ul></li>
11406      * <li><b>failure</b> {Function} (Optional) The function to be called upon failure of the request.
11407      * The callback is passed the following parameters:<ul>
11408      * <li>response {Object} The XMLHttpRequest object containing the response data.</li>
11409      * <li>options {Object} The parameter to the request call.</li>
11410      * </ul></li>
11411      * <li><b>scope</b> {Object} (Optional) The scope in which to execute the callbacks: The "this" object
11412      * for the callback function. Defaults to the browser window.</li>
11413      * <li><b>form</b> {Object/String} (Optional) A form object or id to pull parameters from.</li>
11414      * <li><b>isUpload</b> {Boolean} (Optional) True if the form object is a file upload (will usually be automatically detected).</li>
11415      * <li><b>headers</b> {Object} (Optional) Request headers to set for the request.</li>
11416      * <li><b>xmlData</b> {Object} (Optional) XML document to use for the post. Note: This will be used instead of
11417      * params for the post data. Any params will be appended to the URL.</li>
11418      * <li><b>disableCaching</b> {Boolean} (Optional) True to add a unique cache-buster param to GET requests.</li>
11419      * </ul>
11420      * @return {Number} transactionId
11421      */
11422     request : function(o){
11423         if(this.fireEvent("beforerequest", this, o) !== false){
11424             var p = o.params;
11425
11426             if(typeof p == "function"){
11427                 p = p.call(o.scope||window, o);
11428             }
11429             if(typeof p == "object"){
11430                 p = Roo.urlEncode(o.params);
11431             }
11432             if(this.extraParams){
11433                 var extras = Roo.urlEncode(this.extraParams);
11434                 p = p ? (p + '&' + extras) : extras;
11435             }
11436
11437             var url = o.url || this.url;
11438             if(typeof url == 'function'){
11439                 url = url.call(o.scope||window, o);
11440             }
11441
11442             if(o.form){
11443                 var form = Roo.getDom(o.form);
11444                 url = url || form.action;
11445
11446                 var enctype = form.getAttribute("enctype");
11447                 if(o.isUpload || (enctype && enctype.toLowerCase() == 'multipart/form-data')){
11448                     return this.doFormUpload(o, p, url);
11449                 }
11450                 var f = Roo.lib.Ajax.serializeForm(form);
11451                 p = p ? (p + '&' + f) : f;
11452             }
11453
11454             var hs = o.headers;
11455             if(this.defaultHeaders){
11456                 hs = Roo.apply(hs || {}, this.defaultHeaders);
11457                 if(!o.headers){
11458                     o.headers = hs;
11459                 }
11460             }
11461
11462             var cb = {
11463                 success: this.handleResponse,
11464                 failure: this.handleFailure,
11465                 scope: this,
11466                 argument: {options: o},
11467                 timeout : o.timeout || this.timeout
11468             };
11469
11470             var method = o.method||this.method||(p ? "POST" : "GET");
11471
11472             if(method == 'GET' && (this.disableCaching && o.disableCaching !== false) || o.disableCaching === true){
11473                 url += (url.indexOf('?') != -1 ? '&' : '?') + '_dc=' + (new Date().getTime());
11474             }
11475
11476             if(typeof o.autoAbort == 'boolean'){ // options gets top priority
11477                 if(o.autoAbort){
11478                     this.abort();
11479                 }
11480             }else if(this.autoAbort !== false){
11481                 this.abort();
11482             }
11483
11484             if((method == 'GET' && p) || o.xmlData){
11485                 url += (url.indexOf('?') != -1 ? '&' : '?') + p;
11486                 p = '';
11487             }
11488             this.transId = Roo.lib.Ajax.request(method, url, cb, p, o);
11489             return this.transId;
11490         }else{
11491             Roo.callback(o.callback, o.scope, [o, null, null]);
11492             return null;
11493         }
11494     },
11495
11496     /**
11497      * Determine whether this object has a request outstanding.
11498      * @param {Number} transactionId (Optional) defaults to the last transaction
11499      * @return {Boolean} True if there is an outstanding request.
11500      */
11501     isLoading : function(transId){
11502         if(transId){
11503             return Roo.lib.Ajax.isCallInProgress(transId);
11504         }else{
11505             return this.transId ? true : false;
11506         }
11507     },
11508
11509     /**
11510      * Aborts any outstanding request.
11511      * @param {Number} transactionId (Optional) defaults to the last transaction
11512      */
11513     abort : function(transId){
11514         if(transId || this.isLoading()){
11515             Roo.lib.Ajax.abort(transId || this.transId);
11516         }
11517     },
11518
11519     // private
11520     handleResponse : function(response){
11521         this.transId = false;
11522         var options = response.argument.options;
11523         response.argument = options ? options.argument : null;
11524         this.fireEvent("requestcomplete", this, response, options);
11525         Roo.callback(options.success, options.scope, [response, options]);
11526         Roo.callback(options.callback, options.scope, [options, true, response]);
11527     },
11528
11529     // private
11530     handleFailure : function(response, e){
11531         this.transId = false;
11532         var options = response.argument.options;
11533         response.argument = options ? options.argument : null;
11534         this.fireEvent("requestexception", this, response, options, e);
11535         Roo.callback(options.failure, options.scope, [response, options]);
11536         Roo.callback(options.callback, options.scope, [options, false, response]);
11537     },
11538
11539     // private
11540     doFormUpload : function(o, ps, url){
11541         var id = Roo.id();
11542         var frame = document.createElement('iframe');
11543         frame.id = id;
11544         frame.name = id;
11545         frame.className = 'x-hidden';
11546         if(Roo.isIE){
11547             frame.src = Roo.SSL_SECURE_URL;
11548         }
11549         document.body.appendChild(frame);
11550
11551         if(Roo.isIE){
11552            document.frames[id].name = id;
11553         }
11554
11555         var form = Roo.getDom(o.form);
11556         form.target = id;
11557         form.method = 'POST';
11558         form.enctype = form.encoding = 'multipart/form-data';
11559         if(url){
11560             form.action = url;
11561         }
11562
11563         var hiddens, hd;
11564         if(ps){ // add dynamic params
11565             hiddens = [];
11566             ps = Roo.urlDecode(ps, false);
11567             for(var k in ps){
11568                 if(ps.hasOwnProperty(k)){
11569                     hd = document.createElement('input');
11570                     hd.type = 'hidden';
11571                     hd.name = k;
11572                     hd.value = ps[k];
11573                     form.appendChild(hd);
11574                     hiddens.push(hd);
11575                 }
11576             }
11577         }
11578
11579         function cb(){
11580             var r = {  // bogus response object
11581                 responseText : '',
11582                 responseXML : null
11583             };
11584
11585             r.argument = o ? o.argument : null;
11586
11587             try { //
11588                 var doc;
11589                 if(Roo.isIE){
11590                     doc = frame.contentWindow.document;
11591                 }else {
11592                     doc = (frame.contentDocument || window.frames[id].document);
11593                 }
11594                 if(doc && doc.body){
11595                     r.responseText = doc.body.innerHTML;
11596                 }
11597                 if(doc && doc.XMLDocument){
11598                     r.responseXML = doc.XMLDocument;
11599                 }else {
11600                     r.responseXML = doc;
11601                 }
11602             }
11603             catch(e) {
11604                 // ignore
11605             }
11606
11607             Roo.EventManager.removeListener(frame, 'load', cb, this);
11608
11609             this.fireEvent("requestcomplete", this, r, o);
11610             Roo.callback(o.success, o.scope, [r, o]);
11611             Roo.callback(o.callback, o.scope, [o, true, r]);
11612
11613             setTimeout(function(){document.body.removeChild(frame);}, 100);
11614         }
11615
11616         Roo.EventManager.on(frame, 'load', cb, this);
11617         form.submit();
11618
11619         if(hiddens){ // remove dynamic params
11620             for(var i = 0, len = hiddens.length; i < len; i++){
11621                 form.removeChild(hiddens[i]);
11622             }
11623         }
11624     }
11625 });
11626 /*
11627  * Based on:
11628  * Ext JS Library 1.1.1
11629  * Copyright(c) 2006-2007, Ext JS, LLC.
11630  *
11631  * Originally Released Under LGPL - original licence link has changed is not relivant.
11632  *
11633  * Fork - LGPL
11634  * <script type="text/javascript">
11635  */
11636  
11637 /**
11638  * Global Ajax request class.
11639  * 
11640  * @class Roo.Ajax
11641  * @extends Roo.data.Connection
11642  * @static
11643  * 
11644  * @cfg {String} url  The default URL to be used for requests to the server. (defaults to undefined)
11645  * @cfg {Object} extraParams  An object containing properties which are used as extra parameters to each request made by this object. (defaults to undefined)
11646  * @cfg {Object} defaultHeaders  An object containing request headers which are added to each request made by this object. (defaults to undefined)
11647  * @cfg {String} method (Optional)  The default HTTP method to be used for requests. (defaults to undefined; if not set but parms are present will use POST, otherwise GET)
11648  * @cfg {Number} timeout (Optional) The timeout in milliseconds to be used for requests. (defaults to 30000)
11649  * @cfg {Boolean} autoAbort (Optional) Whether a new request should abort any pending requests. (defaults to false)
11650  * @cfg {Boolean} disableCaching (Optional)   True to add a unique cache-buster param to GET requests. (defaults to true)
11651  */
11652 Roo.Ajax = new Roo.data.Connection({
11653     // fix up the docs
11654     /**
11655      * @scope Roo.Ajax
11656      * @type {Boolear} 
11657      */
11658     autoAbort : false,
11659
11660     /**
11661      * Serialize the passed form into a url encoded string
11662      * @scope Roo.Ajax
11663      * @param {String/HTMLElement} form
11664      * @return {String}
11665      */
11666     serializeForm : function(form){
11667         return Roo.lib.Ajax.serializeForm(form);
11668     }
11669 });/*
11670  * Based on:
11671  * Ext JS Library 1.1.1
11672  * Copyright(c) 2006-2007, Ext JS, LLC.
11673  *
11674  * Originally Released Under LGPL - original licence link has changed is not relivant.
11675  *
11676  * Fork - LGPL
11677  * <script type="text/javascript">
11678  */
11679
11680  
11681 /**
11682  * @class Roo.UpdateManager
11683  * @extends Roo.util.Observable
11684  * Provides AJAX-style update for Element object.<br><br>
11685  * Usage:<br>
11686  * <pre><code>
11687  * // Get it from a Roo.Element object
11688  * var el = Roo.get("foo");
11689  * var mgr = el.getUpdateManager();
11690  * mgr.update("http://myserver.com/index.php", "param1=1&amp;param2=2");
11691  * ...
11692  * mgr.formUpdate("myFormId", "http://myserver.com/index.php");
11693  * <br>
11694  * // or directly (returns the same UpdateManager instance)
11695  * var mgr = new Roo.UpdateManager("myElementId");
11696  * mgr.startAutoRefresh(60, "http://myserver.com/index.php");
11697  * mgr.on("update", myFcnNeedsToKnow);
11698  * <br>
11699    // short handed call directly from the element object
11700    Roo.get("foo").load({
11701         url: "bar.php",
11702         scripts:true,
11703         params: "for=bar",
11704         text: "Loading Foo..."
11705    });
11706  * </code></pre>
11707  * @constructor
11708  * Create new UpdateManager directly.
11709  * @param {String/HTMLElement/Roo.Element} el The element to update
11710  * @param {Boolean} forceNew (optional) By default the constructor checks to see if the passed element already has an UpdateManager and if it does it returns the same instance. This will skip that check (useful for extending this class).
11711  */
11712 Roo.UpdateManager = function(el, forceNew){
11713     el = Roo.get(el);
11714     if(!forceNew && el.updateManager){
11715         return el.updateManager;
11716     }
11717     /**
11718      * The Element object
11719      * @type Roo.Element
11720      */
11721     this.el = el;
11722     /**
11723      * Cached url to use for refreshes. Overwritten every time update() is called unless "discardUrl" param is set to true.
11724      * @type String
11725      */
11726     this.defaultUrl = null;
11727
11728     this.addEvents({
11729         /**
11730          * @event beforeupdate
11731          * Fired before an update is made, return false from your handler and the update is cancelled.
11732          * @param {Roo.Element} el
11733          * @param {String/Object/Function} url
11734          * @param {String/Object} params
11735          */
11736         "beforeupdate": true,
11737         /**
11738          * @event update
11739          * Fired after successful update is made.
11740          * @param {Roo.Element} el
11741          * @param {Object} oResponseObject The response Object
11742          */
11743         "update": true,
11744         /**
11745          * @event failure
11746          * Fired on update failure.
11747          * @param {Roo.Element} el
11748          * @param {Object} oResponseObject The response Object
11749          */
11750         "failure": true
11751     });
11752     var d = Roo.UpdateManager.defaults;
11753     /**
11754      * Blank page URL to use with SSL file uploads (Defaults to Roo.UpdateManager.defaults.sslBlankUrl or "about:blank").
11755      * @type String
11756      */
11757     this.sslBlankUrl = d.sslBlankUrl;
11758     /**
11759      * Whether to append unique parameter on get request to disable caching (Defaults to Roo.UpdateManager.defaults.disableCaching or false).
11760      * @type Boolean
11761      */
11762     this.disableCaching = d.disableCaching;
11763     /**
11764      * Text for loading indicator (Defaults to Roo.UpdateManager.defaults.indicatorText or '&lt;div class="loading-indicator"&gt;Loading...&lt;/div&gt;').
11765      * @type String
11766      */
11767     this.indicatorText = d.indicatorText;
11768     /**
11769      * Whether to show indicatorText when loading (Defaults to Roo.UpdateManager.defaults.showLoadIndicator or true).
11770      * @type String
11771      */
11772     this.showLoadIndicator = d.showLoadIndicator;
11773     /**
11774      * Timeout for requests or form posts in seconds (Defaults to Roo.UpdateManager.defaults.timeout or 30 seconds).
11775      * @type Number
11776      */
11777     this.timeout = d.timeout;
11778
11779     /**
11780      * True to process scripts in the output (Defaults to Roo.UpdateManager.defaults.loadScripts (false)).
11781      * @type Boolean
11782      */
11783     this.loadScripts = d.loadScripts;
11784
11785     /**
11786      * Transaction object of current executing transaction
11787      */
11788     this.transaction = null;
11789
11790     /**
11791      * @private
11792      */
11793     this.autoRefreshProcId = null;
11794     /**
11795      * Delegate for refresh() prebound to "this", use myUpdater.refreshDelegate.createCallback(arg1, arg2) to bind arguments
11796      * @type Function
11797      */
11798     this.refreshDelegate = this.refresh.createDelegate(this);
11799     /**
11800      * Delegate for update() prebound to "this", use myUpdater.updateDelegate.createCallback(arg1, arg2) to bind arguments
11801      * @type Function
11802      */
11803     this.updateDelegate = this.update.createDelegate(this);
11804     /**
11805      * Delegate for formUpdate() prebound to "this", use myUpdater.formUpdateDelegate.createCallback(arg1, arg2) to bind arguments
11806      * @type Function
11807      */
11808     this.formUpdateDelegate = this.formUpdate.createDelegate(this);
11809     /**
11810      * @private
11811      */
11812     this.successDelegate = this.processSuccess.createDelegate(this);
11813     /**
11814      * @private
11815      */
11816     this.failureDelegate = this.processFailure.createDelegate(this);
11817
11818     if(!this.renderer){
11819      /**
11820       * The renderer for this UpdateManager. Defaults to {@link Roo.UpdateManager.BasicRenderer}.
11821       */
11822     this.renderer = new Roo.UpdateManager.BasicRenderer();
11823     }
11824     
11825     Roo.UpdateManager.superclass.constructor.call(this);
11826 };
11827
11828 Roo.extend(Roo.UpdateManager, Roo.util.Observable, {
11829     /**
11830      * Get the Element this UpdateManager is bound to
11831      * @return {Roo.Element} The element
11832      */
11833     getEl : function(){
11834         return this.el;
11835     },
11836     /**
11837      * Performs an async request, updating this element with the response. If params are specified it uses POST, otherwise it uses GET.
11838      * @param {Object/String/Function} url The url for this request or a function to call to get the url or a config object containing any of the following options:
11839 <pre><code>
11840 um.update({<br/>
11841     url: "your-url.php",<br/>
11842     params: {param1: "foo", param2: "bar"}, // or a URL encoded string<br/>
11843     callback: yourFunction,<br/>
11844     scope: yourObject, //(optional scope)  <br/>
11845     discardUrl: false, <br/>
11846     nocache: false,<br/>
11847     text: "Loading...",<br/>
11848     timeout: 30,<br/>
11849     scripts: false<br/>
11850 });
11851 </code></pre>
11852      * The only required property is url. The optional properties nocache, text and scripts
11853      * are shorthand for disableCaching, indicatorText and loadScripts and are used to set their associated property on this UpdateManager instance.
11854      * @param {String/Object} params (optional) The parameters to pass as either a url encoded string "param1=1&amp;param2=2" or an object {param1: 1, param2: 2}
11855      * @param {Function} callback (optional) Callback when transaction is complete - called with signature (oElement, bSuccess, oResponse)
11856      * @param {Boolean} discardUrl (optional) By default when you execute an update the defaultUrl is changed to the last used url. If true, it will not store the url.
11857      */
11858     update : function(url, params, callback, discardUrl){
11859         if(this.fireEvent("beforeupdate", this.el, url, params) !== false){
11860             var method = this.method,
11861                 cfg;
11862             if(typeof url == "object"){ // must be config object
11863                 cfg = url;
11864                 url = cfg.url;
11865                 params = params || cfg.params;
11866                 callback = callback || cfg.callback;
11867                 discardUrl = discardUrl || cfg.discardUrl;
11868                 if(callback && cfg.scope){
11869                     callback = callback.createDelegate(cfg.scope);
11870                 }
11871                 if(typeof cfg.method != "undefined"){method = cfg.method;};
11872                 if(typeof cfg.nocache != "undefined"){this.disableCaching = cfg.nocache;};
11873                 if(typeof cfg.text != "undefined"){this.indicatorText = '<div class="loading-indicator">'+cfg.text+"</div>";};
11874                 if(typeof cfg.scripts != "undefined"){this.loadScripts = cfg.scripts;};
11875                 if(typeof cfg.timeout != "undefined"){this.timeout = cfg.timeout;};
11876             }
11877             this.showLoading();
11878             if(!discardUrl){
11879                 this.defaultUrl = url;
11880             }
11881             if(typeof url == "function"){
11882                 url = url.call(this);
11883             }
11884
11885             method = method || (params ? "POST" : "GET");
11886             if(method == "GET"){
11887                 url = this.prepareUrl(url);
11888             }
11889
11890             var o = Roo.apply(cfg ||{}, {
11891                 url : url,
11892                 params: params,
11893                 success: this.successDelegate,
11894                 failure: this.failureDelegate,
11895                 callback: undefined,
11896                 timeout: (this.timeout*1000),
11897                 argument: {"url": url, "form": null, "callback": callback, "params": params}
11898             });
11899             Roo.log("updated manager called with timeout of " + o.timeout);
11900             this.transaction = Roo.Ajax.request(o);
11901         }
11902     },
11903
11904     /**
11905      * Performs an async form post, updating this element with the response. If the form has the attribute enctype="multipart/form-data", it assumes it's a file upload.
11906      * Uses this.sslBlankUrl for SSL file uploads to prevent IE security warning.
11907      * @param {String/HTMLElement} form The form Id or form element
11908      * @param {String} url (optional) The url to pass the form to. If omitted the action attribute on the form will be used.
11909      * @param {Boolean} reset (optional) Whether to try to reset the form after the update
11910      * @param {Function} callback (optional) Callback when transaction is complete - called with signature (oElement, bSuccess, oResponse)
11911      */
11912     formUpdate : function(form, url, reset, callback){
11913         if(this.fireEvent("beforeupdate", this.el, form, url) !== false){
11914             if(typeof url == "function"){
11915                 url = url.call(this);
11916             }
11917             form = Roo.getDom(form);
11918             this.transaction = Roo.Ajax.request({
11919                 form: form,
11920                 url:url,
11921                 success: this.successDelegate,
11922                 failure: this.failureDelegate,
11923                 timeout: (this.timeout*1000),
11924                 argument: {"url": url, "form": form, "callback": callback, "reset": reset}
11925             });
11926             this.showLoading.defer(1, this);
11927         }
11928     },
11929
11930     /**
11931      * Refresh the element with the last used url or defaultUrl. If there is no url, it returns immediately
11932      * @param {Function} callback (optional) Callback when transaction is complete - called with signature (oElement, bSuccess)
11933      */
11934     refresh : function(callback){
11935         if(this.defaultUrl == null){
11936             return;
11937         }
11938         this.update(this.defaultUrl, null, callback, true);
11939     },
11940
11941     /**
11942      * Set this element to auto refresh.
11943      * @param {Number} interval How often to update (in seconds).
11944      * @param {String/Function} url (optional) The url for this request or a function to call to get the url (Defaults to the last used url)
11945      * @param {String/Object} params (optional) The parameters to pass as either a url encoded string "&param1=1&param2=2" or as an object {param1: 1, param2: 2}
11946      * @param {Function} callback (optional) Callback when transaction is complete - called with signature (oElement, bSuccess)
11947      * @param {Boolean} refreshNow (optional) Whether to execute the refresh now, or wait the interval
11948      */
11949     startAutoRefresh : function(interval, url, params, callback, refreshNow){
11950         if(refreshNow){
11951             this.update(url || this.defaultUrl, params, callback, true);
11952         }
11953         if(this.autoRefreshProcId){
11954             clearInterval(this.autoRefreshProcId);
11955         }
11956         this.autoRefreshProcId = setInterval(this.update.createDelegate(this, [url || this.defaultUrl, params, callback, true]), interval*1000);
11957     },
11958
11959     /**
11960      * Stop auto refresh on this element.
11961      */
11962      stopAutoRefresh : function(){
11963         if(this.autoRefreshProcId){
11964             clearInterval(this.autoRefreshProcId);
11965             delete this.autoRefreshProcId;
11966         }
11967     },
11968
11969     isAutoRefreshing : function(){
11970        return this.autoRefreshProcId ? true : false;
11971     },
11972     /**
11973      * Called to update the element to "Loading" state. Override to perform custom action.
11974      */
11975     showLoading : function(){
11976         if(this.showLoadIndicator){
11977             this.el.update(this.indicatorText);
11978         }
11979     },
11980
11981     /**
11982      * Adds unique parameter to query string if disableCaching = true
11983      * @private
11984      */
11985     prepareUrl : function(url){
11986         if(this.disableCaching){
11987             var append = "_dc=" + (new Date().getTime());
11988             if(url.indexOf("?") !== -1){
11989                 url += "&" + append;
11990             }else{
11991                 url += "?" + append;
11992             }
11993         }
11994         return url;
11995     },
11996
11997     /**
11998      * @private
11999      */
12000     processSuccess : function(response){
12001         this.transaction = null;
12002         if(response.argument.form && response.argument.reset){
12003             try{ // put in try/catch since some older FF releases had problems with this
12004                 response.argument.form.reset();
12005             }catch(e){}
12006         }
12007         if(this.loadScripts){
12008             this.renderer.render(this.el, response, this,
12009                 this.updateComplete.createDelegate(this, [response]));
12010         }else{
12011             this.renderer.render(this.el, response, this);
12012             this.updateComplete(response);
12013         }
12014     },
12015
12016     updateComplete : function(response){
12017         this.fireEvent("update", this.el, response);
12018         if(typeof response.argument.callback == "function"){
12019             response.argument.callback(this.el, true, response);
12020         }
12021     },
12022
12023     /**
12024      * @private
12025      */
12026     processFailure : function(response){
12027         this.transaction = null;
12028         this.fireEvent("failure", this.el, response);
12029         if(typeof response.argument.callback == "function"){
12030             response.argument.callback(this.el, false, response);
12031         }
12032     },
12033
12034     /**
12035      * Set the content renderer for this UpdateManager. See {@link Roo.UpdateManager.BasicRenderer#render} for more details.
12036      * @param {Object} renderer The object implementing the render() method
12037      */
12038     setRenderer : function(renderer){
12039         this.renderer = renderer;
12040     },
12041
12042     getRenderer : function(){
12043        return this.renderer;
12044     },
12045
12046     /**
12047      * Set the defaultUrl used for updates
12048      * @param {String/Function} defaultUrl The url or a function to call to get the url
12049      */
12050     setDefaultUrl : function(defaultUrl){
12051         this.defaultUrl = defaultUrl;
12052     },
12053
12054     /**
12055      * Aborts the executing transaction
12056      */
12057     abort : function(){
12058         if(this.transaction){
12059             Roo.Ajax.abort(this.transaction);
12060         }
12061     },
12062
12063     /**
12064      * Returns true if an update is in progress
12065      * @return {Boolean}
12066      */
12067     isUpdating : function(){
12068         if(this.transaction){
12069             return Roo.Ajax.isLoading(this.transaction);
12070         }
12071         return false;
12072     }
12073 });
12074
12075 /**
12076  * @class Roo.UpdateManager.defaults
12077  * @static (not really - but it helps the doc tool)
12078  * The defaults collection enables customizing the default properties of UpdateManager
12079  */
12080    Roo.UpdateManager.defaults = {
12081        /**
12082          * Timeout for requests or form posts in seconds (Defaults 30 seconds).
12083          * @type Number
12084          */
12085          timeout : 30,
12086
12087          /**
12088          * True to process scripts by default (Defaults to false).
12089          * @type Boolean
12090          */
12091         loadScripts : false,
12092
12093         /**
12094         * Blank page URL to use with SSL file uploads (Defaults to "javascript:false").
12095         * @type String
12096         */
12097         sslBlankUrl : (Roo.SSL_SECURE_URL || "javascript:false"),
12098         /**
12099          * Whether to append unique parameter on get request to disable caching (Defaults to false).
12100          * @type Boolean
12101          */
12102         disableCaching : false,
12103         /**
12104          * Whether to show indicatorText when loading (Defaults to true).
12105          * @type Boolean
12106          */
12107         showLoadIndicator : true,
12108         /**
12109          * Text for loading indicator (Defaults to '&lt;div class="loading-indicator"&gt;Loading...&lt;/div&gt;').
12110          * @type String
12111          */
12112         indicatorText : '<div class="loading-indicator">Loading...</div>'
12113    };
12114
12115 /**
12116  * Static convenience method. This method is deprecated in favor of el.load({url:'foo.php', ...}).
12117  *Usage:
12118  * <pre><code>Roo.UpdateManager.updateElement("my-div", "stuff.php");</code></pre>
12119  * @param {String/HTMLElement/Roo.Element} el The element to update
12120  * @param {String} url The url
12121  * @param {String/Object} params (optional) Url encoded param string or an object of name/value pairs
12122  * @param {Object} options (optional) A config object with any of the UpdateManager properties you want to set - for example: {disableCaching:true, indicatorText: "Loading data..."}
12123  * @static
12124  * @deprecated
12125  * @member Roo.UpdateManager
12126  */
12127 Roo.UpdateManager.updateElement = function(el, url, params, options){
12128     var um = Roo.get(el, true).getUpdateManager();
12129     Roo.apply(um, options);
12130     um.update(url, params, options ? options.callback : null);
12131 };
12132 // alias for backwards compat
12133 Roo.UpdateManager.update = Roo.UpdateManager.updateElement;
12134 /**
12135  * @class Roo.UpdateManager.BasicRenderer
12136  * Default Content renderer. Updates the elements innerHTML with the responseText.
12137  */
12138 Roo.UpdateManager.BasicRenderer = function(){};
12139
12140 Roo.UpdateManager.BasicRenderer.prototype = {
12141     /**
12142      * This is called when the transaction is completed and it's time to update the element - The BasicRenderer
12143      * updates the elements innerHTML with the responseText - To perform a custom render (i.e. XML or JSON processing),
12144      * create an object with a "render(el, response)" method and pass it to setRenderer on the UpdateManager.
12145      * @param {Roo.Element} el The element being rendered
12146      * @param {Object} response The YUI Connect response object
12147      * @param {UpdateManager} updateManager The calling update manager
12148      * @param {Function} callback A callback that will need to be called if loadScripts is true on the UpdateManager
12149      */
12150      render : function(el, response, updateManager, callback){
12151         el.update(response.responseText, updateManager.loadScripts, callback);
12152     }
12153 };
12154 /*
12155  * Based on:
12156  * Roo JS
12157  * (c)) Alan Knowles
12158  * Licence : LGPL
12159  */
12160
12161
12162 /**
12163  * @class Roo.DomTemplate
12164  * @extends Roo.Template
12165  * An effort at a dom based template engine..
12166  *
12167  * Similar to XTemplate, except it uses dom parsing to create the template..
12168  *
12169  * Supported features:
12170  *
12171  *  Tags:
12172
12173 <pre><code>
12174       {a_variable} - output encoded.
12175       {a_variable.format:("Y-m-d")} - call a method on the variable
12176       {a_variable:raw} - unencoded output
12177       {a_variable:toFixed(1,2)} - Roo.util.Format."toFixed"
12178       {a_variable:this.method_on_template(...)} - call a method on the template object.
12179  
12180 </code></pre>
12181  *  The tpl tag:
12182 <pre><code>
12183         &lt;div roo-for="a_variable or condition.."&gt;&lt;/div&gt;
12184         &lt;div roo-if="a_variable or condition"&gt;&lt;/div&gt;
12185         &lt;div roo-exec="some javascript"&gt;&lt;/div&gt;
12186         &lt;div roo-name="named_template"&gt;&lt;/div&gt; 
12187   
12188 </code></pre>
12189  *      
12190  */
12191 Roo.DomTemplate = function()
12192 {
12193      Roo.DomTemplate.superclass.constructor.apply(this, arguments);
12194      if (this.html) {
12195         this.compile();
12196      }
12197 };
12198
12199
12200 Roo.extend(Roo.DomTemplate, Roo.Template, {
12201     /**
12202      * id counter for sub templates.
12203      */
12204     id : 0,
12205     /**
12206      * flag to indicate if dom parser is inside a pre,
12207      * it will strip whitespace if not.
12208      */
12209     inPre : false,
12210     
12211     /**
12212      * The various sub templates
12213      */
12214     tpls : false,
12215     
12216     
12217     
12218     /**
12219      *
12220      * basic tag replacing syntax
12221      * WORD:WORD()
12222      *
12223      * // you can fake an object call by doing this
12224      *  x.t:(test,tesT) 
12225      * 
12226      */
12227     re : /(\{|\%7B)([\w-\.]+)(?:\:([\w\.]*)(?:\(([^)]*?)?\))?)?(\}|\%7D)/g,
12228     //re : /\{([\w-\.]+)(?:\:([\w\.]*)(?:\((.*?)?\))?)?\}/g,
12229     
12230     iterChild : function (node, method) {
12231         
12232         var oldPre = this.inPre;
12233         if (node.tagName == 'PRE') {
12234             this.inPre = true;
12235         }
12236         for( var i = 0; i < node.childNodes.length; i++) {
12237             method.call(this, node.childNodes[i]);
12238         }
12239         this.inPre = oldPre;
12240     },
12241     
12242     
12243     
12244     /**
12245      * compile the template
12246      *
12247      * This is not recursive, so I'm not sure how nested templates are really going to be handled..
12248      *
12249      */
12250     compile: function()
12251     {
12252         var s = this.html;
12253         
12254         // covert the html into DOM...
12255         var doc = false;
12256         var div =false;
12257         try {
12258             doc = document.implementation.createHTMLDocument("");
12259             doc.documentElement.innerHTML =   this.html  ;
12260             div = doc.documentElement;
12261         } catch (e) {
12262             // old IE... - nasty -- it causes all sorts of issues.. with
12263             // images getting pulled from server..
12264             div = document.createElement('div');
12265             div.innerHTML = this.html;
12266         }
12267         //doc.documentElement.innerHTML = htmlBody
12268          
12269         
12270         
12271         this.tpls = [];
12272         var _t = this;
12273         this.iterChild(div, function(n) {_t.compileNode(n, true); });
12274         
12275         var tpls = this.tpls;
12276         
12277         // create a top level template from the snippet..
12278         
12279         //Roo.log(div.innerHTML);
12280         
12281         var tpl = {
12282             uid : 'master',
12283             id : this.id++,
12284             attr : false,
12285             value : false,
12286             body : div.innerHTML,
12287             
12288             forCall : false,
12289             execCall : false,
12290             dom : div,
12291             isTop : true
12292             
12293         };
12294         tpls.unshift(tpl);
12295         
12296         
12297         // compile them...
12298         this.tpls = [];
12299         Roo.each(tpls, function(tp){
12300             this.compileTpl(tp);
12301             this.tpls[tp.id] = tp;
12302         }, this);
12303         
12304         this.master = tpls[0];
12305         return this;
12306         
12307         
12308     },
12309     
12310     compileNode : function(node, istop) {
12311         // test for
12312         //Roo.log(node);
12313         
12314         
12315         // skip anything not a tag..
12316         if (node.nodeType != 1) {
12317             if (node.nodeType == 3 && !this.inPre) {
12318                 // reduce white space..
12319                 node.nodeValue = node.nodeValue.replace(/\s+/g, ' '); 
12320                 
12321             }
12322             return;
12323         }
12324         
12325         var tpl = {
12326             uid : false,
12327             id : false,
12328             attr : false,
12329             value : false,
12330             body : '',
12331             
12332             forCall : false,
12333             execCall : false,
12334             dom : false,
12335             isTop : istop
12336             
12337             
12338         };
12339         
12340         
12341         switch(true) {
12342             case (node.hasAttribute('roo-for')): tpl.attr = 'for'; break;
12343             case (node.hasAttribute('roo-if')): tpl.attr = 'if'; break;
12344             case (node.hasAttribute('roo-name')): tpl.attr = 'name'; break;
12345             case (node.hasAttribute('roo-exec')): tpl.attr = 'exec'; break;
12346             // no default..
12347         }
12348         
12349         
12350         if (!tpl.attr) {
12351             // just itterate children..
12352             this.iterChild(node,this.compileNode);
12353             return;
12354         }
12355         tpl.uid = this.id++;
12356         tpl.value = node.getAttribute('roo-' +  tpl.attr);
12357         node.removeAttribute('roo-'+ tpl.attr);
12358         if (tpl.attr != 'name') {
12359             var placeholder = document.createTextNode('{domtpl' + tpl.uid + '}');
12360             node.parentNode.replaceChild(placeholder,  node);
12361         } else {
12362             
12363             var placeholder =  document.createElement('span');
12364             placeholder.className = 'roo-tpl-' + tpl.value;
12365             node.parentNode.replaceChild(placeholder,  node);
12366         }
12367         
12368         // parent now sees '{domtplXXXX}
12369         this.iterChild(node,this.compileNode);
12370         
12371         // we should now have node body...
12372         var div = document.createElement('div');
12373         div.appendChild(node);
12374         tpl.dom = node;
12375         // this has the unfortunate side effect of converting tagged attributes
12376         // eg. href="{...}" into %7C...%7D
12377         // this has been fixed by searching for those combo's although it's a bit hacky..
12378         
12379         
12380         tpl.body = div.innerHTML;
12381         
12382         
12383          
12384         tpl.id = tpl.uid;
12385         switch(tpl.attr) {
12386             case 'for' :
12387                 switch (tpl.value) {
12388                     case '.':  tpl.forCall = new Function('values', 'parent', 'with(values){ return values; }'); break;
12389                     case '..': tpl.forCall= new Function('values', 'parent', 'with(values){ return parent; }'); break;
12390                     default:   tpl.forCall= new Function('values', 'parent', 'with(values){ return '+tpl.value+'; }');
12391                 }
12392                 break;
12393             
12394             case 'exec':
12395                 tpl.execCall = new Function('values', 'parent', 'with(values){ '+(Roo.util.Format.htmlDecode(tpl.value))+'; }');
12396                 break;
12397             
12398             case 'if':     
12399                 tpl.ifCall = new Function('values', 'parent', 'with(values){ return '+(Roo.util.Format.htmlDecode(tpl.value))+'; }');
12400                 break;
12401             
12402             case 'name':
12403                 tpl.id  = tpl.value; // replace non characters???
12404                 break;
12405             
12406         }
12407         
12408         
12409         this.tpls.push(tpl);
12410         
12411         
12412         
12413     },
12414     
12415     
12416     
12417     
12418     /**
12419      * Compile a segment of the template into a 'sub-template'
12420      *
12421      * 
12422      * 
12423      *
12424      */
12425     compileTpl : function(tpl)
12426     {
12427         var fm = Roo.util.Format;
12428         var useF = this.disableFormats !== true;
12429         
12430         var sep = Roo.isGecko ? "+\n" : ",\n";
12431         
12432         var undef = function(str) {
12433             Roo.debug && Roo.log("Property not found :"  + str);
12434             return '';
12435         };
12436           
12437         //Roo.log(tpl.body);
12438         
12439         
12440         
12441         var fn = function(m, lbrace, name, format, args)
12442         {
12443             //Roo.log("ARGS");
12444             //Roo.log(arguments);
12445             args = args ? args.replace(/\\'/g,"'") : args;
12446             //["{TEST:(a,b,c)}", "TEST", "", "a,b,c", 0, "{TEST:(a,b,c)}"]
12447             if (typeof(format) == 'undefined') {
12448                 format =  'htmlEncode'; 
12449             }
12450             if (format == 'raw' ) {
12451                 format = false;
12452             }
12453             
12454             if(name.substr(0, 6) == 'domtpl'){
12455                 return "'"+ sep +'this.applySubTemplate('+name.substr(6)+', values, parent)'+sep+"'";
12456             }
12457             
12458             // build an array of options to determine if value is undefined..
12459             
12460             // basically get 'xxxx.yyyy' then do
12461             // (typeof(xxxx) == 'undefined' || typeof(xxx.yyyy) == 'undefined') ?
12462             //    (function () { Roo.log("Property not found"); return ''; })() :
12463             //    ......
12464             
12465             var udef_ar = [];
12466             var lookfor = '';
12467             Roo.each(name.split('.'), function(st) {
12468                 lookfor += (lookfor.length ? '.': '') + st;
12469                 udef_ar.push(  "(typeof(" + lookfor + ") == 'undefined')"  );
12470             });
12471             
12472             var udef_st = '((' + udef_ar.join(" || ") +") ? undef('" + name + "') : "; // .. needs )
12473             
12474             
12475             if(format && useF){
12476                 
12477                 args = args ? ',' + args : "";
12478                  
12479                 if(format.substr(0, 5) != "this."){
12480                     format = "fm." + format + '(';
12481                 }else{
12482                     format = 'this.call("'+ format.substr(5) + '", ';
12483                     args = ", values";
12484                 }
12485                 
12486                 return "'"+ sep +   udef_st   +    format + name + args + "))"+sep+"'";
12487             }
12488              
12489             if (args && args.length) {
12490                 // called with xxyx.yuu:(test,test)
12491                 // change to ()
12492                 return "'"+ sep + udef_st  + name + '(' +  args + "))"+sep+"'";
12493             }
12494             // raw.. - :raw modifier..
12495             return "'"+ sep + udef_st  + name + ")"+sep+"'";
12496             
12497         };
12498         var body;
12499         // branched to use + in gecko and [].join() in others
12500         if(Roo.isGecko){
12501             body = "tpl.compiled = function(values, parent){  with(values) { return '" +
12502                    tpl.body.replace(/(\r\n|\n)/g, '\\n').replace(/'/g, "\\'").replace(this.re, fn) +
12503                     "';};};";
12504         }else{
12505             body = ["tpl.compiled = function(values, parent){  with (values) { return ['"];
12506             body.push(tpl.body.replace(/(\r\n|\n)/g,
12507                             '\\n').replace(/'/g, "\\'").replace(this.re, fn));
12508             body.push("'].join('');};};");
12509             body = body.join('');
12510         }
12511         
12512         Roo.debug && Roo.log(body.replace(/\\n/,'\n'));
12513        
12514         /** eval:var:tpl eval:var:fm eval:var:useF eval:var:undef  */
12515         eval(body);
12516         
12517         return this;
12518     },
12519      
12520     /**
12521      * same as applyTemplate, except it's done to one of the subTemplates
12522      * when using named templates, you can do:
12523      *
12524      * var str = pl.applySubTemplate('your-name', values);
12525      *
12526      * 
12527      * @param {Number} id of the template
12528      * @param {Object} values to apply to template
12529      * @param {Object} parent (normaly the instance of this object)
12530      */
12531     applySubTemplate : function(id, values, parent)
12532     {
12533         
12534         
12535         var t = this.tpls[id];
12536         
12537         
12538         try { 
12539             if(t.ifCall && !t.ifCall.call(this, values, parent)){
12540                 Roo.debug && Roo.log('if call on ' + t.value + ' return false');
12541                 return '';
12542             }
12543         } catch(e) {
12544             Roo.log('Xtemplate.applySubTemplate('+ id+ '): Exception thrown on roo-if="' + t.value + '" - ' + e.toString());
12545             Roo.log(values);
12546           
12547             return '';
12548         }
12549         try { 
12550             
12551             if(t.execCall && t.execCall.call(this, values, parent)){
12552                 return '';
12553             }
12554         } catch(e) {
12555             Roo.log('Xtemplate.applySubTemplate('+ id+ '): Exception thrown on roo-for="' + t.value + '" - ' + e.toString());
12556             Roo.log(values);
12557             return '';
12558         }
12559         
12560         try {
12561             var vs = t.forCall ? t.forCall.call(this, values, parent) : values;
12562             parent = t.target ? values : parent;
12563             if(t.forCall && vs instanceof Array){
12564                 var buf = [];
12565                 for(var i = 0, len = vs.length; i < len; i++){
12566                     try {
12567                         buf[buf.length] = t.compiled.call(this, vs[i], parent);
12568                     } catch (e) {
12569                         Roo.log('Xtemplate.applySubTemplate('+ id+ '): Exception thrown on body="' + t.value + '" - ' + e.toString());
12570                         Roo.log(e.body);
12571                         //Roo.log(t.compiled);
12572                         Roo.log(vs[i]);
12573                     }   
12574                 }
12575                 return buf.join('');
12576             }
12577         } catch (e) {
12578             Roo.log('Xtemplate.applySubTemplate('+ id+ '): Exception thrown on roo-for="' + t.value + '" - ' + e.toString());
12579             Roo.log(values);
12580             return '';
12581         }
12582         try {
12583             return t.compiled.call(this, vs, parent);
12584         } catch (e) {
12585             Roo.log('Xtemplate.applySubTemplate('+ id+ '): Exception thrown on body="' + t.value + '" - ' + e.toString());
12586             Roo.log(e.body);
12587             //Roo.log(t.compiled);
12588             Roo.log(values);
12589             return '';
12590         }
12591     },
12592
12593    
12594
12595     applyTemplate : function(values){
12596         return this.master.compiled.call(this, values, {});
12597         //var s = this.subs;
12598     },
12599
12600     apply : function(){
12601         return this.applyTemplate.apply(this, arguments);
12602     }
12603
12604  });
12605
12606 Roo.DomTemplate.from = function(el){
12607     el = Roo.getDom(el);
12608     return new Roo.Domtemplate(el.value || el.innerHTML);
12609 };/*
12610  * Based on:
12611  * Ext JS Library 1.1.1
12612  * Copyright(c) 2006-2007, Ext JS, LLC.
12613  *
12614  * Originally Released Under LGPL - original licence link has changed is not relivant.
12615  *
12616  * Fork - LGPL
12617  * <script type="text/javascript">
12618  */
12619
12620 /**
12621  * @class Roo.util.DelayedTask
12622  * Provides a convenient method of performing setTimeout where a new
12623  * timeout cancels the old timeout. An example would be performing validation on a keypress.
12624  * You can use this class to buffer
12625  * the keypress events for a certain number of milliseconds, and perform only if they stop
12626  * for that amount of time.
12627  * @constructor The parameters to this constructor serve as defaults and are not required.
12628  * @param {Function} fn (optional) The default function to timeout
12629  * @param {Object} scope (optional) The default scope of that timeout
12630  * @param {Array} args (optional) The default Array of arguments
12631  */
12632 Roo.util.DelayedTask = function(fn, scope, args){
12633     var id = null, d, t;
12634
12635     var call = function(){
12636         var now = new Date().getTime();
12637         if(now - t >= d){
12638             clearInterval(id);
12639             id = null;
12640             fn.apply(scope, args || []);
12641         }
12642     };
12643     /**
12644      * Cancels any pending timeout and queues a new one
12645      * @param {Number} delay The milliseconds to delay
12646      * @param {Function} newFn (optional) Overrides function passed to constructor
12647      * @param {Object} newScope (optional) Overrides scope passed to constructor
12648      * @param {Array} newArgs (optional) Overrides args passed to constructor
12649      */
12650     this.delay = function(delay, newFn, newScope, newArgs){
12651         if(id && delay != d){
12652             this.cancel();
12653         }
12654         d = delay;
12655         t = new Date().getTime();
12656         fn = newFn || fn;
12657         scope = newScope || scope;
12658         args = newArgs || args;
12659         if(!id){
12660             id = setInterval(call, d);
12661         }
12662     };
12663
12664     /**
12665      * Cancel the last queued timeout
12666      */
12667     this.cancel = function(){
12668         if(id){
12669             clearInterval(id);
12670             id = null;
12671         }
12672     };
12673 };/*
12674  * Based on:
12675  * Ext JS Library 1.1.1
12676  * Copyright(c) 2006-2007, Ext JS, LLC.
12677  *
12678  * Originally Released Under LGPL - original licence link has changed is not relivant.
12679  *
12680  * Fork - LGPL
12681  * <script type="text/javascript">
12682  */
12683  
12684  
12685 Roo.util.TaskRunner = function(interval){
12686     interval = interval || 10;
12687     var tasks = [], removeQueue = [];
12688     var id = 0;
12689     var running = false;
12690
12691     var stopThread = function(){
12692         running = false;
12693         clearInterval(id);
12694         id = 0;
12695     };
12696
12697     var startThread = function(){
12698         if(!running){
12699             running = true;
12700             id = setInterval(runTasks, interval);
12701         }
12702     };
12703
12704     var removeTask = function(task){
12705         removeQueue.push(task);
12706         if(task.onStop){
12707             task.onStop();
12708         }
12709     };
12710
12711     var runTasks = function(){
12712         if(removeQueue.length > 0){
12713             for(var i = 0, len = removeQueue.length; i < len; i++){
12714                 tasks.remove(removeQueue[i]);
12715             }
12716             removeQueue = [];
12717             if(tasks.length < 1){
12718                 stopThread();
12719                 return;
12720             }
12721         }
12722         var now = new Date().getTime();
12723         for(var i = 0, len = tasks.length; i < len; ++i){
12724             var t = tasks[i];
12725             var itime = now - t.taskRunTime;
12726             if(t.interval <= itime){
12727                 var rt = t.run.apply(t.scope || t, t.args || [++t.taskRunCount]);
12728                 t.taskRunTime = now;
12729                 if(rt === false || t.taskRunCount === t.repeat){
12730                     removeTask(t);
12731                     return;
12732                 }
12733             }
12734             if(t.duration && t.duration <= (now - t.taskStartTime)){
12735                 removeTask(t);
12736             }
12737         }
12738     };
12739
12740     /**
12741      * Queues a new task.
12742      * @param {Object} task
12743      */
12744     this.start = function(task){
12745         tasks.push(task);
12746         task.taskStartTime = new Date().getTime();
12747         task.taskRunTime = 0;
12748         task.taskRunCount = 0;
12749         startThread();
12750         return task;
12751     };
12752
12753     this.stop = function(task){
12754         removeTask(task);
12755         return task;
12756     };
12757
12758     this.stopAll = function(){
12759         stopThread();
12760         for(var i = 0, len = tasks.length; i < len; i++){
12761             if(tasks[i].onStop){
12762                 tasks[i].onStop();
12763             }
12764         }
12765         tasks = [];
12766         removeQueue = [];
12767     };
12768 };
12769
12770 Roo.TaskMgr = new Roo.util.TaskRunner();/*
12771  * Based on:
12772  * Ext JS Library 1.1.1
12773  * Copyright(c) 2006-2007, Ext JS, LLC.
12774  *
12775  * Originally Released Under LGPL - original licence link has changed is not relivant.
12776  *
12777  * Fork - LGPL
12778  * <script type="text/javascript">
12779  */
12780
12781  
12782 /**
12783  * @class Roo.util.MixedCollection
12784  * @extends Roo.util.Observable
12785  * A Collection class that maintains both numeric indexes and keys and exposes events.
12786  * @constructor
12787  * @param {Boolean} allowFunctions True if the addAll function should add function references to the
12788  * collection (defaults to false)
12789  * @param {Function} keyFn A function that can accept an item of the type(s) stored in this MixedCollection
12790  * and return the key value for that item.  This is used when available to look up the key on items that
12791  * were passed without an explicit key parameter to a MixedCollection method.  Passing this parameter is
12792  * equivalent to providing an implementation for the {@link #getKey} method.
12793  */
12794 Roo.util.MixedCollection = function(allowFunctions, keyFn){
12795     this.items = [];
12796     this.map = {};
12797     this.keys = [];
12798     this.length = 0;
12799     this.addEvents({
12800         /**
12801          * @event clear
12802          * Fires when the collection is cleared.
12803          */
12804         "clear" : true,
12805         /**
12806          * @event add
12807          * Fires when an item is added to the collection.
12808          * @param {Number} index The index at which the item was added.
12809          * @param {Object} o The item added.
12810          * @param {String} key The key associated with the added item.
12811          */
12812         "add" : true,
12813         /**
12814          * @event replace
12815          * Fires when an item is replaced in the collection.
12816          * @param {String} key he key associated with the new added.
12817          * @param {Object} old The item being replaced.
12818          * @param {Object} new The new item.
12819          */
12820         "replace" : true,
12821         /**
12822          * @event remove
12823          * Fires when an item is removed from the collection.
12824          * @param {Object} o The item being removed.
12825          * @param {String} key (optional) The key associated with the removed item.
12826          */
12827         "remove" : true,
12828         "sort" : true
12829     });
12830     this.allowFunctions = allowFunctions === true;
12831     if(keyFn){
12832         this.getKey = keyFn;
12833     }
12834     Roo.util.MixedCollection.superclass.constructor.call(this);
12835 };
12836
12837 Roo.extend(Roo.util.MixedCollection, Roo.util.Observable, {
12838     allowFunctions : false,
12839     
12840 /**
12841  * Adds an item to the collection.
12842  * @param {String} key The key to associate with the item
12843  * @param {Object} o The item to add.
12844  * @return {Object} The item added.
12845  */
12846     add : function(key, o){
12847         if(arguments.length == 1){
12848             o = arguments[0];
12849             key = this.getKey(o);
12850         }
12851         if(typeof key == "undefined" || key === null){
12852             this.length++;
12853             this.items.push(o);
12854             this.keys.push(null);
12855         }else{
12856             var old = this.map[key];
12857             if(old){
12858                 return this.replace(key, o);
12859             }
12860             this.length++;
12861             this.items.push(o);
12862             this.map[key] = o;
12863             this.keys.push(key);
12864         }
12865         this.fireEvent("add", this.length-1, o, key);
12866         return o;
12867     },
12868        
12869 /**
12870   * MixedCollection has a generic way to fetch keys if you implement getKey.
12871 <pre><code>
12872 // normal way
12873 var mc = new Roo.util.MixedCollection();
12874 mc.add(someEl.dom.id, someEl);
12875 mc.add(otherEl.dom.id, otherEl);
12876 //and so on
12877
12878 // using getKey
12879 var mc = new Roo.util.MixedCollection();
12880 mc.getKey = function(el){
12881    return el.dom.id;
12882 };
12883 mc.add(someEl);
12884 mc.add(otherEl);
12885
12886 // or via the constructor
12887 var mc = new Roo.util.MixedCollection(false, function(el){
12888    return el.dom.id;
12889 });
12890 mc.add(someEl);
12891 mc.add(otherEl);
12892 </code></pre>
12893  * @param o {Object} The item for which to find the key.
12894  * @return {Object} The key for the passed item.
12895  */
12896     getKey : function(o){
12897          return o.id; 
12898     },
12899    
12900 /**
12901  * Replaces an item in the collection.
12902  * @param {String} key The key associated with the item to replace, or the item to replace.
12903  * @param o {Object} o (optional) If the first parameter passed was a key, the item to associate with that key.
12904  * @return {Object}  The new item.
12905  */
12906     replace : function(key, o){
12907         if(arguments.length == 1){
12908             o = arguments[0];
12909             key = this.getKey(o);
12910         }
12911         var old = this.item(key);
12912         if(typeof key == "undefined" || key === null || typeof old == "undefined"){
12913              return this.add(key, o);
12914         }
12915         var index = this.indexOfKey(key);
12916         this.items[index] = o;
12917         this.map[key] = o;
12918         this.fireEvent("replace", key, old, o);
12919         return o;
12920     },
12921    
12922 /**
12923  * Adds all elements of an Array or an Object to the collection.
12924  * @param {Object/Array} objs An Object containing properties which will be added to the collection, or
12925  * an Array of values, each of which are added to the collection.
12926  */
12927     addAll : function(objs){
12928         if(arguments.length > 1 || objs instanceof Array){
12929             var args = arguments.length > 1 ? arguments : objs;
12930             for(var i = 0, len = args.length; i < len; i++){
12931                 this.add(args[i]);
12932             }
12933         }else{
12934             for(var key in objs){
12935                 if(this.allowFunctions || typeof objs[key] != "function"){
12936                     this.add(key, objs[key]);
12937                 }
12938             }
12939         }
12940     },
12941    
12942 /**
12943  * Executes the specified function once for every item in the collection, passing each
12944  * item as the first and only parameter. returning false from the function will stop the iteration.
12945  * @param {Function} fn The function to execute for each item.
12946  * @param {Object} scope (optional) The scope in which to execute the function.
12947  */
12948     each : function(fn, scope){
12949         var items = [].concat(this.items); // each safe for removal
12950         for(var i = 0, len = items.length; i < len; i++){
12951             if(fn.call(scope || items[i], items[i], i, len) === false){
12952                 break;
12953             }
12954         }
12955     },
12956    
12957 /**
12958  * Executes the specified function once for every key in the collection, passing each
12959  * key, and its associated item as the first two parameters.
12960  * @param {Function} fn The function to execute for each item.
12961  * @param {Object} scope (optional) The scope in which to execute the function.
12962  */
12963     eachKey : function(fn, scope){
12964         for(var i = 0, len = this.keys.length; i < len; i++){
12965             fn.call(scope || window, this.keys[i], this.items[i], i, len);
12966         }
12967     },
12968    
12969 /**
12970  * Returns the first item in the collection which elicits a true return value from the
12971  * passed selection function.
12972  * @param {Function} fn The selection function to execute for each item.
12973  * @param {Object} scope (optional) The scope in which to execute the function.
12974  * @return {Object} The first item in the collection which returned true from the selection function.
12975  */
12976     find : function(fn, scope){
12977         for(var i = 0, len = this.items.length; i < len; i++){
12978             if(fn.call(scope || window, this.items[i], this.keys[i])){
12979                 return this.items[i];
12980             }
12981         }
12982         return null;
12983     },
12984    
12985 /**
12986  * Inserts an item at the specified index in the collection.
12987  * @param {Number} index The index to insert the item at.
12988  * @param {String} key The key to associate with the new item, or the item itself.
12989  * @param {Object} o  (optional) If the second parameter was a key, the new item.
12990  * @return {Object} The item inserted.
12991  */
12992     insert : function(index, key, o){
12993         if(arguments.length == 2){
12994             o = arguments[1];
12995             key = this.getKey(o);
12996         }
12997         if(index >= this.length){
12998             return this.add(key, o);
12999         }
13000         this.length++;
13001         this.items.splice(index, 0, o);
13002         if(typeof key != "undefined" && key != null){
13003             this.map[key] = o;
13004         }
13005         this.keys.splice(index, 0, key);
13006         this.fireEvent("add", index, o, key);
13007         return o;
13008     },
13009    
13010 /**
13011  * Removed an item from the collection.
13012  * @param {Object} o The item to remove.
13013  * @return {Object} The item removed.
13014  */
13015     remove : function(o){
13016         return this.removeAt(this.indexOf(o));
13017     },
13018    
13019 /**
13020  * Remove an item from a specified index in the collection.
13021  * @param {Number} index The index within the collection of the item to remove.
13022  */
13023     removeAt : function(index){
13024         if(index < this.length && index >= 0){
13025             this.length--;
13026             var o = this.items[index];
13027             this.items.splice(index, 1);
13028             var key = this.keys[index];
13029             if(typeof key != "undefined"){
13030                 delete this.map[key];
13031             }
13032             this.keys.splice(index, 1);
13033             this.fireEvent("remove", o, key);
13034         }
13035     },
13036    
13037 /**
13038  * Removed an item associated with the passed key fom the collection.
13039  * @param {String} key The key of the item to remove.
13040  */
13041     removeKey : function(key){
13042         return this.removeAt(this.indexOfKey(key));
13043     },
13044    
13045 /**
13046  * Returns the number of items in the collection.
13047  * @return {Number} the number of items in the collection.
13048  */
13049     getCount : function(){
13050         return this.length; 
13051     },
13052    
13053 /**
13054  * Returns index within the collection of the passed Object.
13055  * @param {Object} o The item to find the index of.
13056  * @return {Number} index of the item.
13057  */
13058     indexOf : function(o){
13059         if(!this.items.indexOf){
13060             for(var i = 0, len = this.items.length; i < len; i++){
13061                 if(this.items[i] == o) return i;
13062             }
13063             return -1;
13064         }else{
13065             return this.items.indexOf(o);
13066         }
13067     },
13068    
13069 /**
13070  * Returns index within the collection of the passed key.
13071  * @param {String} key The key to find the index of.
13072  * @return {Number} index of the key.
13073  */
13074     indexOfKey : function(key){
13075         if(!this.keys.indexOf){
13076             for(var i = 0, len = this.keys.length; i < len; i++){
13077                 if(this.keys[i] == key) return i;
13078             }
13079             return -1;
13080         }else{
13081             return this.keys.indexOf(key);
13082         }
13083     },
13084    
13085 /**
13086  * Returns the item associated with the passed key OR index. Key has priority over index.
13087  * @param {String/Number} key The key or index of the item.
13088  * @return {Object} The item associated with the passed key.
13089  */
13090     item : function(key){
13091         var item = typeof this.map[key] != "undefined" ? this.map[key] : this.items[key];
13092         return typeof item != 'function' || this.allowFunctions ? item : null; // for prototype!
13093     },
13094     
13095 /**
13096  * Returns the item at the specified index.
13097  * @param {Number} index The index of the item.
13098  * @return {Object}
13099  */
13100     itemAt : function(index){
13101         return this.items[index];
13102     },
13103     
13104 /**
13105  * Returns the item associated with the passed key.
13106  * @param {String/Number} key The key of the item.
13107  * @return {Object} The item associated with the passed key.
13108  */
13109     key : function(key){
13110         return this.map[key];
13111     },
13112    
13113 /**
13114  * Returns true if the collection contains the passed Object as an item.
13115  * @param {Object} o  The Object to look for in the collection.
13116  * @return {Boolean} True if the collection contains the Object as an item.
13117  */
13118     contains : function(o){
13119         return this.indexOf(o) != -1;
13120     },
13121    
13122 /**
13123  * Returns true if the collection contains the passed Object as a key.
13124  * @param {String} key The key to look for in the collection.
13125  * @return {Boolean} True if the collection contains the Object as a key.
13126  */
13127     containsKey : function(key){
13128         return typeof this.map[key] != "undefined";
13129     },
13130    
13131 /**
13132  * Removes all items from the collection.
13133  */
13134     clear : function(){
13135         this.length = 0;
13136         this.items = [];
13137         this.keys = [];
13138         this.map = {};
13139         this.fireEvent("clear");
13140     },
13141    
13142 /**
13143  * Returns the first item in the collection.
13144  * @return {Object} the first item in the collection..
13145  */
13146     first : function(){
13147         return this.items[0]; 
13148     },
13149    
13150 /**
13151  * Returns the last item in the collection.
13152  * @return {Object} the last item in the collection..
13153  */
13154     last : function(){
13155         return this.items[this.length-1];   
13156     },
13157     
13158     _sort : function(property, dir, fn){
13159         var dsc = String(dir).toUpperCase() == "DESC" ? -1 : 1;
13160         fn = fn || function(a, b){
13161             return a-b;
13162         };
13163         var c = [], k = this.keys, items = this.items;
13164         for(var i = 0, len = items.length; i < len; i++){
13165             c[c.length] = {key: k[i], value: items[i], index: i};
13166         }
13167         c.sort(function(a, b){
13168             var v = fn(a[property], b[property]) * dsc;
13169             if(v == 0){
13170                 v = (a.index < b.index ? -1 : 1);
13171             }
13172             return v;
13173         });
13174         for(var i = 0, len = c.length; i < len; i++){
13175             items[i] = c[i].value;
13176             k[i] = c[i].key;
13177         }
13178         this.fireEvent("sort", this);
13179     },
13180     
13181     /**
13182      * Sorts this collection with the passed comparison function
13183      * @param {String} direction (optional) "ASC" or "DESC"
13184      * @param {Function} fn (optional) comparison function
13185      */
13186     sort : function(dir, fn){
13187         this._sort("value", dir, fn);
13188     },
13189     
13190     /**
13191      * Sorts this collection by keys
13192      * @param {String} direction (optional) "ASC" or "DESC"
13193      * @param {Function} fn (optional) a comparison function (defaults to case insensitive string)
13194      */
13195     keySort : function(dir, fn){
13196         this._sort("key", dir, fn || function(a, b){
13197             return String(a).toUpperCase()-String(b).toUpperCase();
13198         });
13199     },
13200     
13201     /**
13202      * Returns a range of items in this collection
13203      * @param {Number} startIndex (optional) defaults to 0
13204      * @param {Number} endIndex (optional) default to the last item
13205      * @return {Array} An array of items
13206      */
13207     getRange : function(start, end){
13208         var items = this.items;
13209         if(items.length < 1){
13210             return [];
13211         }
13212         start = start || 0;
13213         end = Math.min(typeof end == "undefined" ? this.length-1 : end, this.length-1);
13214         var r = [];
13215         if(start <= end){
13216             for(var i = start; i <= end; i++) {
13217                     r[r.length] = items[i];
13218             }
13219         }else{
13220             for(var i = start; i >= end; i--) {
13221                     r[r.length] = items[i];
13222             }
13223         }
13224         return r;
13225     },
13226         
13227     /**
13228      * Filter the <i>objects</i> in this collection by a specific property. 
13229      * Returns a new collection that has been filtered.
13230      * @param {String} property A property on your objects
13231      * @param {String/RegExp} value Either string that the property values 
13232      * should start with or a RegExp to test against the property
13233      * @return {MixedCollection} The new filtered collection
13234      */
13235     filter : function(property, value){
13236         if(!value.exec){ // not a regex
13237             value = String(value);
13238             if(value.length == 0){
13239                 return this.clone();
13240             }
13241             value = new RegExp("^" + Roo.escapeRe(value), "i");
13242         }
13243         return this.filterBy(function(o){
13244             return o && value.test(o[property]);
13245         });
13246         },
13247     
13248     /**
13249      * Filter by a function. * Returns a new collection that has been filtered.
13250      * The passed function will be called with each 
13251      * object in the collection. If the function returns true, the value is included 
13252      * otherwise it is filtered.
13253      * @param {Function} fn The function to be called, it will receive the args o (the object), k (the key)
13254      * @param {Object} scope (optional) The scope of the function (defaults to this) 
13255      * @return {MixedCollection} The new filtered collection
13256      */
13257     filterBy : function(fn, scope){
13258         var r = new Roo.util.MixedCollection();
13259         r.getKey = this.getKey;
13260         var k = this.keys, it = this.items;
13261         for(var i = 0, len = it.length; i < len; i++){
13262             if(fn.call(scope||this, it[i], k[i])){
13263                                 r.add(k[i], it[i]);
13264                         }
13265         }
13266         return r;
13267     },
13268     
13269     /**
13270      * Creates a duplicate of this collection
13271      * @return {MixedCollection}
13272      */
13273     clone : function(){
13274         var r = new Roo.util.MixedCollection();
13275         var k = this.keys, it = this.items;
13276         for(var i = 0, len = it.length; i < len; i++){
13277             r.add(k[i], it[i]);
13278         }
13279         r.getKey = this.getKey;
13280         return r;
13281     }
13282 });
13283 /**
13284  * Returns the item associated with the passed key or index.
13285  * @method
13286  * @param {String/Number} key The key or index of the item.
13287  * @return {Object} The item associated with the passed key.
13288  */
13289 Roo.util.MixedCollection.prototype.get = Roo.util.MixedCollection.prototype.item;/*
13290  * Based on:
13291  * Ext JS Library 1.1.1
13292  * Copyright(c) 2006-2007, Ext JS, LLC.
13293  *
13294  * Originally Released Under LGPL - original licence link has changed is not relivant.
13295  *
13296  * Fork - LGPL
13297  * <script type="text/javascript">
13298  */
13299 /**
13300  * @class Roo.util.JSON
13301  * Modified version of Douglas Crockford"s json.js that doesn"t
13302  * mess with the Object prototype 
13303  * http://www.json.org/js.html
13304  * @singleton
13305  */
13306 Roo.util.JSON = new (function(){
13307     var useHasOwn = {}.hasOwnProperty ? true : false;
13308     
13309     // crashes Safari in some instances
13310     //var validRE = /^("(\\.|[^"\\\n\r])*?"|[,:{}\[\]0-9.\-+Eaeflnr-u \n\r\t])+?$/;
13311     
13312     var pad = function(n) {
13313         return n < 10 ? "0" + n : n;
13314     };
13315     
13316     var m = {
13317         "\b": '\\b',
13318         "\t": '\\t',
13319         "\n": '\\n',
13320         "\f": '\\f',
13321         "\r": '\\r',
13322         '"' : '\\"',
13323         "\\": '\\\\'
13324     };
13325
13326     var encodeString = function(s){
13327         if (/["\\\x00-\x1f]/.test(s)) {
13328             return '"' + s.replace(/([\x00-\x1f\\"])/g, function(a, b) {
13329                 var c = m[b];
13330                 if(c){
13331                     return c;
13332                 }
13333                 c = b.charCodeAt();
13334                 return "\\u00" +
13335                     Math.floor(c / 16).toString(16) +
13336                     (c % 16).toString(16);
13337             }) + '"';
13338         }
13339         return '"' + s + '"';
13340     };
13341     
13342     var encodeArray = function(o){
13343         var a = ["["], b, i, l = o.length, v;
13344             for (i = 0; i < l; i += 1) {
13345                 v = o[i];
13346                 switch (typeof v) {
13347                     case "undefined":
13348                     case "function":
13349                     case "unknown":
13350                         break;
13351                     default:
13352                         if (b) {
13353                             a.push(',');
13354                         }
13355                         a.push(v === null ? "null" : Roo.util.JSON.encode(v));
13356                         b = true;
13357                 }
13358             }
13359             a.push("]");
13360             return a.join("");
13361     };
13362     
13363     var encodeDate = function(o){
13364         return '"' + o.getFullYear() + "-" +
13365                 pad(o.getMonth() + 1) + "-" +
13366                 pad(o.getDate()) + "T" +
13367                 pad(o.getHours()) + ":" +
13368                 pad(o.getMinutes()) + ":" +
13369                 pad(o.getSeconds()) + '"';
13370     };
13371     
13372     /**
13373      * Encodes an Object, Array or other value
13374      * @param {Mixed} o The variable to encode
13375      * @return {String} The JSON string
13376      */
13377     this.encode = function(o)
13378     {
13379         // should this be extended to fully wrap stringify..
13380         
13381         if(typeof o == "undefined" || o === null){
13382             return "null";
13383         }else if(o instanceof Array){
13384             return encodeArray(o);
13385         }else if(o instanceof Date){
13386             return encodeDate(o);
13387         }else if(typeof o == "string"){
13388             return encodeString(o);
13389         }else if(typeof o == "number"){
13390             return isFinite(o) ? String(o) : "null";
13391         }else if(typeof o == "boolean"){
13392             return String(o);
13393         }else {
13394             var a = ["{"], b, i, v;
13395             for (i in o) {
13396                 if(!useHasOwn || o.hasOwnProperty(i)) {
13397                     v = o[i];
13398                     switch (typeof v) {
13399                     case "undefined":
13400                     case "function":
13401                     case "unknown":
13402                         break;
13403                     default:
13404                         if(b){
13405                             a.push(',');
13406                         }
13407                         a.push(this.encode(i), ":",
13408                                 v === null ? "null" : this.encode(v));
13409                         b = true;
13410                     }
13411                 }
13412             }
13413             a.push("}");
13414             return a.join("");
13415         }
13416     };
13417     
13418     /**
13419      * Decodes (parses) a JSON string to an object. If the JSON is invalid, this function throws a SyntaxError.
13420      * @param {String} json The JSON string
13421      * @return {Object} The resulting object
13422      */
13423     this.decode = function(json){
13424         
13425         return  /** eval:var:json */ eval("(" + json + ')');
13426     };
13427 })();
13428 /** 
13429  * Shorthand for {@link Roo.util.JSON#encode}
13430  * @member Roo encode 
13431  * @method */
13432 Roo.encode = typeof(JSON) != 'undefined' && JSON.stringify ? JSON.stringify : Roo.util.JSON.encode;
13433 /** 
13434  * Shorthand for {@link Roo.util.JSON#decode}
13435  * @member Roo decode 
13436  * @method */
13437 Roo.decode = typeof(JSON) != 'undefined' && JSON.parse ? JSON.parse : Roo.util.JSON.decode;
13438 /*
13439  * Based on:
13440  * Ext JS Library 1.1.1
13441  * Copyright(c) 2006-2007, Ext JS, LLC.
13442  *
13443  * Originally Released Under LGPL - original licence link has changed is not relivant.
13444  *
13445  * Fork - LGPL
13446  * <script type="text/javascript">
13447  */
13448  
13449 /**
13450  * @class Roo.util.Format
13451  * Reusable data formatting functions
13452  * @singleton
13453  */
13454 Roo.util.Format = function(){
13455     var trimRe = /^\s+|\s+$/g;
13456     return {
13457         /**
13458          * Truncate a string and add an ellipsis ('...') to the end if it exceeds the specified length
13459          * @param {String} value The string to truncate
13460          * @param {Number} length The maximum length to allow before truncating
13461          * @return {String} The converted text
13462          */
13463         ellipsis : function(value, len){
13464             if(value && value.length > len){
13465                 return value.substr(0, len-3)+"...";
13466             }
13467             return value;
13468         },
13469
13470         /**
13471          * Checks a reference and converts it to empty string if it is undefined
13472          * @param {Mixed} value Reference to check
13473          * @return {Mixed} Empty string if converted, otherwise the original value
13474          */
13475         undef : function(value){
13476             return typeof value != "undefined" ? value : "";
13477         },
13478
13479         /**
13480          * Convert certain characters (&, <, >, and ') to their HTML character equivalents for literal display in web pages.
13481          * @param {String} value The string to encode
13482          * @return {String} The encoded text
13483          */
13484         htmlEncode : function(value){
13485             return !value ? value : String(value).replace(/&/g, "&amp;").replace(/>/g, "&gt;").replace(/</g, "&lt;").replace(/"/g, "&quot;");
13486         },
13487
13488         /**
13489          * Convert certain characters (&, <, >, and ') from their HTML character equivalents.
13490          * @param {String} value The string to decode
13491          * @return {String} The decoded text
13492          */
13493         htmlDecode : function(value){
13494             return !value ? value : String(value).replace(/&amp;/g, "&").replace(/&gt;/g, ">").replace(/&lt;/g, "<").replace(/&quot;/g, '"');
13495         },
13496
13497         /**
13498          * Trims any whitespace from either side of a string
13499          * @param {String} value The text to trim
13500          * @return {String} The trimmed text
13501          */
13502         trim : function(value){
13503             return String(value).replace(trimRe, "");
13504         },
13505
13506         /**
13507          * Returns a substring from within an original string
13508          * @param {String} value The original text
13509          * @param {Number} start The start index of the substring
13510          * @param {Number} length The length of the substring
13511          * @return {String} The substring
13512          */
13513         substr : function(value, start, length){
13514             return String(value).substr(start, length);
13515         },
13516
13517         /**
13518          * Converts a string to all lower case letters
13519          * @param {String} value The text to convert
13520          * @return {String} The converted text
13521          */
13522         lowercase : function(value){
13523             return String(value).toLowerCase();
13524         },
13525
13526         /**
13527          * Converts a string to all upper case letters
13528          * @param {String} value The text to convert
13529          * @return {String} The converted text
13530          */
13531         uppercase : function(value){
13532             return String(value).toUpperCase();
13533         },
13534
13535         /**
13536          * Converts the first character only of a string to upper case
13537          * @param {String} value The text to convert
13538          * @return {String} The converted text
13539          */
13540         capitalize : function(value){
13541             return !value ? value : value.charAt(0).toUpperCase() + value.substr(1).toLowerCase();
13542         },
13543
13544         // private
13545         call : function(value, fn){
13546             if(arguments.length > 2){
13547                 var args = Array.prototype.slice.call(arguments, 2);
13548                 args.unshift(value);
13549                  
13550                 return /** eval:var:value */  eval(fn).apply(window, args);
13551             }else{
13552                 /** eval:var:value */
13553                 return /** eval:var:value */ eval(fn).call(window, value);
13554             }
13555         },
13556
13557        
13558         /**
13559          * safer version of Math.toFixed..??/
13560          * @param {Number/String} value The numeric value to format
13561          * @param {Number/String} value Decimal places 
13562          * @return {String} The formatted currency string
13563          */
13564         toFixed : function(v, n)
13565         {
13566             // why not use to fixed - precision is buggered???
13567             if (!n) {
13568                 return Math.round(v-0);
13569             }
13570             var fact = Math.pow(10,n+1);
13571             v = (Math.round((v-0)*fact))/fact;
13572             var z = (''+fact).substring(2);
13573             if (v == Math.floor(v)) {
13574                 return Math.floor(v) + '.' + z;
13575             }
13576             
13577             // now just padd decimals..
13578             var ps = String(v).split('.');
13579             var fd = (ps[1] + z);
13580             var r = fd.substring(0,n); 
13581             var rm = fd.substring(n); 
13582             if (rm < 5) {
13583                 return ps[0] + '.' + r;
13584             }
13585             r*=1; // turn it into a number;
13586             r++;
13587             if (String(r).length != n) {
13588                 ps[0]*=1;
13589                 ps[0]++;
13590                 r = String(r).substring(1); // chop the end off.
13591             }
13592             
13593             return ps[0] + '.' + r;
13594              
13595         },
13596         
13597         /**
13598          * Format a number as US currency
13599          * @param {Number/String} value The numeric value to format
13600          * @return {String} The formatted currency string
13601          */
13602         usMoney : function(v){
13603             return '$' + Roo.util.Format.number(v);
13604         },
13605         
13606         /**
13607          * Format a number
13608          * eventually this should probably emulate php's number_format
13609          * @param {Number/String} value The numeric value to format
13610          * @param {Number} decimals number of decimal places
13611          * @return {String} The formatted currency string
13612          */
13613         number : function(v,decimals)
13614         {
13615             // multiply and round.
13616             decimals = typeof(decimals) == 'undefined' ? 2 : decimals;
13617             var mul = Math.pow(10, decimals);
13618             var zero = String(mul).substring(1);
13619             v = (Math.round((v-0)*mul))/mul;
13620             
13621             // if it's '0' number.. then
13622             
13623             //v = (v == Math.floor(v)) ? v + "." + zero : ((v*10 == Math.floor(v*10)) ? v + "0" : v);
13624             v = String(v);
13625             var ps = v.split('.');
13626             var whole = ps[0];
13627             
13628             
13629             var r = /(\d+)(\d{3})/;
13630             // add comma's
13631             while (r.test(whole)) {
13632                 whole = whole.replace(r, '$1' + ',' + '$2');
13633             }
13634             
13635             
13636             var sub = ps[1] ?
13637                     // has decimals..
13638                     (decimals ?  ('.'+ ps[1] + zero.substring(ps[1].length)) : '') :
13639                     // does not have decimals
13640                     (decimals ? ('.' + zero) : '');
13641             
13642             
13643             return whole + sub ;
13644         },
13645         
13646         /**
13647          * Parse a value into a formatted date using the specified format pattern.
13648          * @param {Mixed} value The value to format
13649          * @param {String} format (optional) Any valid date format string (defaults to 'm/d/Y')
13650          * @return {String} The formatted date string
13651          */
13652         date : function(v, format){
13653             if(!v){
13654                 return "";
13655             }
13656             if(!(v instanceof Date)){
13657                 v = new Date(Date.parse(v));
13658             }
13659             return v.dateFormat(format || Roo.util.Format.defaults.date);
13660         },
13661
13662         /**
13663          * Returns a date rendering function that can be reused to apply a date format multiple times efficiently
13664          * @param {String} format Any valid date format string
13665          * @return {Function} The date formatting function
13666          */
13667         dateRenderer : function(format){
13668             return function(v){
13669                 return Roo.util.Format.date(v, format);  
13670             };
13671         },
13672
13673         // private
13674         stripTagsRE : /<\/?[^>]+>/gi,
13675         
13676         /**
13677          * Strips all HTML tags
13678          * @param {Mixed} value The text from which to strip tags
13679          * @return {String} The stripped text
13680          */
13681         stripTags : function(v){
13682             return !v ? v : String(v).replace(this.stripTagsRE, "");
13683         }
13684     };
13685 }();
13686 Roo.util.Format.defaults = {
13687     date : 'd/M/Y'
13688 };/*
13689  * Based on:
13690  * Ext JS Library 1.1.1
13691  * Copyright(c) 2006-2007, Ext JS, LLC.
13692  *
13693  * Originally Released Under LGPL - original licence link has changed is not relivant.
13694  *
13695  * Fork - LGPL
13696  * <script type="text/javascript">
13697  */
13698
13699
13700  
13701
13702 /**
13703  * @class Roo.MasterTemplate
13704  * @extends Roo.Template
13705  * Provides a template that can have child templates. The syntax is:
13706 <pre><code>
13707 var t = new Roo.MasterTemplate(
13708         '&lt;select name="{name}"&gt;',
13709                 '&lt;tpl name="options"&gt;&lt;option value="{value:trim}"&gt;{text:ellipsis(10)}&lt;/option&gt;&lt;/tpl&gt;',
13710         '&lt;/select&gt;'
13711 );
13712 t.add('options', {value: 'foo', text: 'bar'});
13713 // or you can add multiple child elements in one shot
13714 t.addAll('options', [
13715     {value: 'foo', text: 'bar'},
13716     {value: 'foo2', text: 'bar2'},
13717     {value: 'foo3', text: 'bar3'}
13718 ]);
13719 // then append, applying the master template values
13720 t.append('my-form', {name: 'my-select'});
13721 </code></pre>
13722 * A name attribute for the child template is not required if you have only one child
13723 * template or you want to refer to them by index.
13724  */
13725 Roo.MasterTemplate = function(){
13726     Roo.MasterTemplate.superclass.constructor.apply(this, arguments);
13727     this.originalHtml = this.html;
13728     var st = {};
13729     var m, re = this.subTemplateRe;
13730     re.lastIndex = 0;
13731     var subIndex = 0;
13732     while(m = re.exec(this.html)){
13733         var name = m[1], content = m[2];
13734         st[subIndex] = {
13735             name: name,
13736             index: subIndex,
13737             buffer: [],
13738             tpl : new Roo.Template(content)
13739         };
13740         if(name){
13741             st[name] = st[subIndex];
13742         }
13743         st[subIndex].tpl.compile();
13744         st[subIndex].tpl.call = this.call.createDelegate(this);
13745         subIndex++;
13746     }
13747     this.subCount = subIndex;
13748     this.subs = st;
13749 };
13750 Roo.extend(Roo.MasterTemplate, Roo.Template, {
13751     /**
13752     * The regular expression used to match sub templates
13753     * @type RegExp
13754     * @property
13755     */
13756     subTemplateRe : /<tpl(?:\sname="([\w-]+)")?>((?:.|\n)*?)<\/tpl>/gi,
13757
13758     /**
13759      * Applies the passed values to a child template.
13760      * @param {String/Number} name (optional) The name or index of the child template
13761      * @param {Array/Object} values The values to be applied to the template
13762      * @return {MasterTemplate} this
13763      */
13764      add : function(name, values){
13765         if(arguments.length == 1){
13766             values = arguments[0];
13767             name = 0;
13768         }
13769         var s = this.subs[name];
13770         s.buffer[s.buffer.length] = s.tpl.apply(values);
13771         return this;
13772     },
13773
13774     /**
13775      * Applies all the passed values to a child template.
13776      * @param {String/Number} name (optional) The name or index of the child template
13777      * @param {Array} values The values to be applied to the template, this should be an array of objects.
13778      * @param {Boolean} reset (optional) True to reset the template first
13779      * @return {MasterTemplate} this
13780      */
13781     fill : function(name, values, reset){
13782         var a = arguments;
13783         if(a.length == 1 || (a.length == 2 && typeof a[1] == "boolean")){
13784             values = a[0];
13785             name = 0;
13786             reset = a[1];
13787         }
13788         if(reset){
13789             this.reset();
13790         }
13791         for(var i = 0, len = values.length; i < len; i++){
13792             this.add(name, values[i]);
13793         }
13794         return this;
13795     },
13796
13797     /**
13798      * Resets the template for reuse
13799      * @return {MasterTemplate} this
13800      */
13801      reset : function(){
13802         var s = this.subs;
13803         for(var i = 0; i < this.subCount; i++){
13804             s[i].buffer = [];
13805         }
13806         return this;
13807     },
13808
13809     applyTemplate : function(values){
13810         var s = this.subs;
13811         var replaceIndex = -1;
13812         this.html = this.originalHtml.replace(this.subTemplateRe, function(m, name){
13813             return s[++replaceIndex].buffer.join("");
13814         });
13815         return Roo.MasterTemplate.superclass.applyTemplate.call(this, values);
13816     },
13817
13818     apply : function(){
13819         return this.applyTemplate.apply(this, arguments);
13820     },
13821
13822     compile : function(){return this;}
13823 });
13824
13825 /**
13826  * Alias for fill().
13827  * @method
13828  */
13829 Roo.MasterTemplate.prototype.addAll = Roo.MasterTemplate.prototype.fill;
13830  /**
13831  * Creates a template from the passed element's value (display:none textarea, preferred) or innerHTML. e.g.
13832  * var tpl = Roo.MasterTemplate.from('element-id');
13833  * @param {String/HTMLElement} el
13834  * @param {Object} config
13835  * @static
13836  */
13837 Roo.MasterTemplate.from = function(el, config){
13838     el = Roo.getDom(el);
13839     return new Roo.MasterTemplate(el.value || el.innerHTML, config || '');
13840 };/*
13841  * Based on:
13842  * Ext JS Library 1.1.1
13843  * Copyright(c) 2006-2007, Ext JS, LLC.
13844  *
13845  * Originally Released Under LGPL - original licence link has changed is not relivant.
13846  *
13847  * Fork - LGPL
13848  * <script type="text/javascript">
13849  */
13850
13851  
13852 /**
13853  * @class Roo.util.CSS
13854  * Utility class for manipulating CSS rules
13855  * @singleton
13856  */
13857 Roo.util.CSS = function(){
13858         var rules = null;
13859         var doc = document;
13860
13861     var camelRe = /(-[a-z])/gi;
13862     var camelFn = function(m, a){ return a.charAt(1).toUpperCase(); };
13863
13864    return {
13865    /**
13866     * Very simple dynamic creation of stylesheets from a text blob of rules.  The text will wrapped in a style
13867     * tag and appended to the HEAD of the document.
13868     * @param {String|Object} cssText The text containing the css rules
13869     * @param {String} id An id to add to the stylesheet for later removal
13870     * @return {StyleSheet}
13871     */
13872     createStyleSheet : function(cssText, id){
13873         var ss;
13874         var head = doc.getElementsByTagName("head")[0];
13875         var nrules = doc.createElement("style");
13876         nrules.setAttribute("type", "text/css");
13877         if(id){
13878             nrules.setAttribute("id", id);
13879         }
13880         if (typeof(cssText) != 'string') {
13881             // support object maps..
13882             // not sure if this a good idea.. 
13883             // perhaps it should be merged with the general css handling
13884             // and handle js style props.
13885             var cssTextNew = [];
13886             for(var n in cssText) {
13887                 var citems = [];
13888                 for(var k in cssText[n]) {
13889                     citems.push( k + ' : ' +cssText[n][k] + ';' );
13890                 }
13891                 cssTextNew.push( n + ' { ' + citems.join(' ') + '} ');
13892                 
13893             }
13894             cssText = cssTextNew.join("\n");
13895             
13896         }
13897        
13898        
13899        if(Roo.isIE){
13900            head.appendChild(nrules);
13901            ss = nrules.styleSheet;
13902            ss.cssText = cssText;
13903        }else{
13904            try{
13905                 nrules.appendChild(doc.createTextNode(cssText));
13906            }catch(e){
13907                nrules.cssText = cssText; 
13908            }
13909            head.appendChild(nrules);
13910            ss = nrules.styleSheet ? nrules.styleSheet : (nrules.sheet || doc.styleSheets[doc.styleSheets.length-1]);
13911        }
13912        this.cacheStyleSheet(ss);
13913        return ss;
13914    },
13915
13916    /**
13917     * Removes a style or link tag by id
13918     * @param {String} id The id of the tag
13919     */
13920    removeStyleSheet : function(id){
13921        var existing = doc.getElementById(id);
13922        if(existing){
13923            existing.parentNode.removeChild(existing);
13924        }
13925    },
13926
13927    /**
13928     * Dynamically swaps an existing stylesheet reference for a new one
13929     * @param {String} id The id of an existing link tag to remove
13930     * @param {String} url The href of the new stylesheet to include
13931     */
13932    swapStyleSheet : function(id, url){
13933        this.removeStyleSheet(id);
13934        var ss = doc.createElement("link");
13935        ss.setAttribute("rel", "stylesheet");
13936        ss.setAttribute("type", "text/css");
13937        ss.setAttribute("id", id);
13938        ss.setAttribute("href", url);
13939        doc.getElementsByTagName("head")[0].appendChild(ss);
13940    },
13941    
13942    /**
13943     * Refresh the rule cache if you have dynamically added stylesheets
13944     * @return {Object} An object (hash) of rules indexed by selector
13945     */
13946    refreshCache : function(){
13947        return this.getRules(true);
13948    },
13949
13950    // private
13951    cacheStyleSheet : function(stylesheet){
13952        if(!rules){
13953            rules = {};
13954        }
13955        try{// try catch for cross domain access issue
13956            var ssRules = stylesheet.cssRules || stylesheet.rules;
13957            for(var j = ssRules.length-1; j >= 0; --j){
13958                rules[ssRules[j].selectorText] = ssRules[j];
13959            }
13960        }catch(e){}
13961    },
13962    
13963    /**
13964     * Gets all css rules for the document
13965     * @param {Boolean} refreshCache true to refresh the internal cache
13966     * @return {Object} An object (hash) of rules indexed by selector
13967     */
13968    getRules : function(refreshCache){
13969                 if(rules == null || refreshCache){
13970                         rules = {};
13971                         var ds = doc.styleSheets;
13972                         for(var i =0, len = ds.length; i < len; i++){
13973                             try{
13974                         this.cacheStyleSheet(ds[i]);
13975                     }catch(e){} 
13976                 }
13977                 }
13978                 return rules;
13979         },
13980         
13981         /**
13982     * Gets an an individual CSS rule by selector(s)
13983     * @param {String/Array} selector The CSS selector or an array of selectors to try. The first selector that is found is returned.
13984     * @param {Boolean} refreshCache true to refresh the internal cache if you have recently updated any rules or added styles dynamically
13985     * @return {CSSRule} The CSS rule or null if one is not found
13986     */
13987    getRule : function(selector, refreshCache){
13988                 var rs = this.getRules(refreshCache);
13989                 if(!(selector instanceof Array)){
13990                     return rs[selector];
13991                 }
13992                 for(var i = 0; i < selector.length; i++){
13993                         if(rs[selector[i]]){
13994                                 return rs[selector[i]];
13995                         }
13996                 }
13997                 return null;
13998         },
13999         
14000         
14001         /**
14002     * Updates a rule property
14003     * @param {String/Array} selector If it's an array it tries each selector until it finds one. Stops immediately once one is found.
14004     * @param {String} property The css property
14005     * @param {String} value The new value for the property
14006     * @return {Boolean} true If a rule was found and updated
14007     */
14008    updateRule : function(selector, property, value){
14009                 if(!(selector instanceof Array)){
14010                         var rule = this.getRule(selector);
14011                         if(rule){
14012                                 rule.style[property.replace(camelRe, camelFn)] = value;
14013                                 return true;
14014                         }
14015                 }else{
14016                         for(var i = 0; i < selector.length; i++){
14017                                 if(this.updateRule(selector[i], property, value)){
14018                                         return true;
14019                                 }
14020                         }
14021                 }
14022                 return false;
14023         }
14024    };   
14025 }();/*
14026  * Based on:
14027  * Ext JS Library 1.1.1
14028  * Copyright(c) 2006-2007, Ext JS, LLC.
14029  *
14030  * Originally Released Under LGPL - original licence link has changed is not relivant.
14031  *
14032  * Fork - LGPL
14033  * <script type="text/javascript">
14034  */
14035
14036  
14037
14038 /**
14039  * @class Roo.util.ClickRepeater
14040  * @extends Roo.util.Observable
14041  * 
14042  * A wrapper class which can be applied to any element. Fires a "click" event while the
14043  * mouse is pressed. The interval between firings may be specified in the config but
14044  * defaults to 10 milliseconds.
14045  * 
14046  * Optionally, a CSS class may be applied to the element during the time it is pressed.
14047  * 
14048  * @cfg {String/HTMLElement/Element} el The element to act as a button.
14049  * @cfg {Number} delay The initial delay before the repeating event begins firing.
14050  * Similar to an autorepeat key delay.
14051  * @cfg {Number} interval The interval between firings of the "click" event. Default 10 ms.
14052  * @cfg {String} pressClass A CSS class name to be applied to the element while pressed.
14053  * @cfg {Boolean} accelerate True if autorepeating should start slowly and accelerate.
14054  *           "interval" and "delay" are ignored. "immediate" is honored.
14055  * @cfg {Boolean} preventDefault True to prevent the default click event
14056  * @cfg {Boolean} stopDefault True to stop the default click event
14057  * 
14058  * @history
14059  *     2007-02-02 jvs Original code contributed by Nige "Animal" White
14060  *     2007-02-02 jvs Renamed to ClickRepeater
14061  *   2007-02-03 jvs Modifications for FF Mac and Safari 
14062  *
14063  *  @constructor
14064  * @param {String/HTMLElement/Element} el The element to listen on
14065  * @param {Object} config
14066  **/
14067 Roo.util.ClickRepeater = function(el, config)
14068 {
14069     this.el = Roo.get(el);
14070     this.el.unselectable();
14071
14072     Roo.apply(this, config);
14073
14074     this.addEvents({
14075     /**
14076      * @event mousedown
14077      * Fires when the mouse button is depressed.
14078      * @param {Roo.util.ClickRepeater} this
14079      */
14080         "mousedown" : true,
14081     /**
14082      * @event click
14083      * Fires on a specified interval during the time the element is pressed.
14084      * @param {Roo.util.ClickRepeater} this
14085      */
14086         "click" : true,
14087     /**
14088      * @event mouseup
14089      * Fires when the mouse key is released.
14090      * @param {Roo.util.ClickRepeater} this
14091      */
14092         "mouseup" : true
14093     });
14094
14095     this.el.on("mousedown", this.handleMouseDown, this);
14096     if(this.preventDefault || this.stopDefault){
14097         this.el.on("click", function(e){
14098             if(this.preventDefault){
14099                 e.preventDefault();
14100             }
14101             if(this.stopDefault){
14102                 e.stopEvent();
14103             }
14104         }, this);
14105     }
14106
14107     // allow inline handler
14108     if(this.handler){
14109         this.on("click", this.handler,  this.scope || this);
14110     }
14111
14112     Roo.util.ClickRepeater.superclass.constructor.call(this);
14113 };
14114
14115 Roo.extend(Roo.util.ClickRepeater, Roo.util.Observable, {
14116     interval : 20,
14117     delay: 250,
14118     preventDefault : true,
14119     stopDefault : false,
14120     timer : 0,
14121
14122     // private
14123     handleMouseDown : function(){
14124         clearTimeout(this.timer);
14125         this.el.blur();
14126         if(this.pressClass){
14127             this.el.addClass(this.pressClass);
14128         }
14129         this.mousedownTime = new Date();
14130
14131         Roo.get(document).on("mouseup", this.handleMouseUp, this);
14132         this.el.on("mouseout", this.handleMouseOut, this);
14133
14134         this.fireEvent("mousedown", this);
14135         this.fireEvent("click", this);
14136         
14137         this.timer = this.click.defer(this.delay || this.interval, this);
14138     },
14139
14140     // private
14141     click : function(){
14142         this.fireEvent("click", this);
14143         this.timer = this.click.defer(this.getInterval(), this);
14144     },
14145
14146     // private
14147     getInterval: function(){
14148         if(!this.accelerate){
14149             return this.interval;
14150         }
14151         var pressTime = this.mousedownTime.getElapsed();
14152         if(pressTime < 500){
14153             return 400;
14154         }else if(pressTime < 1700){
14155             return 320;
14156         }else if(pressTime < 2600){
14157             return 250;
14158         }else if(pressTime < 3500){
14159             return 180;
14160         }else if(pressTime < 4400){
14161             return 140;
14162         }else if(pressTime < 5300){
14163             return 80;
14164         }else if(pressTime < 6200){
14165             return 50;
14166         }else{
14167             return 10;
14168         }
14169     },
14170
14171     // private
14172     handleMouseOut : function(){
14173         clearTimeout(this.timer);
14174         if(this.pressClass){
14175             this.el.removeClass(this.pressClass);
14176         }
14177         this.el.on("mouseover", this.handleMouseReturn, this);
14178     },
14179
14180     // private
14181     handleMouseReturn : function(){
14182         this.el.un("mouseover", this.handleMouseReturn);
14183         if(this.pressClass){
14184             this.el.addClass(this.pressClass);
14185         }
14186         this.click();
14187     },
14188
14189     // private
14190     handleMouseUp : function(){
14191         clearTimeout(this.timer);
14192         this.el.un("mouseover", this.handleMouseReturn);
14193         this.el.un("mouseout", this.handleMouseOut);
14194         Roo.get(document).un("mouseup", this.handleMouseUp);
14195         this.el.removeClass(this.pressClass);
14196         this.fireEvent("mouseup", this);
14197     }
14198 });/*
14199  * Based on:
14200  * Ext JS Library 1.1.1
14201  * Copyright(c) 2006-2007, Ext JS, LLC.
14202  *
14203  * Originally Released Under LGPL - original licence link has changed is not relivant.
14204  *
14205  * Fork - LGPL
14206  * <script type="text/javascript">
14207  */
14208
14209  
14210 /**
14211  * @class Roo.KeyNav
14212  * <p>Provides a convenient wrapper for normalized keyboard navigation.  KeyNav allows you to bind
14213  * navigation keys to function calls that will get called when the keys are pressed, providing an easy
14214  * way to implement custom navigation schemes for any UI component.</p>
14215  * <p>The following are all of the possible keys that can be implemented: enter, left, right, up, down, tab, esc,
14216  * pageUp, pageDown, del, home, end.  Usage:</p>
14217  <pre><code>
14218 var nav = new Roo.KeyNav("my-element", {
14219     "left" : function(e){
14220         this.moveLeft(e.ctrlKey);
14221     },
14222     "right" : function(e){
14223         this.moveRight(e.ctrlKey);
14224     },
14225     "enter" : function(e){
14226         this.save();
14227     },
14228     scope : this
14229 });
14230 </code></pre>
14231  * @constructor
14232  * @param {String/HTMLElement/Roo.Element} el The element to bind to
14233  * @param {Object} config The config
14234  */
14235 Roo.KeyNav = function(el, config){
14236     this.el = Roo.get(el);
14237     Roo.apply(this, config);
14238     if(!this.disabled){
14239         this.disabled = true;
14240         this.enable();
14241     }
14242 };
14243
14244 Roo.KeyNav.prototype = {
14245     /**
14246      * @cfg {Boolean} disabled
14247      * True to disable this KeyNav instance (defaults to false)
14248      */
14249     disabled : false,
14250     /**
14251      * @cfg {String} defaultEventAction
14252      * The method to call on the {@link Roo.EventObject} after this KeyNav intercepts a key.  Valid values are
14253      * {@link Roo.EventObject#stopEvent}, {@link Roo.EventObject#preventDefault} and
14254      * {@link Roo.EventObject#stopPropagation} (defaults to 'stopEvent')
14255      */
14256     defaultEventAction: "stopEvent",
14257     /**
14258      * @cfg {Boolean} forceKeyDown
14259      * Handle the keydown event instead of keypress (defaults to false).  KeyNav automatically does this for IE since
14260      * IE does not propagate special keys on keypress, but setting this to true will force other browsers to also
14261      * handle keydown instead of keypress.
14262      */
14263     forceKeyDown : false,
14264
14265     // private
14266     prepareEvent : function(e){
14267         var k = e.getKey();
14268         var h = this.keyToHandler[k];
14269         //if(h && this[h]){
14270         //    e.stopPropagation();
14271         //}
14272         if(Roo.isSafari && h && k >= 37 && k <= 40){
14273             e.stopEvent();
14274         }
14275     },
14276
14277     // private
14278     relay : function(e){
14279         var k = e.getKey();
14280         var h = this.keyToHandler[k];
14281         if(h && this[h]){
14282             if(this.doRelay(e, this[h], h) !== true){
14283                 e[this.defaultEventAction]();
14284             }
14285         }
14286     },
14287
14288     // private
14289     doRelay : function(e, h, hname){
14290         return h.call(this.scope || this, e);
14291     },
14292
14293     // possible handlers
14294     enter : false,
14295     left : false,
14296     right : false,
14297     up : false,
14298     down : false,
14299     tab : false,
14300     esc : false,
14301     pageUp : false,
14302     pageDown : false,
14303     del : false,
14304     home : false,
14305     end : false,
14306
14307     // quick lookup hash
14308     keyToHandler : {
14309         37 : "left",
14310         39 : "right",
14311         38 : "up",
14312         40 : "down",
14313         33 : "pageUp",
14314         34 : "pageDown",
14315         46 : "del",
14316         36 : "home",
14317         35 : "end",
14318         13 : "enter",
14319         27 : "esc",
14320         9  : "tab"
14321     },
14322
14323         /**
14324          * Enable this KeyNav
14325          */
14326         enable: function(){
14327                 if(this.disabled){
14328             // ie won't do special keys on keypress, no one else will repeat keys with keydown
14329             // the EventObject will normalize Safari automatically
14330             if(this.forceKeyDown || Roo.isIE || Roo.isAir){
14331                 this.el.on("keydown", this.relay,  this);
14332             }else{
14333                 this.el.on("keydown", this.prepareEvent,  this);
14334                 this.el.on("keypress", this.relay,  this);
14335             }
14336                     this.disabled = false;
14337                 }
14338         },
14339
14340         /**
14341          * Disable this KeyNav
14342          */
14343         disable: function(){
14344                 if(!this.disabled){
14345                     if(this.forceKeyDown || Roo.isIE || Roo.isAir){
14346                 this.el.un("keydown", this.relay);
14347             }else{
14348                 this.el.un("keydown", this.prepareEvent);
14349                 this.el.un("keypress", this.relay);
14350             }
14351                     this.disabled = true;
14352                 }
14353         }
14354 };/*
14355  * Based on:
14356  * Ext JS Library 1.1.1
14357  * Copyright(c) 2006-2007, Ext JS, LLC.
14358  *
14359  * Originally Released Under LGPL - original licence link has changed is not relivant.
14360  *
14361  * Fork - LGPL
14362  * <script type="text/javascript">
14363  */
14364
14365  
14366 /**
14367  * @class Roo.KeyMap
14368  * Handles mapping keys to actions for an element. One key map can be used for multiple actions.
14369  * The constructor accepts the same config object as defined by {@link #addBinding}.
14370  * If you bind a callback function to a KeyMap, anytime the KeyMap handles an expected key
14371  * combination it will call the function with this signature (if the match is a multi-key
14372  * combination the callback will still be called only once): (String key, Roo.EventObject e)
14373  * A KeyMap can also handle a string representation of keys.<br />
14374  * Usage:
14375  <pre><code>
14376 // map one key by key code
14377 var map = new Roo.KeyMap("my-element", {
14378     key: 13, // or Roo.EventObject.ENTER
14379     fn: myHandler,
14380     scope: myObject
14381 });
14382
14383 // map multiple keys to one action by string
14384 var map = new Roo.KeyMap("my-element", {
14385     key: "a\r\n\t",
14386     fn: myHandler,
14387     scope: myObject
14388 });
14389
14390 // map multiple keys to multiple actions by strings and array of codes
14391 var map = new Roo.KeyMap("my-element", [
14392     {
14393         key: [10,13],
14394         fn: function(){ alert("Return was pressed"); }
14395     }, {
14396         key: "abc",
14397         fn: function(){ alert('a, b or c was pressed'); }
14398     }, {
14399         key: "\t",
14400         ctrl:true,
14401         shift:true,
14402         fn: function(){ alert('Control + shift + tab was pressed.'); }
14403     }
14404 ]);
14405 </code></pre>
14406  * <b>Note: A KeyMap starts enabled</b>
14407  * @constructor
14408  * @param {String/HTMLElement/Roo.Element} el The element to bind to
14409  * @param {Object} config The config (see {@link #addBinding})
14410  * @param {String} eventName (optional) The event to bind to (defaults to "keydown")
14411  */
14412 Roo.KeyMap = function(el, config, eventName){
14413     this.el  = Roo.get(el);
14414     this.eventName = eventName || "keydown";
14415     this.bindings = [];
14416     if(config){
14417         this.addBinding(config);
14418     }
14419     this.enable();
14420 };
14421
14422 Roo.KeyMap.prototype = {
14423     /**
14424      * True to stop the event from bubbling and prevent the default browser action if the
14425      * key was handled by the KeyMap (defaults to false)
14426      * @type Boolean
14427      */
14428     stopEvent : false,
14429
14430     /**
14431      * Add a new binding to this KeyMap. The following config object properties are supported:
14432      * <pre>
14433 Property    Type             Description
14434 ----------  ---------------  ----------------------------------------------------------------------
14435 key         String/Array     A single keycode or an array of keycodes to handle
14436 shift       Boolean          True to handle key only when shift is pressed (defaults to false)
14437 ctrl        Boolean          True to handle key only when ctrl is pressed (defaults to false)
14438 alt         Boolean          True to handle key only when alt is pressed (defaults to false)
14439 fn          Function         The function to call when KeyMap finds the expected key combination
14440 scope       Object           The scope of the callback function
14441 </pre>
14442      *
14443      * Usage:
14444      * <pre><code>
14445 // Create a KeyMap
14446 var map = new Roo.KeyMap(document, {
14447     key: Roo.EventObject.ENTER,
14448     fn: handleKey,
14449     scope: this
14450 });
14451
14452 //Add a new binding to the existing KeyMap later
14453 map.addBinding({
14454     key: 'abc',
14455     shift: true,
14456     fn: handleKey,
14457     scope: this
14458 });
14459 </code></pre>
14460      * @param {Object/Array} config A single KeyMap config or an array of configs
14461      */
14462         addBinding : function(config){
14463         if(config instanceof Array){
14464             for(var i = 0, len = config.length; i < len; i++){
14465                 this.addBinding(config[i]);
14466             }
14467             return;
14468         }
14469         var keyCode = config.key,
14470             shift = config.shift, 
14471             ctrl = config.ctrl, 
14472             alt = config.alt,
14473             fn = config.fn,
14474             scope = config.scope;
14475         if(typeof keyCode == "string"){
14476             var ks = [];
14477             var keyString = keyCode.toUpperCase();
14478             for(var j = 0, len = keyString.length; j < len; j++){
14479                 ks.push(keyString.charCodeAt(j));
14480             }
14481             keyCode = ks;
14482         }
14483         var keyArray = keyCode instanceof Array;
14484         var handler = function(e){
14485             if((!shift || e.shiftKey) && (!ctrl || e.ctrlKey) &&  (!alt || e.altKey)){
14486                 var k = e.getKey();
14487                 if(keyArray){
14488                     for(var i = 0, len = keyCode.length; i < len; i++){
14489                         if(keyCode[i] == k){
14490                           if(this.stopEvent){
14491                               e.stopEvent();
14492                           }
14493                           fn.call(scope || window, k, e);
14494                           return;
14495                         }
14496                     }
14497                 }else{
14498                     if(k == keyCode){
14499                         if(this.stopEvent){
14500                            e.stopEvent();
14501                         }
14502                         fn.call(scope || window, k, e);
14503                     }
14504                 }
14505             }
14506         };
14507         this.bindings.push(handler);  
14508         },
14509
14510     /**
14511      * Shorthand for adding a single key listener
14512      * @param {Number/Array/Object} key Either the numeric key code, array of key codes or an object with the
14513      * following options:
14514      * {key: (number or array), shift: (true/false), ctrl: (true/false), alt: (true/false)}
14515      * @param {Function} fn The function to call
14516      * @param {Object} scope (optional) The scope of the function
14517      */
14518     on : function(key, fn, scope){
14519         var keyCode, shift, ctrl, alt;
14520         if(typeof key == "object" && !(key instanceof Array)){
14521             keyCode = key.key;
14522             shift = key.shift;
14523             ctrl = key.ctrl;
14524             alt = key.alt;
14525         }else{
14526             keyCode = key;
14527         }
14528         this.addBinding({
14529             key: keyCode,
14530             shift: shift,
14531             ctrl: ctrl,
14532             alt: alt,
14533             fn: fn,
14534             scope: scope
14535         })
14536     },
14537
14538     // private
14539     handleKeyDown : function(e){
14540             if(this.enabled){ //just in case
14541             var b = this.bindings;
14542             for(var i = 0, len = b.length; i < len; i++){
14543                 b[i].call(this, e);
14544             }
14545             }
14546         },
14547         
14548         /**
14549          * Returns true if this KeyMap is enabled
14550          * @return {Boolean} 
14551          */
14552         isEnabled : function(){
14553             return this.enabled;  
14554         },
14555         
14556         /**
14557          * Enables this KeyMap
14558          */
14559         enable: function(){
14560                 if(!this.enabled){
14561                     this.el.on(this.eventName, this.handleKeyDown, this);
14562                     this.enabled = true;
14563                 }
14564         },
14565
14566         /**
14567          * Disable this KeyMap
14568          */
14569         disable: function(){
14570                 if(this.enabled){
14571                     this.el.removeListener(this.eventName, this.handleKeyDown, this);
14572                     this.enabled = false;
14573                 }
14574         }
14575 };/*
14576  * Based on:
14577  * Ext JS Library 1.1.1
14578  * Copyright(c) 2006-2007, Ext JS, LLC.
14579  *
14580  * Originally Released Under LGPL - original licence link has changed is not relivant.
14581  *
14582  * Fork - LGPL
14583  * <script type="text/javascript">
14584  */
14585
14586  
14587 /**
14588  * @class Roo.util.TextMetrics
14589  * Provides precise pixel measurements for blocks of text so that you can determine exactly how high and
14590  * wide, in pixels, a given block of text will be.
14591  * @singleton
14592  */
14593 Roo.util.TextMetrics = function(){
14594     var shared;
14595     return {
14596         /**
14597          * Measures the size of the specified text
14598          * @param {String/HTMLElement} el The element, dom node or id from which to copy existing CSS styles
14599          * that can affect the size of the rendered text
14600          * @param {String} text The text to measure
14601          * @param {Number} fixedWidth (optional) If the text will be multiline, you have to set a fixed width
14602          * in order to accurately measure the text height
14603          * @return {Object} An object containing the text's size {width: (width), height: (height)}
14604          */
14605         measure : function(el, text, fixedWidth){
14606             if(!shared){
14607                 shared = Roo.util.TextMetrics.Instance(el, fixedWidth);
14608             }
14609             shared.bind(el);
14610             shared.setFixedWidth(fixedWidth || 'auto');
14611             return shared.getSize(text);
14612         },
14613
14614         /**
14615          * Return a unique TextMetrics instance that can be bound directly to an element and reused.  This reduces
14616          * the overhead of multiple calls to initialize the style properties on each measurement.
14617          * @param {String/HTMLElement} el The element, dom node or id that the instance will be bound to
14618          * @param {Number} fixedWidth (optional) If the text will be multiline, you have to set a fixed width
14619          * in order to accurately measure the text height
14620          * @return {Roo.util.TextMetrics.Instance} instance The new instance
14621          */
14622         createInstance : function(el, fixedWidth){
14623             return Roo.util.TextMetrics.Instance(el, fixedWidth);
14624         }
14625     };
14626 }();
14627
14628  
14629
14630 Roo.util.TextMetrics.Instance = function(bindTo, fixedWidth){
14631     var ml = new Roo.Element(document.createElement('div'));
14632     document.body.appendChild(ml.dom);
14633     ml.position('absolute');
14634     ml.setLeftTop(-1000, -1000);
14635     ml.hide();
14636
14637     if(fixedWidth){
14638         ml.setWidth(fixedWidth);
14639     }
14640      
14641     var instance = {
14642         /**
14643          * Returns the size of the specified text based on the internal element's style and width properties
14644          * @memberOf Roo.util.TextMetrics.Instance#
14645          * @param {String} text The text to measure
14646          * @return {Object} An object containing the text's size {width: (width), height: (height)}
14647          */
14648         getSize : function(text){
14649             ml.update(text);
14650             var s = ml.getSize();
14651             ml.update('');
14652             return s;
14653         },
14654
14655         /**
14656          * Binds this TextMetrics instance to an element from which to copy existing CSS styles
14657          * that can affect the size of the rendered text
14658          * @memberOf Roo.util.TextMetrics.Instance#
14659          * @param {String/HTMLElement} el The element, dom node or id
14660          */
14661         bind : function(el){
14662             ml.setStyle(
14663                 Roo.fly(el).getStyles('font-size','font-style', 'font-weight', 'font-family','line-height')
14664             );
14665         },
14666
14667         /**
14668          * Sets a fixed width on the internal measurement element.  If the text will be multiline, you have
14669          * to set a fixed width in order to accurately measure the text height.
14670          * @memberOf Roo.util.TextMetrics.Instance#
14671          * @param {Number} width The width to set on the element
14672          */
14673         setFixedWidth : function(width){
14674             ml.setWidth(width);
14675         },
14676
14677         /**
14678          * Returns the measured width of the specified text
14679          * @memberOf Roo.util.TextMetrics.Instance#
14680          * @param {String} text The text to measure
14681          * @return {Number} width The width in pixels
14682          */
14683         getWidth : function(text){
14684             ml.dom.style.width = 'auto';
14685             return this.getSize(text).width;
14686         },
14687
14688         /**
14689          * Returns the measured height of the specified text.  For multiline text, be sure to call
14690          * {@link #setFixedWidth} if necessary.
14691          * @memberOf Roo.util.TextMetrics.Instance#
14692          * @param {String} text The text to measure
14693          * @return {Number} height The height in pixels
14694          */
14695         getHeight : function(text){
14696             return this.getSize(text).height;
14697         }
14698     };
14699
14700     instance.bind(bindTo);
14701
14702     return instance;
14703 };
14704
14705 // backwards compat
14706 Roo.Element.measureText = Roo.util.TextMetrics.measure;/*
14707  * Based on:
14708  * Ext JS Library 1.1.1
14709  * Copyright(c) 2006-2007, Ext JS, LLC.
14710  *
14711  * Originally Released Under LGPL - original licence link has changed is not relivant.
14712  *
14713  * Fork - LGPL
14714  * <script type="text/javascript">
14715  */
14716
14717 /**
14718  * @class Roo.state.Provider
14719  * Abstract base class for state provider implementations. This class provides methods
14720  * for encoding and decoding <b>typed</b> variables including dates and defines the 
14721  * Provider interface.
14722  */
14723 Roo.state.Provider = function(){
14724     /**
14725      * @event statechange
14726      * Fires when a state change occurs.
14727      * @param {Provider} this This state provider
14728      * @param {String} key The state key which was changed
14729      * @param {String} value The encoded value for the state
14730      */
14731     this.addEvents({
14732         "statechange": true
14733     });
14734     this.state = {};
14735     Roo.state.Provider.superclass.constructor.call(this);
14736 };
14737 Roo.extend(Roo.state.Provider, Roo.util.Observable, {
14738     /**
14739      * Returns the current value for a key
14740      * @param {String} name The key name
14741      * @param {Mixed} defaultValue A default value to return if the key's value is not found
14742      * @return {Mixed} The state data
14743      */
14744     get : function(name, defaultValue){
14745         return typeof this.state[name] == "undefined" ?
14746             defaultValue : this.state[name];
14747     },
14748     
14749     /**
14750      * Clears a value from the state
14751      * @param {String} name The key name
14752      */
14753     clear : function(name){
14754         delete this.state[name];
14755         this.fireEvent("statechange", this, name, null);
14756     },
14757     
14758     /**
14759      * Sets the value for a key
14760      * @param {String} name The key name
14761      * @param {Mixed} value The value to set
14762      */
14763     set : function(name, value){
14764         this.state[name] = value;
14765         this.fireEvent("statechange", this, name, value);
14766     },
14767     
14768     /**
14769      * Decodes a string previously encoded with {@link #encodeValue}.
14770      * @param {String} value The value to decode
14771      * @return {Mixed} The decoded value
14772      */
14773     decodeValue : function(cookie){
14774         var re = /^(a|n|d|b|s|o)\:(.*)$/;
14775         var matches = re.exec(unescape(cookie));
14776         if(!matches || !matches[1]) return; // non state cookie
14777         var type = matches[1];
14778         var v = matches[2];
14779         switch(type){
14780             case "n":
14781                 return parseFloat(v);
14782             case "d":
14783                 return new Date(Date.parse(v));
14784             case "b":
14785                 return (v == "1");
14786             case "a":
14787                 var all = [];
14788                 var values = v.split("^");
14789                 for(var i = 0, len = values.length; i < len; i++){
14790                     all.push(this.decodeValue(values[i]));
14791                 }
14792                 return all;
14793            case "o":
14794                 var all = {};
14795                 var values = v.split("^");
14796                 for(var i = 0, len = values.length; i < len; i++){
14797                     var kv = values[i].split("=");
14798                     all[kv[0]] = this.decodeValue(kv[1]);
14799                 }
14800                 return all;
14801            default:
14802                 return v;
14803         }
14804     },
14805     
14806     /**
14807      * Encodes a value including type information.  Decode with {@link #decodeValue}.
14808      * @param {Mixed} value The value to encode
14809      * @return {String} The encoded value
14810      */
14811     encodeValue : function(v){
14812         var enc;
14813         if(typeof v == "number"){
14814             enc = "n:" + v;
14815         }else if(typeof v == "boolean"){
14816             enc = "b:" + (v ? "1" : "0");
14817         }else if(v instanceof Date){
14818             enc = "d:" + v.toGMTString();
14819         }else if(v instanceof Array){
14820             var flat = "";
14821             for(var i = 0, len = v.length; i < len; i++){
14822                 flat += this.encodeValue(v[i]);
14823                 if(i != len-1) flat += "^";
14824             }
14825             enc = "a:" + flat;
14826         }else if(typeof v == "object"){
14827             var flat = "";
14828             for(var key in v){
14829                 if(typeof v[key] != "function"){
14830                     flat += key + "=" + this.encodeValue(v[key]) + "^";
14831                 }
14832             }
14833             enc = "o:" + flat.substring(0, flat.length-1);
14834         }else{
14835             enc = "s:" + v;
14836         }
14837         return escape(enc);        
14838     }
14839 });
14840
14841 /*
14842  * Based on:
14843  * Ext JS Library 1.1.1
14844  * Copyright(c) 2006-2007, Ext JS, LLC.
14845  *
14846  * Originally Released Under LGPL - original licence link has changed is not relivant.
14847  *
14848  * Fork - LGPL
14849  * <script type="text/javascript">
14850  */
14851 /**
14852  * @class Roo.state.Manager
14853  * This is the global state manager. By default all components that are "state aware" check this class
14854  * for state information if you don't pass them a custom state provider. In order for this class
14855  * to be useful, it must be initialized with a provider when your application initializes.
14856  <pre><code>
14857 // in your initialization function
14858 init : function(){
14859    Roo.state.Manager.setProvider(new Roo.state.CookieProvider());
14860    ...
14861    // supposed you have a {@link Roo.BorderLayout}
14862    var layout = new Roo.BorderLayout(...);
14863    layout.restoreState();
14864    // or a {Roo.BasicDialog}
14865    var dialog = new Roo.BasicDialog(...);
14866    dialog.restoreState();
14867  </code></pre>
14868  * @singleton
14869  */
14870 Roo.state.Manager = function(){
14871     var provider = new Roo.state.Provider();
14872     
14873     return {
14874         /**
14875          * Configures the default state provider for your application
14876          * @param {Provider} stateProvider The state provider to set
14877          */
14878         setProvider : function(stateProvider){
14879             provider = stateProvider;
14880         },
14881         
14882         /**
14883          * Returns the current value for a key
14884          * @param {String} name The key name
14885          * @param {Mixed} defaultValue The default value to return if the key lookup does not match
14886          * @return {Mixed} The state data
14887          */
14888         get : function(key, defaultValue){
14889             return provider.get(key, defaultValue);
14890         },
14891         
14892         /**
14893          * Sets the value for a key
14894          * @param {String} name The key name
14895          * @param {Mixed} value The state data
14896          */
14897          set : function(key, value){
14898             provider.set(key, value);
14899         },
14900         
14901         /**
14902          * Clears a value from the state
14903          * @param {String} name The key name
14904          */
14905         clear : function(key){
14906             provider.clear(key);
14907         },
14908         
14909         /**
14910          * Gets the currently configured state provider
14911          * @return {Provider} The state provider
14912          */
14913         getProvider : function(){
14914             return provider;
14915         }
14916     };
14917 }();
14918 /*
14919  * Based on:
14920  * Ext JS Library 1.1.1
14921  * Copyright(c) 2006-2007, Ext JS, LLC.
14922  *
14923  * Originally Released Under LGPL - original licence link has changed is not relivant.
14924  *
14925  * Fork - LGPL
14926  * <script type="text/javascript">
14927  */
14928 /**
14929  * @class Roo.state.CookieProvider
14930  * @extends Roo.state.Provider
14931  * The default Provider implementation which saves state via cookies.
14932  * <br />Usage:
14933  <pre><code>
14934    var cp = new Roo.state.CookieProvider({
14935        path: "/cgi-bin/",
14936        expires: new Date(new Date().getTime()+(1000*60*60*24*30)); //30 days
14937        domain: "roojs.com"
14938    })
14939    Roo.state.Manager.setProvider(cp);
14940  </code></pre>
14941  * @cfg {String} path The path for which the cookie is active (defaults to root '/' which makes it active for all pages in the site)
14942  * @cfg {Date} expires The cookie expiration date (defaults to 7 days from now)
14943  * @cfg {String} domain The domain to save the cookie for.  Note that you cannot specify a different domain than
14944  * your page is on, but you can specify a sub-domain, or simply the domain itself like 'roojs.com' to include
14945  * all sub-domains if you need to access cookies across different sub-domains (defaults to null which uses the same
14946  * domain the page is running on including the 'www' like 'www.roojs.com')
14947  * @cfg {Boolean} secure True if the site is using SSL (defaults to false)
14948  * @constructor
14949  * Create a new CookieProvider
14950  * @param {Object} config The configuration object
14951  */
14952 Roo.state.CookieProvider = function(config){
14953     Roo.state.CookieProvider.superclass.constructor.call(this);
14954     this.path = "/";
14955     this.expires = new Date(new Date().getTime()+(1000*60*60*24*7)); //7 days
14956     this.domain = null;
14957     this.secure = false;
14958     Roo.apply(this, config);
14959     this.state = this.readCookies();
14960 };
14961
14962 Roo.extend(Roo.state.CookieProvider, Roo.state.Provider, {
14963     // private
14964     set : function(name, value){
14965         if(typeof value == "undefined" || value === null){
14966             this.clear(name);
14967             return;
14968         }
14969         this.setCookie(name, value);
14970         Roo.state.CookieProvider.superclass.set.call(this, name, value);
14971     },
14972
14973     // private
14974     clear : function(name){
14975         this.clearCookie(name);
14976         Roo.state.CookieProvider.superclass.clear.call(this, name);
14977     },
14978
14979     // private
14980     readCookies : function(){
14981         var cookies = {};
14982         var c = document.cookie + ";";
14983         var re = /\s?(.*?)=(.*?);/g;
14984         var matches;
14985         while((matches = re.exec(c)) != null){
14986             var name = matches[1];
14987             var value = matches[2];
14988             if(name && name.substring(0,3) == "ys-"){
14989                 cookies[name.substr(3)] = this.decodeValue(value);
14990             }
14991         }
14992         return cookies;
14993     },
14994
14995     // private
14996     setCookie : function(name, value){
14997         document.cookie = "ys-"+ name + "=" + this.encodeValue(value) +
14998            ((this.expires == null) ? "" : ("; expires=" + this.expires.toGMTString())) +
14999            ((this.path == null) ? "" : ("; path=" + this.path)) +
15000            ((this.domain == null) ? "" : ("; domain=" + this.domain)) +
15001            ((this.secure == true) ? "; secure" : "");
15002     },
15003
15004     // private
15005     clearCookie : function(name){
15006         document.cookie = "ys-" + name + "=null; expires=Thu, 01-Jan-70 00:00:01 GMT" +
15007            ((this.path == null) ? "" : ("; path=" + this.path)) +
15008            ((this.domain == null) ? "" : ("; domain=" + this.domain)) +
15009            ((this.secure == true) ? "; secure" : "");
15010     }
15011 });/*
15012  * Based on:
15013  * Ext JS Library 1.1.1
15014  * Copyright(c) 2006-2007, Ext JS, LLC.
15015  *
15016  * Originally Released Under LGPL - original licence link has changed is not relivant.
15017  *
15018  * Fork - LGPL
15019  * <script type="text/javascript">
15020  */
15021  
15022
15023 /**
15024  * @class Roo.ComponentMgr
15025  * Provides a common registry of all components on a page so that they can be easily accessed by component id (see {@link Roo.getCmp}).
15026  * @singleton
15027  */
15028 Roo.ComponentMgr = function(){
15029     var all = new Roo.util.MixedCollection();
15030
15031     return {
15032         /**
15033          * Registers a component.
15034          * @param {Roo.Component} c The component
15035          */
15036         register : function(c){
15037             all.add(c);
15038         },
15039
15040         /**
15041          * Unregisters a component.
15042          * @param {Roo.Component} c The component
15043          */
15044         unregister : function(c){
15045             all.remove(c);
15046         },
15047
15048         /**
15049          * Returns a component by id
15050          * @param {String} id The component id
15051          */
15052         get : function(id){
15053             return all.get(id);
15054         },
15055
15056         /**
15057          * Registers a function that will be called when a specified component is added to ComponentMgr
15058          * @param {String} id The component id
15059          * @param {Funtction} fn The callback function
15060          * @param {Object} scope The scope of the callback
15061          */
15062         onAvailable : function(id, fn, scope){
15063             all.on("add", function(index, o){
15064                 if(o.id == id){
15065                     fn.call(scope || o, o);
15066                     all.un("add", fn, scope);
15067                 }
15068             });
15069         }
15070     };
15071 }();/*
15072  * Based on:
15073  * Ext JS Library 1.1.1
15074  * Copyright(c) 2006-2007, Ext JS, LLC.
15075  *
15076  * Originally Released Under LGPL - original licence link has changed is not relivant.
15077  *
15078  * Fork - LGPL
15079  * <script type="text/javascript">
15080  */
15081  
15082 /**
15083  * @class Roo.Component
15084  * @extends Roo.util.Observable
15085  * Base class for all major Roo components.  All subclasses of Component can automatically participate in the standard
15086  * Roo component lifecycle of creation, rendering and destruction.  They also have automatic support for basic hide/show
15087  * and enable/disable behavior.  Component allows any subclass to be lazy-rendered into any {@link Roo.Container} and
15088  * to be automatically registered with the {@link Roo.ComponentMgr} so that it can be referenced at any time via {@link Roo.getCmp}.
15089  * All visual components (widgets) that require rendering into a layout should subclass Component.
15090  * @constructor
15091  * @param {Roo.Element/String/Object} config The configuration options.  If an element is passed, it is set as the internal
15092  * element and its id used as the component id.  If a string is passed, it is assumed to be the id of an existing element
15093  * and is used as the component id.  Otherwise, it is assumed to be a standard config object and is applied to the component.
15094  */
15095 Roo.Component = function(config){
15096     config = config || {};
15097     if(config.tagName || config.dom || typeof config == "string"){ // element object
15098         config = {el: config, id: config.id || config};
15099     }
15100     this.initialConfig = config;
15101
15102     Roo.apply(this, config);
15103     this.addEvents({
15104         /**
15105          * @event disable
15106          * Fires after the component is disabled.
15107              * @param {Roo.Component} this
15108              */
15109         disable : true,
15110         /**
15111          * @event enable
15112          * Fires after the component is enabled.
15113              * @param {Roo.Component} this
15114              */
15115         enable : true,
15116         /**
15117          * @event beforeshow
15118          * Fires before the component is shown.  Return false to stop the show.
15119              * @param {Roo.Component} this
15120              */
15121         beforeshow : true,
15122         /**
15123          * @event show
15124          * Fires after the component is shown.
15125              * @param {Roo.Component} this
15126              */
15127         show : true,
15128         /**
15129          * @event beforehide
15130          * Fires before the component is hidden. Return false to stop the hide.
15131              * @param {Roo.Component} this
15132              */
15133         beforehide : true,
15134         /**
15135          * @event hide
15136          * Fires after the component is hidden.
15137              * @param {Roo.Component} this
15138              */
15139         hide : true,
15140         /**
15141          * @event beforerender
15142          * Fires before the component is rendered. Return false to stop the render.
15143              * @param {Roo.Component} this
15144              */
15145         beforerender : true,
15146         /**
15147          * @event render
15148          * Fires after the component is rendered.
15149              * @param {Roo.Component} this
15150              */
15151         render : true,
15152         /**
15153          * @event beforedestroy
15154          * Fires before the component is destroyed. Return false to stop the destroy.
15155              * @param {Roo.Component} this
15156              */
15157         beforedestroy : true,
15158         /**
15159          * @event destroy
15160          * Fires after the component is destroyed.
15161              * @param {Roo.Component} this
15162              */
15163         destroy : true
15164     });
15165     if(!this.id){
15166         this.id = "ext-comp-" + (++Roo.Component.AUTO_ID);
15167     }
15168     Roo.ComponentMgr.register(this);
15169     Roo.Component.superclass.constructor.call(this);
15170     this.initComponent();
15171     if(this.renderTo){ // not supported by all components yet. use at your own risk!
15172         this.render(this.renderTo);
15173         delete this.renderTo;
15174     }
15175 };
15176
15177 /** @private */
15178 Roo.Component.AUTO_ID = 1000;
15179
15180 Roo.extend(Roo.Component, Roo.util.Observable, {
15181     /**
15182      * @scope Roo.Component.prototype
15183      * @type {Boolean}
15184      * true if this component is hidden. Read-only.
15185      */
15186     hidden : false,
15187     /**
15188      * @type {Boolean}
15189      * true if this component is disabled. Read-only.
15190      */
15191     disabled : false,
15192     /**
15193      * @type {Boolean}
15194      * true if this component has been rendered. Read-only.
15195      */
15196     rendered : false,
15197     
15198     /** @cfg {String} disableClass
15199      * CSS class added to the component when it is disabled (defaults to "x-item-disabled").
15200      */
15201     disabledClass : "x-item-disabled",
15202         /** @cfg {Boolean} allowDomMove
15203          * Whether the component can move the Dom node when rendering (defaults to true).
15204          */
15205     allowDomMove : true,
15206     /** @cfg {String} hideMode
15207      * How this component should hidden. Supported values are
15208      * "visibility" (css visibility), "offsets" (negative offset position) and
15209      * "display" (css display) - defaults to "display".
15210      */
15211     hideMode: 'display',
15212
15213     /** @private */
15214     ctype : "Roo.Component",
15215
15216     /**
15217      * @cfg {String} actionMode 
15218      * which property holds the element that used for  hide() / show() / disable() / enable()
15219      * default is 'el' 
15220      */
15221     actionMode : "el",
15222
15223     /** @private */
15224     getActionEl : function(){
15225         return this[this.actionMode];
15226     },
15227
15228     initComponent : Roo.emptyFn,
15229     /**
15230      * If this is a lazy rendering component, render it to its container element.
15231      * @param {String/HTMLElement/Element} container (optional) The element this component should be rendered into. If it is being applied to existing markup, this should be left off.
15232      */
15233     render : function(container, position){
15234         if(!this.rendered && this.fireEvent("beforerender", this) !== false){
15235             if(!container && this.el){
15236                 this.el = Roo.get(this.el);
15237                 container = this.el.dom.parentNode;
15238                 this.allowDomMove = false;
15239             }
15240             this.container = Roo.get(container);
15241             this.rendered = true;
15242             if(position !== undefined){
15243                 if(typeof position == 'number'){
15244                     position = this.container.dom.childNodes[position];
15245                 }else{
15246                     position = Roo.getDom(position);
15247                 }
15248             }
15249             this.onRender(this.container, position || null);
15250             if(this.cls){
15251                 this.el.addClass(this.cls);
15252                 delete this.cls;
15253             }
15254             if(this.style){
15255                 this.el.applyStyles(this.style);
15256                 delete this.style;
15257             }
15258             this.fireEvent("render", this);
15259             this.afterRender(this.container);
15260             if(this.hidden){
15261                 this.hide();
15262             }
15263             if(this.disabled){
15264                 this.disable();
15265             }
15266         }
15267         return this;
15268     },
15269
15270     /** @private */
15271     // default function is not really useful
15272     onRender : function(ct, position){
15273         if(this.el){
15274             this.el = Roo.get(this.el);
15275             if(this.allowDomMove !== false){
15276                 ct.dom.insertBefore(this.el.dom, position);
15277             }
15278         }
15279     },
15280
15281     /** @private */
15282     getAutoCreate : function(){
15283         var cfg = typeof this.autoCreate == "object" ?
15284                       this.autoCreate : Roo.apply({}, this.defaultAutoCreate);
15285         if(this.id && !cfg.id){
15286             cfg.id = this.id;
15287         }
15288         return cfg;
15289     },
15290
15291     /** @private */
15292     afterRender : Roo.emptyFn,
15293
15294     /**
15295      * Destroys this component by purging any event listeners, removing the component's element from the DOM,
15296      * removing the component from its {@link Roo.Container} (if applicable) and unregistering it from {@link Roo.ComponentMgr}.
15297      */
15298     destroy : function(){
15299         if(this.fireEvent("beforedestroy", this) !== false){
15300             this.purgeListeners();
15301             this.beforeDestroy();
15302             if(this.rendered){
15303                 this.el.removeAllListeners();
15304                 this.el.remove();
15305                 if(this.actionMode == "container"){
15306                     this.container.remove();
15307                 }
15308             }
15309             this.onDestroy();
15310             Roo.ComponentMgr.unregister(this);
15311             this.fireEvent("destroy", this);
15312         }
15313     },
15314
15315         /** @private */
15316     beforeDestroy : function(){
15317
15318     },
15319
15320         /** @private */
15321         onDestroy : function(){
15322
15323     },
15324
15325     /**
15326      * Returns the underlying {@link Roo.Element}.
15327      * @return {Roo.Element} The element
15328      */
15329     getEl : function(){
15330         return this.el;
15331     },
15332
15333     /**
15334      * Returns the id of this component.
15335      * @return {String}
15336      */
15337     getId : function(){
15338         return this.id;
15339     },
15340
15341     /**
15342      * Try to focus this component.
15343      * @param {Boolean} selectText True to also select the text in this component (if applicable)
15344      * @return {Roo.Component} this
15345      */
15346     focus : function(selectText){
15347         if(this.rendered){
15348             this.el.focus();
15349             if(selectText === true){
15350                 this.el.dom.select();
15351             }
15352         }
15353         return this;
15354     },
15355
15356     /** @private */
15357     blur : function(){
15358         if(this.rendered){
15359             this.el.blur();
15360         }
15361         return this;
15362     },
15363
15364     /**
15365      * Disable this component.
15366      * @return {Roo.Component} this
15367      */
15368     disable : function(){
15369         if(this.rendered){
15370             this.onDisable();
15371         }
15372         this.disabled = true;
15373         this.fireEvent("disable", this);
15374         return this;
15375     },
15376
15377         // private
15378     onDisable : function(){
15379         this.getActionEl().addClass(this.disabledClass);
15380         this.el.dom.disabled = true;
15381     },
15382
15383     /**
15384      * Enable this component.
15385      * @return {Roo.Component} this
15386      */
15387     enable : function(){
15388         if(this.rendered){
15389             this.onEnable();
15390         }
15391         this.disabled = false;
15392         this.fireEvent("enable", this);
15393         return this;
15394     },
15395
15396         // private
15397     onEnable : function(){
15398         this.getActionEl().removeClass(this.disabledClass);
15399         this.el.dom.disabled = false;
15400     },
15401
15402     /**
15403      * Convenience function for setting disabled/enabled by boolean.
15404      * @param {Boolean} disabled
15405      */
15406     setDisabled : function(disabled){
15407         this[disabled ? "disable" : "enable"]();
15408     },
15409
15410     /**
15411      * Show this component.
15412      * @return {Roo.Component} this
15413      */
15414     show: function(){
15415         if(this.fireEvent("beforeshow", this) !== false){
15416             this.hidden = false;
15417             if(this.rendered){
15418                 this.onShow();
15419             }
15420             this.fireEvent("show", this);
15421         }
15422         return this;
15423     },
15424
15425     // private
15426     onShow : function(){
15427         var ae = this.getActionEl();
15428         if(this.hideMode == 'visibility'){
15429             ae.dom.style.visibility = "visible";
15430         }else if(this.hideMode == 'offsets'){
15431             ae.removeClass('x-hidden');
15432         }else{
15433             ae.dom.style.display = "";
15434         }
15435     },
15436
15437     /**
15438      * Hide this component.
15439      * @return {Roo.Component} this
15440      */
15441     hide: function(){
15442         if(this.fireEvent("beforehide", this) !== false){
15443             this.hidden = true;
15444             if(this.rendered){
15445                 this.onHide();
15446             }
15447             this.fireEvent("hide", this);
15448         }
15449         return this;
15450     },
15451
15452     // private
15453     onHide : function(){
15454         var ae = this.getActionEl();
15455         if(this.hideMode == 'visibility'){
15456             ae.dom.style.visibility = "hidden";
15457         }else if(this.hideMode == 'offsets'){
15458             ae.addClass('x-hidden');
15459         }else{
15460             ae.dom.style.display = "none";
15461         }
15462     },
15463
15464     /**
15465      * Convenience function to hide or show this component by boolean.
15466      * @param {Boolean} visible True to show, false to hide
15467      * @return {Roo.Component} this
15468      */
15469     setVisible: function(visible){
15470         if(visible) {
15471             this.show();
15472         }else{
15473             this.hide();
15474         }
15475         return this;
15476     },
15477
15478     /**
15479      * Returns true if this component is visible.
15480      */
15481     isVisible : function(){
15482         return this.getActionEl().isVisible();
15483     },
15484
15485     cloneConfig : function(overrides){
15486         overrides = overrides || {};
15487         var id = overrides.id || Roo.id();
15488         var cfg = Roo.applyIf(overrides, this.initialConfig);
15489         cfg.id = id; // prevent dup id
15490         return new this.constructor(cfg);
15491     }
15492 });/*
15493  * Based on:
15494  * Ext JS Library 1.1.1
15495  * Copyright(c) 2006-2007, Ext JS, LLC.
15496  *
15497  * Originally Released Under LGPL - original licence link has changed is not relivant.
15498  *
15499  * Fork - LGPL
15500  * <script type="text/javascript">
15501  */
15502
15503 /**
15504  * @class Roo.BoxComponent
15505  * @extends Roo.Component
15506  * Base class for any visual {@link Roo.Component} that uses a box container.  BoxComponent provides automatic box
15507  * model adjustments for sizing and positioning and will work correctly withnin the Component rendering model.  All
15508  * container classes should subclass BoxComponent so that they will work consistently when nested within other Ext
15509  * layout containers.
15510  * @constructor
15511  * @param {Roo.Element/String/Object} config The configuration options.
15512  */
15513 Roo.BoxComponent = function(config){
15514     Roo.Component.call(this, config);
15515     this.addEvents({
15516         /**
15517          * @event resize
15518          * Fires after the component is resized.
15519              * @param {Roo.Component} this
15520              * @param {Number} adjWidth The box-adjusted width that was set
15521              * @param {Number} adjHeight The box-adjusted height that was set
15522              * @param {Number} rawWidth The width that was originally specified
15523              * @param {Number} rawHeight The height that was originally specified
15524              */
15525         resize : true,
15526         /**
15527          * @event move
15528          * Fires after the component is moved.
15529              * @param {Roo.Component} this
15530              * @param {Number} x The new x position
15531              * @param {Number} y The new y position
15532              */
15533         move : true
15534     });
15535 };
15536
15537 Roo.extend(Roo.BoxComponent, Roo.Component, {
15538     // private, set in afterRender to signify that the component has been rendered
15539     boxReady : false,
15540     // private, used to defer height settings to subclasses
15541     deferHeight: false,
15542     /** @cfg {Number} width
15543      * width (optional) size of component
15544      */
15545      /** @cfg {Number} height
15546      * height (optional) size of component
15547      */
15548      
15549     /**
15550      * Sets the width and height of the component.  This method fires the resize event.  This method can accept
15551      * either width and height as separate numeric arguments, or you can pass a size object like {width:10, height:20}.
15552      * @param {Number/Object} width The new width to set, or a size object in the format {width, height}
15553      * @param {Number} height The new height to set (not required if a size object is passed as the first arg)
15554      * @return {Roo.BoxComponent} this
15555      */
15556     setSize : function(w, h){
15557         // support for standard size objects
15558         if(typeof w == 'object'){
15559             h = w.height;
15560             w = w.width;
15561         }
15562         // not rendered
15563         if(!this.boxReady){
15564             this.width = w;
15565             this.height = h;
15566             return this;
15567         }
15568
15569         // prevent recalcs when not needed
15570         if(this.lastSize && this.lastSize.width == w && this.lastSize.height == h){
15571             return this;
15572         }
15573         this.lastSize = {width: w, height: h};
15574
15575         var adj = this.adjustSize(w, h);
15576         var aw = adj.width, ah = adj.height;
15577         if(aw !== undefined || ah !== undefined){ // this code is nasty but performs better with floaters
15578             var rz = this.getResizeEl();
15579             if(!this.deferHeight && aw !== undefined && ah !== undefined){
15580                 rz.setSize(aw, ah);
15581             }else if(!this.deferHeight && ah !== undefined){
15582                 rz.setHeight(ah);
15583             }else if(aw !== undefined){
15584                 rz.setWidth(aw);
15585             }
15586             this.onResize(aw, ah, w, h);
15587             this.fireEvent('resize', this, aw, ah, w, h);
15588         }
15589         return this;
15590     },
15591
15592     /**
15593      * Gets the current size of the component's underlying element.
15594      * @return {Object} An object containing the element's size {width: (element width), height: (element height)}
15595      */
15596     getSize : function(){
15597         return this.el.getSize();
15598     },
15599
15600     /**
15601      * Gets the current XY position of the component's underlying element.
15602      * @param {Boolean} local (optional) If true the element's left and top are returned instead of page XY (defaults to false)
15603      * @return {Array} The XY position of the element (e.g., [100, 200])
15604      */
15605     getPosition : function(local){
15606         if(local === true){
15607             return [this.el.getLeft(true), this.el.getTop(true)];
15608         }
15609         return this.xy || this.el.getXY();
15610     },
15611
15612     /**
15613      * Gets the current box measurements of the component's underlying element.
15614      * @param {Boolean} local (optional) If true the element's left and top are returned instead of page XY (defaults to false)
15615      * @returns {Object} box An object in the format {x, y, width, height}
15616      */
15617     getBox : function(local){
15618         var s = this.el.getSize();
15619         if(local){
15620             s.x = this.el.getLeft(true);
15621             s.y = this.el.getTop(true);
15622         }else{
15623             var xy = this.xy || this.el.getXY();
15624             s.x = xy[0];
15625             s.y = xy[1];
15626         }
15627         return s;
15628     },
15629
15630     /**
15631      * Sets the current box measurements of the component's underlying element.
15632      * @param {Object} box An object in the format {x, y, width, height}
15633      * @returns {Roo.BoxComponent} this
15634      */
15635     updateBox : function(box){
15636         this.setSize(box.width, box.height);
15637         this.setPagePosition(box.x, box.y);
15638         return this;
15639     },
15640
15641     // protected
15642     getResizeEl : function(){
15643         return this.resizeEl || this.el;
15644     },
15645
15646     // protected
15647     getPositionEl : function(){
15648         return this.positionEl || this.el;
15649     },
15650
15651     /**
15652      * Sets the left and top of the component.  To set the page XY position instead, use {@link #setPagePosition}.
15653      * This method fires the move event.
15654      * @param {Number} left The new left
15655      * @param {Number} top The new top
15656      * @returns {Roo.BoxComponent} this
15657      */
15658     setPosition : function(x, y){
15659         this.x = x;
15660         this.y = y;
15661         if(!this.boxReady){
15662             return this;
15663         }
15664         var adj = this.adjustPosition(x, y);
15665         var ax = adj.x, ay = adj.y;
15666
15667         var el = this.getPositionEl();
15668         if(ax !== undefined || ay !== undefined){
15669             if(ax !== undefined && ay !== undefined){
15670                 el.setLeftTop(ax, ay);
15671             }else if(ax !== undefined){
15672                 el.setLeft(ax);
15673             }else if(ay !== undefined){
15674                 el.setTop(ay);
15675             }
15676             this.onPosition(ax, ay);
15677             this.fireEvent('move', this, ax, ay);
15678         }
15679         return this;
15680     },
15681
15682     /**
15683      * Sets the page XY position of the component.  To set the left and top instead, use {@link #setPosition}.
15684      * This method fires the move event.
15685      * @param {Number} x The new x position
15686      * @param {Number} y The new y position
15687      * @returns {Roo.BoxComponent} this
15688      */
15689     setPagePosition : function(x, y){
15690         this.pageX = x;
15691         this.pageY = y;
15692         if(!this.boxReady){
15693             return;
15694         }
15695         if(x === undefined || y === undefined){ // cannot translate undefined points
15696             return;
15697         }
15698         var p = this.el.translatePoints(x, y);
15699         this.setPosition(p.left, p.top);
15700         return this;
15701     },
15702
15703     // private
15704     onRender : function(ct, position){
15705         Roo.BoxComponent.superclass.onRender.call(this, ct, position);
15706         if(this.resizeEl){
15707             this.resizeEl = Roo.get(this.resizeEl);
15708         }
15709         if(this.positionEl){
15710             this.positionEl = Roo.get(this.positionEl);
15711         }
15712     },
15713
15714     // private
15715     afterRender : function(){
15716         Roo.BoxComponent.superclass.afterRender.call(this);
15717         this.boxReady = true;
15718         this.setSize(this.width, this.height);
15719         if(this.x || this.y){
15720             this.setPosition(this.x, this.y);
15721         }
15722         if(this.pageX || this.pageY){
15723             this.setPagePosition(this.pageX, this.pageY);
15724         }
15725     },
15726
15727     /**
15728      * Force the component's size to recalculate based on the underlying element's current height and width.
15729      * @returns {Roo.BoxComponent} this
15730      */
15731     syncSize : function(){
15732         delete this.lastSize;
15733         this.setSize(this.el.getWidth(), this.el.getHeight());
15734         return this;
15735     },
15736
15737     /**
15738      * Called after the component is resized, this method is empty by default but can be implemented by any
15739      * subclass that needs to perform custom logic after a resize occurs.
15740      * @param {Number} adjWidth The box-adjusted width that was set
15741      * @param {Number} adjHeight The box-adjusted height that was set
15742      * @param {Number} rawWidth The width that was originally specified
15743      * @param {Number} rawHeight The height that was originally specified
15744      */
15745     onResize : function(adjWidth, adjHeight, rawWidth, rawHeight){
15746
15747     },
15748
15749     /**
15750      * Called after the component is moved, this method is empty by default but can be implemented by any
15751      * subclass that needs to perform custom logic after a move occurs.
15752      * @param {Number} x The new x position
15753      * @param {Number} y The new y position
15754      */
15755     onPosition : function(x, y){
15756
15757     },
15758
15759     // private
15760     adjustSize : function(w, h){
15761         if(this.autoWidth){
15762             w = 'auto';
15763         }
15764         if(this.autoHeight){
15765             h = 'auto';
15766         }
15767         return {width : w, height: h};
15768     },
15769
15770     // private
15771     adjustPosition : function(x, y){
15772         return {x : x, y: y};
15773     }
15774 });/*
15775  * Original code for Roojs - LGPL
15776  * <script type="text/javascript">
15777  */
15778  
15779 /**
15780  * @class Roo.XComponent
15781  * A delayed Element creator...
15782  * Or a way to group chunks of interface together.
15783  * technically this is a wrapper around a tree of Roo elements (which defines a 'module'),
15784  *  used in conjunction with XComponent.build() it will create an instance of each element,
15785  *  then call addxtype() to build the User interface.
15786  * 
15787  * Mypart.xyx = new Roo.XComponent({
15788
15789     parent : 'Mypart.xyz', // empty == document.element.!!
15790     order : '001',
15791     name : 'xxxx'
15792     region : 'xxxx'
15793     disabled : function() {} 
15794      
15795     tree : function() { // return an tree of xtype declared components
15796         var MODULE = this;
15797         return 
15798         {
15799             xtype : 'NestedLayoutPanel',
15800             // technicall
15801         }
15802      ]
15803  *})
15804  *
15805  *
15806  * It can be used to build a big heiracy, with parent etc.
15807  * or you can just use this to render a single compoent to a dom element
15808  * MYPART.render(Roo.Element | String(id) | dom_element )
15809  *
15810  *
15811  * Usage patterns.
15812  *
15813  * Classic Roo
15814  *
15815  * Roo is designed primarily as a single page application, so the UI build for a standard interface will
15816  * expect a single 'TOP' level module normally indicated by the 'parent' of the XComponent definition being defined as false.
15817  *
15818  * Each sub module is expected to have a parent pointing to the class name of it's parent module.
15819  *
15820  * When the top level is false, a 'Roo.BorderLayout' is created and the element is flagged as 'topModule'
15821  * - if mulitple topModules exist, the last one is defined as the top module.
15822  *
15823  * Embeded Roo
15824  * 
15825  * When the top level or multiple modules are to embedded into a existing HTML page,
15826  * the parent element can container '#id' of the element where the module will be drawn.
15827  *
15828  * Bootstrap Roo
15829  *
15830  * Unlike classic Roo, the bootstrap tends not to be used as a single page.
15831  * it relies more on a include mechanism, where sub modules are included into an outer page.
15832  * This is normally managed by the builder tools using Roo.apply( options, Included.Sub.Module )
15833  * 
15834  * Bootstrap Roo Included elements
15835  *
15836  * Our builder application needs the ability to preview these sub compoennts. They will normally have parent=false set,
15837  * hence confusing the component builder as it thinks there are multiple top level elements. 
15838  *
15839  * 
15840  * 
15841  * @extends Roo.util.Observable
15842  * @constructor
15843  * @param cfg {Object} configuration of component
15844  * 
15845  */
15846 Roo.XComponent = function(cfg) {
15847     Roo.apply(this, cfg);
15848     this.addEvents({ 
15849         /**
15850              * @event built
15851              * Fires when this the componnt is built
15852              * @param {Roo.XComponent} c the component
15853              */
15854         'built' : true
15855         
15856     });
15857     this.region = this.region || 'center'; // default..
15858     Roo.XComponent.register(this);
15859     this.modules = false;
15860     this.el = false; // where the layout goes..
15861     
15862     
15863 }
15864 Roo.extend(Roo.XComponent, Roo.util.Observable, {
15865     /**
15866      * @property el
15867      * The created element (with Roo.factory())
15868      * @type {Roo.Layout}
15869      */
15870     el  : false,
15871     
15872     /**
15873      * @property el
15874      * for BC  - use el in new code
15875      * @type {Roo.Layout}
15876      */
15877     panel : false,
15878     
15879     /**
15880      * @property layout
15881      * for BC  - use el in new code
15882      * @type {Roo.Layout}
15883      */
15884     layout : false,
15885     
15886      /**
15887      * @cfg {Function|boolean} disabled
15888      * If this module is disabled by some rule, return true from the funtion
15889      */
15890     disabled : false,
15891     
15892     /**
15893      * @cfg {String} parent 
15894      * Name of parent element which it get xtype added to..
15895      */
15896     parent: false,
15897     
15898     /**
15899      * @cfg {String} order
15900      * Used to set the order in which elements are created (usefull for multiple tabs)
15901      */
15902     
15903     order : false,
15904     /**
15905      * @cfg {String} name
15906      * String to display while loading.
15907      */
15908     name : false,
15909     /**
15910      * @cfg {String} region
15911      * Region to render component to (defaults to center)
15912      */
15913     region : 'center',
15914     
15915     /**
15916      * @cfg {Array} items
15917      * A single item array - the first element is the root of the tree..
15918      * It's done this way to stay compatible with the Xtype system...
15919      */
15920     items : false,
15921     
15922     /**
15923      * @property _tree
15924      * The method that retuns the tree of parts that make up this compoennt 
15925      * @type {function}
15926      */
15927     _tree  : false,
15928     
15929      /**
15930      * render
15931      * render element to dom or tree
15932      * @param {Roo.Element|String|DomElement} optional render to if parent is not set.
15933      */
15934     
15935     render : function(el)
15936     {
15937         
15938         el = el || false;
15939         var hp = this.parent ? 1 : 0;
15940         Roo.log(this);
15941         
15942         if (!el && typeof(this.parent) == 'string' && this.parent.substring(0,1) == '#') {
15943             // if parent is a '#.....' string, then let's use that..
15944             var ename = this.parent.substr(1);
15945             this.parent = false;
15946             Roo.log(ename);
15947             switch (ename) {
15948                 case 'bootstrap-body' :
15949                     if (typeof(Roo.bootstrap.Body) != 'undefined') {
15950                         this.parent = { el :  new  Roo.bootstrap.Body() };
15951                         Roo.log("setting el to doc body");
15952                          
15953                     } else {
15954                         throw "Container is bootstrap body, but Roo.bootstrap.Body is not defined";
15955                     }
15956                     break;
15957                 case 'bootstrap':
15958                     this.parent = { el : true};
15959                     // fall through
15960                 default:
15961                     el = Roo.get(ename);
15962                     break;
15963             }
15964                 
15965             
15966             if (!el && !this.parent) {
15967                 Roo.log("Warning - element can not be found :#" + ename );
15968                 return;
15969             }
15970         }
15971         Roo.log("EL:");Roo.log(el);
15972         Roo.log("this.parent.el:");Roo.log(this.parent.el);
15973         
15974         var tree = this._tree ? this._tree() : this.tree();
15975
15976         // altertive root elements ??? - we need a better way to indicate these.
15977         var is_alt = (typeof(Roo.bootstrap) != 'undefined' && tree.xns == Roo.bootstrap) ||
15978                         (typeof(Roo.mailer) != 'undefined' && tree.xns == Roo.mailer) ;
15979         
15980         if (!this.parent && is_alt) {
15981             //el = Roo.get(document.body);
15982             this.parent = { el : true };
15983         }
15984             
15985             
15986         
15987         if (!this.parent) {
15988             
15989             Roo.log("no parent - creating one");
15990             
15991             el = el ? Roo.get(el) : false;      
15992             
15993             // it's a top level one..
15994             this.parent =  {
15995                 el : new Roo.BorderLayout(el || document.body, {
15996                 
15997                      center: {
15998                          titlebar: false,
15999                          autoScroll:false,
16000                          closeOnTab: true,
16001                          tabPosition: 'top',
16002                           //resizeTabs: true,
16003                          alwaysShowTabs: el && hp? false :  true,
16004                          hideTabs: el || !hp ? true :  false,
16005                          minTabWidth: 140
16006                      }
16007                  })
16008             }
16009         }
16010         
16011         if (!this.parent.el) {
16012                 // probably an old style ctor, which has been disabled.
16013                 return;
16014
16015         }
16016                 // The 'tree' method is  '_tree now' 
16017             
16018         tree.region = tree.region || this.region;
16019         
16020         if (this.parent.el === true) {
16021             // bootstrap... - body..
16022             this.parent.el = Roo.factory(tree);
16023         }
16024         
16025         this.el = this.parent.el.addxtype(tree);
16026         this.fireEvent('built', this);
16027         
16028         this.panel = this.el;
16029         this.layout = this.panel.layout;
16030         this.parentLayout = this.parent.layout  || false;  
16031          
16032     }
16033     
16034 });
16035
16036 Roo.apply(Roo.XComponent, {
16037     /**
16038      * @property  hideProgress
16039      * true to disable the building progress bar.. usefull on single page renders.
16040      * @type Boolean
16041      */
16042     hideProgress : false,
16043     /**
16044      * @property  buildCompleted
16045      * True when the builder has completed building the interface.
16046      * @type Boolean
16047      */
16048     buildCompleted : false,
16049      
16050     /**
16051      * @property  topModule
16052      * the upper most module - uses document.element as it's constructor.
16053      * @type Object
16054      */
16055      
16056     topModule  : false,
16057       
16058     /**
16059      * @property  modules
16060      * array of modules to be created by registration system.
16061      * @type {Array} of Roo.XComponent
16062      */
16063     
16064     modules : [],
16065     /**
16066      * @property  elmodules
16067      * array of modules to be created by which use #ID 
16068      * @type {Array} of Roo.XComponent
16069      */
16070      
16071     elmodules : [],
16072
16073      /**
16074      * @property  build_from_html
16075      * Build elements from html - used by bootstrap HTML stuff 
16076      *    - this is cleared after build is completed
16077      * @type {boolean} true  (default false)
16078      */
16079      
16080     build_from_html : false,
16081
16082     /**
16083      * Register components to be built later.
16084      *
16085      * This solves the following issues
16086      * - Building is not done on page load, but after an authentication process has occured.
16087      * - Interface elements are registered on page load
16088      * - Parent Interface elements may not be loaded before child, so this handles that..
16089      * 
16090      *
16091      * example:
16092      * 
16093      * MyApp.register({
16094           order : '000001',
16095           module : 'Pman.Tab.projectMgr',
16096           region : 'center',
16097           parent : 'Pman.layout',
16098           disabled : false,  // or use a function..
16099         })
16100      
16101      * * @param {Object} details about module
16102      */
16103     register : function(obj) {
16104                 
16105         Roo.XComponent.event.fireEvent('register', obj);
16106         switch(typeof(obj.disabled) ) {
16107                 
16108             case 'undefined':
16109                 break;
16110             
16111             case 'function':
16112                 if ( obj.disabled() ) {
16113                         return;
16114                 }
16115                 break;
16116             
16117             default:
16118                 if (obj.disabled) {
16119                         return;
16120                 }
16121                 break;
16122         }
16123                 
16124         this.modules.push(obj);
16125          
16126     },
16127     /**
16128      * convert a string to an object..
16129      * eg. 'AAA.BBB' -> finds AAA.BBB
16130
16131      */
16132     
16133     toObject : function(str)
16134     {
16135         if (!str || typeof(str) == 'object') {
16136             return str;
16137         }
16138         if (str.substring(0,1) == '#') {
16139             return str;
16140         }
16141
16142         var ar = str.split('.');
16143         var rt, o;
16144         rt = ar.shift();
16145             /** eval:var:o */
16146         try {
16147             eval('if (typeof ' + rt + ' == "undefined"){ o = false;} o = ' + rt + ';');
16148         } catch (e) {
16149             throw "Module not found : " + str;
16150         }
16151         
16152         if (o === false) {
16153             throw "Module not found : " + str;
16154         }
16155         Roo.each(ar, function(e) {
16156             if (typeof(o[e]) == 'undefined') {
16157                 throw "Module not found : " + str;
16158             }
16159             o = o[e];
16160         });
16161         
16162         return o;
16163         
16164     },
16165     
16166     
16167     /**
16168      * move modules into their correct place in the tree..
16169      * 
16170      */
16171     preBuild : function ()
16172     {
16173         var _t = this;
16174         Roo.each(this.modules , function (obj)
16175         {
16176             Roo.XComponent.event.fireEvent('beforebuild', obj);
16177             
16178             var opar = obj.parent;
16179             try { 
16180                 obj.parent = this.toObject(opar);
16181             } catch(e) {
16182                 Roo.log("parent:toObject failed: " + e.toString());
16183                 return;
16184             }
16185             
16186             if (!obj.parent) {
16187                 Roo.debug && Roo.log("GOT top level module");
16188                 Roo.debug && Roo.log(obj);
16189                 obj.modules = new Roo.util.MixedCollection(false, 
16190                     function(o) { return o.order + '' }
16191                 );
16192                 this.topModule = obj;
16193                 return;
16194             }
16195                         // parent is a string (usually a dom element name..)
16196             if (typeof(obj.parent) == 'string') {
16197                 this.elmodules.push(obj);
16198                 return;
16199             }
16200             if (obj.parent.constructor != Roo.XComponent) {
16201                 Roo.log("Warning : Object Parent is not instance of XComponent:" + obj.name)
16202             }
16203             if (!obj.parent.modules) {
16204                 obj.parent.modules = new Roo.util.MixedCollection(false, 
16205                     function(o) { return o.order + '' }
16206                 );
16207             }
16208             if (obj.parent.disabled) {
16209                 obj.disabled = true;
16210             }
16211             obj.parent.modules.add(obj);
16212         }, this);
16213     },
16214     
16215      /**
16216      * make a list of modules to build.
16217      * @return {Array} list of modules. 
16218      */ 
16219     
16220     buildOrder : function()
16221     {
16222         var _this = this;
16223         var cmp = function(a,b) {   
16224             return String(a).toUpperCase() > String(b).toUpperCase() ? 1 : -1;
16225         };
16226         if ((!this.topModule || !this.topModule.modules) && !this.elmodules.length) {
16227             throw "No top level modules to build";
16228         }
16229         
16230         // make a flat list in order of modules to build.
16231         var mods = this.topModule ? [ this.topModule ] : [];
16232                 
16233         
16234         // elmodules (is a list of DOM based modules )
16235         Roo.each(this.elmodules, function(e) {
16236             mods.push(e);
16237             if (!this.topModule &&
16238                 typeof(e.parent) == 'string' &&
16239                 e.parent.substring(0,1) == '#' &&
16240                 Roo.get(e.parent.substr(1))
16241                ) {
16242                 
16243                 _this.topModule = e;
16244             }
16245             
16246         });
16247
16248         
16249         // add modules to their parents..
16250         var addMod = function(m) {
16251             Roo.debug && Roo.log("build Order: add: " + m.name);
16252                 
16253             mods.push(m);
16254             if (m.modules && !m.disabled) {
16255                 Roo.debug && Roo.log("build Order: " + m.modules.length + " child modules");
16256                 m.modules.keySort('ASC',  cmp );
16257                 Roo.debug && Roo.log("build Order: " + m.modules.length + " child modules (after sort)");
16258     
16259                 m.modules.each(addMod);
16260             } else {
16261                 Roo.debug && Roo.log("build Order: no child modules");
16262             }
16263             // not sure if this is used any more..
16264             if (m.finalize) {
16265                 m.finalize.name = m.name + " (clean up) ";
16266                 mods.push(m.finalize);
16267             }
16268             
16269         }
16270         if (this.topModule && this.topModule.modules) { 
16271             this.topModule.modules.keySort('ASC',  cmp );
16272             this.topModule.modules.each(addMod);
16273         } 
16274         return mods;
16275     },
16276     
16277      /**
16278      * Build the registered modules.
16279      * @param {Object} parent element.
16280      * @param {Function} optional method to call after module has been added.
16281      * 
16282      */ 
16283    
16284     build : function(opts) 
16285     {
16286         
16287         if (typeof(opts) != 'undefined') {
16288             Roo.apply(this,opts);
16289         }
16290         
16291         this.preBuild();
16292         var mods = this.buildOrder();
16293       
16294         //this.allmods = mods;
16295         //Roo.debug && Roo.log(mods);
16296         //return;
16297         if (!mods.length) { // should not happen
16298             throw "NO modules!!!";
16299         }
16300         
16301         
16302         var msg = "Building Interface...";
16303         // flash it up as modal - so we store the mask!?
16304         if (!this.hideProgress && Roo.MessageBox) {
16305             Roo.MessageBox.show({ title: 'loading' });
16306             Roo.MessageBox.show({
16307                title: "Please wait...",
16308                msg: msg,
16309                width:450,
16310                progress:true,
16311                closable:false,
16312                modal: false
16313               
16314             });
16315         }
16316         var total = mods.length;
16317         
16318         var _this = this;
16319         var progressRun = function() {
16320             if (!mods.length) {
16321                 Roo.debug && Roo.log('hide?');
16322                 if (!this.hideProgress && Roo.MessageBox) {
16323                     Roo.MessageBox.hide();
16324                 }
16325                 Roo.XComponent.build_from_html = false; // reset, so dialogs will be build from javascript
16326                 
16327                 Roo.XComponent.event.fireEvent('buildcomplete', _this.topModule);
16328                 
16329                 // THE END...
16330                 return false;   
16331             }
16332             
16333             var m = mods.shift();
16334             
16335             
16336             Roo.debug && Roo.log(m);
16337             // not sure if this is supported any more.. - modules that are are just function
16338             if (typeof(m) == 'function') { 
16339                 m.call(this);
16340                 return progressRun.defer(10, _this);
16341             } 
16342             
16343             
16344             msg = "Building Interface " + (total  - mods.length) + 
16345                     " of " + total + 
16346                     (m.name ? (' - ' + m.name) : '');
16347                         Roo.debug && Roo.log(msg);
16348             if (!this.hideProgress &&  Roo.MessageBox) { 
16349                 Roo.MessageBox.updateProgress(  (total  - mods.length)/total, msg  );
16350             }
16351             
16352          
16353             // is the module disabled?
16354             var disabled = (typeof(m.disabled) == 'function') ?
16355                 m.disabled.call(m.module.disabled) : m.disabled;    
16356             
16357             
16358             if (disabled) {
16359                 return progressRun(); // we do not update the display!
16360             }
16361             
16362             // now build 
16363             
16364                         
16365                         
16366             m.render();
16367             // it's 10 on top level, and 1 on others??? why...
16368             return progressRun.defer(10, _this);
16369              
16370         }
16371         progressRun.defer(1, _this);
16372      
16373         
16374         
16375     },
16376         
16377         
16378         /**
16379          * Event Object.
16380          *
16381          *
16382          */
16383         event: false, 
16384     /**
16385          * wrapper for event.on - aliased later..  
16386          * Typically use to register a event handler for register:
16387          *
16388          * eg. Roo.XComponent.on('register', function(comp) { comp.disable = true } );
16389          *
16390          */
16391     on : false
16392    
16393     
16394     
16395 });
16396
16397 Roo.XComponent.event = new Roo.util.Observable({
16398                 events : { 
16399                         /**
16400                          * @event register
16401                          * Fires when an Component is registered,
16402                          * set the disable property on the Component to stop registration.
16403                          * @param {Roo.XComponent} c the component being registerd.
16404                          * 
16405                          */
16406                         'register' : true,
16407             /**
16408                          * @event beforebuild
16409                          * Fires before each Component is built
16410                          * can be used to apply permissions.
16411                          * @param {Roo.XComponent} c the component being registerd.
16412                          * 
16413                          */
16414                         'beforebuild' : true,
16415                         /**
16416                          * @event buildcomplete
16417                          * Fires on the top level element when all elements have been built
16418                          * @param {Roo.XComponent} the top level component.
16419                          */
16420                         'buildcomplete' : true
16421                         
16422                 }
16423 });
16424
16425 Roo.XComponent.on = Roo.XComponent.event.on.createDelegate(Roo.XComponent.event); 
16426  /*
16427  * Based on:
16428  * Ext JS Library 1.1.1
16429  * Copyright(c) 2006-2007, Ext JS, LLC.
16430  *
16431  * Originally Released Under LGPL - original licence link has changed is not relivant.
16432  *
16433  * Fork - LGPL
16434  * <script type="text/javascript">
16435  */
16436
16437
16438
16439 /*
16440  * These classes are derivatives of the similarly named classes in the YUI Library.
16441  * The original license:
16442  * Copyright (c) 2006, Yahoo! Inc. All rights reserved.
16443  * Code licensed under the BSD License:
16444  * http://developer.yahoo.net/yui/license.txt
16445  */
16446
16447 (function() {
16448
16449 var Event=Roo.EventManager;
16450 var Dom=Roo.lib.Dom;
16451
16452 /**
16453  * @class Roo.dd.DragDrop
16454  * @extends Roo.util.Observable
16455  * Defines the interface and base operation of items that that can be
16456  * dragged or can be drop targets.  It was designed to be extended, overriding
16457  * the event handlers for startDrag, onDrag, onDragOver and onDragOut.
16458  * Up to three html elements can be associated with a DragDrop instance:
16459  * <ul>
16460  * <li>linked element: the element that is passed into the constructor.
16461  * This is the element which defines the boundaries for interaction with
16462  * other DragDrop objects.</li>
16463  * <li>handle element(s): The drag operation only occurs if the element that
16464  * was clicked matches a handle element.  By default this is the linked
16465  * element, but there are times that you will want only a portion of the
16466  * linked element to initiate the drag operation, and the setHandleElId()
16467  * method provides a way to define this.</li>
16468  * <li>drag element: this represents the element that would be moved along
16469  * with the cursor during a drag operation.  By default, this is the linked
16470  * element itself as in {@link Roo.dd.DD}.  setDragElId() lets you define
16471  * a separate element that would be moved, as in {@link Roo.dd.DDProxy}.
16472  * </li>
16473  * </ul>
16474  * This class should not be instantiated until the onload event to ensure that
16475  * the associated elements are available.
16476  * The following would define a DragDrop obj that would interact with any
16477  * other DragDrop obj in the "group1" group:
16478  * <pre>
16479  *  dd = new Roo.dd.DragDrop("div1", "group1");
16480  * </pre>
16481  * Since none of the event handlers have been implemented, nothing would
16482  * actually happen if you were to run the code above.  Normally you would
16483  * override this class or one of the default implementations, but you can
16484  * also override the methods you want on an instance of the class...
16485  * <pre>
16486  *  dd.onDragDrop = function(e, id) {
16487  *  &nbsp;&nbsp;alert("dd was dropped on " + id);
16488  *  }
16489  * </pre>
16490  * @constructor
16491  * @param {String} id of the element that is linked to this instance
16492  * @param {String} sGroup the group of related DragDrop objects
16493  * @param {object} config an object containing configurable attributes
16494  *                Valid properties for DragDrop:
16495  *                    padding, isTarget, maintainOffset, primaryButtonOnly
16496  */
16497 Roo.dd.DragDrop = function(id, sGroup, config) {
16498     if (id) {
16499         this.init(id, sGroup, config);
16500     }
16501     
16502 };
16503
16504 Roo.extend(Roo.dd.DragDrop, Roo.util.Observable , {
16505
16506     /**
16507      * The id of the element associated with this object.  This is what we
16508      * refer to as the "linked element" because the size and position of
16509      * this element is used to determine when the drag and drop objects have
16510      * interacted.
16511      * @property id
16512      * @type String
16513      */
16514     id: null,
16515
16516     /**
16517      * Configuration attributes passed into the constructor
16518      * @property config
16519      * @type object
16520      */
16521     config: null,
16522
16523     /**
16524      * The id of the element that will be dragged.  By default this is same
16525      * as the linked element , but could be changed to another element. Ex:
16526      * Roo.dd.DDProxy
16527      * @property dragElId
16528      * @type String
16529      * @private
16530      */
16531     dragElId: null,
16532
16533     /**
16534      * the id of the element that initiates the drag operation.  By default
16535      * this is the linked element, but could be changed to be a child of this
16536      * element.  This lets us do things like only starting the drag when the
16537      * header element within the linked html element is clicked.
16538      * @property handleElId
16539      * @type String
16540      * @private
16541      */
16542     handleElId: null,
16543
16544     /**
16545      * An associative array of HTML tags that will be ignored if clicked.
16546      * @property invalidHandleTypes
16547      * @type {string: string}
16548      */
16549     invalidHandleTypes: null,
16550
16551     /**
16552      * An associative array of ids for elements that will be ignored if clicked
16553      * @property invalidHandleIds
16554      * @type {string: string}
16555      */
16556     invalidHandleIds: null,
16557
16558     /**
16559      * An indexted array of css class names for elements that will be ignored
16560      * if clicked.
16561      * @property invalidHandleClasses
16562      * @type string[]
16563      */
16564     invalidHandleClasses: null,
16565
16566     /**
16567      * The linked element's absolute X position at the time the drag was
16568      * started
16569      * @property startPageX
16570      * @type int
16571      * @private
16572      */
16573     startPageX: 0,
16574
16575     /**
16576      * The linked element's absolute X position at the time the drag was
16577      * started
16578      * @property startPageY
16579      * @type int
16580      * @private
16581      */
16582     startPageY: 0,
16583
16584     /**
16585      * The group defines a logical collection of DragDrop objects that are
16586      * related.  Instances only get events when interacting with other
16587      * DragDrop object in the same group.  This lets us define multiple
16588      * groups using a single DragDrop subclass if we want.
16589      * @property groups
16590      * @type {string: string}
16591      */
16592     groups: null,
16593
16594     /**
16595      * Individual drag/drop instances can be locked.  This will prevent
16596      * onmousedown start drag.
16597      * @property locked
16598      * @type boolean
16599      * @private
16600      */
16601     locked: false,
16602
16603     /**
16604      * Lock this instance
16605      * @method lock
16606      */
16607     lock: function() { this.locked = true; },
16608
16609     /**
16610      * Unlock this instace
16611      * @method unlock
16612      */
16613     unlock: function() { this.locked = false; },
16614
16615     /**
16616      * By default, all insances can be a drop target.  This can be disabled by
16617      * setting isTarget to false.
16618      * @method isTarget
16619      * @type boolean
16620      */
16621     isTarget: true,
16622
16623     /**
16624      * The padding configured for this drag and drop object for calculating
16625      * the drop zone intersection with this object.
16626      * @method padding
16627      * @type int[]
16628      */
16629     padding: null,
16630
16631     /**
16632      * Cached reference to the linked element
16633      * @property _domRef
16634      * @private
16635      */
16636     _domRef: null,
16637
16638     /**
16639      * Internal typeof flag
16640      * @property __ygDragDrop
16641      * @private
16642      */
16643     __ygDragDrop: true,
16644
16645     /**
16646      * Set to true when horizontal contraints are applied
16647      * @property constrainX
16648      * @type boolean
16649      * @private
16650      */
16651     constrainX: false,
16652
16653     /**
16654      * Set to true when vertical contraints are applied
16655      * @property constrainY
16656      * @type boolean
16657      * @private
16658      */
16659     constrainY: false,
16660
16661     /**
16662      * The left constraint
16663      * @property minX
16664      * @type int
16665      * @private
16666      */
16667     minX: 0,
16668
16669     /**
16670      * The right constraint
16671      * @property maxX
16672      * @type int
16673      * @private
16674      */
16675     maxX: 0,
16676
16677     /**
16678      * The up constraint
16679      * @property minY
16680      * @type int
16681      * @type int
16682      * @private
16683      */
16684     minY: 0,
16685
16686     /**
16687      * The down constraint
16688      * @property maxY
16689      * @type int
16690      * @private
16691      */
16692     maxY: 0,
16693
16694     /**
16695      * Maintain offsets when we resetconstraints.  Set to true when you want
16696      * the position of the element relative to its parent to stay the same
16697      * when the page changes
16698      *
16699      * @property maintainOffset
16700      * @type boolean
16701      */
16702     maintainOffset: false,
16703
16704     /**
16705      * Array of pixel locations the element will snap to if we specified a
16706      * horizontal graduation/interval.  This array is generated automatically
16707      * when you define a tick interval.
16708      * @property xTicks
16709      * @type int[]
16710      */
16711     xTicks: null,
16712
16713     /**
16714      * Array of pixel locations the element will snap to if we specified a
16715      * vertical graduation/interval.  This array is generated automatically
16716      * when you define a tick interval.
16717      * @property yTicks
16718      * @type int[]
16719      */
16720     yTicks: null,
16721
16722     /**
16723      * By default the drag and drop instance will only respond to the primary
16724      * button click (left button for a right-handed mouse).  Set to true to
16725      * allow drag and drop to start with any mouse click that is propogated
16726      * by the browser
16727      * @property primaryButtonOnly
16728      * @type boolean
16729      */
16730     primaryButtonOnly: true,
16731
16732     /**
16733      * The availabe property is false until the linked dom element is accessible.
16734      * @property available
16735      * @type boolean
16736      */
16737     available: false,
16738
16739     /**
16740      * By default, drags can only be initiated if the mousedown occurs in the
16741      * region the linked element is.  This is done in part to work around a
16742      * bug in some browsers that mis-report the mousedown if the previous
16743      * mouseup happened outside of the window.  This property is set to true
16744      * if outer handles are defined.
16745      *
16746      * @property hasOuterHandles
16747      * @type boolean
16748      * @default false
16749      */
16750     hasOuterHandles: false,
16751
16752     /**
16753      * Code that executes immediately before the startDrag event
16754      * @method b4StartDrag
16755      * @private
16756      */
16757     b4StartDrag: function(x, y) { },
16758
16759     /**
16760      * Abstract method called after a drag/drop object is clicked
16761      * and the drag or mousedown time thresholds have beeen met.
16762      * @method startDrag
16763      * @param {int} X click location
16764      * @param {int} Y click location
16765      */
16766     startDrag: function(x, y) { /* override this */ },
16767
16768     /**
16769      * Code that executes immediately before the onDrag event
16770      * @method b4Drag
16771      * @private
16772      */
16773     b4Drag: function(e) { },
16774
16775     /**
16776      * Abstract method called during the onMouseMove event while dragging an
16777      * object.
16778      * @method onDrag
16779      * @param {Event} e the mousemove event
16780      */
16781     onDrag: function(e) { /* override this */ },
16782
16783     /**
16784      * Abstract method called when this element fist begins hovering over
16785      * another DragDrop obj
16786      * @method onDragEnter
16787      * @param {Event} e the mousemove event
16788      * @param {String|DragDrop[]} id In POINT mode, the element
16789      * id this is hovering over.  In INTERSECT mode, an array of one or more
16790      * dragdrop items being hovered over.
16791      */
16792     onDragEnter: function(e, id) { /* override this */ },
16793
16794     /**
16795      * Code that executes immediately before the onDragOver event
16796      * @method b4DragOver
16797      * @private
16798      */
16799     b4DragOver: function(e) { },
16800
16801     /**
16802      * Abstract method called when this element is hovering over another
16803      * DragDrop obj
16804      * @method onDragOver
16805      * @param {Event} e the mousemove event
16806      * @param {String|DragDrop[]} id In POINT mode, the element
16807      * id this is hovering over.  In INTERSECT mode, an array of dd items
16808      * being hovered over.
16809      */
16810     onDragOver: function(e, id) { /* override this */ },
16811
16812     /**
16813      * Code that executes immediately before the onDragOut event
16814      * @method b4DragOut
16815      * @private
16816      */
16817     b4DragOut: function(e) { },
16818
16819     /**
16820      * Abstract method called when we are no longer hovering over an element
16821      * @method onDragOut
16822      * @param {Event} e the mousemove event
16823      * @param {String|DragDrop[]} id In POINT mode, the element
16824      * id this was hovering over.  In INTERSECT mode, an array of dd items
16825      * that the mouse is no longer over.
16826      */
16827     onDragOut: function(e, id) { /* override this */ },
16828
16829     /**
16830      * Code that executes immediately before the onDragDrop event
16831      * @method b4DragDrop
16832      * @private
16833      */
16834     b4DragDrop: function(e) { },
16835
16836     /**
16837      * Abstract method called when this item is dropped on another DragDrop
16838      * obj
16839      * @method onDragDrop
16840      * @param {Event} e the mouseup event
16841      * @param {String|DragDrop[]} id In POINT mode, the element
16842      * id this was dropped on.  In INTERSECT mode, an array of dd items this
16843      * was dropped on.
16844      */
16845     onDragDrop: function(e, id) { /* override this */ },
16846
16847     /**
16848      * Abstract method called when this item is dropped on an area with no
16849      * drop target
16850      * @method onInvalidDrop
16851      * @param {Event} e the mouseup event
16852      */
16853     onInvalidDrop: function(e) { /* override this */ },
16854
16855     /**
16856      * Code that executes immediately before the endDrag event
16857      * @method b4EndDrag
16858      * @private
16859      */
16860     b4EndDrag: function(e) { },
16861
16862     /**
16863      * Fired when we are done dragging the object
16864      * @method endDrag
16865      * @param {Event} e the mouseup event
16866      */
16867     endDrag: function(e) { /* override this */ },
16868
16869     /**
16870      * Code executed immediately before the onMouseDown event
16871      * @method b4MouseDown
16872      * @param {Event} e the mousedown event
16873      * @private
16874      */
16875     b4MouseDown: function(e) {  },
16876
16877     /**
16878      * Event handler that fires when a drag/drop obj gets a mousedown
16879      * @method onMouseDown
16880      * @param {Event} e the mousedown event
16881      */
16882     onMouseDown: function(e) { /* override this */ },
16883
16884     /**
16885      * Event handler that fires when a drag/drop obj gets a mouseup
16886      * @method onMouseUp
16887      * @param {Event} e the mouseup event
16888      */
16889     onMouseUp: function(e) { /* override this */ },
16890
16891     /**
16892      * Override the onAvailable method to do what is needed after the initial
16893      * position was determined.
16894      * @method onAvailable
16895      */
16896     onAvailable: function () {
16897     },
16898
16899     /*
16900      * Provides default constraint padding to "constrainTo" elements (defaults to {left: 0, right:0, top:0, bottom:0}).
16901      * @type Object
16902      */
16903     defaultPadding : {left:0, right:0, top:0, bottom:0},
16904
16905     /*
16906      * Initializes the drag drop object's constraints to restrict movement to a certain element.
16907  *
16908  * Usage:
16909  <pre><code>
16910  var dd = new Roo.dd.DDProxy("dragDiv1", "proxytest",
16911                 { dragElId: "existingProxyDiv" });
16912  dd.startDrag = function(){
16913      this.constrainTo("parent-id");
16914  };
16915  </code></pre>
16916  * Or you can initalize it using the {@link Roo.Element} object:
16917  <pre><code>
16918  Roo.get("dragDiv1").initDDProxy("proxytest", {dragElId: "existingProxyDiv"}, {
16919      startDrag : function(){
16920          this.constrainTo("parent-id");
16921      }
16922  });
16923  </code></pre>
16924      * @param {String/HTMLElement/Element} constrainTo The element to constrain to.
16925      * @param {Object/Number} pad (optional) Pad provides a way to specify "padding" of the constraints,
16926      * and can be either a number for symmetrical padding (4 would be equal to {left:4, right:4, top:4, bottom:4}) or
16927      * an object containing the sides to pad. For example: {right:10, bottom:10}
16928      * @param {Boolean} inContent (optional) Constrain the draggable in the content box of the element (inside padding and borders)
16929      */
16930     constrainTo : function(constrainTo, pad, inContent){
16931         if(typeof pad == "number"){
16932             pad = {left: pad, right:pad, top:pad, bottom:pad};
16933         }
16934         pad = pad || this.defaultPadding;
16935         var b = Roo.get(this.getEl()).getBox();
16936         var ce = Roo.get(constrainTo);
16937         var s = ce.getScroll();
16938         var c, cd = ce.dom;
16939         if(cd == document.body){
16940             c = { x: s.left, y: s.top, width: Roo.lib.Dom.getViewWidth(), height: Roo.lib.Dom.getViewHeight()};
16941         }else{
16942             xy = ce.getXY();
16943             c = {x : xy[0]+s.left, y: xy[1]+s.top, width: cd.clientWidth, height: cd.clientHeight};
16944         }
16945
16946
16947         var topSpace = b.y - c.y;
16948         var leftSpace = b.x - c.x;
16949
16950         this.resetConstraints();
16951         this.setXConstraint(leftSpace - (pad.left||0), // left
16952                 c.width - leftSpace - b.width - (pad.right||0) //right
16953         );
16954         this.setYConstraint(topSpace - (pad.top||0), //top
16955                 c.height - topSpace - b.height - (pad.bottom||0) //bottom
16956         );
16957     },
16958
16959     /**
16960      * Returns a reference to the linked element
16961      * @method getEl
16962      * @return {HTMLElement} the html element
16963      */
16964     getEl: function() {
16965         if (!this._domRef) {
16966             this._domRef = Roo.getDom(this.id);
16967         }
16968
16969         return this._domRef;
16970     },
16971
16972     /**
16973      * Returns a reference to the actual element to drag.  By default this is
16974      * the same as the html element, but it can be assigned to another
16975      * element. An example of this can be found in Roo.dd.DDProxy
16976      * @method getDragEl
16977      * @return {HTMLElement} the html element
16978      */
16979     getDragEl: function() {
16980         return Roo.getDom(this.dragElId);
16981     },
16982
16983     /**
16984      * Sets up the DragDrop object.  Must be called in the constructor of any
16985      * Roo.dd.DragDrop subclass
16986      * @method init
16987      * @param id the id of the linked element
16988      * @param {String} sGroup the group of related items
16989      * @param {object} config configuration attributes
16990      */
16991     init: function(id, sGroup, config) {
16992         this.initTarget(id, sGroup, config);
16993         if (!Roo.isTouch) {
16994             Event.on(this.id, "mousedown", this.handleMouseDown, this);
16995         }
16996         Event.on(this.id, "touchstart", this.handleMouseDown, this);
16997         // Event.on(this.id, "selectstart", Event.preventDefault);
16998     },
16999
17000     /**
17001      * Initializes Targeting functionality only... the object does not
17002      * get a mousedown handler.
17003      * @method initTarget
17004      * @param id the id of the linked element
17005      * @param {String} sGroup the group of related items
17006      * @param {object} config configuration attributes
17007      */
17008     initTarget: function(id, sGroup, config) {
17009
17010         // configuration attributes
17011         this.config = config || {};
17012
17013         // create a local reference to the drag and drop manager
17014         this.DDM = Roo.dd.DDM;
17015         // initialize the groups array
17016         this.groups = {};
17017
17018         // assume that we have an element reference instead of an id if the
17019         // parameter is not a string
17020         if (typeof id !== "string") {
17021             id = Roo.id(id);
17022         }
17023
17024         // set the id
17025         this.id = id;
17026
17027         // add to an interaction group
17028         this.addToGroup((sGroup) ? sGroup : "default");
17029
17030         // We don't want to register this as the handle with the manager
17031         // so we just set the id rather than calling the setter.
17032         this.handleElId = id;
17033
17034         // the linked element is the element that gets dragged by default
17035         this.setDragElId(id);
17036
17037         // by default, clicked anchors will not start drag operations.
17038         this.invalidHandleTypes = { A: "A" };
17039         this.invalidHandleIds = {};
17040         this.invalidHandleClasses = [];
17041
17042         this.applyConfig();
17043
17044         this.handleOnAvailable();
17045     },
17046
17047     /**
17048      * Applies the configuration parameters that were passed into the constructor.
17049      * This is supposed to happen at each level through the inheritance chain.  So
17050      * a DDProxy implentation will execute apply config on DDProxy, DD, and
17051      * DragDrop in order to get all of the parameters that are available in
17052      * each object.
17053      * @method applyConfig
17054      */
17055     applyConfig: function() {
17056
17057         // configurable properties:
17058         //    padding, isTarget, maintainOffset, primaryButtonOnly
17059         this.padding           = this.config.padding || [0, 0, 0, 0];
17060         this.isTarget          = (this.config.isTarget !== false);
17061         this.maintainOffset    = (this.config.maintainOffset);
17062         this.primaryButtonOnly = (this.config.primaryButtonOnly !== false);
17063
17064     },
17065
17066     /**
17067      * Executed when the linked element is available
17068      * @method handleOnAvailable
17069      * @private
17070      */
17071     handleOnAvailable: function() {
17072         this.available = true;
17073         this.resetConstraints();
17074         this.onAvailable();
17075     },
17076
17077      /**
17078      * Configures the padding for the target zone in px.  Effectively expands
17079      * (or reduces) the virtual object size for targeting calculations.
17080      * Supports css-style shorthand; if only one parameter is passed, all sides
17081      * will have that padding, and if only two are passed, the top and bottom
17082      * will have the first param, the left and right the second.
17083      * @method setPadding
17084      * @param {int} iTop    Top pad
17085      * @param {int} iRight  Right pad
17086      * @param {int} iBot    Bot pad
17087      * @param {int} iLeft   Left pad
17088      */
17089     setPadding: function(iTop, iRight, iBot, iLeft) {
17090         // this.padding = [iLeft, iRight, iTop, iBot];
17091         if (!iRight && 0 !== iRight) {
17092             this.padding = [iTop, iTop, iTop, iTop];
17093         } else if (!iBot && 0 !== iBot) {
17094             this.padding = [iTop, iRight, iTop, iRight];
17095         } else {
17096             this.padding = [iTop, iRight, iBot, iLeft];
17097         }
17098     },
17099
17100     /**
17101      * Stores the initial placement of the linked element.
17102      * @method setInitialPosition
17103      * @param {int} diffX   the X offset, default 0
17104      * @param {int} diffY   the Y offset, default 0
17105      */
17106     setInitPosition: function(diffX, diffY) {
17107         var el = this.getEl();
17108
17109         if (!this.DDM.verifyEl(el)) {
17110             return;
17111         }
17112
17113         var dx = diffX || 0;
17114         var dy = diffY || 0;
17115
17116         var p = Dom.getXY( el );
17117
17118         this.initPageX = p[0] - dx;
17119         this.initPageY = p[1] - dy;
17120
17121         this.lastPageX = p[0];
17122         this.lastPageY = p[1];
17123
17124
17125         this.setStartPosition(p);
17126     },
17127
17128     /**
17129      * Sets the start position of the element.  This is set when the obj
17130      * is initialized, the reset when a drag is started.
17131      * @method setStartPosition
17132      * @param pos current position (from previous lookup)
17133      * @private
17134      */
17135     setStartPosition: function(pos) {
17136         var p = pos || Dom.getXY( this.getEl() );
17137         this.deltaSetXY = null;
17138
17139         this.startPageX = p[0];
17140         this.startPageY = p[1];
17141     },
17142
17143     /**
17144      * Add this instance to a group of related drag/drop objects.  All
17145      * instances belong to at least one group, and can belong to as many
17146      * groups as needed.
17147      * @method addToGroup
17148      * @param sGroup {string} the name of the group
17149      */
17150     addToGroup: function(sGroup) {
17151         this.groups[sGroup] = true;
17152         this.DDM.regDragDrop(this, sGroup);
17153     },
17154
17155     /**
17156      * Remove's this instance from the supplied interaction group
17157      * @method removeFromGroup
17158      * @param {string}  sGroup  The group to drop
17159      */
17160     removeFromGroup: function(sGroup) {
17161         if (this.groups[sGroup]) {
17162             delete this.groups[sGroup];
17163         }
17164
17165         this.DDM.removeDDFromGroup(this, sGroup);
17166     },
17167
17168     /**
17169      * Allows you to specify that an element other than the linked element
17170      * will be moved with the cursor during a drag
17171      * @method setDragElId
17172      * @param id {string} the id of the element that will be used to initiate the drag
17173      */
17174     setDragElId: function(id) {
17175         this.dragElId = id;
17176     },
17177
17178     /**
17179      * Allows you to specify a child of the linked element that should be
17180      * used to initiate the drag operation.  An example of this would be if
17181      * you have a content div with text and links.  Clicking anywhere in the
17182      * content area would normally start the drag operation.  Use this method
17183      * to specify that an element inside of the content div is the element
17184      * that starts the drag operation.
17185      * @method setHandleElId
17186      * @param id {string} the id of the element that will be used to
17187      * initiate the drag.
17188      */
17189     setHandleElId: function(id) {
17190         if (typeof id !== "string") {
17191             id = Roo.id(id);
17192         }
17193         this.handleElId = id;
17194         this.DDM.regHandle(this.id, id);
17195     },
17196
17197     /**
17198      * Allows you to set an element outside of the linked element as a drag
17199      * handle
17200      * @method setOuterHandleElId
17201      * @param id the id of the element that will be used to initiate the drag
17202      */
17203     setOuterHandleElId: function(id) {
17204         if (typeof id !== "string") {
17205             id = Roo.id(id);
17206         }
17207         Event.on(id, "mousedown",
17208                 this.handleMouseDown, this);
17209         this.setHandleElId(id);
17210
17211         this.hasOuterHandles = true;
17212     },
17213
17214     /**
17215      * Remove all drag and drop hooks for this element
17216      * @method unreg
17217      */
17218     unreg: function() {
17219         Event.un(this.id, "mousedown",
17220                 this.handleMouseDown);
17221         Event.un(this.id, "touchstart",
17222                 this.handleMouseDown);
17223         this._domRef = null;
17224         this.DDM._remove(this);
17225     },
17226
17227     destroy : function(){
17228         this.unreg();
17229     },
17230
17231     /**
17232      * Returns true if this instance is locked, or the drag drop mgr is locked
17233      * (meaning that all drag/drop is disabled on the page.)
17234      * @method isLocked
17235      * @return {boolean} true if this obj or all drag/drop is locked, else
17236      * false
17237      */
17238     isLocked: function() {
17239         return (this.DDM.isLocked() || this.locked);
17240     },
17241
17242     /**
17243      * Fired when this object is clicked
17244      * @method handleMouseDown
17245      * @param {Event} e
17246      * @param {Roo.dd.DragDrop} oDD the clicked dd object (this dd obj)
17247      * @private
17248      */
17249     handleMouseDown: function(e, oDD){
17250      
17251         if (!Roo.isTouch && this.primaryButtonOnly && e.button != 0) {
17252             //Roo.log('not touch/ button !=0');
17253             return;
17254         }
17255         if (e.browserEvent.touches && e.browserEvent.touches.length != 1) {
17256             return; // double touch..
17257         }
17258         
17259
17260         if (this.isLocked()) {
17261             //Roo.log('locked');
17262             return;
17263         }
17264
17265         this.DDM.refreshCache(this.groups);
17266 //        Roo.log([Roo.lib.Event.getPageX(e), Roo.lib.Event.getPageY(e)]);
17267         var pt = new Roo.lib.Point(Roo.lib.Event.getPageX(e), Roo.lib.Event.getPageY(e));
17268         if (!this.hasOuterHandles && !this.DDM.isOverTarget(pt, this) )  {
17269             //Roo.log('no outer handes or not over target');
17270                 // do nothing.
17271         } else {
17272 //            Roo.log('check validator');
17273             if (this.clickValidator(e)) {
17274 //                Roo.log('validate success');
17275                 // set the initial element position
17276                 this.setStartPosition();
17277
17278
17279                 this.b4MouseDown(e);
17280                 this.onMouseDown(e);
17281
17282                 this.DDM.handleMouseDown(e, this);
17283
17284                 this.DDM.stopEvent(e);
17285             } else {
17286
17287
17288             }
17289         }
17290     },
17291
17292     clickValidator: function(e) {
17293         var target = e.getTarget();
17294         return ( this.isValidHandleChild(target) &&
17295                     (this.id == this.handleElId ||
17296                         this.DDM.handleWasClicked(target, this.id)) );
17297     },
17298
17299     /**
17300      * Allows you to specify a tag name that should not start a drag operation
17301      * when clicked.  This is designed to facilitate embedding links within a
17302      * drag handle that do something other than start the drag.
17303      * @method addInvalidHandleType
17304      * @param {string} tagName the type of element to exclude
17305      */
17306     addInvalidHandleType: function(tagName) {
17307         var type = tagName.toUpperCase();
17308         this.invalidHandleTypes[type] = type;
17309     },
17310
17311     /**
17312      * Lets you to specify an element id for a child of a drag handle
17313      * that should not initiate a drag
17314      * @method addInvalidHandleId
17315      * @param {string} id the element id of the element you wish to ignore
17316      */
17317     addInvalidHandleId: function(id) {
17318         if (typeof id !== "string") {
17319             id = Roo.id(id);
17320         }
17321         this.invalidHandleIds[id] = id;
17322     },
17323
17324     /**
17325      * Lets you specify a css class of elements that will not initiate a drag
17326      * @method addInvalidHandleClass
17327      * @param {string} cssClass the class of the elements you wish to ignore
17328      */
17329     addInvalidHandleClass: function(cssClass) {
17330         this.invalidHandleClasses.push(cssClass);
17331     },
17332
17333     /**
17334      * Unsets an excluded tag name set by addInvalidHandleType
17335      * @method removeInvalidHandleType
17336      * @param {string} tagName the type of element to unexclude
17337      */
17338     removeInvalidHandleType: function(tagName) {
17339         var type = tagName.toUpperCase();
17340         // this.invalidHandleTypes[type] = null;
17341         delete this.invalidHandleTypes[type];
17342     },
17343
17344     /**
17345      * Unsets an invalid handle id
17346      * @method removeInvalidHandleId
17347      * @param {string} id the id of the element to re-enable
17348      */
17349     removeInvalidHandleId: function(id) {
17350         if (typeof id !== "string") {
17351             id = Roo.id(id);
17352         }
17353         delete this.invalidHandleIds[id];
17354     },
17355
17356     /**
17357      * Unsets an invalid css class
17358      * @method removeInvalidHandleClass
17359      * @param {string} cssClass the class of the element(s) you wish to
17360      * re-enable
17361      */
17362     removeInvalidHandleClass: function(cssClass) {
17363         for (var i=0, len=this.invalidHandleClasses.length; i<len; ++i) {
17364             if (this.invalidHandleClasses[i] == cssClass) {
17365                 delete this.invalidHandleClasses[i];
17366             }
17367         }
17368     },
17369
17370     /**
17371      * Checks the tag exclusion list to see if this click should be ignored
17372      * @method isValidHandleChild
17373      * @param {HTMLElement} node the HTMLElement to evaluate
17374      * @return {boolean} true if this is a valid tag type, false if not
17375      */
17376     isValidHandleChild: function(node) {
17377
17378         var valid = true;
17379         // var n = (node.nodeName == "#text") ? node.parentNode : node;
17380         var nodeName;
17381         try {
17382             nodeName = node.nodeName.toUpperCase();
17383         } catch(e) {
17384             nodeName = node.nodeName;
17385         }
17386         valid = valid && !this.invalidHandleTypes[nodeName];
17387         valid = valid && !this.invalidHandleIds[node.id];
17388
17389         for (var i=0, len=this.invalidHandleClasses.length; valid && i<len; ++i) {
17390             valid = !Dom.hasClass(node, this.invalidHandleClasses[i]);
17391         }
17392
17393
17394         return valid;
17395
17396     },
17397
17398     /**
17399      * Create the array of horizontal tick marks if an interval was specified
17400      * in setXConstraint().
17401      * @method setXTicks
17402      * @private
17403      */
17404     setXTicks: function(iStartX, iTickSize) {
17405         this.xTicks = [];
17406         this.xTickSize = iTickSize;
17407
17408         var tickMap = {};
17409
17410         for (var i = this.initPageX; i >= this.minX; i = i - iTickSize) {
17411             if (!tickMap[i]) {
17412                 this.xTicks[this.xTicks.length] = i;
17413                 tickMap[i] = true;
17414             }
17415         }
17416
17417         for (i = this.initPageX; i <= this.maxX; i = i + iTickSize) {
17418             if (!tickMap[i]) {
17419                 this.xTicks[this.xTicks.length] = i;
17420                 tickMap[i] = true;
17421             }
17422         }
17423
17424         this.xTicks.sort(this.DDM.numericSort) ;
17425     },
17426
17427     /**
17428      * Create the array of vertical tick marks if an interval was specified in
17429      * setYConstraint().
17430      * @method setYTicks
17431      * @private
17432      */
17433     setYTicks: function(iStartY, iTickSize) {
17434         this.yTicks = [];
17435         this.yTickSize = iTickSize;
17436
17437         var tickMap = {};
17438
17439         for (var i = this.initPageY; i >= this.minY; i = i - iTickSize) {
17440             if (!tickMap[i]) {
17441                 this.yTicks[this.yTicks.length] = i;
17442                 tickMap[i] = true;
17443             }
17444         }
17445
17446         for (i = this.initPageY; i <= this.maxY; i = i + iTickSize) {
17447             if (!tickMap[i]) {
17448                 this.yTicks[this.yTicks.length] = i;
17449                 tickMap[i] = true;
17450             }
17451         }
17452
17453         this.yTicks.sort(this.DDM.numericSort) ;
17454     },
17455
17456     /**
17457      * By default, the element can be dragged any place on the screen.  Use
17458      * this method to limit the horizontal travel of the element.  Pass in
17459      * 0,0 for the parameters if you want to lock the drag to the y axis.
17460      * @method setXConstraint
17461      * @param {int} iLeft the number of pixels the element can move to the left
17462      * @param {int} iRight the number of pixels the element can move to the
17463      * right
17464      * @param {int} iTickSize optional parameter for specifying that the
17465      * element
17466      * should move iTickSize pixels at a time.
17467      */
17468     setXConstraint: function(iLeft, iRight, iTickSize) {
17469         this.leftConstraint = iLeft;
17470         this.rightConstraint = iRight;
17471
17472         this.minX = this.initPageX - iLeft;
17473         this.maxX = this.initPageX + iRight;
17474         if (iTickSize) { this.setXTicks(this.initPageX, iTickSize); }
17475
17476         this.constrainX = true;
17477     },
17478
17479     /**
17480      * Clears any constraints applied to this instance.  Also clears ticks
17481      * since they can't exist independent of a constraint at this time.
17482      * @method clearConstraints
17483      */
17484     clearConstraints: function() {
17485         this.constrainX = false;
17486         this.constrainY = false;
17487         this.clearTicks();
17488     },
17489
17490     /**
17491      * Clears any tick interval defined for this instance
17492      * @method clearTicks
17493      */
17494     clearTicks: function() {
17495         this.xTicks = null;
17496         this.yTicks = null;
17497         this.xTickSize = 0;
17498         this.yTickSize = 0;
17499     },
17500
17501     /**
17502      * By default, the element can be dragged any place on the screen.  Set
17503      * this to limit the vertical travel of the element.  Pass in 0,0 for the
17504      * parameters if you want to lock the drag to the x axis.
17505      * @method setYConstraint
17506      * @param {int} iUp the number of pixels the element can move up
17507      * @param {int} iDown the number of pixels the element can move down
17508      * @param {int} iTickSize optional parameter for specifying that the
17509      * element should move iTickSize pixels at a time.
17510      */
17511     setYConstraint: function(iUp, iDown, iTickSize) {
17512         this.topConstraint = iUp;
17513         this.bottomConstraint = iDown;
17514
17515         this.minY = this.initPageY - iUp;
17516         this.maxY = this.initPageY + iDown;
17517         if (iTickSize) { this.setYTicks(this.initPageY, iTickSize); }
17518
17519         this.constrainY = true;
17520
17521     },
17522
17523     /**
17524      * resetConstraints must be called if you manually reposition a dd element.
17525      * @method resetConstraints
17526      * @param {boolean} maintainOffset
17527      */
17528     resetConstraints: function() {
17529
17530
17531         // Maintain offsets if necessary
17532         if (this.initPageX || this.initPageX === 0) {
17533             // figure out how much this thing has moved
17534             var dx = (this.maintainOffset) ? this.lastPageX - this.initPageX : 0;
17535             var dy = (this.maintainOffset) ? this.lastPageY - this.initPageY : 0;
17536
17537             this.setInitPosition(dx, dy);
17538
17539         // This is the first time we have detected the element's position
17540         } else {
17541             this.setInitPosition();
17542         }
17543
17544         if (this.constrainX) {
17545             this.setXConstraint( this.leftConstraint,
17546                                  this.rightConstraint,
17547                                  this.xTickSize        );
17548         }
17549
17550         if (this.constrainY) {
17551             this.setYConstraint( this.topConstraint,
17552                                  this.bottomConstraint,
17553                                  this.yTickSize         );
17554         }
17555     },
17556
17557     /**
17558      * Normally the drag element is moved pixel by pixel, but we can specify
17559      * that it move a number of pixels at a time.  This method resolves the
17560      * location when we have it set up like this.
17561      * @method getTick
17562      * @param {int} val where we want to place the object
17563      * @param {int[]} tickArray sorted array of valid points
17564      * @return {int} the closest tick
17565      * @private
17566      */
17567     getTick: function(val, tickArray) {
17568
17569         if (!tickArray) {
17570             // If tick interval is not defined, it is effectively 1 pixel,
17571             // so we return the value passed to us.
17572             return val;
17573         } else if (tickArray[0] >= val) {
17574             // The value is lower than the first tick, so we return the first
17575             // tick.
17576             return tickArray[0];
17577         } else {
17578             for (var i=0, len=tickArray.length; i<len; ++i) {
17579                 var next = i + 1;
17580                 if (tickArray[next] && tickArray[next] >= val) {
17581                     var diff1 = val - tickArray[i];
17582                     var diff2 = tickArray[next] - val;
17583                     return (diff2 > diff1) ? tickArray[i] : tickArray[next];
17584                 }
17585             }
17586
17587             // The value is larger than the last tick, so we return the last
17588             // tick.
17589             return tickArray[tickArray.length - 1];
17590         }
17591     },
17592
17593     /**
17594      * toString method
17595      * @method toString
17596      * @return {string} string representation of the dd obj
17597      */
17598     toString: function() {
17599         return ("DragDrop " + this.id);
17600     }
17601
17602 });
17603
17604 })();
17605 /*
17606  * Based on:
17607  * Ext JS Library 1.1.1
17608  * Copyright(c) 2006-2007, Ext JS, LLC.
17609  *
17610  * Originally Released Under LGPL - original licence link has changed is not relivant.
17611  *
17612  * Fork - LGPL
17613  * <script type="text/javascript">
17614  */
17615
17616
17617 /**
17618  * The drag and drop utility provides a framework for building drag and drop
17619  * applications.  In addition to enabling drag and drop for specific elements,
17620  * the drag and drop elements are tracked by the manager class, and the
17621  * interactions between the various elements are tracked during the drag and
17622  * the implementing code is notified about these important moments.
17623  */
17624
17625 // Only load the library once.  Rewriting the manager class would orphan
17626 // existing drag and drop instances.
17627 if (!Roo.dd.DragDropMgr) {
17628
17629 /**
17630  * @class Roo.dd.DragDropMgr
17631  * DragDropMgr is a singleton that tracks the element interaction for
17632  * all DragDrop items in the window.  Generally, you will not call
17633  * this class directly, but it does have helper methods that could
17634  * be useful in your DragDrop implementations.
17635  * @singleton
17636  */
17637 Roo.dd.DragDropMgr = function() {
17638
17639     var Event = Roo.EventManager;
17640
17641     return {
17642
17643         /**
17644          * Two dimensional Array of registered DragDrop objects.  The first
17645          * dimension is the DragDrop item group, the second the DragDrop
17646          * object.
17647          * @property ids
17648          * @type {string: string}
17649          * @private
17650          * @static
17651          */
17652         ids: {},
17653
17654         /**
17655          * Array of element ids defined as drag handles.  Used to determine
17656          * if the element that generated the mousedown event is actually the
17657          * handle and not the html element itself.
17658          * @property handleIds
17659          * @type {string: string}
17660          * @private
17661          * @static
17662          */
17663         handleIds: {},
17664
17665         /**
17666          * the DragDrop object that is currently being dragged
17667          * @property dragCurrent
17668          * @type DragDrop
17669          * @private
17670          * @static
17671          **/
17672         dragCurrent: null,
17673
17674         /**
17675          * the DragDrop object(s) that are being hovered over
17676          * @property dragOvers
17677          * @type Array
17678          * @private
17679          * @static
17680          */
17681         dragOvers: {},
17682
17683         /**
17684          * the X distance between the cursor and the object being dragged
17685          * @property deltaX
17686          * @type int
17687          * @private
17688          * @static
17689          */
17690         deltaX: 0,
17691
17692         /**
17693          * the Y distance between the cursor and the object being dragged
17694          * @property deltaY
17695          * @type int
17696          * @private
17697          * @static
17698          */
17699         deltaY: 0,
17700
17701         /**
17702          * Flag to determine if we should prevent the default behavior of the
17703          * events we define. By default this is true, but this can be set to
17704          * false if you need the default behavior (not recommended)
17705          * @property preventDefault
17706          * @type boolean
17707          * @static
17708          */
17709         preventDefault: true,
17710
17711         /**
17712          * Flag to determine if we should stop the propagation of the events
17713          * we generate. This is true by default but you may want to set it to
17714          * false if the html element contains other features that require the
17715          * mouse click.
17716          * @property stopPropagation
17717          * @type boolean
17718          * @static
17719          */
17720         stopPropagation: true,
17721
17722         /**
17723          * Internal flag that is set to true when drag and drop has been
17724          * intialized
17725          * @property initialized
17726          * @private
17727          * @static
17728          */
17729         initalized: false,
17730
17731         /**
17732          * All drag and drop can be disabled.
17733          * @property locked
17734          * @private
17735          * @static
17736          */
17737         locked: false,
17738
17739         /**
17740          * Called the first time an element is registered.
17741          * @method init
17742          * @private
17743          * @static
17744          */
17745         init: function() {
17746             this.initialized = true;
17747         },
17748
17749         /**
17750          * In point mode, drag and drop interaction is defined by the
17751          * location of the cursor during the drag/drop
17752          * @property POINT
17753          * @type int
17754          * @static
17755          */
17756         POINT: 0,
17757
17758         /**
17759          * In intersect mode, drag and drop interactio nis defined by the
17760          * overlap of two or more drag and drop objects.
17761          * @property INTERSECT
17762          * @type int
17763          * @static
17764          */
17765         INTERSECT: 1,
17766
17767         /**
17768          * The current drag and drop mode.  Default: POINT
17769          * @property mode
17770          * @type int
17771          * @static
17772          */
17773         mode: 0,
17774
17775         /**
17776          * Runs method on all drag and drop objects
17777          * @method _execOnAll
17778          * @private
17779          * @static
17780          */
17781         _execOnAll: function(sMethod, args) {
17782             for (var i in this.ids) {
17783                 for (var j in this.ids[i]) {
17784                     var oDD = this.ids[i][j];
17785                     if (! this.isTypeOfDD(oDD)) {
17786                         continue;
17787                     }
17788                     oDD[sMethod].apply(oDD, args);
17789                 }
17790             }
17791         },
17792
17793         /**
17794          * Drag and drop initialization.  Sets up the global event handlers
17795          * @method _onLoad
17796          * @private
17797          * @static
17798          */
17799         _onLoad: function() {
17800
17801             this.init();
17802
17803             if (!Roo.isTouch) {
17804                 Event.on(document, "mouseup",   this.handleMouseUp, this, true);
17805                 Event.on(document, "mousemove", this.handleMouseMove, this, true);
17806             }
17807             Event.on(document, "touchend",   this.handleMouseUp, this, true);
17808             Event.on(document, "touchmove", this.handleMouseMove, this, true);
17809             
17810             Event.on(window,   "unload",    this._onUnload, this, true);
17811             Event.on(window,   "resize",    this._onResize, this, true);
17812             // Event.on(window,   "mouseout",    this._test);
17813
17814         },
17815
17816         /**
17817          * Reset constraints on all drag and drop objs
17818          * @method _onResize
17819          * @private
17820          * @static
17821          */
17822         _onResize: function(e) {
17823             this._execOnAll("resetConstraints", []);
17824         },
17825
17826         /**
17827          * Lock all drag and drop functionality
17828          * @method lock
17829          * @static
17830          */
17831         lock: function() { this.locked = true; },
17832
17833         /**
17834          * Unlock all drag and drop functionality
17835          * @method unlock
17836          * @static
17837          */
17838         unlock: function() { this.locked = false; },
17839
17840         /**
17841          * Is drag and drop locked?
17842          * @method isLocked
17843          * @return {boolean} True if drag and drop is locked, false otherwise.
17844          * @static
17845          */
17846         isLocked: function() { return this.locked; },
17847
17848         /**
17849          * Location cache that is set for all drag drop objects when a drag is
17850          * initiated, cleared when the drag is finished.
17851          * @property locationCache
17852          * @private
17853          * @static
17854          */
17855         locationCache: {},
17856
17857         /**
17858          * Set useCache to false if you want to force object the lookup of each
17859          * drag and drop linked element constantly during a drag.
17860          * @property useCache
17861          * @type boolean
17862          * @static
17863          */
17864         useCache: true,
17865
17866         /**
17867          * The number of pixels that the mouse needs to move after the
17868          * mousedown before the drag is initiated.  Default=3;
17869          * @property clickPixelThresh
17870          * @type int
17871          * @static
17872          */
17873         clickPixelThresh: 3,
17874
17875         /**
17876          * The number of milliseconds after the mousedown event to initiate the
17877          * drag if we don't get a mouseup event. Default=1000
17878          * @property clickTimeThresh
17879          * @type int
17880          * @static
17881          */
17882         clickTimeThresh: 350,
17883
17884         /**
17885          * Flag that indicates that either the drag pixel threshold or the
17886          * mousdown time threshold has been met
17887          * @property dragThreshMet
17888          * @type boolean
17889          * @private
17890          * @static
17891          */
17892         dragThreshMet: false,
17893
17894         /**
17895          * Timeout used for the click time threshold
17896          * @property clickTimeout
17897          * @type Object
17898          * @private
17899          * @static
17900          */
17901         clickTimeout: null,
17902
17903         /**
17904          * The X position of the mousedown event stored for later use when a
17905          * drag threshold is met.
17906          * @property startX
17907          * @type int
17908          * @private
17909          * @static
17910          */
17911         startX: 0,
17912
17913         /**
17914          * The Y position of the mousedown event stored for later use when a
17915          * drag threshold is met.
17916          * @property startY
17917          * @type int
17918          * @private
17919          * @static
17920          */
17921         startY: 0,
17922
17923         /**
17924          * Each DragDrop instance must be registered with the DragDropMgr.
17925          * This is executed in DragDrop.init()
17926          * @method regDragDrop
17927          * @param {DragDrop} oDD the DragDrop object to register
17928          * @param {String} sGroup the name of the group this element belongs to
17929          * @static
17930          */
17931         regDragDrop: function(oDD, sGroup) {
17932             if (!this.initialized) { this.init(); }
17933
17934             if (!this.ids[sGroup]) {
17935                 this.ids[sGroup] = {};
17936             }
17937             this.ids[sGroup][oDD.id] = oDD;
17938         },
17939
17940         /**
17941          * Removes the supplied dd instance from the supplied group. Executed
17942          * by DragDrop.removeFromGroup, so don't call this function directly.
17943          * @method removeDDFromGroup
17944          * @private
17945          * @static
17946          */
17947         removeDDFromGroup: function(oDD, sGroup) {
17948             if (!this.ids[sGroup]) {
17949                 this.ids[sGroup] = {};
17950             }
17951
17952             var obj = this.ids[sGroup];
17953             if (obj && obj[oDD.id]) {
17954                 delete obj[oDD.id];
17955             }
17956         },
17957
17958         /**
17959          * Unregisters a drag and drop item.  This is executed in
17960          * DragDrop.unreg, use that method instead of calling this directly.
17961          * @method _remove
17962          * @private
17963          * @static
17964          */
17965         _remove: function(oDD) {
17966             for (var g in oDD.groups) {
17967                 if (g && this.ids[g][oDD.id]) {
17968                     delete this.ids[g][oDD.id];
17969                 }
17970             }
17971             delete this.handleIds[oDD.id];
17972         },
17973
17974         /**
17975          * Each DragDrop handle element must be registered.  This is done
17976          * automatically when executing DragDrop.setHandleElId()
17977          * @method regHandle
17978          * @param {String} sDDId the DragDrop id this element is a handle for
17979          * @param {String} sHandleId the id of the element that is the drag
17980          * handle
17981          * @static
17982          */
17983         regHandle: function(sDDId, sHandleId) {
17984             if (!this.handleIds[sDDId]) {
17985                 this.handleIds[sDDId] = {};
17986             }
17987             this.handleIds[sDDId][sHandleId] = sHandleId;
17988         },
17989
17990         /**
17991          * Utility function to determine if a given element has been
17992          * registered as a drag drop item.
17993          * @method isDragDrop
17994          * @param {String} id the element id to check
17995          * @return {boolean} true if this element is a DragDrop item,
17996          * false otherwise
17997          * @static
17998          */
17999         isDragDrop: function(id) {
18000             return ( this.getDDById(id) ) ? true : false;
18001         },
18002
18003         /**
18004          * Returns the drag and drop instances that are in all groups the
18005          * passed in instance belongs to.
18006          * @method getRelated
18007          * @param {DragDrop} p_oDD the obj to get related data for
18008          * @param {boolean} bTargetsOnly if true, only return targetable objs
18009          * @return {DragDrop[]} the related instances
18010          * @static
18011          */
18012         getRelated: function(p_oDD, bTargetsOnly) {
18013             var oDDs = [];
18014             for (var i in p_oDD.groups) {
18015                 for (j in this.ids[i]) {
18016                     var dd = this.ids[i][j];
18017                     if (! this.isTypeOfDD(dd)) {
18018                         continue;
18019                     }
18020                     if (!bTargetsOnly || dd.isTarget) {
18021                         oDDs[oDDs.length] = dd;
18022                     }
18023                 }
18024             }
18025
18026             return oDDs;
18027         },
18028
18029         /**
18030          * Returns true if the specified dd target is a legal target for
18031          * the specifice drag obj
18032          * @method isLegalTarget
18033          * @param {DragDrop} the drag obj
18034          * @param {DragDrop} the target
18035          * @return {boolean} true if the target is a legal target for the
18036          * dd obj
18037          * @static
18038          */
18039         isLegalTarget: function (oDD, oTargetDD) {
18040             var targets = this.getRelated(oDD, true);
18041             for (var i=0, len=targets.length;i<len;++i) {
18042                 if (targets[i].id == oTargetDD.id) {
18043                     return true;
18044                 }
18045             }
18046
18047             return false;
18048         },
18049
18050         /**
18051          * My goal is to be able to transparently determine if an object is
18052          * typeof DragDrop, and the exact subclass of DragDrop.  typeof
18053          * returns "object", oDD.constructor.toString() always returns
18054          * "DragDrop" and not the name of the subclass.  So for now it just
18055          * evaluates a well-known variable in DragDrop.
18056          * @method isTypeOfDD
18057          * @param {Object} the object to evaluate
18058          * @return {boolean} true if typeof oDD = DragDrop
18059          * @static
18060          */
18061         isTypeOfDD: function (oDD) {
18062             return (oDD && oDD.__ygDragDrop);
18063         },
18064
18065         /**
18066          * Utility function to determine if a given element has been
18067          * registered as a drag drop handle for the given Drag Drop object.
18068          * @method isHandle
18069          * @param {String} id the element id to check
18070          * @return {boolean} true if this element is a DragDrop handle, false
18071          * otherwise
18072          * @static
18073          */
18074         isHandle: function(sDDId, sHandleId) {
18075             return ( this.handleIds[sDDId] &&
18076                             this.handleIds[sDDId][sHandleId] );
18077         },
18078
18079         /**
18080          * Returns the DragDrop instance for a given id
18081          * @method getDDById
18082          * @param {String} id the id of the DragDrop object
18083          * @return {DragDrop} the drag drop object, null if it is not found
18084          * @static
18085          */
18086         getDDById: function(id) {
18087             for (var i in this.ids) {
18088                 if (this.ids[i][id]) {
18089                     return this.ids[i][id];
18090                 }
18091             }
18092             return null;
18093         },
18094
18095         /**
18096          * Fired after a registered DragDrop object gets the mousedown event.
18097          * Sets up the events required to track the object being dragged
18098          * @method handleMouseDown
18099          * @param {Event} e the event
18100          * @param oDD the DragDrop object being dragged
18101          * @private
18102          * @static
18103          */
18104         handleMouseDown: function(e, oDD) {
18105             if(Roo.QuickTips){
18106                 Roo.QuickTips.disable();
18107             }
18108             this.currentTarget = e.getTarget();
18109
18110             this.dragCurrent = oDD;
18111
18112             var el = oDD.getEl();
18113
18114             // track start position
18115             this.startX = e.getPageX();
18116             this.startY = e.getPageY();
18117
18118             this.deltaX = this.startX - el.offsetLeft;
18119             this.deltaY = this.startY - el.offsetTop;
18120
18121             this.dragThreshMet = false;
18122
18123             this.clickTimeout = setTimeout(
18124                     function() {
18125                         var DDM = Roo.dd.DDM;
18126                         DDM.startDrag(DDM.startX, DDM.startY);
18127                     },
18128                     this.clickTimeThresh );
18129         },
18130
18131         /**
18132          * Fired when either the drag pixel threshol or the mousedown hold
18133          * time threshold has been met.
18134          * @method startDrag
18135          * @param x {int} the X position of the original mousedown
18136          * @param y {int} the Y position of the original mousedown
18137          * @static
18138          */
18139         startDrag: function(x, y) {
18140             clearTimeout(this.clickTimeout);
18141             if (this.dragCurrent) {
18142                 this.dragCurrent.b4StartDrag(x, y);
18143                 this.dragCurrent.startDrag(x, y);
18144             }
18145             this.dragThreshMet = true;
18146         },
18147
18148         /**
18149          * Internal function to handle the mouseup event.  Will be invoked
18150          * from the context of the document.
18151          * @method handleMouseUp
18152          * @param {Event} e the event
18153          * @private
18154          * @static
18155          */
18156         handleMouseUp: function(e) {
18157
18158             if(Roo.QuickTips){
18159                 Roo.QuickTips.enable();
18160             }
18161             if (! this.dragCurrent) {
18162                 return;
18163             }
18164
18165             clearTimeout(this.clickTimeout);
18166
18167             if (this.dragThreshMet) {
18168                 this.fireEvents(e, true);
18169             } else {
18170             }
18171
18172             this.stopDrag(e);
18173
18174             this.stopEvent(e);
18175         },
18176
18177         /**
18178          * Utility to stop event propagation and event default, if these
18179          * features are turned on.
18180          * @method stopEvent
18181          * @param {Event} e the event as returned by this.getEvent()
18182          * @static
18183          */
18184         stopEvent: function(e){
18185             if(this.stopPropagation) {
18186                 e.stopPropagation();
18187             }
18188
18189             if (this.preventDefault) {
18190                 e.preventDefault();
18191             }
18192         },
18193
18194         /**
18195          * Internal function to clean up event handlers after the drag
18196          * operation is complete
18197          * @method stopDrag
18198          * @param {Event} e the event
18199          * @private
18200          * @static
18201          */
18202         stopDrag: function(e) {
18203             // Fire the drag end event for the item that was dragged
18204             if (this.dragCurrent) {
18205                 if (this.dragThreshMet) {
18206                     this.dragCurrent.b4EndDrag(e);
18207                     this.dragCurrent.endDrag(e);
18208                 }
18209
18210                 this.dragCurrent.onMouseUp(e);
18211             }
18212
18213             this.dragCurrent = null;
18214             this.dragOvers = {};
18215         },
18216
18217         /**
18218          * Internal function to handle the mousemove event.  Will be invoked
18219          * from the context of the html element.
18220          *
18221          * @TODO figure out what we can do about mouse events lost when the
18222          * user drags objects beyond the window boundary.  Currently we can
18223          * detect this in internet explorer by verifying that the mouse is
18224          * down during the mousemove event.  Firefox doesn't give us the
18225          * button state on the mousemove event.
18226          * @method handleMouseMove
18227          * @param {Event} e the event
18228          * @private
18229          * @static
18230          */
18231         handleMouseMove: function(e) {
18232             if (! this.dragCurrent) {
18233                 return true;
18234             }
18235
18236             // var button = e.which || e.button;
18237
18238             // check for IE mouseup outside of page boundary
18239             if (Roo.isIE && (e.button !== 0 && e.button !== 1 && e.button !== 2)) {
18240                 this.stopEvent(e);
18241                 return this.handleMouseUp(e);
18242             }
18243
18244             if (!this.dragThreshMet) {
18245                 var diffX = Math.abs(this.startX - e.getPageX());
18246                 var diffY = Math.abs(this.startY - e.getPageY());
18247                 if (diffX > this.clickPixelThresh ||
18248                             diffY > this.clickPixelThresh) {
18249                     this.startDrag(this.startX, this.startY);
18250                 }
18251             }
18252
18253             if (this.dragThreshMet) {
18254                 this.dragCurrent.b4Drag(e);
18255                 this.dragCurrent.onDrag(e);
18256                 if(!this.dragCurrent.moveOnly){
18257                     this.fireEvents(e, false);
18258                 }
18259             }
18260
18261             this.stopEvent(e);
18262
18263             return true;
18264         },
18265
18266         /**
18267          * Iterates over all of the DragDrop elements to find ones we are
18268          * hovering over or dropping on
18269          * @method fireEvents
18270          * @param {Event} e the event
18271          * @param {boolean} isDrop is this a drop op or a mouseover op?
18272          * @private
18273          * @static
18274          */
18275         fireEvents: function(e, isDrop) {
18276             var dc = this.dragCurrent;
18277
18278             // If the user did the mouse up outside of the window, we could
18279             // get here even though we have ended the drag.
18280             if (!dc || dc.isLocked()) {
18281                 return;
18282             }
18283
18284             var pt = e.getPoint();
18285
18286             // cache the previous dragOver array
18287             var oldOvers = [];
18288
18289             var outEvts   = [];
18290             var overEvts  = [];
18291             var dropEvts  = [];
18292             var enterEvts = [];
18293
18294             // Check to see if the object(s) we were hovering over is no longer
18295             // being hovered over so we can fire the onDragOut event
18296             for (var i in this.dragOvers) {
18297
18298                 var ddo = this.dragOvers[i];
18299
18300                 if (! this.isTypeOfDD(ddo)) {
18301                     continue;
18302                 }
18303
18304                 if (! this.isOverTarget(pt, ddo, this.mode)) {
18305                     outEvts.push( ddo );
18306                 }
18307
18308                 oldOvers[i] = true;
18309                 delete this.dragOvers[i];
18310             }
18311
18312             for (var sGroup in dc.groups) {
18313
18314                 if ("string" != typeof sGroup) {
18315                     continue;
18316                 }
18317
18318                 for (i in this.ids[sGroup]) {
18319                     var oDD = this.ids[sGroup][i];
18320                     if (! this.isTypeOfDD(oDD)) {
18321                         continue;
18322                     }
18323
18324                     if (oDD.isTarget && !oDD.isLocked() && oDD != dc) {
18325                         if (this.isOverTarget(pt, oDD, this.mode)) {
18326                             // look for drop interactions
18327                             if (isDrop) {
18328                                 dropEvts.push( oDD );
18329                             // look for drag enter and drag over interactions
18330                             } else {
18331
18332                                 // initial drag over: dragEnter fires
18333                                 if (!oldOvers[oDD.id]) {
18334                                     enterEvts.push( oDD );
18335                                 // subsequent drag overs: dragOver fires
18336                                 } else {
18337                                     overEvts.push( oDD );
18338                                 }
18339
18340                                 this.dragOvers[oDD.id] = oDD;
18341                             }
18342                         }
18343                     }
18344                 }
18345             }
18346
18347             if (this.mode) {
18348                 if (outEvts.length) {
18349                     dc.b4DragOut(e, outEvts);
18350                     dc.onDragOut(e, outEvts);
18351                 }
18352
18353                 if (enterEvts.length) {
18354                     dc.onDragEnter(e, enterEvts);
18355                 }
18356
18357                 if (overEvts.length) {
18358                     dc.b4DragOver(e, overEvts);
18359                     dc.onDragOver(e, overEvts);
18360                 }
18361
18362                 if (dropEvts.length) {
18363                     dc.b4DragDrop(e, dropEvts);
18364                     dc.onDragDrop(e, dropEvts);
18365                 }
18366
18367             } else {
18368                 // fire dragout events
18369                 var len = 0;
18370                 for (i=0, len=outEvts.length; i<len; ++i) {
18371                     dc.b4DragOut(e, outEvts[i].id);
18372                     dc.onDragOut(e, outEvts[i].id);
18373                 }
18374
18375                 // fire enter events
18376                 for (i=0,len=enterEvts.length; i<len; ++i) {
18377                     // dc.b4DragEnter(e, oDD.id);
18378                     dc.onDragEnter(e, enterEvts[i].id);
18379                 }
18380
18381                 // fire over events
18382                 for (i=0,len=overEvts.length; i<len; ++i) {
18383                     dc.b4DragOver(e, overEvts[i].id);
18384                     dc.onDragOver(e, overEvts[i].id);
18385                 }
18386
18387                 // fire drop events
18388                 for (i=0, len=dropEvts.length; i<len; ++i) {
18389                     dc.b4DragDrop(e, dropEvts[i].id);
18390                     dc.onDragDrop(e, dropEvts[i].id);
18391                 }
18392
18393             }
18394
18395             // notify about a drop that did not find a target
18396             if (isDrop && !dropEvts.length) {
18397                 dc.onInvalidDrop(e);
18398             }
18399
18400         },
18401
18402         /**
18403          * Helper function for getting the best match from the list of drag
18404          * and drop objects returned by the drag and drop events when we are
18405          * in INTERSECT mode.  It returns either the first object that the
18406          * cursor is over, or the object that has the greatest overlap with
18407          * the dragged element.
18408          * @method getBestMatch
18409          * @param  {DragDrop[]} dds The array of drag and drop objects
18410          * targeted
18411          * @return {DragDrop}       The best single match
18412          * @static
18413          */
18414         getBestMatch: function(dds) {
18415             var winner = null;
18416             // Return null if the input is not what we expect
18417             //if (!dds || !dds.length || dds.length == 0) {
18418                // winner = null;
18419             // If there is only one item, it wins
18420             //} else if (dds.length == 1) {
18421
18422             var len = dds.length;
18423
18424             if (len == 1) {
18425                 winner = dds[0];
18426             } else {
18427                 // Loop through the targeted items
18428                 for (var i=0; i<len; ++i) {
18429                     var dd = dds[i];
18430                     // If the cursor is over the object, it wins.  If the
18431                     // cursor is over multiple matches, the first one we come
18432                     // to wins.
18433                     if (dd.cursorIsOver) {
18434                         winner = dd;
18435                         break;
18436                     // Otherwise the object with the most overlap wins
18437                     } else {
18438                         if (!winner ||
18439                             winner.overlap.getArea() < dd.overlap.getArea()) {
18440                             winner = dd;
18441                         }
18442                     }
18443                 }
18444             }
18445
18446             return winner;
18447         },
18448
18449         /**
18450          * Refreshes the cache of the top-left and bottom-right points of the
18451          * drag and drop objects in the specified group(s).  This is in the
18452          * format that is stored in the drag and drop instance, so typical
18453          * usage is:
18454          * <code>
18455          * Roo.dd.DragDropMgr.refreshCache(ddinstance.groups);
18456          * </code>
18457          * Alternatively:
18458          * <code>
18459          * Roo.dd.DragDropMgr.refreshCache({group1:true, group2:true});
18460          * </code>
18461          * @TODO this really should be an indexed array.  Alternatively this
18462          * method could accept both.
18463          * @method refreshCache
18464          * @param {Object} groups an associative array of groups to refresh
18465          * @static
18466          */
18467         refreshCache: function(groups) {
18468             for (var sGroup in groups) {
18469                 if ("string" != typeof sGroup) {
18470                     continue;
18471                 }
18472                 for (var i in this.ids[sGroup]) {
18473                     var oDD = this.ids[sGroup][i];
18474
18475                     if (this.isTypeOfDD(oDD)) {
18476                     // if (this.isTypeOfDD(oDD) && oDD.isTarget) {
18477                         var loc = this.getLocation(oDD);
18478                         if (loc) {
18479                             this.locationCache[oDD.id] = loc;
18480                         } else {
18481                             delete this.locationCache[oDD.id];
18482                             // this will unregister the drag and drop object if
18483                             // the element is not in a usable state
18484                             // oDD.unreg();
18485                         }
18486                     }
18487                 }
18488             }
18489         },
18490
18491         /**
18492          * This checks to make sure an element exists and is in the DOM.  The
18493          * main purpose is to handle cases where innerHTML is used to remove
18494          * drag and drop objects from the DOM.  IE provides an 'unspecified
18495          * error' when trying to access the offsetParent of such an element
18496          * @method verifyEl
18497          * @param {HTMLElement} el the element to check
18498          * @return {boolean} true if the element looks usable
18499          * @static
18500          */
18501         verifyEl: function(el) {
18502             if (el) {
18503                 var parent;
18504                 if(Roo.isIE){
18505                     try{
18506                         parent = el.offsetParent;
18507                     }catch(e){}
18508                 }else{
18509                     parent = el.offsetParent;
18510                 }
18511                 if (parent) {
18512                     return true;
18513                 }
18514             }
18515
18516             return false;
18517         },
18518
18519         /**
18520          * Returns a Region object containing the drag and drop element's position
18521          * and size, including the padding configured for it
18522          * @method getLocation
18523          * @param {DragDrop} oDD the drag and drop object to get the
18524          *                       location for
18525          * @return {Roo.lib.Region} a Region object representing the total area
18526          *                             the element occupies, including any padding
18527          *                             the instance is configured for.
18528          * @static
18529          */
18530         getLocation: function(oDD) {
18531             if (! this.isTypeOfDD(oDD)) {
18532                 return null;
18533             }
18534
18535             var el = oDD.getEl(), pos, x1, x2, y1, y2, t, r, b, l;
18536
18537             try {
18538                 pos= Roo.lib.Dom.getXY(el);
18539             } catch (e) { }
18540
18541             if (!pos) {
18542                 return null;
18543             }
18544
18545             x1 = pos[0];
18546             x2 = x1 + el.offsetWidth;
18547             y1 = pos[1];
18548             y2 = y1 + el.offsetHeight;
18549
18550             t = y1 - oDD.padding[0];
18551             r = x2 + oDD.padding[1];
18552             b = y2 + oDD.padding[2];
18553             l = x1 - oDD.padding[3];
18554
18555             return new Roo.lib.Region( t, r, b, l );
18556         },
18557
18558         /**
18559          * Checks the cursor location to see if it over the target
18560          * @method isOverTarget
18561          * @param {Roo.lib.Point} pt The point to evaluate
18562          * @param {DragDrop} oTarget the DragDrop object we are inspecting
18563          * @return {boolean} true if the mouse is over the target
18564          * @private
18565          * @static
18566          */
18567         isOverTarget: function(pt, oTarget, intersect) {
18568             // use cache if available
18569             var loc = this.locationCache[oTarget.id];
18570             if (!loc || !this.useCache) {
18571                 loc = this.getLocation(oTarget);
18572                 this.locationCache[oTarget.id] = loc;
18573
18574             }
18575
18576             if (!loc) {
18577                 return false;
18578             }
18579
18580             oTarget.cursorIsOver = loc.contains( pt );
18581
18582             // DragDrop is using this as a sanity check for the initial mousedown
18583             // in this case we are done.  In POINT mode, if the drag obj has no
18584             // contraints, we are also done. Otherwise we need to evaluate the
18585             // location of the target as related to the actual location of the
18586             // dragged element.
18587             var dc = this.dragCurrent;
18588             if (!dc || !dc.getTargetCoord ||
18589                     (!intersect && !dc.constrainX && !dc.constrainY)) {
18590                 return oTarget.cursorIsOver;
18591             }
18592
18593             oTarget.overlap = null;
18594
18595             // Get the current location of the drag element, this is the
18596             // location of the mouse event less the delta that represents
18597             // where the original mousedown happened on the element.  We
18598             // need to consider constraints and ticks as well.
18599             var pos = dc.getTargetCoord(pt.x, pt.y);
18600
18601             var el = dc.getDragEl();
18602             var curRegion = new Roo.lib.Region( pos.y,
18603                                                    pos.x + el.offsetWidth,
18604                                                    pos.y + el.offsetHeight,
18605                                                    pos.x );
18606
18607             var overlap = curRegion.intersect(loc);
18608
18609             if (overlap) {
18610                 oTarget.overlap = overlap;
18611                 return (intersect) ? true : oTarget.cursorIsOver;
18612             } else {
18613                 return false;
18614             }
18615         },
18616
18617         /**
18618          * unload event handler
18619          * @method _onUnload
18620          * @private
18621          * @static
18622          */
18623         _onUnload: function(e, me) {
18624             Roo.dd.DragDropMgr.unregAll();
18625         },
18626
18627         /**
18628          * Cleans up the drag and drop events and objects.
18629          * @method unregAll
18630          * @private
18631          * @static
18632          */
18633         unregAll: function() {
18634
18635             if (this.dragCurrent) {
18636                 this.stopDrag();
18637                 this.dragCurrent = null;
18638             }
18639
18640             this._execOnAll("unreg", []);
18641
18642             for (i in this.elementCache) {
18643                 delete this.elementCache[i];
18644             }
18645
18646             this.elementCache = {};
18647             this.ids = {};
18648         },
18649
18650         /**
18651          * A cache of DOM elements
18652          * @property elementCache
18653          * @private
18654          * @static
18655          */
18656         elementCache: {},
18657
18658         /**
18659          * Get the wrapper for the DOM element specified
18660          * @method getElWrapper
18661          * @param {String} id the id of the element to get
18662          * @return {Roo.dd.DDM.ElementWrapper} the wrapped element
18663          * @private
18664          * @deprecated This wrapper isn't that useful
18665          * @static
18666          */
18667         getElWrapper: function(id) {
18668             var oWrapper = this.elementCache[id];
18669             if (!oWrapper || !oWrapper.el) {
18670                 oWrapper = this.elementCache[id] =
18671                     new this.ElementWrapper(Roo.getDom(id));
18672             }
18673             return oWrapper;
18674         },
18675
18676         /**
18677          * Returns the actual DOM element
18678          * @method getElement
18679          * @param {String} id the id of the elment to get
18680          * @return {Object} The element
18681          * @deprecated use Roo.getDom instead
18682          * @static
18683          */
18684         getElement: function(id) {
18685             return Roo.getDom(id);
18686         },
18687
18688         /**
18689          * Returns the style property for the DOM element (i.e.,
18690          * document.getElById(id).style)
18691          * @method getCss
18692          * @param {String} id the id of the elment to get
18693          * @return {Object} The style property of the element
18694          * @deprecated use Roo.getDom instead
18695          * @static
18696          */
18697         getCss: function(id) {
18698             var el = Roo.getDom(id);
18699             return (el) ? el.style : null;
18700         },
18701
18702         /**
18703          * Inner class for cached elements
18704          * @class DragDropMgr.ElementWrapper
18705          * @for DragDropMgr
18706          * @private
18707          * @deprecated
18708          */
18709         ElementWrapper: function(el) {
18710                 /**
18711                  * The element
18712                  * @property el
18713                  */
18714                 this.el = el || null;
18715                 /**
18716                  * The element id
18717                  * @property id
18718                  */
18719                 this.id = this.el && el.id;
18720                 /**
18721                  * A reference to the style property
18722                  * @property css
18723                  */
18724                 this.css = this.el && el.style;
18725             },
18726
18727         /**
18728          * Returns the X position of an html element
18729          * @method getPosX
18730          * @param el the element for which to get the position
18731          * @return {int} the X coordinate
18732          * @for DragDropMgr
18733          * @deprecated use Roo.lib.Dom.getX instead
18734          * @static
18735          */
18736         getPosX: function(el) {
18737             return Roo.lib.Dom.getX(el);
18738         },
18739
18740         /**
18741          * Returns the Y position of an html element
18742          * @method getPosY
18743          * @param el the element for which to get the position
18744          * @return {int} the Y coordinate
18745          * @deprecated use Roo.lib.Dom.getY instead
18746          * @static
18747          */
18748         getPosY: function(el) {
18749             return Roo.lib.Dom.getY(el);
18750         },
18751
18752         /**
18753          * Swap two nodes.  In IE, we use the native method, for others we
18754          * emulate the IE behavior
18755          * @method swapNode
18756          * @param n1 the first node to swap
18757          * @param n2 the other node to swap
18758          * @static
18759          */
18760         swapNode: function(n1, n2) {
18761             if (n1.swapNode) {
18762                 n1.swapNode(n2);
18763             } else {
18764                 var p = n2.parentNode;
18765                 var s = n2.nextSibling;
18766
18767                 if (s == n1) {
18768                     p.insertBefore(n1, n2);
18769                 } else if (n2 == n1.nextSibling) {
18770                     p.insertBefore(n2, n1);
18771                 } else {
18772                     n1.parentNode.replaceChild(n2, n1);
18773                     p.insertBefore(n1, s);
18774                 }
18775             }
18776         },
18777
18778         /**
18779          * Returns the current scroll position
18780          * @method getScroll
18781          * @private
18782          * @static
18783          */
18784         getScroll: function () {
18785             var t, l, dde=document.documentElement, db=document.body;
18786             if (dde && (dde.scrollTop || dde.scrollLeft)) {
18787                 t = dde.scrollTop;
18788                 l = dde.scrollLeft;
18789             } else if (db) {
18790                 t = db.scrollTop;
18791                 l = db.scrollLeft;
18792             } else {
18793
18794             }
18795             return { top: t, left: l };
18796         },
18797
18798         /**
18799          * Returns the specified element style property
18800          * @method getStyle
18801          * @param {HTMLElement} el          the element
18802          * @param {string}      styleProp   the style property
18803          * @return {string} The value of the style property
18804          * @deprecated use Roo.lib.Dom.getStyle
18805          * @static
18806          */
18807         getStyle: function(el, styleProp) {
18808             return Roo.fly(el).getStyle(styleProp);
18809         },
18810
18811         /**
18812          * Gets the scrollTop
18813          * @method getScrollTop
18814          * @return {int} the document's scrollTop
18815          * @static
18816          */
18817         getScrollTop: function () { return this.getScroll().top; },
18818
18819         /**
18820          * Gets the scrollLeft
18821          * @method getScrollLeft
18822          * @return {int} the document's scrollTop
18823          * @static
18824          */
18825         getScrollLeft: function () { return this.getScroll().left; },
18826
18827         /**
18828          * Sets the x/y position of an element to the location of the
18829          * target element.
18830          * @method moveToEl
18831          * @param {HTMLElement} moveEl      The element to move
18832          * @param {HTMLElement} targetEl    The position reference element
18833          * @static
18834          */
18835         moveToEl: function (moveEl, targetEl) {
18836             var aCoord = Roo.lib.Dom.getXY(targetEl);
18837             Roo.lib.Dom.setXY(moveEl, aCoord);
18838         },
18839
18840         /**
18841          * Numeric array sort function
18842          * @method numericSort
18843          * @static
18844          */
18845         numericSort: function(a, b) { return (a - b); },
18846
18847         /**
18848          * Internal counter
18849          * @property _timeoutCount
18850          * @private
18851          * @static
18852          */
18853         _timeoutCount: 0,
18854
18855         /**
18856          * Trying to make the load order less important.  Without this we get
18857          * an error if this file is loaded before the Event Utility.
18858          * @method _addListeners
18859          * @private
18860          * @static
18861          */
18862         _addListeners: function() {
18863             var DDM = Roo.dd.DDM;
18864             if ( Roo.lib.Event && document ) {
18865                 DDM._onLoad();
18866             } else {
18867                 if (DDM._timeoutCount > 2000) {
18868                 } else {
18869                     setTimeout(DDM._addListeners, 10);
18870                     if (document && document.body) {
18871                         DDM._timeoutCount += 1;
18872                     }
18873                 }
18874             }
18875         },
18876
18877         /**
18878          * Recursively searches the immediate parent and all child nodes for
18879          * the handle element in order to determine wheter or not it was
18880          * clicked.
18881          * @method handleWasClicked
18882          * @param node the html element to inspect
18883          * @static
18884          */
18885         handleWasClicked: function(node, id) {
18886             if (this.isHandle(id, node.id)) {
18887                 return true;
18888             } else {
18889                 // check to see if this is a text node child of the one we want
18890                 var p = node.parentNode;
18891
18892                 while (p) {
18893                     if (this.isHandle(id, p.id)) {
18894                         return true;
18895                     } else {
18896                         p = p.parentNode;
18897                     }
18898                 }
18899             }
18900
18901             return false;
18902         }
18903
18904     };
18905
18906 }();
18907
18908 // shorter alias, save a few bytes
18909 Roo.dd.DDM = Roo.dd.DragDropMgr;
18910 Roo.dd.DDM._addListeners();
18911
18912 }/*
18913  * Based on:
18914  * Ext JS Library 1.1.1
18915  * Copyright(c) 2006-2007, Ext JS, LLC.
18916  *
18917  * Originally Released Under LGPL - original licence link has changed is not relivant.
18918  *
18919  * Fork - LGPL
18920  * <script type="text/javascript">
18921  */
18922
18923 /**
18924  * @class Roo.dd.DD
18925  * A DragDrop implementation where the linked element follows the
18926  * mouse cursor during a drag.
18927  * @extends Roo.dd.DragDrop
18928  * @constructor
18929  * @param {String} id the id of the linked element
18930  * @param {String} sGroup the group of related DragDrop items
18931  * @param {object} config an object containing configurable attributes
18932  *                Valid properties for DD:
18933  *                    scroll
18934  */
18935 Roo.dd.DD = function(id, sGroup, config) {
18936     if (id) {
18937         this.init(id, sGroup, config);
18938     }
18939 };
18940
18941 Roo.extend(Roo.dd.DD, Roo.dd.DragDrop, {
18942
18943     /**
18944      * When set to true, the utility automatically tries to scroll the browser
18945      * window wehn a drag and drop element is dragged near the viewport boundary.
18946      * Defaults to true.
18947      * @property scroll
18948      * @type boolean
18949      */
18950     scroll: true,
18951
18952     /**
18953      * Sets the pointer offset to the distance between the linked element's top
18954      * left corner and the location the element was clicked
18955      * @method autoOffset
18956      * @param {int} iPageX the X coordinate of the click
18957      * @param {int} iPageY the Y coordinate of the click
18958      */
18959     autoOffset: function(iPageX, iPageY) {
18960         var x = iPageX - this.startPageX;
18961         var y = iPageY - this.startPageY;
18962         this.setDelta(x, y);
18963     },
18964
18965     /**
18966      * Sets the pointer offset.  You can call this directly to force the
18967      * offset to be in a particular location (e.g., pass in 0,0 to set it
18968      * to the center of the object)
18969      * @method setDelta
18970      * @param {int} iDeltaX the distance from the left
18971      * @param {int} iDeltaY the distance from the top
18972      */
18973     setDelta: function(iDeltaX, iDeltaY) {
18974         this.deltaX = iDeltaX;
18975         this.deltaY = iDeltaY;
18976     },
18977
18978     /**
18979      * Sets the drag element to the location of the mousedown or click event,
18980      * maintaining the cursor location relative to the location on the element
18981      * that was clicked.  Override this if you want to place the element in a
18982      * location other than where the cursor is.
18983      * @method setDragElPos
18984      * @param {int} iPageX the X coordinate of the mousedown or drag event
18985      * @param {int} iPageY the Y coordinate of the mousedown or drag event
18986      */
18987     setDragElPos: function(iPageX, iPageY) {
18988         // the first time we do this, we are going to check to make sure
18989         // the element has css positioning
18990
18991         var el = this.getDragEl();
18992         this.alignElWithMouse(el, iPageX, iPageY);
18993     },
18994
18995     /**
18996      * Sets the element to the location of the mousedown or click event,
18997      * maintaining the cursor location relative to the location on the element
18998      * that was clicked.  Override this if you want to place the element in a
18999      * location other than where the cursor is.
19000      * @method alignElWithMouse
19001      * @param {HTMLElement} el the element to move
19002      * @param {int} iPageX the X coordinate of the mousedown or drag event
19003      * @param {int} iPageY the Y coordinate of the mousedown or drag event
19004      */
19005     alignElWithMouse: function(el, iPageX, iPageY) {
19006         var oCoord = this.getTargetCoord(iPageX, iPageY);
19007         var fly = el.dom ? el : Roo.fly(el);
19008         if (!this.deltaSetXY) {
19009             var aCoord = [oCoord.x, oCoord.y];
19010             fly.setXY(aCoord);
19011             var newLeft = fly.getLeft(true);
19012             var newTop  = fly.getTop(true);
19013             this.deltaSetXY = [ newLeft - oCoord.x, newTop - oCoord.y ];
19014         } else {
19015             fly.setLeftTop(oCoord.x + this.deltaSetXY[0], oCoord.y + this.deltaSetXY[1]);
19016         }
19017
19018         this.cachePosition(oCoord.x, oCoord.y);
19019         this.autoScroll(oCoord.x, oCoord.y, el.offsetHeight, el.offsetWidth);
19020         return oCoord;
19021     },
19022
19023     /**
19024      * Saves the most recent position so that we can reset the constraints and
19025      * tick marks on-demand.  We need to know this so that we can calculate the
19026      * number of pixels the element is offset from its original position.
19027      * @method cachePosition
19028      * @param iPageX the current x position (optional, this just makes it so we
19029      * don't have to look it up again)
19030      * @param iPageY the current y position (optional, this just makes it so we
19031      * don't have to look it up again)
19032      */
19033     cachePosition: function(iPageX, iPageY) {
19034         if (iPageX) {
19035             this.lastPageX = iPageX;
19036             this.lastPageY = iPageY;
19037         } else {
19038             var aCoord = Roo.lib.Dom.getXY(this.getEl());
19039             this.lastPageX = aCoord[0];
19040             this.lastPageY = aCoord[1];
19041         }
19042     },
19043
19044     /**
19045      * Auto-scroll the window if the dragged object has been moved beyond the
19046      * visible window boundary.
19047      * @method autoScroll
19048      * @param {int} x the drag element's x position
19049      * @param {int} y the drag element's y position
19050      * @param {int} h the height of the drag element
19051      * @param {int} w the width of the drag element
19052      * @private
19053      */
19054     autoScroll: function(x, y, h, w) {
19055
19056         if (this.scroll) {
19057             // The client height
19058             var clientH = Roo.lib.Dom.getViewWidth();
19059
19060             // The client width
19061             var clientW = Roo.lib.Dom.getViewHeight();
19062
19063             // The amt scrolled down
19064             var st = this.DDM.getScrollTop();
19065
19066             // The amt scrolled right
19067             var sl = this.DDM.getScrollLeft();
19068
19069             // Location of the bottom of the element
19070             var bot = h + y;
19071
19072             // Location of the right of the element
19073             var right = w + x;
19074
19075             // The distance from the cursor to the bottom of the visible area,
19076             // adjusted so that we don't scroll if the cursor is beyond the
19077             // element drag constraints
19078             var toBot = (clientH + st - y - this.deltaY);
19079
19080             // The distance from the cursor to the right of the visible area
19081             var toRight = (clientW + sl - x - this.deltaX);
19082
19083
19084             // How close to the edge the cursor must be before we scroll
19085             // var thresh = (document.all) ? 100 : 40;
19086             var thresh = 40;
19087
19088             // How many pixels to scroll per autoscroll op.  This helps to reduce
19089             // clunky scrolling. IE is more sensitive about this ... it needs this
19090             // value to be higher.
19091             var scrAmt = (document.all) ? 80 : 30;
19092
19093             // Scroll down if we are near the bottom of the visible page and the
19094             // obj extends below the crease
19095             if ( bot > clientH && toBot < thresh ) {
19096                 window.scrollTo(sl, st + scrAmt);
19097             }
19098
19099             // Scroll up if the window is scrolled down and the top of the object
19100             // goes above the top border
19101             if ( y < st && st > 0 && y - st < thresh ) {
19102                 window.scrollTo(sl, st - scrAmt);
19103             }
19104
19105             // Scroll right if the obj is beyond the right border and the cursor is
19106             // near the border.
19107             if ( right > clientW && toRight < thresh ) {
19108                 window.scrollTo(sl + scrAmt, st);
19109             }
19110
19111             // Scroll left if the window has been scrolled to the right and the obj
19112             // extends past the left border
19113             if ( x < sl && sl > 0 && x - sl < thresh ) {
19114                 window.scrollTo(sl - scrAmt, st);
19115             }
19116         }
19117     },
19118
19119     /**
19120      * Finds the location the element should be placed if we want to move
19121      * it to where the mouse location less the click offset would place us.
19122      * @method getTargetCoord
19123      * @param {int} iPageX the X coordinate of the click
19124      * @param {int} iPageY the Y coordinate of the click
19125      * @return an object that contains the coordinates (Object.x and Object.y)
19126      * @private
19127      */
19128     getTargetCoord: function(iPageX, iPageY) {
19129
19130
19131         var x = iPageX - this.deltaX;
19132         var y = iPageY - this.deltaY;
19133
19134         if (this.constrainX) {
19135             if (x < this.minX) { x = this.minX; }
19136             if (x > this.maxX) { x = this.maxX; }
19137         }
19138
19139         if (this.constrainY) {
19140             if (y < this.minY) { y = this.minY; }
19141             if (y > this.maxY) { y = this.maxY; }
19142         }
19143
19144         x = this.getTick(x, this.xTicks);
19145         y = this.getTick(y, this.yTicks);
19146
19147
19148         return {x:x, y:y};
19149     },
19150
19151     /*
19152      * Sets up config options specific to this class. Overrides
19153      * Roo.dd.DragDrop, but all versions of this method through the
19154      * inheritance chain are called
19155      */
19156     applyConfig: function() {
19157         Roo.dd.DD.superclass.applyConfig.call(this);
19158         this.scroll = (this.config.scroll !== false);
19159     },
19160
19161     /*
19162      * Event that fires prior to the onMouseDown event.  Overrides
19163      * Roo.dd.DragDrop.
19164      */
19165     b4MouseDown: function(e) {
19166         // this.resetConstraints();
19167         this.autoOffset(e.getPageX(),
19168                             e.getPageY());
19169     },
19170
19171     /*
19172      * Event that fires prior to the onDrag event.  Overrides
19173      * Roo.dd.DragDrop.
19174      */
19175     b4Drag: function(e) {
19176         this.setDragElPos(e.getPageX(),
19177                             e.getPageY());
19178     },
19179
19180     toString: function() {
19181         return ("DD " + this.id);
19182     }
19183
19184     //////////////////////////////////////////////////////////////////////////
19185     // Debugging ygDragDrop events that can be overridden
19186     //////////////////////////////////////////////////////////////////////////
19187     /*
19188     startDrag: function(x, y) {
19189     },
19190
19191     onDrag: function(e) {
19192     },
19193
19194     onDragEnter: function(e, id) {
19195     },
19196
19197     onDragOver: function(e, id) {
19198     },
19199
19200     onDragOut: function(e, id) {
19201     },
19202
19203     onDragDrop: function(e, id) {
19204     },
19205
19206     endDrag: function(e) {
19207     }
19208
19209     */
19210
19211 });/*
19212  * Based on:
19213  * Ext JS Library 1.1.1
19214  * Copyright(c) 2006-2007, Ext JS, LLC.
19215  *
19216  * Originally Released Under LGPL - original licence link has changed is not relivant.
19217  *
19218  * Fork - LGPL
19219  * <script type="text/javascript">
19220  */
19221
19222 /**
19223  * @class Roo.dd.DDProxy
19224  * A DragDrop implementation that inserts an empty, bordered div into
19225  * the document that follows the cursor during drag operations.  At the time of
19226  * the click, the frame div is resized to the dimensions of the linked html
19227  * element, and moved to the exact location of the linked element.
19228  *
19229  * References to the "frame" element refer to the single proxy element that
19230  * was created to be dragged in place of all DDProxy elements on the
19231  * page.
19232  *
19233  * @extends Roo.dd.DD
19234  * @constructor
19235  * @param {String} id the id of the linked html element
19236  * @param {String} sGroup the group of related DragDrop objects
19237  * @param {object} config an object containing configurable attributes
19238  *                Valid properties for DDProxy in addition to those in DragDrop:
19239  *                   resizeFrame, centerFrame, dragElId
19240  */
19241 Roo.dd.DDProxy = function(id, sGroup, config) {
19242     if (id) {
19243         this.init(id, sGroup, config);
19244         this.initFrame();
19245     }
19246 };
19247
19248 /**
19249  * The default drag frame div id
19250  * @property Roo.dd.DDProxy.dragElId
19251  * @type String
19252  * @static
19253  */
19254 Roo.dd.DDProxy.dragElId = "ygddfdiv";
19255
19256 Roo.extend(Roo.dd.DDProxy, Roo.dd.DD, {
19257
19258     /**
19259      * By default we resize the drag frame to be the same size as the element
19260      * we want to drag (this is to get the frame effect).  We can turn it off
19261      * if we want a different behavior.
19262      * @property resizeFrame
19263      * @type boolean
19264      */
19265     resizeFrame: true,
19266
19267     /**
19268      * By default the frame is positioned exactly where the drag element is, so
19269      * we use the cursor offset provided by Roo.dd.DD.  Another option that works only if
19270      * you do not have constraints on the obj is to have the drag frame centered
19271      * around the cursor.  Set centerFrame to true for this effect.
19272      * @property centerFrame
19273      * @type boolean
19274      */
19275     centerFrame: false,
19276
19277     /**
19278      * Creates the proxy element if it does not yet exist
19279      * @method createFrame
19280      */
19281     createFrame: function() {
19282         var self = this;
19283         var body = document.body;
19284
19285         if (!body || !body.firstChild) {
19286             setTimeout( function() { self.createFrame(); }, 50 );
19287             return;
19288         }
19289
19290         var div = this.getDragEl();
19291
19292         if (!div) {
19293             div    = document.createElement("div");
19294             div.id = this.dragElId;
19295             var s  = div.style;
19296
19297             s.position   = "absolute";
19298             s.visibility = "hidden";
19299             s.cursor     = "move";
19300             s.border     = "2px solid #aaa";
19301             s.zIndex     = 999;
19302
19303             // appendChild can blow up IE if invoked prior to the window load event
19304             // while rendering a table.  It is possible there are other scenarios
19305             // that would cause this to happen as well.
19306             body.insertBefore(div, body.firstChild);
19307         }
19308     },
19309
19310     /**
19311      * Initialization for the drag frame element.  Must be called in the
19312      * constructor of all subclasses
19313      * @method initFrame
19314      */
19315     initFrame: function() {
19316         this.createFrame();
19317     },
19318
19319     applyConfig: function() {
19320         Roo.dd.DDProxy.superclass.applyConfig.call(this);
19321
19322         this.resizeFrame = (this.config.resizeFrame !== false);
19323         this.centerFrame = (this.config.centerFrame);
19324         this.setDragElId(this.config.dragElId || Roo.dd.DDProxy.dragElId);
19325     },
19326
19327     /**
19328      * Resizes the drag frame to the dimensions of the clicked object, positions
19329      * it over the object, and finally displays it
19330      * @method showFrame
19331      * @param {int} iPageX X click position
19332      * @param {int} iPageY Y click position
19333      * @private
19334      */
19335     showFrame: function(iPageX, iPageY) {
19336         var el = this.getEl();
19337         var dragEl = this.getDragEl();
19338         var s = dragEl.style;
19339
19340         this._resizeProxy();
19341
19342         if (this.centerFrame) {
19343             this.setDelta( Math.round(parseInt(s.width,  10)/2),
19344                            Math.round(parseInt(s.height, 10)/2) );
19345         }
19346
19347         this.setDragElPos(iPageX, iPageY);
19348
19349         Roo.fly(dragEl).show();
19350     },
19351
19352     /**
19353      * The proxy is automatically resized to the dimensions of the linked
19354      * element when a drag is initiated, unless resizeFrame is set to false
19355      * @method _resizeProxy
19356      * @private
19357      */
19358     _resizeProxy: function() {
19359         if (this.resizeFrame) {
19360             var el = this.getEl();
19361             Roo.fly(this.getDragEl()).setSize(el.offsetWidth, el.offsetHeight);
19362         }
19363     },
19364
19365     // overrides Roo.dd.DragDrop
19366     b4MouseDown: function(e) {
19367         var x = e.getPageX();
19368         var y = e.getPageY();
19369         this.autoOffset(x, y);
19370         this.setDragElPos(x, y);
19371     },
19372
19373     // overrides Roo.dd.DragDrop
19374     b4StartDrag: function(x, y) {
19375         // show the drag frame
19376         this.showFrame(x, y);
19377     },
19378
19379     // overrides Roo.dd.DragDrop
19380     b4EndDrag: function(e) {
19381         Roo.fly(this.getDragEl()).hide();
19382     },
19383
19384     // overrides Roo.dd.DragDrop
19385     // By default we try to move the element to the last location of the frame.
19386     // This is so that the default behavior mirrors that of Roo.dd.DD.
19387     endDrag: function(e) {
19388
19389         var lel = this.getEl();
19390         var del = this.getDragEl();
19391
19392         // Show the drag frame briefly so we can get its position
19393         del.style.visibility = "";
19394
19395         this.beforeMove();
19396         // Hide the linked element before the move to get around a Safari
19397         // rendering bug.
19398         lel.style.visibility = "hidden";
19399         Roo.dd.DDM.moveToEl(lel, del);
19400         del.style.visibility = "hidden";
19401         lel.style.visibility = "";
19402
19403         this.afterDrag();
19404     },
19405
19406     beforeMove : function(){
19407
19408     },
19409
19410     afterDrag : function(){
19411
19412     },
19413
19414     toString: function() {
19415         return ("DDProxy " + this.id);
19416     }
19417
19418 });
19419 /*
19420  * Based on:
19421  * Ext JS Library 1.1.1
19422  * Copyright(c) 2006-2007, Ext JS, LLC.
19423  *
19424  * Originally Released Under LGPL - original licence link has changed is not relivant.
19425  *
19426  * Fork - LGPL
19427  * <script type="text/javascript">
19428  */
19429
19430  /**
19431  * @class Roo.dd.DDTarget
19432  * A DragDrop implementation that does not move, but can be a drop
19433  * target.  You would get the same result by simply omitting implementation
19434  * for the event callbacks, but this way we reduce the processing cost of the
19435  * event listener and the callbacks.
19436  * @extends Roo.dd.DragDrop
19437  * @constructor
19438  * @param {String} id the id of the element that is a drop target
19439  * @param {String} sGroup the group of related DragDrop objects
19440  * @param {object} config an object containing configurable attributes
19441  *                 Valid properties for DDTarget in addition to those in
19442  *                 DragDrop:
19443  *                    none
19444  */
19445 Roo.dd.DDTarget = function(id, sGroup, config) {
19446     if (id) {
19447         this.initTarget(id, sGroup, config);
19448     }
19449     if (config.listeners || config.events) { 
19450        Roo.dd.DragDrop.superclass.constructor.call(this,  { 
19451             listeners : config.listeners || {}, 
19452             events : config.events || {} 
19453         });    
19454     }
19455 };
19456
19457 // Roo.dd.DDTarget.prototype = new Roo.dd.DragDrop();
19458 Roo.extend(Roo.dd.DDTarget, Roo.dd.DragDrop, {
19459     toString: function() {
19460         return ("DDTarget " + this.id);
19461     }
19462 });
19463 /*
19464  * Based on:
19465  * Ext JS Library 1.1.1
19466  * Copyright(c) 2006-2007, Ext JS, LLC.
19467  *
19468  * Originally Released Under LGPL - original licence link has changed is not relivant.
19469  *
19470  * Fork - LGPL
19471  * <script type="text/javascript">
19472  */
19473  
19474
19475 /**
19476  * @class Roo.dd.ScrollManager
19477  * Provides automatic scrolling of overflow regions in the page during drag operations.<br><br>
19478  * <b>Note: This class uses "Point Mode" and is untested in "Intersect Mode".</b>
19479  * @singleton
19480  */
19481 Roo.dd.ScrollManager = function(){
19482     var ddm = Roo.dd.DragDropMgr;
19483     var els = {};
19484     var dragEl = null;
19485     var proc = {};
19486     
19487     
19488     
19489     var onStop = function(e){
19490         dragEl = null;
19491         clearProc();
19492     };
19493     
19494     var triggerRefresh = function(){
19495         if(ddm.dragCurrent){
19496              ddm.refreshCache(ddm.dragCurrent.groups);
19497         }
19498     };
19499     
19500     var doScroll = function(){
19501         if(ddm.dragCurrent){
19502             var dds = Roo.dd.ScrollManager;
19503             if(!dds.animate){
19504                 if(proc.el.scroll(proc.dir, dds.increment)){
19505                     triggerRefresh();
19506                 }
19507             }else{
19508                 proc.el.scroll(proc.dir, dds.increment, true, dds.animDuration, triggerRefresh);
19509             }
19510         }
19511     };
19512     
19513     var clearProc = function(){
19514         if(proc.id){
19515             clearInterval(proc.id);
19516         }
19517         proc.id = 0;
19518         proc.el = null;
19519         proc.dir = "";
19520     };
19521     
19522     var startProc = function(el, dir){
19523          Roo.log('scroll startproc');
19524         clearProc();
19525         proc.el = el;
19526         proc.dir = dir;
19527         proc.id = setInterval(doScroll, Roo.dd.ScrollManager.frequency);
19528     };
19529     
19530     var onFire = function(e, isDrop){
19531        
19532         if(isDrop || !ddm.dragCurrent){ return; }
19533         var dds = Roo.dd.ScrollManager;
19534         if(!dragEl || dragEl != ddm.dragCurrent){
19535             dragEl = ddm.dragCurrent;
19536             // refresh regions on drag start
19537             dds.refreshCache();
19538         }
19539         
19540         var xy = Roo.lib.Event.getXY(e);
19541         var pt = new Roo.lib.Point(xy[0], xy[1]);
19542         for(var id in els){
19543             var el = els[id], r = el._region;
19544             if(r && r.contains(pt) && el.isScrollable()){
19545                 if(r.bottom - pt.y <= dds.thresh){
19546                     if(proc.el != el){
19547                         startProc(el, "down");
19548                     }
19549                     return;
19550                 }else if(r.right - pt.x <= dds.thresh){
19551                     if(proc.el != el){
19552                         startProc(el, "left");
19553                     }
19554                     return;
19555                 }else if(pt.y - r.top <= dds.thresh){
19556                     if(proc.el != el){
19557                         startProc(el, "up");
19558                     }
19559                     return;
19560                 }else if(pt.x - r.left <= dds.thresh){
19561                     if(proc.el != el){
19562                         startProc(el, "right");
19563                     }
19564                     return;
19565                 }
19566             }
19567         }
19568         clearProc();
19569     };
19570     
19571     ddm.fireEvents = ddm.fireEvents.createSequence(onFire, ddm);
19572     ddm.stopDrag = ddm.stopDrag.createSequence(onStop, ddm);
19573     
19574     return {
19575         /**
19576          * Registers new overflow element(s) to auto scroll
19577          * @param {String/HTMLElement/Element/Array} el The id of or the element to be scrolled or an array of either
19578          */
19579         register : function(el){
19580             if(el instanceof Array){
19581                 for(var i = 0, len = el.length; i < len; i++) {
19582                         this.register(el[i]);
19583                 }
19584             }else{
19585                 el = Roo.get(el);
19586                 els[el.id] = el;
19587             }
19588             Roo.dd.ScrollManager.els = els;
19589         },
19590         
19591         /**
19592          * Unregisters overflow element(s) so they are no longer scrolled
19593          * @param {String/HTMLElement/Element/Array} el The id of or the element to be removed or an array of either
19594          */
19595         unregister : function(el){
19596             if(el instanceof Array){
19597                 for(var i = 0, len = el.length; i < len; i++) {
19598                         this.unregister(el[i]);
19599                 }
19600             }else{
19601                 el = Roo.get(el);
19602                 delete els[el.id];
19603             }
19604         },
19605         
19606         /**
19607          * The number of pixels from the edge of a container the pointer needs to be to 
19608          * trigger scrolling (defaults to 25)
19609          * @type Number
19610          */
19611         thresh : 25,
19612         
19613         /**
19614          * The number of pixels to scroll in each scroll increment (defaults to 50)
19615          * @type Number
19616          */
19617         increment : 100,
19618         
19619         /**
19620          * The frequency of scrolls in milliseconds (defaults to 500)
19621          * @type Number
19622          */
19623         frequency : 500,
19624         
19625         /**
19626          * True to animate the scroll (defaults to true)
19627          * @type Boolean
19628          */
19629         animate: true,
19630         
19631         /**
19632          * The animation duration in seconds - 
19633          * MUST BE less than Roo.dd.ScrollManager.frequency! (defaults to .4)
19634          * @type Number
19635          */
19636         animDuration: .4,
19637         
19638         /**
19639          * Manually trigger a cache refresh.
19640          */
19641         refreshCache : function(){
19642             for(var id in els){
19643                 if(typeof els[id] == 'object'){ // for people extending the object prototype
19644                     els[id]._region = els[id].getRegion();
19645                 }
19646             }
19647         }
19648     };
19649 }();/*
19650  * Based on:
19651  * Ext JS Library 1.1.1
19652  * Copyright(c) 2006-2007, Ext JS, LLC.
19653  *
19654  * Originally Released Under LGPL - original licence link has changed is not relivant.
19655  *
19656  * Fork - LGPL
19657  * <script type="text/javascript">
19658  */
19659  
19660
19661 /**
19662  * @class Roo.dd.Registry
19663  * Provides easy access to all drag drop components that are registered on a page.  Items can be retrieved either
19664  * directly by DOM node id, or by passing in the drag drop event that occurred and looking up the event target.
19665  * @singleton
19666  */
19667 Roo.dd.Registry = function(){
19668     var elements = {}; 
19669     var handles = {}; 
19670     var autoIdSeed = 0;
19671
19672     var getId = function(el, autogen){
19673         if(typeof el == "string"){
19674             return el;
19675         }
19676         var id = el.id;
19677         if(!id && autogen !== false){
19678             id = "roodd-" + (++autoIdSeed);
19679             el.id = id;
19680         }
19681         return id;
19682     };
19683     
19684     return {
19685     /**
19686      * Register a drag drop element
19687      * @param {String|HTMLElement} element The id or DOM node to register
19688      * @param {Object} data (optional) A custom data object that will be passed between the elements that are involved
19689      * in drag drop operations.  You can populate this object with any arbitrary properties that your own code
19690      * knows how to interpret, plus there are some specific properties known to the Registry that should be
19691      * populated in the data object (if applicable):
19692      * <pre>
19693 Value      Description<br />
19694 ---------  ------------------------------------------<br />
19695 handles    Array of DOM nodes that trigger dragging<br />
19696            for the element being registered<br />
19697 isHandle   True if the element passed in triggers<br />
19698            dragging itself, else false
19699 </pre>
19700      */
19701         register : function(el, data){
19702             data = data || {};
19703             if(typeof el == "string"){
19704                 el = document.getElementById(el);
19705             }
19706             data.ddel = el;
19707             elements[getId(el)] = data;
19708             if(data.isHandle !== false){
19709                 handles[data.ddel.id] = data;
19710             }
19711             if(data.handles){
19712                 var hs = data.handles;
19713                 for(var i = 0, len = hs.length; i < len; i++){
19714                         handles[getId(hs[i])] = data;
19715                 }
19716             }
19717         },
19718
19719     /**
19720      * Unregister a drag drop element
19721      * @param {String|HTMLElement}  element The id or DOM node to unregister
19722      */
19723         unregister : function(el){
19724             var id = getId(el, false);
19725             var data = elements[id];
19726             if(data){
19727                 delete elements[id];
19728                 if(data.handles){
19729                     var hs = data.handles;
19730                     for(var i = 0, len = hs.length; i < len; i++){
19731                         delete handles[getId(hs[i], false)];
19732                     }
19733                 }
19734             }
19735         },
19736
19737     /**
19738      * Returns the handle registered for a DOM Node by id
19739      * @param {String|HTMLElement} id The DOM node or id to look up
19740      * @return {Object} handle The custom handle data
19741      */
19742         getHandle : function(id){
19743             if(typeof id != "string"){ // must be element?
19744                 id = id.id;
19745             }
19746             return handles[id];
19747         },
19748
19749     /**
19750      * Returns the handle that is registered for the DOM node that is the target of the event
19751      * @param {Event} e The event
19752      * @return {Object} handle The custom handle data
19753      */
19754         getHandleFromEvent : function(e){
19755             var t = Roo.lib.Event.getTarget(e);
19756             return t ? handles[t.id] : null;
19757         },
19758
19759     /**
19760      * Returns a custom data object that is registered for a DOM node by id
19761      * @param {String|HTMLElement} id The DOM node or id to look up
19762      * @return {Object} data The custom data
19763      */
19764         getTarget : function(id){
19765             if(typeof id != "string"){ // must be element?
19766                 id = id.id;
19767             }
19768             return elements[id];
19769         },
19770
19771     /**
19772      * Returns a custom data object that is registered for the DOM node that is the target of the event
19773      * @param {Event} e The event
19774      * @return {Object} data The custom data
19775      */
19776         getTargetFromEvent : function(e){
19777             var t = Roo.lib.Event.getTarget(e);
19778             return t ? elements[t.id] || handles[t.id] : null;
19779         }
19780     };
19781 }();/*
19782  * Based on:
19783  * Ext JS Library 1.1.1
19784  * Copyright(c) 2006-2007, Ext JS, LLC.
19785  *
19786  * Originally Released Under LGPL - original licence link has changed is not relivant.
19787  *
19788  * Fork - LGPL
19789  * <script type="text/javascript">
19790  */
19791  
19792
19793 /**
19794  * @class Roo.dd.StatusProxy
19795  * A specialized drag proxy that supports a drop status icon, {@link Roo.Layer} styles and auto-repair.  This is the
19796  * default drag proxy used by all Roo.dd components.
19797  * @constructor
19798  * @param {Object} config
19799  */
19800 Roo.dd.StatusProxy = function(config){
19801     Roo.apply(this, config);
19802     this.id = this.id || Roo.id();
19803     this.el = new Roo.Layer({
19804         dh: {
19805             id: this.id, tag: "div", cls: "x-dd-drag-proxy "+this.dropNotAllowed, children: [
19806                 {tag: "div", cls: "x-dd-drop-icon"},
19807                 {tag: "div", cls: "x-dd-drag-ghost"}
19808             ]
19809         }, 
19810         shadow: !config || config.shadow !== false
19811     });
19812     this.ghost = Roo.get(this.el.dom.childNodes[1]);
19813     this.dropStatus = this.dropNotAllowed;
19814 };
19815
19816 Roo.dd.StatusProxy.prototype = {
19817     /**
19818      * @cfg {String} dropAllowed
19819      * The CSS class to apply to the status element when drop is allowed (defaults to "x-dd-drop-ok").
19820      */
19821     dropAllowed : "x-dd-drop-ok",
19822     /**
19823      * @cfg {String} dropNotAllowed
19824      * The CSS class to apply to the status element when drop is not allowed (defaults to "x-dd-drop-nodrop").
19825      */
19826     dropNotAllowed : "x-dd-drop-nodrop",
19827
19828     /**
19829      * Updates the proxy's visual element to indicate the status of whether or not drop is allowed
19830      * over the current target element.
19831      * @param {String} cssClass The css class for the new drop status indicator image
19832      */
19833     setStatus : function(cssClass){
19834         cssClass = cssClass || this.dropNotAllowed;
19835         if(this.dropStatus != cssClass){
19836             this.el.replaceClass(this.dropStatus, cssClass);
19837             this.dropStatus = cssClass;
19838         }
19839     },
19840
19841     /**
19842      * Resets the status indicator to the default dropNotAllowed value
19843      * @param {Boolean} clearGhost True to also remove all content from the ghost, false to preserve it
19844      */
19845     reset : function(clearGhost){
19846         this.el.dom.className = "x-dd-drag-proxy " + this.dropNotAllowed;
19847         this.dropStatus = this.dropNotAllowed;
19848         if(clearGhost){
19849             this.ghost.update("");
19850         }
19851     },
19852
19853     /**
19854      * Updates the contents of the ghost element
19855      * @param {String} html The html that will replace the current innerHTML of the ghost element
19856      */
19857     update : function(html){
19858         if(typeof html == "string"){
19859             this.ghost.update(html);
19860         }else{
19861             this.ghost.update("");
19862             html.style.margin = "0";
19863             this.ghost.dom.appendChild(html);
19864         }
19865         // ensure float = none set?? cant remember why though.
19866         var el = this.ghost.dom.firstChild;
19867                 if(el){
19868                         Roo.fly(el).setStyle('float', 'none');
19869                 }
19870     },
19871     
19872     /**
19873      * Returns the underlying proxy {@link Roo.Layer}
19874      * @return {Roo.Layer} el
19875     */
19876     getEl : function(){
19877         return this.el;
19878     },
19879
19880     /**
19881      * Returns the ghost element
19882      * @return {Roo.Element} el
19883      */
19884     getGhost : function(){
19885         return this.ghost;
19886     },
19887
19888     /**
19889      * Hides the proxy
19890      * @param {Boolean} clear True to reset the status and clear the ghost contents, false to preserve them
19891      */
19892     hide : function(clear){
19893         this.el.hide();
19894         if(clear){
19895             this.reset(true);
19896         }
19897     },
19898
19899     /**
19900      * Stops the repair animation if it's currently running
19901      */
19902     stop : function(){
19903         if(this.anim && this.anim.isAnimated && this.anim.isAnimated()){
19904             this.anim.stop();
19905         }
19906     },
19907
19908     /**
19909      * Displays this proxy
19910      */
19911     show : function(){
19912         this.el.show();
19913     },
19914
19915     /**
19916      * Force the Layer to sync its shadow and shim positions to the element
19917      */
19918     sync : function(){
19919         this.el.sync();
19920     },
19921
19922     /**
19923      * Causes the proxy to return to its position of origin via an animation.  Should be called after an
19924      * invalid drop operation by the item being dragged.
19925      * @param {Array} xy The XY position of the element ([x, y])
19926      * @param {Function} callback The function to call after the repair is complete
19927      * @param {Object} scope The scope in which to execute the callback
19928      */
19929     repair : function(xy, callback, scope){
19930         this.callback = callback;
19931         this.scope = scope;
19932         if(xy && this.animRepair !== false){
19933             this.el.addClass("x-dd-drag-repair");
19934             this.el.hideUnders(true);
19935             this.anim = this.el.shift({
19936                 duration: this.repairDuration || .5,
19937                 easing: 'easeOut',
19938                 xy: xy,
19939                 stopFx: true,
19940                 callback: this.afterRepair,
19941                 scope: this
19942             });
19943         }else{
19944             this.afterRepair();
19945         }
19946     },
19947
19948     // private
19949     afterRepair : function(){
19950         this.hide(true);
19951         if(typeof this.callback == "function"){
19952             this.callback.call(this.scope || this);
19953         }
19954         this.callback = null;
19955         this.scope = null;
19956     }
19957 };/*
19958  * Based on:
19959  * Ext JS Library 1.1.1
19960  * Copyright(c) 2006-2007, Ext JS, LLC.
19961  *
19962  * Originally Released Under LGPL - original licence link has changed is not relivant.
19963  *
19964  * Fork - LGPL
19965  * <script type="text/javascript">
19966  */
19967
19968 /**
19969  * @class Roo.dd.DragSource
19970  * @extends Roo.dd.DDProxy
19971  * A simple class that provides the basic implementation needed to make any element draggable.
19972  * @constructor
19973  * @param {String/HTMLElement/Element} el The container element
19974  * @param {Object} config
19975  */
19976 Roo.dd.DragSource = function(el, config){
19977     this.el = Roo.get(el);
19978     this.dragData = {};
19979     
19980     Roo.apply(this, config);
19981     
19982     if(!this.proxy){
19983         this.proxy = new Roo.dd.StatusProxy();
19984     }
19985
19986     Roo.dd.DragSource.superclass.constructor.call(this, this.el.dom, this.ddGroup || this.group,
19987           {dragElId : this.proxy.id, resizeFrame: false, isTarget: false, scroll: this.scroll === true});
19988     
19989     this.dragging = false;
19990 };
19991
19992 Roo.extend(Roo.dd.DragSource, Roo.dd.DDProxy, {
19993     /**
19994      * @cfg {String} dropAllowed
19995      * The CSS class returned to the drag source when drop is allowed (defaults to "x-dd-drop-ok").
19996      */
19997     dropAllowed : "x-dd-drop-ok",
19998     /**
19999      * @cfg {String} dropNotAllowed
20000      * The CSS class returned to the drag source when drop is not allowed (defaults to "x-dd-drop-nodrop").
20001      */
20002     dropNotAllowed : "x-dd-drop-nodrop",
20003
20004     /**
20005      * Returns the data object associated with this drag source
20006      * @return {Object} data An object containing arbitrary data
20007      */
20008     getDragData : function(e){
20009         return this.dragData;
20010     },
20011
20012     // private
20013     onDragEnter : function(e, id){
20014         var target = Roo.dd.DragDropMgr.getDDById(id);
20015         this.cachedTarget = target;
20016         if(this.beforeDragEnter(target, e, id) !== false){
20017             if(target.isNotifyTarget){
20018                 var status = target.notifyEnter(this, e, this.dragData);
20019                 this.proxy.setStatus(status);
20020             }else{
20021                 this.proxy.setStatus(this.dropAllowed);
20022             }
20023             
20024             if(this.afterDragEnter){
20025                 /**
20026                  * An empty function by default, but provided so that you can perform a custom action
20027                  * when the dragged item enters the drop target by providing an implementation.
20028                  * @param {Roo.dd.DragDrop} target The drop target
20029                  * @param {Event} e The event object
20030                  * @param {String} id The id of the dragged element
20031                  * @method afterDragEnter
20032                  */
20033                 this.afterDragEnter(target, e, id);
20034             }
20035         }
20036     },
20037
20038     /**
20039      * An empty function by default, but provided so that you can perform a custom action
20040      * before the dragged item enters the drop target and optionally cancel the onDragEnter.
20041      * @param {Roo.dd.DragDrop} target The drop target
20042      * @param {Event} e The event object
20043      * @param {String} id The id of the dragged element
20044      * @return {Boolean} isValid True if the drag event is valid, else false to cancel
20045      */
20046     beforeDragEnter : function(target, e, id){
20047         return true;
20048     },
20049
20050     // private
20051     alignElWithMouse: function() {
20052         Roo.dd.DragSource.superclass.alignElWithMouse.apply(this, arguments);
20053         this.proxy.sync();
20054     },
20055
20056     // private
20057     onDragOver : function(e, id){
20058         var target = this.cachedTarget || Roo.dd.DragDropMgr.getDDById(id);
20059         if(this.beforeDragOver(target, e, id) !== false){
20060             if(target.isNotifyTarget){
20061                 var status = target.notifyOver(this, e, this.dragData);
20062                 this.proxy.setStatus(status);
20063             }
20064
20065             if(this.afterDragOver){
20066                 /**
20067                  * An empty function by default, but provided so that you can perform a custom action
20068                  * while the dragged item is over the drop target by providing an implementation.
20069                  * @param {Roo.dd.DragDrop} target The drop target
20070                  * @param {Event} e The event object
20071                  * @param {String} id The id of the dragged element
20072                  * @method afterDragOver
20073                  */
20074                 this.afterDragOver(target, e, id);
20075             }
20076         }
20077     },
20078
20079     /**
20080      * An empty function by default, but provided so that you can perform a custom action
20081      * while the dragged item is over the drop target and optionally cancel the onDragOver.
20082      * @param {Roo.dd.DragDrop} target The drop target
20083      * @param {Event} e The event object
20084      * @param {String} id The id of the dragged element
20085      * @return {Boolean} isValid True if the drag event is valid, else false to cancel
20086      */
20087     beforeDragOver : function(target, e, id){
20088         return true;
20089     },
20090
20091     // private
20092     onDragOut : function(e, id){
20093         var target = this.cachedTarget || Roo.dd.DragDropMgr.getDDById(id);
20094         if(this.beforeDragOut(target, e, id) !== false){
20095             if(target.isNotifyTarget){
20096                 target.notifyOut(this, e, this.dragData);
20097             }
20098             this.proxy.reset();
20099             if(this.afterDragOut){
20100                 /**
20101                  * An empty function by default, but provided so that you can perform a custom action
20102                  * after the dragged item is dragged out of the target without dropping.
20103                  * @param {Roo.dd.DragDrop} target The drop target
20104                  * @param {Event} e The event object
20105                  * @param {String} id The id of the dragged element
20106                  * @method afterDragOut
20107                  */
20108                 this.afterDragOut(target, e, id);
20109             }
20110         }
20111         this.cachedTarget = null;
20112     },
20113
20114     /**
20115      * An empty function by default, but provided so that you can perform a custom action before the dragged
20116      * item is dragged out of the target without dropping, and optionally cancel the onDragOut.
20117      * @param {Roo.dd.DragDrop} target The drop target
20118      * @param {Event} e The event object
20119      * @param {String} id The id of the dragged element
20120      * @return {Boolean} isValid True if the drag event is valid, else false to cancel
20121      */
20122     beforeDragOut : function(target, e, id){
20123         return true;
20124     },
20125     
20126     // private
20127     onDragDrop : function(e, id){
20128         var target = this.cachedTarget || Roo.dd.DragDropMgr.getDDById(id);
20129         if(this.beforeDragDrop(target, e, id) !== false){
20130             if(target.isNotifyTarget){
20131                 if(target.notifyDrop(this, e, this.dragData)){ // valid drop?
20132                     this.onValidDrop(target, e, id);
20133                 }else{
20134                     this.onInvalidDrop(target, e, id);
20135                 }
20136             }else{
20137                 this.onValidDrop(target, e, id);
20138             }
20139             
20140             if(this.afterDragDrop){
20141                 /**
20142                  * An empty function by default, but provided so that you can perform a custom action
20143                  * after a valid drag drop has occurred by providing an implementation.
20144                  * @param {Roo.dd.DragDrop} target The drop target
20145                  * @param {Event} e The event object
20146                  * @param {String} id The id of the dropped element
20147                  * @method afterDragDrop
20148                  */
20149                 this.afterDragDrop(target, e, id);
20150             }
20151         }
20152         delete this.cachedTarget;
20153     },
20154
20155     /**
20156      * An empty function by default, but provided so that you can perform a custom action before the dragged
20157      * item is dropped onto the target and optionally cancel the onDragDrop.
20158      * @param {Roo.dd.DragDrop} target The drop target
20159      * @param {Event} e The event object
20160      * @param {String} id The id of the dragged element
20161      * @return {Boolean} isValid True if the drag drop event is valid, else false to cancel
20162      */
20163     beforeDragDrop : function(target, e, id){
20164         return true;
20165     },
20166
20167     // private
20168     onValidDrop : function(target, e, id){
20169         this.hideProxy();
20170         if(this.afterValidDrop){
20171             /**
20172              * An empty function by default, but provided so that you can perform a custom action
20173              * after a valid drop has occurred by providing an implementation.
20174              * @param {Object} target The target DD 
20175              * @param {Event} e The event object
20176              * @param {String} id The id of the dropped element
20177              * @method afterInvalidDrop
20178              */
20179             this.afterValidDrop(target, e, id);
20180         }
20181     },
20182
20183     // private
20184     getRepairXY : function(e, data){
20185         return this.el.getXY();  
20186     },
20187
20188     // private
20189     onInvalidDrop : function(target, e, id){
20190         this.beforeInvalidDrop(target, e, id);
20191         if(this.cachedTarget){
20192             if(this.cachedTarget.isNotifyTarget){
20193                 this.cachedTarget.notifyOut(this, e, this.dragData);
20194             }
20195             this.cacheTarget = null;
20196         }
20197         this.proxy.repair(this.getRepairXY(e, this.dragData), this.afterRepair, this);
20198
20199         if(this.afterInvalidDrop){
20200             /**
20201              * An empty function by default, but provided so that you can perform a custom action
20202              * after an invalid drop has occurred by providing an implementation.
20203              * @param {Event} e The event object
20204              * @param {String} id The id of the dropped element
20205              * @method afterInvalidDrop
20206              */
20207             this.afterInvalidDrop(e, id);
20208         }
20209     },
20210
20211     // private
20212     afterRepair : function(){
20213         if(Roo.enableFx){
20214             this.el.highlight(this.hlColor || "c3daf9");
20215         }
20216         this.dragging = false;
20217     },
20218
20219     /**
20220      * An empty function by default, but provided so that you can perform a custom action after an invalid
20221      * drop has occurred.
20222      * @param {Roo.dd.DragDrop} target The drop target
20223      * @param {Event} e The event object
20224      * @param {String} id The id of the dragged element
20225      * @return {Boolean} isValid True if the invalid drop should proceed, else false to cancel
20226      */
20227     beforeInvalidDrop : function(target, e, id){
20228         return true;
20229     },
20230
20231     // private
20232     handleMouseDown : function(e){
20233         if(this.dragging) {
20234             return;
20235         }
20236         var data = this.getDragData(e);
20237         if(data && this.onBeforeDrag(data, e) !== false){
20238             this.dragData = data;
20239             this.proxy.stop();
20240             Roo.dd.DragSource.superclass.handleMouseDown.apply(this, arguments);
20241         } 
20242     },
20243
20244     /**
20245      * An empty function by default, but provided so that you can perform a custom action before the initial
20246      * drag event begins and optionally cancel it.
20247      * @param {Object} data An object containing arbitrary data to be shared with drop targets
20248      * @param {Event} e The event object
20249      * @return {Boolean} isValid True if the drag event is valid, else false to cancel
20250      */
20251     onBeforeDrag : function(data, e){
20252         return true;
20253     },
20254
20255     /**
20256      * An empty function by default, but provided so that you can perform a custom action once the initial
20257      * drag event has begun.  The drag cannot be canceled from this function.
20258      * @param {Number} x The x position of the click on the dragged object
20259      * @param {Number} y The y position of the click on the dragged object
20260      */
20261     onStartDrag : Roo.emptyFn,
20262
20263     // private - YUI override
20264     startDrag : function(x, y){
20265         this.proxy.reset();
20266         this.dragging = true;
20267         this.proxy.update("");
20268         this.onInitDrag(x, y);
20269         this.proxy.show();
20270     },
20271
20272     // private
20273     onInitDrag : function(x, y){
20274         var clone = this.el.dom.cloneNode(true);
20275         clone.id = Roo.id(); // prevent duplicate ids
20276         this.proxy.update(clone);
20277         this.onStartDrag(x, y);
20278         return true;
20279     },
20280
20281     /**
20282      * Returns the drag source's underlying {@link Roo.dd.StatusProxy}
20283      * @return {Roo.dd.StatusProxy} proxy The StatusProxy
20284      */
20285     getProxy : function(){
20286         return this.proxy;  
20287     },
20288
20289     /**
20290      * Hides the drag source's {@link Roo.dd.StatusProxy}
20291      */
20292     hideProxy : function(){
20293         this.proxy.hide();  
20294         this.proxy.reset(true);
20295         this.dragging = false;
20296     },
20297
20298     // private
20299     triggerCacheRefresh : function(){
20300         Roo.dd.DDM.refreshCache(this.groups);
20301     },
20302
20303     // private - override to prevent hiding
20304     b4EndDrag: function(e) {
20305     },
20306
20307     // private - override to prevent moving
20308     endDrag : function(e){
20309         this.onEndDrag(this.dragData, e);
20310     },
20311
20312     // private
20313     onEndDrag : function(data, e){
20314     },
20315     
20316     // private - pin to cursor
20317     autoOffset : function(x, y) {
20318         this.setDelta(-12, -20);
20319     }    
20320 });/*
20321  * Based on:
20322  * Ext JS Library 1.1.1
20323  * Copyright(c) 2006-2007, Ext JS, LLC.
20324  *
20325  * Originally Released Under LGPL - original licence link has changed is not relivant.
20326  *
20327  * Fork - LGPL
20328  * <script type="text/javascript">
20329  */
20330
20331
20332 /**
20333  * @class Roo.dd.DropTarget
20334  * @extends Roo.dd.DDTarget
20335  * A simple class that provides the basic implementation needed to make any element a drop target that can have
20336  * draggable items dropped onto it.  The drop has no effect until an implementation of notifyDrop is provided.
20337  * @constructor
20338  * @param {String/HTMLElement/Element} el The container element
20339  * @param {Object} config
20340  */
20341 Roo.dd.DropTarget = function(el, config){
20342     this.el = Roo.get(el);
20343     
20344     var listeners = false; ;
20345     if (config && config.listeners) {
20346         listeners= config.listeners;
20347         delete config.listeners;
20348     }
20349     Roo.apply(this, config);
20350     
20351     if(this.containerScroll){
20352         Roo.dd.ScrollManager.register(this.el);
20353     }
20354     this.addEvents( {
20355          /**
20356          * @scope Roo.dd.DropTarget
20357          */
20358          
20359          /**
20360          * @event enter
20361          * The function a {@link Roo.dd.DragSource} calls once to notify this drop target that the source is now over the
20362          * target.  This default implementation adds the CSS class specified by overClass (if any) to the drop element
20363          * and returns the dropAllowed config value.  This method should be overridden if drop validation is required.
20364          * 
20365          * IMPORTANT : it should set this.overClass and this.dropAllowed
20366          * 
20367          * @param {Roo.dd.DragSource} source The drag source that was dragged over this drop target
20368          * @param {Event} e The event
20369          * @param {Object} data An object containing arbitrary data supplied by the drag source
20370          */
20371         "enter" : true,
20372         
20373          /**
20374          * @event over
20375          * The function a {@link Roo.dd.DragSource} calls continuously while it is being dragged over the target.
20376          * This method will be called on every mouse movement while the drag source is over the drop target.
20377          * This default implementation simply returns the dropAllowed config value.
20378          * 
20379          * IMPORTANT : it should set this.dropAllowed
20380          * 
20381          * @param {Roo.dd.DragSource} source The drag source that was dragged over this drop target
20382          * @param {Event} e The event
20383          * @param {Object} data An object containing arbitrary data supplied by the drag source
20384          
20385          */
20386         "over" : true,
20387         /**
20388          * @event out
20389          * The function a {@link Roo.dd.DragSource} calls once to notify this drop target that the source has been dragged
20390          * out of the target without dropping.  This default implementation simply removes the CSS class specified by
20391          * overClass (if any) from the drop element.
20392          * 
20393          * @param {Roo.dd.DragSource} source The drag source that was dragged over this drop target
20394          * @param {Event} e The event
20395          * @param {Object} data An object containing arbitrary data supplied by the drag source
20396          */
20397          "out" : true,
20398          
20399         /**
20400          * @event drop
20401          * The function a {@link Roo.dd.DragSource} calls once to notify this drop target that the dragged item has
20402          * been dropped on it.  This method has no default implementation and returns false, so you must provide an
20403          * implementation that does something to process the drop event and returns true so that the drag source's
20404          * repair action does not run.
20405          * 
20406          * IMPORTANT : it should set this.success
20407          * 
20408          * @param {Roo.dd.DragSource} source The drag source that was dragged over this drop target
20409          * @param {Event} e The event
20410          * @param {Object} data An object containing arbitrary data supplied by the drag source
20411         */
20412          "drop" : true
20413     });
20414             
20415      
20416     Roo.dd.DropTarget.superclass.constructor.call(  this, 
20417         this.el.dom, 
20418         this.ddGroup || this.group,
20419         {
20420             isTarget: true,
20421             listeners : listeners || {} 
20422            
20423         
20424         }
20425     );
20426
20427 };
20428
20429 Roo.extend(Roo.dd.DropTarget, Roo.dd.DDTarget, {
20430     /**
20431      * @cfg {String} overClass
20432      * The CSS class applied to the drop target element while the drag source is over it (defaults to "").
20433      */
20434      /**
20435      * @cfg {String} ddGroup
20436      * The drag drop group to handle drop events for
20437      */
20438      
20439     /**
20440      * @cfg {String} dropAllowed
20441      * The CSS class returned to the drag source when drop is allowed (defaults to "x-dd-drop-ok").
20442      */
20443     dropAllowed : "x-dd-drop-ok",
20444     /**
20445      * @cfg {String} dropNotAllowed
20446      * The CSS class returned to the drag source when drop is not allowed (defaults to "x-dd-drop-nodrop").
20447      */
20448     dropNotAllowed : "x-dd-drop-nodrop",
20449     /**
20450      * @cfg {boolean} success
20451      * set this after drop listener.. 
20452      */
20453     success : false,
20454     /**
20455      * @cfg {boolean|String} valid true/false or string (ok-add/ok-sub/ok/nodrop)
20456      * if the drop point is valid for over/enter..
20457      */
20458     valid : false,
20459     // private
20460     isTarget : true,
20461
20462     // private
20463     isNotifyTarget : true,
20464     
20465     /**
20466      * @hide
20467      */
20468     notifyEnter : function(dd, e, data)
20469     {
20470         this.valid = true;
20471         this.fireEvent('enter', dd, e, data);
20472         if(this.overClass){
20473             this.el.addClass(this.overClass);
20474         }
20475         return typeof(this.valid) == 'string' ? 'x-dd-drop-' + this.valid : (
20476             this.valid ? this.dropAllowed : this.dropNotAllowed
20477         );
20478     },
20479
20480     /**
20481      * @hide
20482      */
20483     notifyOver : function(dd, e, data)
20484     {
20485         this.valid = true;
20486         this.fireEvent('over', dd, e, data);
20487         return typeof(this.valid) == 'string' ? 'x-dd-drop-' + this.valid : (
20488             this.valid ? this.dropAllowed : this.dropNotAllowed
20489         );
20490     },
20491
20492     /**
20493      * @hide
20494      */
20495     notifyOut : function(dd, e, data)
20496     {
20497         this.fireEvent('out', dd, e, data);
20498         if(this.overClass){
20499             this.el.removeClass(this.overClass);
20500         }
20501     },
20502
20503     /**
20504      * @hide
20505      */
20506     notifyDrop : function(dd, e, data)
20507     {
20508         this.success = false;
20509         this.fireEvent('drop', dd, e, data);
20510         return this.success;
20511     }
20512 });/*
20513  * Based on:
20514  * Ext JS Library 1.1.1
20515  * Copyright(c) 2006-2007, Ext JS, LLC.
20516  *
20517  * Originally Released Under LGPL - original licence link has changed is not relivant.
20518  *
20519  * Fork - LGPL
20520  * <script type="text/javascript">
20521  */
20522
20523
20524 /**
20525  * @class Roo.dd.DragZone
20526  * @extends Roo.dd.DragSource
20527  * This class provides a container DD instance that proxies for multiple child node sources.<br />
20528  * By default, this class requires that draggable child nodes are registered with {@link Roo.dd.Registry}.
20529  * @constructor
20530  * @param {String/HTMLElement/Element} el The container element
20531  * @param {Object} config
20532  */
20533 Roo.dd.DragZone = function(el, config){
20534     Roo.dd.DragZone.superclass.constructor.call(this, el, config);
20535     if(this.containerScroll){
20536         Roo.dd.ScrollManager.register(this.el);
20537     }
20538 };
20539
20540 Roo.extend(Roo.dd.DragZone, Roo.dd.DragSource, {
20541     /**
20542      * @cfg {Boolean} containerScroll True to register this container with the Scrollmanager
20543      * for auto scrolling during drag operations.
20544      */
20545     /**
20546      * @cfg {String} hlColor The color to use when visually highlighting the drag source in the afterRepair
20547      * method after a failed drop (defaults to "c3daf9" - light blue)
20548      */
20549
20550     /**
20551      * Called when a mousedown occurs in this container. Looks in {@link Roo.dd.Registry}
20552      * for a valid target to drag based on the mouse down. Override this method
20553      * to provide your own lookup logic (e.g. finding a child by class name). Make sure your returned
20554      * object has a "ddel" attribute (with an HTML Element) for other functions to work.
20555      * @param {EventObject} e The mouse down event
20556      * @return {Object} The dragData
20557      */
20558     getDragData : function(e){
20559         return Roo.dd.Registry.getHandleFromEvent(e);
20560     },
20561     
20562     /**
20563      * Called once drag threshold has been reached to initialize the proxy element. By default, it clones the
20564      * this.dragData.ddel
20565      * @param {Number} x The x position of the click on the dragged object
20566      * @param {Number} y The y position of the click on the dragged object
20567      * @return {Boolean} true to continue the drag, false to cancel
20568      */
20569     onInitDrag : function(x, y){
20570         this.proxy.update(this.dragData.ddel.cloneNode(true));
20571         this.onStartDrag(x, y);
20572         return true;
20573     },
20574     
20575     /**
20576      * Called after a repair of an invalid drop. By default, highlights this.dragData.ddel 
20577      */
20578     afterRepair : function(){
20579         if(Roo.enableFx){
20580             Roo.Element.fly(this.dragData.ddel).highlight(this.hlColor || "c3daf9");
20581         }
20582         this.dragging = false;
20583     },
20584
20585     /**
20586      * Called before a repair of an invalid drop to get the XY to animate to. By default returns
20587      * the XY of this.dragData.ddel
20588      * @param {EventObject} e The mouse up event
20589      * @return {Array} The xy location (e.g. [100, 200])
20590      */
20591     getRepairXY : function(e){
20592         return Roo.Element.fly(this.dragData.ddel).getXY();  
20593     }
20594 });/*
20595  * Based on:
20596  * Ext JS Library 1.1.1
20597  * Copyright(c) 2006-2007, Ext JS, LLC.
20598  *
20599  * Originally Released Under LGPL - original licence link has changed is not relivant.
20600  *
20601  * Fork - LGPL
20602  * <script type="text/javascript">
20603  */
20604 /**
20605  * @class Roo.dd.DropZone
20606  * @extends Roo.dd.DropTarget
20607  * This class provides a container DD instance that proxies for multiple child node targets.<br />
20608  * By default, this class requires that child nodes accepting drop are registered with {@link Roo.dd.Registry}.
20609  * @constructor
20610  * @param {String/HTMLElement/Element} el The container element
20611  * @param {Object} config
20612  */
20613 Roo.dd.DropZone = function(el, config){
20614     Roo.dd.DropZone.superclass.constructor.call(this, el, config);
20615 };
20616
20617 Roo.extend(Roo.dd.DropZone, Roo.dd.DropTarget, {
20618     /**
20619      * Returns a custom data object associated with the DOM node that is the target of the event.  By default
20620      * this looks up the event target in the {@link Roo.dd.Registry}, although you can override this method to
20621      * provide your own custom lookup.
20622      * @param {Event} e The event
20623      * @return {Object} data The custom data
20624      */
20625     getTargetFromEvent : function(e){
20626         return Roo.dd.Registry.getTargetFromEvent(e);
20627     },
20628
20629     /**
20630      * Called internally when the DropZone determines that a {@link Roo.dd.DragSource} has entered a drop node
20631      * that it has registered.  This method has no default implementation and should be overridden to provide
20632      * node-specific processing if necessary.
20633      * @param {Object} nodeData The custom data associated with the drop node (this is the same value returned from 
20634      * {@link #getTargetFromEvent} for this node)
20635      * @param {Roo.dd.DragSource} source The drag source that was dragged over this drop zone
20636      * @param {Event} e The event
20637      * @param {Object} data An object containing arbitrary data supplied by the drag source
20638      */
20639     onNodeEnter : function(n, dd, e, data){
20640         
20641     },
20642
20643     /**
20644      * Called internally while the DropZone determines that a {@link Roo.dd.DragSource} is over a drop node
20645      * that it has registered.  The default implementation returns this.dropNotAllowed, so it should be
20646      * overridden to provide the proper feedback.
20647      * @param {Object} nodeData The custom data associated with the drop node (this is the same value returned from
20648      * {@link #getTargetFromEvent} for this node)
20649      * @param {Roo.dd.DragSource} source The drag source that was dragged over this drop zone
20650      * @param {Event} e The event
20651      * @param {Object} data An object containing arbitrary data supplied by the drag source
20652      * @return {String} status The CSS class that communicates the drop status back to the source so that the
20653      * underlying {@link Roo.dd.StatusProxy} can be updated
20654      */
20655     onNodeOver : function(n, dd, e, data){
20656         return this.dropAllowed;
20657     },
20658
20659     /**
20660      * Called internally when the DropZone determines that a {@link Roo.dd.DragSource} has been dragged out of
20661      * the drop node without dropping.  This method has no default implementation and should be overridden to provide
20662      * node-specific processing if necessary.
20663      * @param {Object} nodeData The custom data associated with the drop node (this is the same value returned from
20664      * {@link #getTargetFromEvent} for this node)
20665      * @param {Roo.dd.DragSource} source The drag source that was dragged over this drop zone
20666      * @param {Event} e The event
20667      * @param {Object} data An object containing arbitrary data supplied by the drag source
20668      */
20669     onNodeOut : function(n, dd, e, data){
20670         
20671     },
20672
20673     /**
20674      * Called internally when the DropZone determines that a {@link Roo.dd.DragSource} has been dropped onto
20675      * the drop node.  The default implementation returns false, so it should be overridden to provide the
20676      * appropriate processing of the drop event and return true so that the drag source's repair action does not run.
20677      * @param {Object} nodeData The custom data associated with the drop node (this is the same value returned from
20678      * {@link #getTargetFromEvent} for this node)
20679      * @param {Roo.dd.DragSource} source The drag source that was dragged over this drop zone
20680      * @param {Event} e The event
20681      * @param {Object} data An object containing arbitrary data supplied by the drag source
20682      * @return {Boolean} True if the drop was valid, else false
20683      */
20684     onNodeDrop : function(n, dd, e, data){
20685         return false;
20686     },
20687
20688     /**
20689      * Called internally while the DropZone determines that a {@link Roo.dd.DragSource} is being dragged over it,
20690      * but not over any of its registered drop nodes.  The default implementation returns this.dropNotAllowed, so
20691      * it should be overridden to provide the proper feedback if necessary.
20692      * @param {Roo.dd.DragSource} source The drag source that was dragged over this drop zone
20693      * @param {Event} e The event
20694      * @param {Object} data An object containing arbitrary data supplied by the drag source
20695      * @return {String} status The CSS class that communicates the drop status back to the source so that the
20696      * underlying {@link Roo.dd.StatusProxy} can be updated
20697      */
20698     onContainerOver : function(dd, e, data){
20699         return this.dropNotAllowed;
20700     },
20701
20702     /**
20703      * Called internally when the DropZone determines that a {@link Roo.dd.DragSource} has been dropped on it,
20704      * but not on any of its registered drop nodes.  The default implementation returns false, so it should be
20705      * overridden to provide the appropriate processing of the drop event if you need the drop zone itself to
20706      * be able to accept drops.  It should return true when valid so that the drag source's repair action does not run.
20707      * @param {Roo.dd.DragSource} source The drag source that was dragged over this drop zone
20708      * @param {Event} e The event
20709      * @param {Object} data An object containing arbitrary data supplied by the drag source
20710      * @return {Boolean} True if the drop was valid, else false
20711      */
20712     onContainerDrop : function(dd, e, data){
20713         return false;
20714     },
20715
20716     /**
20717      * The function a {@link Roo.dd.DragSource} calls once to notify this drop zone that the source is now over
20718      * the zone.  The default implementation returns this.dropNotAllowed and expects that only registered drop
20719      * nodes can process drag drop operations, so if you need the drop zone itself to be able to process drops
20720      * you should override this method and provide a custom implementation.
20721      * @param {Roo.dd.DragSource} source The drag source that was dragged over this drop zone
20722      * @param {Event} e The event
20723      * @param {Object} data An object containing arbitrary data supplied by the drag source
20724      * @return {String} status The CSS class that communicates the drop status back to the source so that the
20725      * underlying {@link Roo.dd.StatusProxy} can be updated
20726      */
20727     notifyEnter : function(dd, e, data){
20728         return this.dropNotAllowed;
20729     },
20730
20731     /**
20732      * The function a {@link Roo.dd.DragSource} calls continuously while it is being dragged over the drop zone.
20733      * This method will be called on every mouse movement while the drag source is over the drop zone.
20734      * It will call {@link #onNodeOver} while the drag source is over a registered node, and will also automatically
20735      * delegate to the appropriate node-specific methods as necessary when the drag source enters and exits
20736      * registered nodes ({@link #onNodeEnter}, {@link #onNodeOut}). If the drag source is not currently over a
20737      * registered node, it will call {@link #onContainerOver}.
20738      * @param {Roo.dd.DragSource} source The drag source that was dragged over this drop zone
20739      * @param {Event} e The event
20740      * @param {Object} data An object containing arbitrary data supplied by the drag source
20741      * @return {String} status The CSS class that communicates the drop status back to the source so that the
20742      * underlying {@link Roo.dd.StatusProxy} can be updated
20743      */
20744     notifyOver : function(dd, e, data){
20745         var n = this.getTargetFromEvent(e);
20746         if(!n){ // not over valid drop target
20747             if(this.lastOverNode){
20748                 this.onNodeOut(this.lastOverNode, dd, e, data);
20749                 this.lastOverNode = null;
20750             }
20751             return this.onContainerOver(dd, e, data);
20752         }
20753         if(this.lastOverNode != n){
20754             if(this.lastOverNode){
20755                 this.onNodeOut(this.lastOverNode, dd, e, data);
20756             }
20757             this.onNodeEnter(n, dd, e, data);
20758             this.lastOverNode = n;
20759         }
20760         return this.onNodeOver(n, dd, e, data);
20761     },
20762
20763     /**
20764      * The function a {@link Roo.dd.DragSource} calls once to notify this drop zone that the source has been dragged
20765      * out of the zone without dropping.  If the drag source is currently over a registered node, the notification
20766      * will be delegated to {@link #onNodeOut} for node-specific handling, otherwise it will be ignored.
20767      * @param {Roo.dd.DragSource} source The drag source that was dragged over this drop target
20768      * @param {Event} e The event
20769      * @param {Object} data An object containing arbitrary data supplied by the drag zone
20770      */
20771     notifyOut : function(dd, e, data){
20772         if(this.lastOverNode){
20773             this.onNodeOut(this.lastOverNode, dd, e, data);
20774             this.lastOverNode = null;
20775         }
20776     },
20777
20778     /**
20779      * The function a {@link Roo.dd.DragSource} calls once to notify this drop zone that the dragged item has
20780      * been dropped on it.  The drag zone will look up the target node based on the event passed in, and if there
20781      * is a node registered for that event, it will delegate to {@link #onNodeDrop} for node-specific handling,
20782      * otherwise it will call {@link #onContainerDrop}.
20783      * @param {Roo.dd.DragSource} source The drag source that was dragged over this drop zone
20784      * @param {Event} e The event
20785      * @param {Object} data An object containing arbitrary data supplied by the drag source
20786      * @return {Boolean} True if the drop was valid, else false
20787      */
20788     notifyDrop : function(dd, e, data){
20789         if(this.lastOverNode){
20790             this.onNodeOut(this.lastOverNode, dd, e, data);
20791             this.lastOverNode = null;
20792         }
20793         var n = this.getTargetFromEvent(e);
20794         return n ?
20795             this.onNodeDrop(n, dd, e, data) :
20796             this.onContainerDrop(dd, e, data);
20797     },
20798
20799     // private
20800     triggerCacheRefresh : function(){
20801         Roo.dd.DDM.refreshCache(this.groups);
20802     }  
20803 });/*
20804  * Based on:
20805  * Ext JS Library 1.1.1
20806  * Copyright(c) 2006-2007, Ext JS, LLC.
20807  *
20808  * Originally Released Under LGPL - original licence link has changed is not relivant.
20809  *
20810  * Fork - LGPL
20811  * <script type="text/javascript">
20812  */
20813
20814
20815 /**
20816  * @class Roo.data.SortTypes
20817  * @singleton
20818  * Defines the default sorting (casting?) comparison functions used when sorting data.
20819  */
20820 Roo.data.SortTypes = {
20821     /**
20822      * Default sort that does nothing
20823      * @param {Mixed} s The value being converted
20824      * @return {Mixed} The comparison value
20825      */
20826     none : function(s){
20827         return s;
20828     },
20829     
20830     /**
20831      * The regular expression used to strip tags
20832      * @type {RegExp}
20833      * @property
20834      */
20835     stripTagsRE : /<\/?[^>]+>/gi,
20836     
20837     /**
20838      * Strips all HTML tags to sort on text only
20839      * @param {Mixed} s The value being converted
20840      * @return {String} The comparison value
20841      */
20842     asText : function(s){
20843         return String(s).replace(this.stripTagsRE, "");
20844     },
20845     
20846     /**
20847      * Strips all HTML tags to sort on text only - Case insensitive
20848      * @param {Mixed} s The value being converted
20849      * @return {String} The comparison value
20850      */
20851     asUCText : function(s){
20852         return String(s).toUpperCase().replace(this.stripTagsRE, "");
20853     },
20854     
20855     /**
20856      * Case insensitive string
20857      * @param {Mixed} s The value being converted
20858      * @return {String} The comparison value
20859      */
20860     asUCString : function(s) {
20861         return String(s).toUpperCase();
20862     },
20863     
20864     /**
20865      * Date sorting
20866      * @param {Mixed} s The value being converted
20867      * @return {Number} The comparison value
20868      */
20869     asDate : function(s) {
20870         if(!s){
20871             return 0;
20872         }
20873         if(s instanceof Date){
20874             return s.getTime();
20875         }
20876         return Date.parse(String(s));
20877     },
20878     
20879     /**
20880      * Float sorting
20881      * @param {Mixed} s The value being converted
20882      * @return {Float} The comparison value
20883      */
20884     asFloat : function(s) {
20885         var val = parseFloat(String(s).replace(/,/g, ""));
20886         if(isNaN(val)) val = 0;
20887         return val;
20888     },
20889     
20890     /**
20891      * Integer sorting
20892      * @param {Mixed} s The value being converted
20893      * @return {Number} The comparison value
20894      */
20895     asInt : function(s) {
20896         var val = parseInt(String(s).replace(/,/g, ""));
20897         if(isNaN(val)) val = 0;
20898         return val;
20899     }
20900 };/*
20901  * Based on:
20902  * Ext JS Library 1.1.1
20903  * Copyright(c) 2006-2007, Ext JS, LLC.
20904  *
20905  * Originally Released Under LGPL - original licence link has changed is not relivant.
20906  *
20907  * Fork - LGPL
20908  * <script type="text/javascript">
20909  */
20910
20911 /**
20912 * @class Roo.data.Record
20913  * Instances of this class encapsulate both record <em>definition</em> information, and record
20914  * <em>value</em> information for use in {@link Roo.data.Store} objects, or any code which needs
20915  * to access Records cached in an {@link Roo.data.Store} object.<br>
20916  * <p>
20917  * Constructors for this class are generated by passing an Array of field definition objects to {@link #create}.
20918  * Instances are usually only created by {@link Roo.data.Reader} implementations when processing unformatted data
20919  * objects.<br>
20920  * <p>
20921  * Record objects generated by this constructor inherit all the methods of Roo.data.Record listed below.
20922  * @constructor
20923  * This constructor should not be used to create Record objects. Instead, use the constructor generated by
20924  * {@link #create}. The parameters are the same.
20925  * @param {Array} data An associative Array of data values keyed by the field name.
20926  * @param {Object} id (Optional) The id of the record. This id should be unique, and is used by the
20927  * {@link Roo.data.Store} object which owns the Record to index its collection of Records. If
20928  * not specified an integer id is generated.
20929  */
20930 Roo.data.Record = function(data, id){
20931     this.id = (id || id === 0) ? id : ++Roo.data.Record.AUTO_ID;
20932     this.data = data;
20933 };
20934
20935 /**
20936  * Generate a constructor for a specific record layout.
20937  * @param {Array} o An Array of field definition objects which specify field names, and optionally,
20938  * data types, and a mapping for an {@link Roo.data.Reader} to extract the field's value from a data object.
20939  * Each field definition object may contain the following properties: <ul>
20940  * <li><b>name</b> : String<p style="margin-left:1em">The name by which the field is referenced within the Record. This is referenced by,
20941  * for example the <em>dataIndex</em> property in column definition objects passed to {@link Roo.grid.ColumnModel}</p></li>
20942  * <li><b>mapping</b> : String<p style="margin-left:1em">(Optional) A path specification for use by the {@link Roo.data.Reader} implementation
20943  * that is creating the Record to access the data value from the data object. If an {@link Roo.data.JsonReader}
20944  * is being used, then this is a string containing the javascript expression to reference the data relative to 
20945  * the record item's root. If an {@link Roo.data.XmlReader} is being used, this is an {@link Roo.DomQuery} path
20946  * to the data item relative to the record element. If the mapping expression is the same as the field name,
20947  * this may be omitted.</p></li>
20948  * <li><b>type</b> : String<p style="margin-left:1em">(Optional) The data type for conversion to displayable value. Possible values are
20949  * <ul><li>auto (Default, implies no conversion)</li>
20950  * <li>string</li>
20951  * <li>int</li>
20952  * <li>float</li>
20953  * <li>boolean</li>
20954  * <li>date</li></ul></p></li>
20955  * <li><b>sortType</b> : Mixed<p style="margin-left:1em">(Optional) A member of {@link Roo.data.SortTypes}.</p></li>
20956  * <li><b>sortDir</b> : String<p style="margin-left:1em">(Optional) Initial direction to sort. "ASC" or "DESC"</p></li>
20957  * <li><b>convert</b> : Function<p style="margin-left:1em">(Optional) A function which converts the value provided
20958  * by the Reader into an object that will be stored in the Record. It is passed the
20959  * following parameters:<ul>
20960  * <li><b>v</b> : Mixed<p style="margin-left:1em">The data value as read by the Reader.</p></li>
20961  * </ul></p></li>
20962  * <li><b>dateFormat</b> : String<p style="margin-left:1em">(Optional) A format String for the Date.parseDate function.</p></li>
20963  * </ul>
20964  * <br>usage:<br><pre><code>
20965 var TopicRecord = Roo.data.Record.create(
20966     {name: 'title', mapping: 'topic_title'},
20967     {name: 'author', mapping: 'username'},
20968     {name: 'totalPosts', mapping: 'topic_replies', type: 'int'},
20969     {name: 'lastPost', mapping: 'post_time', type: 'date'},
20970     {name: 'lastPoster', mapping: 'user2'},
20971     {name: 'excerpt', mapping: 'post_text'}
20972 );
20973
20974 var myNewRecord = new TopicRecord({
20975     title: 'Do my job please',
20976     author: 'noobie',
20977     totalPosts: 1,
20978     lastPost: new Date(),
20979     lastPoster: 'Animal',
20980     excerpt: 'No way dude!'
20981 });
20982 myStore.add(myNewRecord);
20983 </code></pre>
20984  * @method create
20985  * @static
20986  */
20987 Roo.data.Record.create = function(o){
20988     var f = function(){
20989         f.superclass.constructor.apply(this, arguments);
20990     };
20991     Roo.extend(f, Roo.data.Record);
20992     var p = f.prototype;
20993     p.fields = new Roo.util.MixedCollection(false, function(field){
20994         return field.name;
20995     });
20996     for(var i = 0, len = o.length; i < len; i++){
20997         p.fields.add(new Roo.data.Field(o[i]));
20998     }
20999     f.getField = function(name){
21000         return p.fields.get(name);  
21001     };
21002     return f;
21003 };
21004
21005 Roo.data.Record.AUTO_ID = 1000;
21006 Roo.data.Record.EDIT = 'edit';
21007 Roo.data.Record.REJECT = 'reject';
21008 Roo.data.Record.COMMIT = 'commit';
21009
21010 Roo.data.Record.prototype = {
21011     /**
21012      * Readonly flag - true if this record has been modified.
21013      * @type Boolean
21014      */
21015     dirty : false,
21016     editing : false,
21017     error: null,
21018     modified: null,
21019
21020     // private
21021     join : function(store){
21022         this.store = store;
21023     },
21024
21025     /**
21026      * Set the named field to the specified value.
21027      * @param {String} name The name of the field to set.
21028      * @param {Object} value The value to set the field to.
21029      */
21030     set : function(name, value){
21031         if(this.data[name] == value){
21032             return;
21033         }
21034         this.dirty = true;
21035         if(!this.modified){
21036             this.modified = {};
21037         }
21038         if(typeof this.modified[name] == 'undefined'){
21039             this.modified[name] = this.data[name];
21040         }
21041         this.data[name] = value;
21042         if(!this.editing && this.store){
21043             this.store.afterEdit(this);
21044         }       
21045     },
21046
21047     /**
21048      * Get the value of the named field.
21049      * @param {String} name The name of the field to get the value of.
21050      * @return {Object} The value of the field.
21051      */
21052     get : function(name){
21053         return this.data[name]; 
21054     },
21055
21056     // private
21057     beginEdit : function(){
21058         this.editing = true;
21059         this.modified = {}; 
21060     },
21061
21062     // private
21063     cancelEdit : function(){
21064         this.editing = false;
21065         delete this.modified;
21066     },
21067
21068     // private
21069     endEdit : function(){
21070         this.editing = false;
21071         if(this.dirty && this.store){
21072             this.store.afterEdit(this);
21073         }
21074     },
21075
21076     /**
21077      * Usually called by the {@link Roo.data.Store} which owns the Record.
21078      * Rejects all changes made to the Record since either creation, or the last commit operation.
21079      * Modified fields are reverted to their original values.
21080      * <p>
21081      * Developers should subscribe to the {@link Roo.data.Store#update} event to have their code notified
21082      * of reject operations.
21083      */
21084     reject : function(){
21085         var m = this.modified;
21086         for(var n in m){
21087             if(typeof m[n] != "function"){
21088                 this.data[n] = m[n];
21089             }
21090         }
21091         this.dirty = false;
21092         delete this.modified;
21093         this.editing = false;
21094         if(this.store){
21095             this.store.afterReject(this);
21096         }
21097     },
21098
21099     /**
21100      * Usually called by the {@link Roo.data.Store} which owns the Record.
21101      * Commits all changes made to the Record since either creation, or the last commit operation.
21102      * <p>
21103      * Developers should subscribe to the {@link Roo.data.Store#update} event to have their code notified
21104      * of commit operations.
21105      */
21106     commit : function(){
21107         this.dirty = false;
21108         delete this.modified;
21109         this.editing = false;
21110         if(this.store){
21111             this.store.afterCommit(this);
21112         }
21113     },
21114
21115     // private
21116     hasError : function(){
21117         return this.error != null;
21118     },
21119
21120     // private
21121     clearError : function(){
21122         this.error = null;
21123     },
21124
21125     /**
21126      * Creates a copy of this record.
21127      * @param {String} id (optional) A new record id if you don't want to use this record's id
21128      * @return {Record}
21129      */
21130     copy : function(newId) {
21131         return new this.constructor(Roo.apply({}, this.data), newId || this.id);
21132     }
21133 };/*
21134  * Based on:
21135  * Ext JS Library 1.1.1
21136  * Copyright(c) 2006-2007, Ext JS, LLC.
21137  *
21138  * Originally Released Under LGPL - original licence link has changed is not relivant.
21139  *
21140  * Fork - LGPL
21141  * <script type="text/javascript">
21142  */
21143
21144
21145
21146 /**
21147  * @class Roo.data.Store
21148  * @extends Roo.util.Observable
21149  * The Store class encapsulates a client side cache of {@link Roo.data.Record} objects which provide input data
21150  * for widgets such as the Roo.grid.Grid, or the Roo.form.ComboBox.<br>
21151  * <p>
21152  * A Store object uses an implementation of {@link Roo.data.DataProxy} to access a data object unless you call loadData() directly and pass in your data. The Store object
21153  * has no knowledge of the format of the data returned by the Proxy.<br>
21154  * <p>
21155  * A Store object uses its configured implementation of {@link Roo.data.DataReader} to create {@link Roo.data.Record}
21156  * instances from the data object. These records are cached and made available through accessor functions.
21157  * @constructor
21158  * Creates a new Store.
21159  * @param {Object} config A config object containing the objects needed for the Store to access data,
21160  * and read the data into Records.
21161  */
21162 Roo.data.Store = function(config){
21163     this.data = new Roo.util.MixedCollection(false);
21164     this.data.getKey = function(o){
21165         return o.id;
21166     };
21167     this.baseParams = {};
21168     // private
21169     this.paramNames = {
21170         "start" : "start",
21171         "limit" : "limit",
21172         "sort" : "sort",
21173         "dir" : "dir",
21174         "multisort" : "_multisort"
21175     };
21176
21177     if(config && config.data){
21178         this.inlineData = config.data;
21179         delete config.data;
21180     }
21181
21182     Roo.apply(this, config);
21183     
21184     if(this.reader){ // reader passed
21185         this.reader = Roo.factory(this.reader, Roo.data);
21186         this.reader.xmodule = this.xmodule || false;
21187         if(!this.recordType){
21188             this.recordType = this.reader.recordType;
21189         }
21190         if(this.reader.onMetaChange){
21191             this.reader.onMetaChange = this.onMetaChange.createDelegate(this);
21192         }
21193     }
21194
21195     if(this.recordType){
21196         this.fields = this.recordType.prototype.fields;
21197     }
21198     this.modified = [];
21199
21200     this.addEvents({
21201         /**
21202          * @event datachanged
21203          * Fires when the data cache has changed, and a widget which is using this Store
21204          * as a Record cache should refresh its view.
21205          * @param {Store} this
21206          */
21207         datachanged : true,
21208         /**
21209          * @event metachange
21210          * Fires when this store's reader provides new metadata (fields). This is currently only support for JsonReaders.
21211          * @param {Store} this
21212          * @param {Object} meta The JSON metadata
21213          */
21214         metachange : true,
21215         /**
21216          * @event add
21217          * Fires when Records have been added to the Store
21218          * @param {Store} this
21219          * @param {Roo.data.Record[]} records The array of Records added
21220          * @param {Number} index The index at which the record(s) were added
21221          */
21222         add : true,
21223         /**
21224          * @event remove
21225          * Fires when a Record has been removed from the Store
21226          * @param {Store} this
21227          * @param {Roo.data.Record} record The Record that was removed
21228          * @param {Number} index The index at which the record was removed
21229          */
21230         remove : true,
21231         /**
21232          * @event update
21233          * Fires when a Record has been updated
21234          * @param {Store} this
21235          * @param {Roo.data.Record} record The Record that was updated
21236          * @param {String} operation The update operation being performed.  Value may be one of:
21237          * <pre><code>
21238  Roo.data.Record.EDIT
21239  Roo.data.Record.REJECT
21240  Roo.data.Record.COMMIT
21241          * </code></pre>
21242          */
21243         update : true,
21244         /**
21245          * @event clear
21246          * Fires when the data cache has been cleared.
21247          * @param {Store} this
21248          */
21249         clear : true,
21250         /**
21251          * @event beforeload
21252          * Fires before a request is made for a new data object.  If the beforeload handler returns false
21253          * the load action will be canceled.
21254          * @param {Store} this
21255          * @param {Object} options The loading options that were specified (see {@link #load} for details)
21256          */
21257         beforeload : true,
21258         /**
21259          * @event beforeloadadd
21260          * Fires after a new set of Records has been loaded.
21261          * @param {Store} this
21262          * @param {Roo.data.Record[]} records The Records that were loaded
21263          * @param {Object} options The loading options that were specified (see {@link #load} for details)
21264          */
21265         beforeloadadd : true,
21266         /**
21267          * @event load
21268          * Fires after a new set of Records has been loaded, before they are added to the store.
21269          * @param {Store} this
21270          * @param {Roo.data.Record[]} records The Records that were loaded
21271          * @param {Object} options The loading options that were specified (see {@link #load} for details)
21272          * @params {Object} return from reader
21273          */
21274         load : true,
21275         /**
21276          * @event loadexception
21277          * Fires if an exception occurs in the Proxy during loading.
21278          * Called with the signature of the Proxy's "loadexception" event.
21279          * If you return Json { data: [] , success: false, .... } then this will be thrown with the following args
21280          * 
21281          * @param {Proxy} 
21282          * @param {Object} return from JsonData.reader() - success, totalRecords, records
21283          * @param {Object} load options 
21284          * @param {Object} jsonData from your request (normally this contains the Exception)
21285          */
21286         loadexception : true
21287     });
21288     
21289     if(this.proxy){
21290         this.proxy = Roo.factory(this.proxy, Roo.data);
21291         this.proxy.xmodule = this.xmodule || false;
21292         this.relayEvents(this.proxy,  ["loadexception"]);
21293     }
21294     this.sortToggle = {};
21295     this.sortOrder = []; // array of order of sorting - updated by grid if multisort is enabled.
21296
21297     Roo.data.Store.superclass.constructor.call(this);
21298
21299     if(this.inlineData){
21300         this.loadData(this.inlineData);
21301         delete this.inlineData;
21302     }
21303 };
21304
21305 Roo.extend(Roo.data.Store, Roo.util.Observable, {
21306      /**
21307     * @cfg {boolean} isLocal   flag if data is locally available (and can be always looked up
21308     * without a remote query - used by combo/forms at present.
21309     */
21310     
21311     /**
21312     * @cfg {Roo.data.DataProxy} proxy The Proxy object which provides access to a data object.
21313     */
21314     /**
21315     * @cfg {Array} data Inline data to be loaded when the store is initialized.
21316     */
21317     /**
21318     * @cfg {Roo.data.Reader} reader The Reader object which processes the data object and returns
21319     * an Array of Roo.data.record objects which are cached keyed by their <em>id</em> property.
21320     */
21321     /**
21322     * @cfg {Object} baseParams An object containing properties which are to be sent as parameters
21323     * on any HTTP request
21324     */
21325     /**
21326     * @cfg {Object} sortInfo A config object in the format: {field: "fieldName", direction: "ASC|DESC"}
21327     */
21328     /**
21329     * @cfg {Boolean} multiSort enable multi column sorting (sort is based on the order of columns, remote only at present)
21330     */
21331     multiSort: false,
21332     /**
21333     * @cfg {boolean} remoteSort True if sorting is to be handled by requesting the Proxy to provide a refreshed
21334     * version of the data object in sorted order, as opposed to sorting the Record cache in place (defaults to false).
21335     */
21336     remoteSort : false,
21337
21338     /**
21339     * @cfg {boolean} pruneModifiedRecords True to clear all modified record information each time the store is
21340      * loaded or when a record is removed. (defaults to false).
21341     */
21342     pruneModifiedRecords : false,
21343
21344     // private
21345     lastOptions : null,
21346
21347     /**
21348      * Add Records to the Store and fires the add event.
21349      * @param {Roo.data.Record[]} records An Array of Roo.data.Record objects to add to the cache.
21350      */
21351     add : function(records){
21352         records = [].concat(records);
21353         for(var i = 0, len = records.length; i < len; i++){
21354             records[i].join(this);
21355         }
21356         var index = this.data.length;
21357         this.data.addAll(records);
21358         this.fireEvent("add", this, records, index);
21359     },
21360
21361     /**
21362      * Remove a Record from the Store and fires the remove event.
21363      * @param {Ext.data.Record} record The Roo.data.Record object to remove from the cache.
21364      */
21365     remove : function(record){
21366         var index = this.data.indexOf(record);
21367         this.data.removeAt(index);
21368         if(this.pruneModifiedRecords){
21369             this.modified.remove(record);
21370         }
21371         this.fireEvent("remove", this, record, index);
21372     },
21373
21374     /**
21375      * Remove all Records from the Store and fires the clear event.
21376      */
21377     removeAll : function(){
21378         this.data.clear();
21379         if(this.pruneModifiedRecords){
21380             this.modified = [];
21381         }
21382         this.fireEvent("clear", this);
21383     },
21384
21385     /**
21386      * Inserts Records to the Store at the given index and fires the add event.
21387      * @param {Number} index The start index at which to insert the passed Records.
21388      * @param {Roo.data.Record[]} records An Array of Roo.data.Record objects to add to the cache.
21389      */
21390     insert : function(index, records){
21391         records = [].concat(records);
21392         for(var i = 0, len = records.length; i < len; i++){
21393             this.data.insert(index, records[i]);
21394             records[i].join(this);
21395         }
21396         this.fireEvent("add", this, records, index);
21397     },
21398
21399     /**
21400      * Get the index within the cache of the passed Record.
21401      * @param {Roo.data.Record} record The Roo.data.Record object to to find.
21402      * @return {Number} The index of the passed Record. Returns -1 if not found.
21403      */
21404     indexOf : function(record){
21405         return this.data.indexOf(record);
21406     },
21407
21408     /**
21409      * Get the index within the cache of the Record with the passed id.
21410      * @param {String} id The id of the Record to find.
21411      * @return {Number} The index of the Record. Returns -1 if not found.
21412      */
21413     indexOfId : function(id){
21414         return this.data.indexOfKey(id);
21415     },
21416
21417     /**
21418      * Get the Record with the specified id.
21419      * @param {String} id The id of the Record to find.
21420      * @return {Roo.data.Record} The Record with the passed id. Returns undefined if not found.
21421      */
21422     getById : function(id){
21423         return this.data.key(id);
21424     },
21425
21426     /**
21427      * Get the Record at the specified index.
21428      * @param {Number} index The index of the Record to find.
21429      * @return {Roo.data.Record} The Record at the passed index. Returns undefined if not found.
21430      */
21431     getAt : function(index){
21432         return this.data.itemAt(index);
21433     },
21434
21435     /**
21436      * Returns a range of Records between specified indices.
21437      * @param {Number} startIndex (optional) The starting index (defaults to 0)
21438      * @param {Number} endIndex (optional) The ending index (defaults to the last Record in the Store)
21439      * @return {Roo.data.Record[]} An array of Records
21440      */
21441     getRange : function(start, end){
21442         return this.data.getRange(start, end);
21443     },
21444
21445     // private
21446     storeOptions : function(o){
21447         o = Roo.apply({}, o);
21448         delete o.callback;
21449         delete o.scope;
21450         this.lastOptions = o;
21451     },
21452
21453     /**
21454      * Loads the Record cache from the configured Proxy using the configured Reader.
21455      * <p>
21456      * If using remote paging, then the first load call must specify the <em>start</em>
21457      * and <em>limit</em> properties in the options.params property to establish the initial
21458      * position within the dataset, and the number of Records to cache on each read from the Proxy.
21459      * <p>
21460      * <strong>It is important to note that for remote data sources, loading is asynchronous,
21461      * and this call will return before the new data has been loaded. Perform any post-processing
21462      * in a callback function, or in a "load" event handler.</strong>
21463      * <p>
21464      * @param {Object} options An object containing properties which control loading options:<ul>
21465      * <li>params {Object} An object containing properties to pass as HTTP parameters to a remote data source.</li>
21466      * <li>callback {Function} A function to be called after the Records have been loaded. The callback is
21467      * passed the following arguments:<ul>
21468      * <li>r : Roo.data.Record[]</li>
21469      * <li>options: Options object from the load call</li>
21470      * <li>success: Boolean success indicator</li></ul></li>
21471      * <li>scope {Object} Scope with which to call the callback (defaults to the Store object)</li>
21472      * <li>add {Boolean} indicator to append loaded records rather than replace the current cache.</li>
21473      * </ul>
21474      */
21475     load : function(options){
21476         options = options || {};
21477         if(this.fireEvent("beforeload", this, options) !== false){
21478             this.storeOptions(options);
21479             var p = Roo.apply(options.params || {}, this.baseParams);
21480             // if meta was not loaded from remote source.. try requesting it.
21481             if (!this.reader.metaFromRemote) {
21482                 p._requestMeta = 1;
21483             }
21484             if(this.sortInfo && this.remoteSort){
21485                 var pn = this.paramNames;
21486                 p[pn["sort"]] = this.sortInfo.field;
21487                 p[pn["dir"]] = this.sortInfo.direction;
21488             }
21489             if (this.multiSort) {
21490                 var pn = this.paramNames;
21491                 p[pn["multisort"]] = Roo.encode( { sort : this.sortToggle, order: this.sortOrder });
21492             }
21493             
21494             this.proxy.load(p, this.reader, this.loadRecords, this, options);
21495         }
21496     },
21497
21498     /**
21499      * Reloads the Record cache from the configured Proxy using the configured Reader and
21500      * the options from the last load operation performed.
21501      * @param {Object} options (optional) An object containing properties which may override the options
21502      * used in the last load operation. See {@link #load} for details (defaults to null, in which case
21503      * the most recently used options are reused).
21504      */
21505     reload : function(options){
21506         this.load(Roo.applyIf(options||{}, this.lastOptions));
21507     },
21508
21509     // private
21510     // Called as a callback by the Reader during a load operation.
21511     loadRecords : function(o, options, success){
21512         if(!o || success === false){
21513             if(success !== false){
21514                 this.fireEvent("load", this, [], options, o);
21515             }
21516             if(options.callback){
21517                 options.callback.call(options.scope || this, [], options, false);
21518             }
21519             return;
21520         }
21521         // if data returned failure - throw an exception.
21522         if (o.success === false) {
21523             // show a message if no listener is registered.
21524             if (!this.hasListener('loadexception') && typeof(o.raw.errorMsg) != 'undefined') {
21525                     Roo.MessageBox.alert("Error loading",o.raw.errorMsg);
21526             }
21527             // loadmask wil be hooked into this..
21528             this.fireEvent("loadexception", this, o, options, o.raw.errorMsg);
21529             return;
21530         }
21531         var r = o.records, t = o.totalRecords || r.length;
21532         
21533         this.fireEvent("beforeloadadd", this, r, options, o);
21534         
21535         if(!options || options.add !== true){
21536             if(this.pruneModifiedRecords){
21537                 this.modified = [];
21538             }
21539             for(var i = 0, len = r.length; i < len; i++){
21540                 r[i].join(this);
21541             }
21542             if(this.snapshot){
21543                 this.data = this.snapshot;
21544                 delete this.snapshot;
21545             }
21546             this.data.clear();
21547             this.data.addAll(r);
21548             this.totalLength = t;
21549             this.applySort();
21550             this.fireEvent("datachanged", this);
21551         }else{
21552             this.totalLength = Math.max(t, this.data.length+r.length);
21553             this.add(r);
21554         }
21555         this.fireEvent("load", this, r, options, o);
21556         if(options.callback){
21557             options.callback.call(options.scope || this, r, options, true);
21558         }
21559     },
21560
21561
21562     /**
21563      * Loads data from a passed data block. A Reader which understands the format of the data
21564      * must have been configured in the constructor.
21565      * @param {Object} data The data block from which to read the Records.  The format of the data expected
21566      * is dependent on the type of Reader that is configured and should correspond to that Reader's readRecords parameter.
21567      * @param {Boolean} append (Optional) True to append the new Records rather than replace the existing cache.
21568      */
21569     loadData : function(o, append){
21570         var r = this.reader.readRecords(o);
21571         this.loadRecords(r, {add: append}, true);
21572     },
21573
21574     /**
21575      * Gets the number of cached records.
21576      * <p>
21577      * <em>If using paging, this may not be the total size of the dataset. If the data object
21578      * used by the Reader contains the dataset size, then the getTotalCount() function returns
21579      * the data set size</em>
21580      */
21581     getCount : function(){
21582         return this.data.length || 0;
21583     },
21584
21585     /**
21586      * Gets the total number of records in the dataset as returned by the server.
21587      * <p>
21588      * <em>If using paging, for this to be accurate, the data object used by the Reader must contain
21589      * the dataset size</em>
21590      */
21591     getTotalCount : function(){
21592         return this.totalLength || 0;
21593     },
21594
21595     /**
21596      * Returns the sort state of the Store as an object with two properties:
21597      * <pre><code>
21598  field {String} The name of the field by which the Records are sorted
21599  direction {String} The sort order, "ASC" or "DESC"
21600      * </code></pre>
21601      */
21602     getSortState : function(){
21603         return this.sortInfo;
21604     },
21605
21606     // private
21607     applySort : function(){
21608         if(this.sortInfo && !this.remoteSort){
21609             var s = this.sortInfo, f = s.field;
21610             var st = this.fields.get(f).sortType;
21611             var fn = function(r1, r2){
21612                 var v1 = st(r1.data[f]), v2 = st(r2.data[f]);
21613                 return v1 > v2 ? 1 : (v1 < v2 ? -1 : 0);
21614             };
21615             this.data.sort(s.direction, fn);
21616             if(this.snapshot && this.snapshot != this.data){
21617                 this.snapshot.sort(s.direction, fn);
21618             }
21619         }
21620     },
21621
21622     /**
21623      * Sets the default sort column and order to be used by the next load operation.
21624      * @param {String} fieldName The name of the field to sort by.
21625      * @param {String} dir (optional) The sort order, "ASC" or "DESC" (defaults to "ASC")
21626      */
21627     setDefaultSort : function(field, dir){
21628         this.sortInfo = {field: field, direction: dir ? dir.toUpperCase() : "ASC"};
21629     },
21630
21631     /**
21632      * Sort the Records.
21633      * If remote sorting is used, the sort is performed on the server, and the cache is
21634      * reloaded. If local sorting is used, the cache is sorted internally.
21635      * @param {String} fieldName The name of the field to sort by.
21636      * @param {String} dir (optional) The sort order, "ASC" or "DESC" (defaults to "ASC")
21637      */
21638     sort : function(fieldName, dir){
21639         var f = this.fields.get(fieldName);
21640         if(!dir){
21641             this.sortToggle[f.name] = this.sortToggle[f.name] || f.sortDir;
21642             
21643             if(this.multiSort || (this.sortInfo && this.sortInfo.field == f.name) ){ // toggle sort dir
21644                 dir = (this.sortToggle[f.name] || "ASC").toggle("ASC", "DESC");
21645             }else{
21646                 dir = f.sortDir;
21647             }
21648         }
21649         this.sortToggle[f.name] = dir;
21650         this.sortInfo = {field: f.name, direction: dir};
21651         if(!this.remoteSort){
21652             this.applySort();
21653             this.fireEvent("datachanged", this);
21654         }else{
21655             this.load(this.lastOptions);
21656         }
21657     },
21658
21659     /**
21660      * Calls the specified function for each of the Records in the cache.
21661      * @param {Function} fn The function to call. The Record is passed as the first parameter.
21662      * Returning <em>false</em> aborts and exits the iteration.
21663      * @param {Object} scope (optional) The scope in which to call the function (defaults to the Record).
21664      */
21665     each : function(fn, scope){
21666         this.data.each(fn, scope);
21667     },
21668
21669     /**
21670      * Gets all records modified since the last commit.  Modified records are persisted across load operations
21671      * (e.g., during paging).
21672      * @return {Roo.data.Record[]} An array of Records containing outstanding modifications.
21673      */
21674     getModifiedRecords : function(){
21675         return this.modified;
21676     },
21677
21678     // private
21679     createFilterFn : function(property, value, anyMatch){
21680         if(!value.exec){ // not a regex
21681             value = String(value);
21682             if(value.length == 0){
21683                 return false;
21684             }
21685             value = new RegExp((anyMatch === true ? '' : '^') + Roo.escapeRe(value), "i");
21686         }
21687         return function(r){
21688             return value.test(r.data[property]);
21689         };
21690     },
21691
21692     /**
21693      * Sums the value of <i>property</i> for each record between start and end and returns the result.
21694      * @param {String} property A field on your records
21695      * @param {Number} start The record index to start at (defaults to 0)
21696      * @param {Number} end The last record index to include (defaults to length - 1)
21697      * @return {Number} The sum
21698      */
21699     sum : function(property, start, end){
21700         var rs = this.data.items, v = 0;
21701         start = start || 0;
21702         end = (end || end === 0) ? end : rs.length-1;
21703
21704         for(var i = start; i <= end; i++){
21705             v += (rs[i].data[property] || 0);
21706         }
21707         return v;
21708     },
21709
21710     /**
21711      * Filter the records by a specified property.
21712      * @param {String} field A field on your records
21713      * @param {String/RegExp} value Either a string that the field
21714      * should start with or a RegExp to test against the field
21715      * @param {Boolean} anyMatch True to match any part not just the beginning
21716      */
21717     filter : function(property, value, anyMatch){
21718         var fn = this.createFilterFn(property, value, anyMatch);
21719         return fn ? this.filterBy(fn) : this.clearFilter();
21720     },
21721
21722     /**
21723      * Filter by a function. The specified function will be called with each
21724      * record in this data source. If the function returns true the record is included,
21725      * otherwise it is filtered.
21726      * @param {Function} fn The function to be called, it will receive 2 args (record, id)
21727      * @param {Object} scope (optional) The scope of the function (defaults to this)
21728      */
21729     filterBy : function(fn, scope){
21730         this.snapshot = this.snapshot || this.data;
21731         this.data = this.queryBy(fn, scope||this);
21732         this.fireEvent("datachanged", this);
21733     },
21734
21735     /**
21736      * Query the records by a specified property.
21737      * @param {String} field A field on your records
21738      * @param {String/RegExp} value Either a string that the field
21739      * should start with or a RegExp to test against the field
21740      * @param {Boolean} anyMatch True to match any part not just the beginning
21741      * @return {MixedCollection} Returns an Roo.util.MixedCollection of the matched records
21742      */
21743     query : function(property, value, anyMatch){
21744         var fn = this.createFilterFn(property, value, anyMatch);
21745         return fn ? this.queryBy(fn) : this.data.clone();
21746     },
21747
21748     /**
21749      * Query by a function. The specified function will be called with each
21750      * record in this data source. If the function returns true the record is included
21751      * in the results.
21752      * @param {Function} fn The function to be called, it will receive 2 args (record, id)
21753      * @param {Object} scope (optional) The scope of the function (defaults to this)
21754       @return {MixedCollection} Returns an Roo.util.MixedCollection of the matched records
21755      **/
21756     queryBy : function(fn, scope){
21757         var data = this.snapshot || this.data;
21758         return data.filterBy(fn, scope||this);
21759     },
21760
21761     /**
21762      * Collects unique values for a particular dataIndex from this store.
21763      * @param {String} dataIndex The property to collect
21764      * @param {Boolean} allowNull (optional) Pass true to allow null, undefined or empty string values
21765      * @param {Boolean} bypassFilter (optional) Pass true to collect from all records, even ones which are filtered
21766      * @return {Array} An array of the unique values
21767      **/
21768     collect : function(dataIndex, allowNull, bypassFilter){
21769         var d = (bypassFilter === true && this.snapshot) ?
21770                 this.snapshot.items : this.data.items;
21771         var v, sv, r = [], l = {};
21772         for(var i = 0, len = d.length; i < len; i++){
21773             v = d[i].data[dataIndex];
21774             sv = String(v);
21775             if((allowNull || !Roo.isEmpty(v)) && !l[sv]){
21776                 l[sv] = true;
21777                 r[r.length] = v;
21778             }
21779         }
21780         return r;
21781     },
21782
21783     /**
21784      * Revert to a view of the Record cache with no filtering applied.
21785      * @param {Boolean} suppressEvent If true the filter is cleared silently without notifying listeners
21786      */
21787     clearFilter : function(suppressEvent){
21788         if(this.snapshot && this.snapshot != this.data){
21789             this.data = this.snapshot;
21790             delete this.snapshot;
21791             if(suppressEvent !== true){
21792                 this.fireEvent("datachanged", this);
21793             }
21794         }
21795     },
21796
21797     // private
21798     afterEdit : function(record){
21799         if(this.modified.indexOf(record) == -1){
21800             this.modified.push(record);
21801         }
21802         this.fireEvent("update", this, record, Roo.data.Record.EDIT);
21803     },
21804     
21805     // private
21806     afterReject : function(record){
21807         this.modified.remove(record);
21808         this.fireEvent("update", this, record, Roo.data.Record.REJECT);
21809     },
21810
21811     // private
21812     afterCommit : function(record){
21813         this.modified.remove(record);
21814         this.fireEvent("update", this, record, Roo.data.Record.COMMIT);
21815     },
21816
21817     /**
21818      * Commit all Records with outstanding changes. To handle updates for changes, subscribe to the
21819      * Store's "update" event, and perform updating when the third parameter is Roo.data.Record.COMMIT.
21820      */
21821     commitChanges : function(){
21822         var m = this.modified.slice(0);
21823         this.modified = [];
21824         for(var i = 0, len = m.length; i < len; i++){
21825             m[i].commit();
21826         }
21827     },
21828
21829     /**
21830      * Cancel outstanding changes on all changed records.
21831      */
21832     rejectChanges : function(){
21833         var m = this.modified.slice(0);
21834         this.modified = [];
21835         for(var i = 0, len = m.length; i < len; i++){
21836             m[i].reject();
21837         }
21838     },
21839
21840     onMetaChange : function(meta, rtype, o){
21841         this.recordType = rtype;
21842         this.fields = rtype.prototype.fields;
21843         delete this.snapshot;
21844         this.sortInfo = meta.sortInfo || this.sortInfo;
21845         this.modified = [];
21846         this.fireEvent('metachange', this, this.reader.meta);
21847     },
21848     
21849     moveIndex : function(data, type)
21850     {
21851         var index = this.indexOf(data);
21852         
21853         var newIndex = index + type;
21854         
21855         this.remove(data);
21856         
21857         this.insert(newIndex, data);
21858         
21859     }
21860 });/*
21861  * Based on:
21862  * Ext JS Library 1.1.1
21863  * Copyright(c) 2006-2007, Ext JS, LLC.
21864  *
21865  * Originally Released Under LGPL - original licence link has changed is not relivant.
21866  *
21867  * Fork - LGPL
21868  * <script type="text/javascript">
21869  */
21870
21871 /**
21872  * @class Roo.data.SimpleStore
21873  * @extends Roo.data.Store
21874  * Small helper class to make creating Stores from Array data easier.
21875  * @cfg {Number} id The array index of the record id. Leave blank to auto generate ids.
21876  * @cfg {Array} fields An array of field definition objects, or field name strings.
21877  * @cfg {Array} data The multi-dimensional array of data
21878  * @constructor
21879  * @param {Object} config
21880  */
21881 Roo.data.SimpleStore = function(config){
21882     Roo.data.SimpleStore.superclass.constructor.call(this, {
21883         isLocal : true,
21884         reader: new Roo.data.ArrayReader({
21885                 id: config.id
21886             },
21887             Roo.data.Record.create(config.fields)
21888         ),
21889         proxy : new Roo.data.MemoryProxy(config.data)
21890     });
21891     this.load();
21892 };
21893 Roo.extend(Roo.data.SimpleStore, Roo.data.Store);/*
21894  * Based on:
21895  * Ext JS Library 1.1.1
21896  * Copyright(c) 2006-2007, Ext JS, LLC.
21897  *
21898  * Originally Released Under LGPL - original licence link has changed is not relivant.
21899  *
21900  * Fork - LGPL
21901  * <script type="text/javascript">
21902  */
21903
21904 /**
21905 /**
21906  * @extends Roo.data.Store
21907  * @class Roo.data.JsonStore
21908  * Small helper class to make creating Stores for JSON data easier. <br/>
21909 <pre><code>
21910 var store = new Roo.data.JsonStore({
21911     url: 'get-images.php',
21912     root: 'images',
21913     fields: ['name', 'url', {name:'size', type: 'float'}, {name:'lastmod', type:'date'}]
21914 });
21915 </code></pre>
21916  * <b>Note: Although they are not listed, this class inherits all of the config options of Store,
21917  * JsonReader and HttpProxy (unless inline data is provided).</b>
21918  * @cfg {Array} fields An array of field definition objects, or field name strings.
21919  * @constructor
21920  * @param {Object} config
21921  */
21922 Roo.data.JsonStore = function(c){
21923     Roo.data.JsonStore.superclass.constructor.call(this, Roo.apply(c, {
21924         proxy: !c.data ? new Roo.data.HttpProxy({url: c.url}) : undefined,
21925         reader: new Roo.data.JsonReader(c, c.fields)
21926     }));
21927 };
21928 Roo.extend(Roo.data.JsonStore, Roo.data.Store);/*
21929  * Based on:
21930  * Ext JS Library 1.1.1
21931  * Copyright(c) 2006-2007, Ext JS, LLC.
21932  *
21933  * Originally Released Under LGPL - original licence link has changed is not relivant.
21934  *
21935  * Fork - LGPL
21936  * <script type="text/javascript">
21937  */
21938
21939  
21940 Roo.data.Field = function(config){
21941     if(typeof config == "string"){
21942         config = {name: config};
21943     }
21944     Roo.apply(this, config);
21945     
21946     if(!this.type){
21947         this.type = "auto";
21948     }
21949     
21950     var st = Roo.data.SortTypes;
21951     // named sortTypes are supported, here we look them up
21952     if(typeof this.sortType == "string"){
21953         this.sortType = st[this.sortType];
21954     }
21955     
21956     // set default sortType for strings and dates
21957     if(!this.sortType){
21958         switch(this.type){
21959             case "string":
21960                 this.sortType = st.asUCString;
21961                 break;
21962             case "date":
21963                 this.sortType = st.asDate;
21964                 break;
21965             default:
21966                 this.sortType = st.none;
21967         }
21968     }
21969
21970     // define once
21971     var stripRe = /[\$,%]/g;
21972
21973     // prebuilt conversion function for this field, instead of
21974     // switching every time we're reading a value
21975     if(!this.convert){
21976         var cv, dateFormat = this.dateFormat;
21977         switch(this.type){
21978             case "":
21979             case "auto":
21980             case undefined:
21981                 cv = function(v){ return v; };
21982                 break;
21983             case "string":
21984                 cv = function(v){ return (v === undefined || v === null) ? '' : String(v); };
21985                 break;
21986             case "int":
21987                 cv = function(v){
21988                     return v !== undefined && v !== null && v !== '' ?
21989                            parseInt(String(v).replace(stripRe, ""), 10) : '';
21990                     };
21991                 break;
21992             case "float":
21993                 cv = function(v){
21994                     return v !== undefined && v !== null && v !== '' ?
21995                            parseFloat(String(v).replace(stripRe, ""), 10) : ''; 
21996                     };
21997                 break;
21998             case "bool":
21999             case "boolean":
22000                 cv = function(v){ return v === true || v === "true" || v == 1; };
22001                 break;
22002             case "date":
22003                 cv = function(v){
22004                     if(!v){
22005                         return '';
22006                     }
22007                     if(v instanceof Date){
22008                         return v;
22009                     }
22010                     if(dateFormat){
22011                         if(dateFormat == "timestamp"){
22012                             return new Date(v*1000);
22013                         }
22014                         return Date.parseDate(v, dateFormat);
22015                     }
22016                     var parsed = Date.parse(v);
22017                     return parsed ? new Date(parsed) : null;
22018                 };
22019              break;
22020             
22021         }
22022         this.convert = cv;
22023     }
22024 };
22025
22026 Roo.data.Field.prototype = {
22027     dateFormat: null,
22028     defaultValue: "",
22029     mapping: null,
22030     sortType : null,
22031     sortDir : "ASC"
22032 };/*
22033  * Based on:
22034  * Ext JS Library 1.1.1
22035  * Copyright(c) 2006-2007, Ext JS, LLC.
22036  *
22037  * Originally Released Under LGPL - original licence link has changed is not relivant.
22038  *
22039  * Fork - LGPL
22040  * <script type="text/javascript">
22041  */
22042  
22043 // Base class for reading structured data from a data source.  This class is intended to be
22044 // extended (see ArrayReader, JsonReader and XmlReader) and should not be created directly.
22045
22046 /**
22047  * @class Roo.data.DataReader
22048  * Base class for reading structured data from a data source.  This class is intended to be
22049  * extended (see {Roo.data.ArrayReader}, {Roo.data.JsonReader} and {Roo.data.XmlReader}) and should not be created directly.
22050  */
22051
22052 Roo.data.DataReader = function(meta, recordType){
22053     
22054     this.meta = meta;
22055     
22056     this.recordType = recordType instanceof Array ? 
22057         Roo.data.Record.create(recordType) : recordType;
22058 };
22059
22060 Roo.data.DataReader.prototype = {
22061      /**
22062      * Create an empty record
22063      * @param {Object} data (optional) - overlay some values
22064      * @return {Roo.data.Record} record created.
22065      */
22066     newRow :  function(d) {
22067         var da =  {};
22068         this.recordType.prototype.fields.each(function(c) {
22069             switch( c.type) {
22070                 case 'int' : da[c.name] = 0; break;
22071                 case 'date' : da[c.name] = new Date(); break;
22072                 case 'float' : da[c.name] = 0.0; break;
22073                 case 'boolean' : da[c.name] = false; break;
22074                 default : da[c.name] = ""; break;
22075             }
22076             
22077         });
22078         return new this.recordType(Roo.apply(da, d));
22079     }
22080     
22081 };/*
22082  * Based on:
22083  * Ext JS Library 1.1.1
22084  * Copyright(c) 2006-2007, Ext JS, LLC.
22085  *
22086  * Originally Released Under LGPL - original licence link has changed is not relivant.
22087  *
22088  * Fork - LGPL
22089  * <script type="text/javascript">
22090  */
22091
22092 /**
22093  * @class Roo.data.DataProxy
22094  * @extends Roo.data.Observable
22095  * This class is an abstract base class for implementations which provide retrieval of
22096  * unformatted data objects.<br>
22097  * <p>
22098  * DataProxy implementations are usually used in conjunction with an implementation of Roo.data.DataReader
22099  * (of the appropriate type which knows how to parse the data object) to provide a block of
22100  * {@link Roo.data.Records} to an {@link Roo.data.Store}.<br>
22101  * <p>
22102  * Custom implementations must implement the load method as described in
22103  * {@link Roo.data.HttpProxy#load}.
22104  */
22105 Roo.data.DataProxy = function(){
22106     this.addEvents({
22107         /**
22108          * @event beforeload
22109          * Fires before a network request is made to retrieve a data object.
22110          * @param {Object} This DataProxy object.
22111          * @param {Object} params The params parameter to the load function.
22112          */
22113         beforeload : true,
22114         /**
22115          * @event load
22116          * Fires before the load method's callback is called.
22117          * @param {Object} This DataProxy object.
22118          * @param {Object} o The data object.
22119          * @param {Object} arg The callback argument object passed to the load function.
22120          */
22121         load : true,
22122         /**
22123          * @event loadexception
22124          * Fires if an Exception occurs during data retrieval.
22125          * @param {Object} This DataProxy object.
22126          * @param {Object} o The data object.
22127          * @param {Object} arg The callback argument object passed to the load function.
22128          * @param {Object} e The Exception.
22129          */
22130         loadexception : true
22131     });
22132     Roo.data.DataProxy.superclass.constructor.call(this);
22133 };
22134
22135 Roo.extend(Roo.data.DataProxy, Roo.util.Observable);
22136
22137     /**
22138      * @cfg {void} listeners (Not available) Constructor blocks listeners from being set
22139      */
22140 /*
22141  * Based on:
22142  * Ext JS Library 1.1.1
22143  * Copyright(c) 2006-2007, Ext JS, LLC.
22144  *
22145  * Originally Released Under LGPL - original licence link has changed is not relivant.
22146  *
22147  * Fork - LGPL
22148  * <script type="text/javascript">
22149  */
22150 /**
22151  * @class Roo.data.MemoryProxy
22152  * An implementation of Roo.data.DataProxy that simply passes the data specified in its constructor
22153  * to the Reader when its load method is called.
22154  * @constructor
22155  * @param {Object} data The data object which the Reader uses to construct a block of Roo.data.Records.
22156  */
22157 Roo.data.MemoryProxy = function(data){
22158     if (data.data) {
22159         data = data.data;
22160     }
22161     Roo.data.MemoryProxy.superclass.constructor.call(this);
22162     this.data = data;
22163 };
22164
22165 Roo.extend(Roo.data.MemoryProxy, Roo.data.DataProxy, {
22166     /**
22167      * Load data from the requested source (in this case an in-memory
22168      * data object passed to the constructor), read the data object into
22169      * a block of Roo.data.Records using the passed Roo.data.DataReader implementation, and
22170      * process that block using the passed callback.
22171      * @param {Object} params This parameter is not used by the MemoryProxy class.
22172      * @param {Roo.data.DataReader} reader The Reader object which converts the data
22173      * object into a block of Roo.data.Records.
22174      * @param {Function} callback The function into which to pass the block of Roo.data.records.
22175      * The function must be passed <ul>
22176      * <li>The Record block object</li>
22177      * <li>The "arg" argument from the load function</li>
22178      * <li>A boolean success indicator</li>
22179      * </ul>
22180      * @param {Object} scope The scope in which to call the callback
22181      * @param {Object} arg An optional argument which is passed to the callback as its second parameter.
22182      */
22183     load : function(params, reader, callback, scope, arg){
22184         params = params || {};
22185         var result;
22186         try {
22187             result = reader.readRecords(this.data);
22188         }catch(e){
22189             this.fireEvent("loadexception", this, arg, null, e);
22190             callback.call(scope, null, arg, false);
22191             return;
22192         }
22193         callback.call(scope, result, arg, true);
22194     },
22195     
22196     // private
22197     update : function(params, records){
22198         
22199     }
22200 });/*
22201  * Based on:
22202  * Ext JS Library 1.1.1
22203  * Copyright(c) 2006-2007, Ext JS, LLC.
22204  *
22205  * Originally Released Under LGPL - original licence link has changed is not relivant.
22206  *
22207  * Fork - LGPL
22208  * <script type="text/javascript">
22209  */
22210 /**
22211  * @class Roo.data.HttpProxy
22212  * @extends Roo.data.DataProxy
22213  * An implementation of {@link Roo.data.DataProxy} that reads a data object from an {@link Roo.data.Connection} object
22214  * configured to reference a certain URL.<br><br>
22215  * <p>
22216  * <em>Note that this class cannot be used to retrieve data from a domain other than the domain
22217  * from which the running page was served.<br><br>
22218  * <p>
22219  * For cross-domain access to remote data, use an {@link Roo.data.ScriptTagProxy}.</em><br><br>
22220  * <p>
22221  * Be aware that to enable the browser to parse an XML document, the server must set
22222  * the Content-Type header in the HTTP response to "text/xml".
22223  * @constructor
22224  * @param {Object} conn Connection config options to add to each request (e.g. {url: 'foo.php'} or
22225  * an {@link Roo.data.Connection} object.  If a Connection config is passed, the singleton {@link Roo.Ajax} object
22226  * will be used to make the request.
22227  */
22228 Roo.data.HttpProxy = function(conn){
22229     Roo.data.HttpProxy.superclass.constructor.call(this);
22230     // is conn a conn config or a real conn?
22231     this.conn = conn;
22232     this.useAjax = !conn || !conn.events;
22233   
22234 };
22235
22236 Roo.extend(Roo.data.HttpProxy, Roo.data.DataProxy, {
22237     // thse are take from connection...
22238     
22239     /**
22240      * @cfg {String} url (Optional) The default URL to be used for requests to the server. (defaults to undefined)
22241      */
22242     /**
22243      * @cfg {Object} extraParams (Optional) An object containing properties which are used as
22244      * extra parameters to each request made by this object. (defaults to undefined)
22245      */
22246     /**
22247      * @cfg {Object} defaultHeaders (Optional) An object containing request headers which are added
22248      *  to each request made by this object. (defaults to undefined)
22249      */
22250     /**
22251      * @cfg {String} method (Optional) The default HTTP method to be used for requests. (defaults to undefined; if not set but parms are present will use POST, otherwise GET)
22252      */
22253     /**
22254      * @cfg {Number} timeout (Optional) The timeout in milliseconds to be used for requests. (defaults to 30000)
22255      */
22256      /**
22257      * @cfg {Boolean} autoAbort (Optional) Whether this request should abort any pending requests. (defaults to false)
22258      * @type Boolean
22259      */
22260   
22261
22262     /**
22263      * @cfg {Boolean} disableCaching (Optional) True to add a unique cache-buster param to GET requests. (defaults to true)
22264      * @type Boolean
22265      */
22266     /**
22267      * Return the {@link Roo.data.Connection} object being used by this Proxy.
22268      * @return {Connection} The Connection object. This object may be used to subscribe to events on
22269      * a finer-grained basis than the DataProxy events.
22270      */
22271     getConnection : function(){
22272         return this.useAjax ? Roo.Ajax : this.conn;
22273     },
22274
22275     /**
22276      * Load data from the configured {@link Roo.data.Connection}, read the data object into
22277      * a block of Roo.data.Records using the passed {@link Roo.data.DataReader} implementation, and
22278      * process that block using the passed callback.
22279      * @param {Object} params An object containing properties which are to be used as HTTP parameters
22280      * for the request to the remote server.
22281      * @param {Roo.data.DataReader} reader The Reader object which converts the data
22282      * object into a block of Roo.data.Records.
22283      * @param {Function} callback The function into which to pass the block of Roo.data.Records.
22284      * The function must be passed <ul>
22285      * <li>The Record block object</li>
22286      * <li>The "arg" argument from the load function</li>
22287      * <li>A boolean success indicator</li>
22288      * </ul>
22289      * @param {Object} scope The scope in which to call the callback
22290      * @param {Object} arg An optional argument which is passed to the callback as its second parameter.
22291      */
22292     load : function(params, reader, callback, scope, arg){
22293         if(this.fireEvent("beforeload", this, params) !== false){
22294             var  o = {
22295                 params : params || {},
22296                 request: {
22297                     callback : callback,
22298                     scope : scope,
22299                     arg : arg
22300                 },
22301                 reader: reader,
22302                 callback : this.loadResponse,
22303                 scope: this
22304             };
22305             if(this.useAjax){
22306                 Roo.applyIf(o, this.conn);
22307                 if(this.activeRequest){
22308                     Roo.Ajax.abort(this.activeRequest);
22309                 }
22310                 this.activeRequest = Roo.Ajax.request(o);
22311             }else{
22312                 this.conn.request(o);
22313             }
22314         }else{
22315             callback.call(scope||this, null, arg, false);
22316         }
22317     },
22318
22319     // private
22320     loadResponse : function(o, success, response){
22321         delete this.activeRequest;
22322         if(!success){
22323             this.fireEvent("loadexception", this, o, response);
22324             o.request.callback.call(o.request.scope, null, o.request.arg, false);
22325             return;
22326         }
22327         var result;
22328         try {
22329             result = o.reader.read(response);
22330         }catch(e){
22331             this.fireEvent("loadexception", this, o, response, e);
22332             o.request.callback.call(o.request.scope, null, o.request.arg, false);
22333             return;
22334         }
22335         
22336         this.fireEvent("load", this, o, o.request.arg);
22337         o.request.callback.call(o.request.scope, result, o.request.arg, true);
22338     },
22339
22340     // private
22341     update : function(dataSet){
22342
22343     },
22344
22345     // private
22346     updateResponse : function(dataSet){
22347
22348     }
22349 });/*
22350  * Based on:
22351  * Ext JS Library 1.1.1
22352  * Copyright(c) 2006-2007, Ext JS, LLC.
22353  *
22354  * Originally Released Under LGPL - original licence link has changed is not relivant.
22355  *
22356  * Fork - LGPL
22357  * <script type="text/javascript">
22358  */
22359
22360 /**
22361  * @class Roo.data.ScriptTagProxy
22362  * An implementation of Roo.data.DataProxy that reads a data object from a URL which may be in a domain
22363  * other than the originating domain of the running page.<br><br>
22364  * <p>
22365  * <em>Note that if you are retrieving data from a page that is in a domain that is NOT the same as the originating domain
22366  * of the running page, you must use this class, rather than DataProxy.</em><br><br>
22367  * <p>
22368  * The content passed back from a server resource requested by a ScriptTagProxy is executable JavaScript
22369  * source code that is used as the source inside a &lt;script> tag.<br><br>
22370  * <p>
22371  * In order for the browser to process the returned data, the server must wrap the data object
22372  * with a call to a callback function, the name of which is passed as a parameter by the ScriptTagProxy.
22373  * Below is a Java example for a servlet which returns data for either a ScriptTagProxy, or an HttpProxy
22374  * depending on whether the callback name was passed:
22375  * <p>
22376  * <pre><code>
22377 boolean scriptTag = false;
22378 String cb = request.getParameter("callback");
22379 if (cb != null) {
22380     scriptTag = true;
22381     response.setContentType("text/javascript");
22382 } else {
22383     response.setContentType("application/x-json");
22384 }
22385 Writer out = response.getWriter();
22386 if (scriptTag) {
22387     out.write(cb + "(");
22388 }
22389 out.print(dataBlock.toJsonString());
22390 if (scriptTag) {
22391     out.write(");");
22392 }
22393 </pre></code>
22394  *
22395  * @constructor
22396  * @param {Object} config A configuration object.
22397  */
22398 Roo.data.ScriptTagProxy = function(config){
22399     Roo.data.ScriptTagProxy.superclass.constructor.call(this);
22400     Roo.apply(this, config);
22401     this.head = document.getElementsByTagName("head")[0];
22402 };
22403
22404 Roo.data.ScriptTagProxy.TRANS_ID = 1000;
22405
22406 Roo.extend(Roo.data.ScriptTagProxy, Roo.data.DataProxy, {
22407     /**
22408      * @cfg {String} url The URL from which to request the data object.
22409      */
22410     /**
22411      * @cfg {Number} timeout (Optional) The number of milliseconds to wait for a response. Defaults to 30 seconds.
22412      */
22413     timeout : 30000,
22414     /**
22415      * @cfg {String} callbackParam (Optional) The name of the parameter to pass to the server which tells
22416      * the server the name of the callback function set up by the load call to process the returned data object.
22417      * Defaults to "callback".<p>The server-side processing must read this parameter value, and generate
22418      * javascript output which calls this named function passing the data object as its only parameter.
22419      */
22420     callbackParam : "callback",
22421     /**
22422      *  @cfg {Boolean} nocache (Optional) Defaults to true. Disable cacheing by adding a unique parameter
22423      * name to the request.
22424      */
22425     nocache : true,
22426
22427     /**
22428      * Load data from the configured URL, read the data object into
22429      * a block of Roo.data.Records using the passed Roo.data.DataReader implementation, and
22430      * process that block using the passed callback.
22431      * @param {Object} params An object containing properties which are to be used as HTTP parameters
22432      * for the request to the remote server.
22433      * @param {Roo.data.DataReader} reader The Reader object which converts the data
22434      * object into a block of Roo.data.Records.
22435      * @param {Function} callback The function into which to pass the block of Roo.data.Records.
22436      * The function must be passed <ul>
22437      * <li>The Record block object</li>
22438      * <li>The "arg" argument from the load function</li>
22439      * <li>A boolean success indicator</li>
22440      * </ul>
22441      * @param {Object} scope The scope in which to call the callback
22442      * @param {Object} arg An optional argument which is passed to the callback as its second parameter.
22443      */
22444     load : function(params, reader, callback, scope, arg){
22445         if(this.fireEvent("beforeload", this, params) !== false){
22446
22447             var p = Roo.urlEncode(Roo.apply(params, this.extraParams));
22448
22449             var url = this.url;
22450             url += (url.indexOf("?") != -1 ? "&" : "?") + p;
22451             if(this.nocache){
22452                 url += "&_dc=" + (new Date().getTime());
22453             }
22454             var transId = ++Roo.data.ScriptTagProxy.TRANS_ID;
22455             var trans = {
22456                 id : transId,
22457                 cb : "stcCallback"+transId,
22458                 scriptId : "stcScript"+transId,
22459                 params : params,
22460                 arg : arg,
22461                 url : url,
22462                 callback : callback,
22463                 scope : scope,
22464                 reader : reader
22465             };
22466             var conn = this;
22467
22468             window[trans.cb] = function(o){
22469                 conn.handleResponse(o, trans);
22470             };
22471
22472             url += String.format("&{0}={1}", this.callbackParam, trans.cb);
22473
22474             if(this.autoAbort !== false){
22475                 this.abort();
22476             }
22477
22478             trans.timeoutId = this.handleFailure.defer(this.timeout, this, [trans]);
22479
22480             var script = document.createElement("script");
22481             script.setAttribute("src", url);
22482             script.setAttribute("type", "text/javascript");
22483             script.setAttribute("id", trans.scriptId);
22484             this.head.appendChild(script);
22485
22486             this.trans = trans;
22487         }else{
22488             callback.call(scope||this, null, arg, false);
22489         }
22490     },
22491
22492     // private
22493     isLoading : function(){
22494         return this.trans ? true : false;
22495     },
22496
22497     /**
22498      * Abort the current server request.
22499      */
22500     abort : function(){
22501         if(this.isLoading()){
22502             this.destroyTrans(this.trans);
22503         }
22504     },
22505
22506     // private
22507     destroyTrans : function(trans, isLoaded){
22508         this.head.removeChild(document.getElementById(trans.scriptId));
22509         clearTimeout(trans.timeoutId);
22510         if(isLoaded){
22511             window[trans.cb] = undefined;
22512             try{
22513                 delete window[trans.cb];
22514             }catch(e){}
22515         }else{
22516             // if hasn't been loaded, wait for load to remove it to prevent script error
22517             window[trans.cb] = function(){
22518                 window[trans.cb] = undefined;
22519                 try{
22520                     delete window[trans.cb];
22521                 }catch(e){}
22522             };
22523         }
22524     },
22525
22526     // private
22527     handleResponse : function(o, trans){
22528         this.trans = false;
22529         this.destroyTrans(trans, true);
22530         var result;
22531         try {
22532             result = trans.reader.readRecords(o);
22533         }catch(e){
22534             this.fireEvent("loadexception", this, o, trans.arg, e);
22535             trans.callback.call(trans.scope||window, null, trans.arg, false);
22536             return;
22537         }
22538         this.fireEvent("load", this, o, trans.arg);
22539         trans.callback.call(trans.scope||window, result, trans.arg, true);
22540     },
22541
22542     // private
22543     handleFailure : function(trans){
22544         this.trans = false;
22545         this.destroyTrans(trans, false);
22546         this.fireEvent("loadexception", this, null, trans.arg);
22547         trans.callback.call(trans.scope||window, null, trans.arg, false);
22548     }
22549 });/*
22550  * Based on:
22551  * Ext JS Library 1.1.1
22552  * Copyright(c) 2006-2007, Ext JS, LLC.
22553  *
22554  * Originally Released Under LGPL - original licence link has changed is not relivant.
22555  *
22556  * Fork - LGPL
22557  * <script type="text/javascript">
22558  */
22559
22560 /**
22561  * @class Roo.data.JsonReader
22562  * @extends Roo.data.DataReader
22563  * Data reader class to create an Array of Roo.data.Record objects from a JSON response
22564  * based on mappings in a provided Roo.data.Record constructor.
22565  * 
22566  * The default behaviour of a store is to send ?_requestMeta=1, unless the class has recieved 'metaData' property
22567  * in the reply previously. 
22568  * 
22569  * <p>
22570  * Example code:
22571  * <pre><code>
22572 var RecordDef = Roo.data.Record.create([
22573     {name: 'name', mapping: 'name'},     // "mapping" property not needed if it's the same as "name"
22574     {name: 'occupation'}                 // This field will use "occupation" as the mapping.
22575 ]);
22576 var myReader = new Roo.data.JsonReader({
22577     totalProperty: "results",    // The property which contains the total dataset size (optional)
22578     root: "rows",                // The property which contains an Array of row objects
22579     id: "id"                     // The property within each row object that provides an ID for the record (optional)
22580 }, RecordDef);
22581 </code></pre>
22582  * <p>
22583  * This would consume a JSON file like this:
22584  * <pre><code>
22585 { 'results': 2, 'rows': [
22586     { 'id': 1, 'name': 'Bill', occupation: 'Gardener' },
22587     { 'id': 2, 'name': 'Ben', occupation: 'Horticulturalist' } ]
22588 }
22589 </code></pre>
22590  * @cfg {String} totalProperty Name of the property from which to retrieve the total number of records
22591  * in the dataset. This is only needed if the whole dataset is not passed in one go, but is being
22592  * paged from the remote server.
22593  * @cfg {String} successProperty Name of the property from which to retrieve the success attribute used by forms.
22594  * @cfg {String} root name of the property which contains the Array of row objects.
22595  * @cfg {String} id Name of the property within a row object that contains a record identifier value.
22596  * @constructor
22597  * Create a new JsonReader
22598  * @param {Object} meta Metadata configuration options
22599  * @param {Object} recordType Either an Array of field definition objects,
22600  * or an {@link Roo.data.Record} object created using {@link Roo.data.Record#create}.
22601  */
22602 Roo.data.JsonReader = function(meta, recordType){
22603     
22604     meta = meta || {};
22605     // set some defaults:
22606     Roo.applyIf(meta, {
22607         totalProperty: 'total',
22608         successProperty : 'success',
22609         root : 'data',
22610         id : 'id'
22611     });
22612     
22613     Roo.data.JsonReader.superclass.constructor.call(this, meta, recordType||meta.fields);
22614 };
22615 Roo.extend(Roo.data.JsonReader, Roo.data.DataReader, {
22616     
22617     /**
22618      * @prop {Boolean} metaFromRemote  - if the meta data was loaded from the remote source.
22619      * Used by Store query builder to append _requestMeta to params.
22620      * 
22621      */
22622     metaFromRemote : false,
22623     /**
22624      * This method is only used by a DataProxy which has retrieved data from a remote server.
22625      * @param {Object} response The XHR object which contains the JSON data in its responseText.
22626      * @return {Object} data A data block which is used by an Roo.data.Store object as
22627      * a cache of Roo.data.Records.
22628      */
22629     read : function(response){
22630         var json = response.responseText;
22631        
22632         var o = /* eval:var:o */ eval("("+json+")");
22633         if(!o) {
22634             throw {message: "JsonReader.read: Json object not found"};
22635         }
22636         
22637         if(o.metaData){
22638             
22639             delete this.ef;
22640             this.metaFromRemote = true;
22641             this.meta = o.metaData;
22642             this.recordType = Roo.data.Record.create(o.metaData.fields);
22643             this.onMetaChange(this.meta, this.recordType, o);
22644         }
22645         return this.readRecords(o);
22646     },
22647
22648     // private function a store will implement
22649     onMetaChange : function(meta, recordType, o){
22650
22651     },
22652
22653     /**
22654          * @ignore
22655          */
22656     simpleAccess: function(obj, subsc) {
22657         return obj[subsc];
22658     },
22659
22660         /**
22661          * @ignore
22662          */
22663     getJsonAccessor: function(){
22664         var re = /[\[\.]/;
22665         return function(expr) {
22666             try {
22667                 return(re.test(expr))
22668                     ? new Function("obj", "return obj." + expr)
22669                     : function(obj){
22670                         return obj[expr];
22671                     };
22672             } catch(e){}
22673             return Roo.emptyFn;
22674         };
22675     }(),
22676
22677     /**
22678      * Create a data block containing Roo.data.Records from an XML document.
22679      * @param {Object} o An object which contains an Array of row objects in the property specified
22680      * in the config as 'root, and optionally a property, specified in the config as 'totalProperty'
22681      * which contains the total size of the dataset.
22682      * @return {Object} data A data block which is used by an Roo.data.Store object as
22683      * a cache of Roo.data.Records.
22684      */
22685     readRecords : function(o){
22686         /**
22687          * After any data loads, the raw JSON data is available for further custom processing.
22688          * @type Object
22689          */
22690         this.o = o;
22691         var s = this.meta, Record = this.recordType,
22692             f = Record.prototype.fields, fi = f.items, fl = f.length;
22693
22694 //      Generate extraction functions for the totalProperty, the root, the id, and for each field
22695         if (!this.ef) {
22696             if(s.totalProperty) {
22697                     this.getTotal = this.getJsonAccessor(s.totalProperty);
22698                 }
22699                 if(s.successProperty) {
22700                     this.getSuccess = this.getJsonAccessor(s.successProperty);
22701                 }
22702                 this.getRoot = s.root ? this.getJsonAccessor(s.root) : function(p){return p;};
22703                 if (s.id) {
22704                         var g = this.getJsonAccessor(s.id);
22705                         this.getId = function(rec) {
22706                                 var r = g(rec);  
22707                                 return (r === undefined || r === "") ? null : r;
22708                         };
22709                 } else {
22710                         this.getId = function(){return null;};
22711                 }
22712             this.ef = [];
22713             for(var jj = 0; jj < fl; jj++){
22714                 f = fi[jj];
22715                 var map = (f.mapping !== undefined && f.mapping !== null) ? f.mapping : f.name;
22716                 this.ef[jj] = this.getJsonAccessor(map);
22717             }
22718         }
22719
22720         var root = this.getRoot(o), c = root.length, totalRecords = c, success = true;
22721         if(s.totalProperty){
22722             var vt = parseInt(this.getTotal(o), 10);
22723             if(!isNaN(vt)){
22724                 totalRecords = vt;
22725             }
22726         }
22727         if(s.successProperty){
22728             var vs = this.getSuccess(o);
22729             if(vs === false || vs === 'false'){
22730                 success = false;
22731             }
22732         }
22733         var records = [];
22734             for(var i = 0; i < c; i++){
22735                     var n = root[i];
22736                 var values = {};
22737                 var id = this.getId(n);
22738                 for(var j = 0; j < fl; j++){
22739                     f = fi[j];
22740                 var v = this.ef[j](n);
22741                 if (!f.convert) {
22742                     Roo.log('missing convert for ' + f.name);
22743                     Roo.log(f);
22744                     continue;
22745                 }
22746                 values[f.name] = f.convert((v !== undefined) ? v : f.defaultValue);
22747                 }
22748                 var record = new Record(values, id);
22749                 record.json = n;
22750                 records[i] = record;
22751             }
22752             return {
22753             raw : o,
22754                 success : success,
22755                 records : records,
22756                 totalRecords : totalRecords
22757             };
22758     }
22759 });/*
22760  * Based on:
22761  * Ext JS Library 1.1.1
22762  * Copyright(c) 2006-2007, Ext JS, LLC.
22763  *
22764  * Originally Released Under LGPL - original licence link has changed is not relivant.
22765  *
22766  * Fork - LGPL
22767  * <script type="text/javascript">
22768  */
22769
22770 /**
22771  * @class Roo.data.XmlReader
22772  * @extends Roo.data.DataReader
22773  * Data reader class to create an Array of {@link Roo.data.Record} objects from an XML document
22774  * based on mappings in a provided Roo.data.Record constructor.<br><br>
22775  * <p>
22776  * <em>Note that in order for the browser to parse a returned XML document, the Content-Type
22777  * header in the HTTP response must be set to "text/xml".</em>
22778  * <p>
22779  * Example code:
22780  * <pre><code>
22781 var RecordDef = Roo.data.Record.create([
22782    {name: 'name', mapping: 'name'},     // "mapping" property not needed if it's the same as "name"
22783    {name: 'occupation'}                 // This field will use "occupation" as the mapping.
22784 ]);
22785 var myReader = new Roo.data.XmlReader({
22786    totalRecords: "results", // The element which contains the total dataset size (optional)
22787    record: "row",           // The repeated element which contains row information
22788    id: "id"                 // The element within the row that provides an ID for the record (optional)
22789 }, RecordDef);
22790 </code></pre>
22791  * <p>
22792  * This would consume an XML file like this:
22793  * <pre><code>
22794 &lt;?xml?>
22795 &lt;dataset>
22796  &lt;results>2&lt;/results>
22797  &lt;row>
22798    &lt;id>1&lt;/id>
22799    &lt;name>Bill&lt;/name>
22800    &lt;occupation>Gardener&lt;/occupation>
22801  &lt;/row>
22802  &lt;row>
22803    &lt;id>2&lt;/id>
22804    &lt;name>Ben&lt;/name>
22805    &lt;occupation>Horticulturalist&lt;/occupation>
22806  &lt;/row>
22807 &lt;/dataset>
22808 </code></pre>
22809  * @cfg {String} totalRecords The DomQuery path from which to retrieve the total number of records
22810  * in the dataset. This is only needed if the whole dataset is not passed in one go, but is being
22811  * paged from the remote server.
22812  * @cfg {String} record The DomQuery path to the repeated element which contains record information.
22813  * @cfg {String} success The DomQuery path to the success attribute used by forms.
22814  * @cfg {String} id The DomQuery path relative from the record element to the element that contains
22815  * a record identifier value.
22816  * @constructor
22817  * Create a new XmlReader
22818  * @param {Object} meta Metadata configuration options
22819  * @param {Mixed} recordType The definition of the data record type to produce.  This can be either a valid
22820  * Record subclass created with {@link Roo.data.Record#create}, or an array of objects with which to call
22821  * Roo.data.Record.create.  See the {@link Roo.data.Record} class for more details.
22822  */
22823 Roo.data.XmlReader = function(meta, recordType){
22824     meta = meta || {};
22825     Roo.data.XmlReader.superclass.constructor.call(this, meta, recordType||meta.fields);
22826 };
22827 Roo.extend(Roo.data.XmlReader, Roo.data.DataReader, {
22828     /**
22829      * This method is only used by a DataProxy which has retrieved data from a remote server.
22830          * @param {Object} response The XHR object which contains the parsed XML document.  The response is expected
22831          * to contain a method called 'responseXML' that returns an XML document object.
22832      * @return {Object} records A data block which is used by an {@link Roo.data.Store} as
22833      * a cache of Roo.data.Records.
22834      */
22835     read : function(response){
22836         var doc = response.responseXML;
22837         if(!doc) {
22838             throw {message: "XmlReader.read: XML Document not available"};
22839         }
22840         return this.readRecords(doc);
22841     },
22842
22843     /**
22844      * Create a data block containing Roo.data.Records from an XML document.
22845          * @param {Object} doc A parsed XML document.
22846      * @return {Object} records A data block which is used by an {@link Roo.data.Store} as
22847      * a cache of Roo.data.Records.
22848      */
22849     readRecords : function(doc){
22850         /**
22851          * After any data loads/reads, the raw XML Document is available for further custom processing.
22852          * @type XMLDocument
22853          */
22854         this.xmlData = doc;
22855         var root = doc.documentElement || doc;
22856         var q = Roo.DomQuery;
22857         var recordType = this.recordType, fields = recordType.prototype.fields;
22858         var sid = this.meta.id;
22859         var totalRecords = 0, success = true;
22860         if(this.meta.totalRecords){
22861             totalRecords = q.selectNumber(this.meta.totalRecords, root, 0);
22862         }
22863         
22864         if(this.meta.success){
22865             var sv = q.selectValue(this.meta.success, root, true);
22866             success = sv !== false && sv !== 'false';
22867         }
22868         var records = [];
22869         var ns = q.select(this.meta.record, root);
22870         for(var i = 0, len = ns.length; i < len; i++) {
22871                 var n = ns[i];
22872                 var values = {};
22873                 var id = sid ? q.selectValue(sid, n) : undefined;
22874                 for(var j = 0, jlen = fields.length; j < jlen; j++){
22875                     var f = fields.items[j];
22876                 var v = q.selectValue(f.mapping || f.name, n, f.defaultValue);
22877                     v = f.convert(v);
22878                     values[f.name] = v;
22879                 }
22880                 var record = new recordType(values, id);
22881                 record.node = n;
22882                 records[records.length] = record;
22883             }
22884
22885             return {
22886                 success : success,
22887                 records : records,
22888                 totalRecords : totalRecords || records.length
22889             };
22890     }
22891 });/*
22892  * Based on:
22893  * Ext JS Library 1.1.1
22894  * Copyright(c) 2006-2007, Ext JS, LLC.
22895  *
22896  * Originally Released Under LGPL - original licence link has changed is not relivant.
22897  *
22898  * Fork - LGPL
22899  * <script type="text/javascript">
22900  */
22901
22902 /**
22903  * @class Roo.data.ArrayReader
22904  * @extends Roo.data.DataReader
22905  * Data reader class to create an Array of Roo.data.Record objects from an Array.
22906  * Each element of that Array represents a row of data fields. The
22907  * fields are pulled into a Record object using as a subscript, the <em>mapping</em> property
22908  * of the field definition if it exists, or the field's ordinal position in the definition.<br>
22909  * <p>
22910  * Example code:.
22911  * <pre><code>
22912 var RecordDef = Roo.data.Record.create([
22913     {name: 'name', mapping: 1},         // "mapping" only needed if an "id" field is present which
22914     {name: 'occupation', mapping: 2}    // precludes using the ordinal position as the index.
22915 ]);
22916 var myReader = new Roo.data.ArrayReader({
22917     id: 0                     // The subscript within row Array that provides an ID for the Record (optional)
22918 }, RecordDef);
22919 </code></pre>
22920  * <p>
22921  * This would consume an Array like this:
22922  * <pre><code>
22923 [ [1, 'Bill', 'Gardener'], [2, 'Ben', 'Horticulturalist'] ]
22924   </code></pre>
22925  * @cfg {String} id (optional) The subscript within row Array that provides an ID for the Record
22926  * @constructor
22927  * Create a new JsonReader
22928  * @param {Object} meta Metadata configuration options.
22929  * @param {Object} recordType Either an Array of field definition objects
22930  * as specified to {@link Roo.data.Record#create},
22931  * or an {@link Roo.data.Record} object
22932  * created using {@link Roo.data.Record#create}.
22933  */
22934 Roo.data.ArrayReader = function(meta, recordType){
22935     Roo.data.ArrayReader.superclass.constructor.call(this, meta, recordType);
22936 };
22937
22938 Roo.extend(Roo.data.ArrayReader, Roo.data.JsonReader, {
22939     /**
22940      * Create a data block containing Roo.data.Records from an XML document.
22941      * @param {Object} o An Array of row objects which represents the dataset.
22942      * @return {Object} data A data block which is used by an Roo.data.Store object as
22943      * a cache of Roo.data.Records.
22944      */
22945     readRecords : function(o){
22946         var sid = this.meta ? this.meta.id : null;
22947         var recordType = this.recordType, fields = recordType.prototype.fields;
22948         var records = [];
22949         var root = o;
22950             for(var i = 0; i < root.length; i++){
22951                     var n = root[i];
22952                 var values = {};
22953                 var id = ((sid || sid === 0) && n[sid] !== undefined && n[sid] !== "" ? n[sid] : null);
22954                 for(var j = 0, jlen = fields.length; j < jlen; j++){
22955                 var f = fields.items[j];
22956                 var k = f.mapping !== undefined && f.mapping !== null ? f.mapping : j;
22957                 var v = n[k] !== undefined ? n[k] : f.defaultValue;
22958                 v = f.convert(v);
22959                 values[f.name] = v;
22960             }
22961                 var record = new recordType(values, id);
22962                 record.json = n;
22963                 records[records.length] = record;
22964             }
22965             return {
22966                 records : records,
22967                 totalRecords : records.length
22968             };
22969     }
22970 });/*
22971  * Based on:
22972  * Ext JS Library 1.1.1
22973  * Copyright(c) 2006-2007, Ext JS, LLC.
22974  *
22975  * Originally Released Under LGPL - original licence link has changed is not relivant.
22976  *
22977  * Fork - LGPL
22978  * <script type="text/javascript">
22979  */
22980
22981
22982 /**
22983  * @class Roo.data.Tree
22984  * @extends Roo.util.Observable
22985  * Represents a tree data structure and bubbles all the events for its nodes. The nodes
22986  * in the tree have most standard DOM functionality.
22987  * @constructor
22988  * @param {Node} root (optional) The root node
22989  */
22990 Roo.data.Tree = function(root){
22991    this.nodeHash = {};
22992    /**
22993     * The root node for this tree
22994     * @type Node
22995     */
22996    this.root = null;
22997    if(root){
22998        this.setRootNode(root);
22999    }
23000    this.addEvents({
23001        /**
23002         * @event append
23003         * Fires when a new child node is appended to a node in this tree.
23004         * @param {Tree} tree The owner tree
23005         * @param {Node} parent The parent node
23006         * @param {Node} node The newly appended node
23007         * @param {Number} index The index of the newly appended node
23008         */
23009        "append" : true,
23010        /**
23011         * @event remove
23012         * Fires when a child node is removed from a node in this tree.
23013         * @param {Tree} tree The owner tree
23014         * @param {Node} parent The parent node
23015         * @param {Node} node The child node removed
23016         */
23017        "remove" : true,
23018        /**
23019         * @event move
23020         * Fires when a node is moved to a new location in the tree
23021         * @param {Tree} tree The owner tree
23022         * @param {Node} node The node moved
23023         * @param {Node} oldParent The old parent of this node
23024         * @param {Node} newParent The new parent of this node
23025         * @param {Number} index The index it was moved to
23026         */
23027        "move" : true,
23028        /**
23029         * @event insert
23030         * Fires when a new child node is inserted in a node in this tree.
23031         * @param {Tree} tree The owner tree
23032         * @param {Node} parent The parent node
23033         * @param {Node} node The child node inserted
23034         * @param {Node} refNode The child node the node was inserted before
23035         */
23036        "insert" : true,
23037        /**
23038         * @event beforeappend
23039         * Fires before a new child is appended to a node in this tree, return false to cancel the append.
23040         * @param {Tree} tree The owner tree
23041         * @param {Node} parent The parent node
23042         * @param {Node} node The child node to be appended
23043         */
23044        "beforeappend" : true,
23045        /**
23046         * @event beforeremove
23047         * Fires before a child is removed from a node in this tree, return false to cancel the remove.
23048         * @param {Tree} tree The owner tree
23049         * @param {Node} parent The parent node
23050         * @param {Node} node The child node to be removed
23051         */
23052        "beforeremove" : true,
23053        /**
23054         * @event beforemove
23055         * Fires before a node is moved to a new location in the tree. Return false to cancel the move.
23056         * @param {Tree} tree The owner tree
23057         * @param {Node} node The node being moved
23058         * @param {Node} oldParent The parent of the node
23059         * @param {Node} newParent The new parent the node is moving to
23060         * @param {Number} index The index it is being moved to
23061         */
23062        "beforemove" : true,
23063        /**
23064         * @event beforeinsert
23065         * Fires before a new child is inserted in a node in this tree, return false to cancel the insert.
23066         * @param {Tree} tree The owner tree
23067         * @param {Node} parent The parent node
23068         * @param {Node} node The child node to be inserted
23069         * @param {Node} refNode The child node the node is being inserted before
23070         */
23071        "beforeinsert" : true
23072    });
23073
23074     Roo.data.Tree.superclass.constructor.call(this);
23075 };
23076
23077 Roo.extend(Roo.data.Tree, Roo.util.Observable, {
23078     pathSeparator: "/",
23079
23080     proxyNodeEvent : function(){
23081         return this.fireEvent.apply(this, arguments);
23082     },
23083
23084     /**
23085      * Returns the root node for this tree.
23086      * @return {Node}
23087      */
23088     getRootNode : function(){
23089         return this.root;
23090     },
23091
23092     /**
23093      * Sets the root node for this tree.
23094      * @param {Node} node
23095      * @return {Node}
23096      */
23097     setRootNode : function(node){
23098         this.root = node;
23099         node.ownerTree = this;
23100         node.isRoot = true;
23101         this.registerNode(node);
23102         return node;
23103     },
23104
23105     /**
23106      * Gets a node in this tree by its id.
23107      * @param {String} id
23108      * @return {Node}
23109      */
23110     getNodeById : function(id){
23111         return this.nodeHash[id];
23112     },
23113
23114     registerNode : function(node){
23115         this.nodeHash[node.id] = node;
23116     },
23117
23118     unregisterNode : function(node){
23119         delete this.nodeHash[node.id];
23120     },
23121
23122     toString : function(){
23123         return "[Tree"+(this.id?" "+this.id:"")+"]";
23124     }
23125 });
23126
23127 /**
23128  * @class Roo.data.Node
23129  * @extends Roo.util.Observable
23130  * @cfg {Boolean} leaf true if this node is a leaf and does not have children
23131  * @cfg {String} id The id for this node. If one is not specified, one is generated.
23132  * @constructor
23133  * @param {Object} attributes The attributes/config for the node
23134  */
23135 Roo.data.Node = function(attributes){
23136     /**
23137      * The attributes supplied for the node. You can use this property to access any custom attributes you supplied.
23138      * @type {Object}
23139      */
23140     this.attributes = attributes || {};
23141     this.leaf = this.attributes.leaf;
23142     /**
23143      * The node id. @type String
23144      */
23145     this.id = this.attributes.id;
23146     if(!this.id){
23147         this.id = Roo.id(null, "ynode-");
23148         this.attributes.id = this.id;
23149     }
23150      
23151     
23152     /**
23153      * All child nodes of this node. @type Array
23154      */
23155     this.childNodes = [];
23156     if(!this.childNodes.indexOf){ // indexOf is a must
23157         this.childNodes.indexOf = function(o){
23158             for(var i = 0, len = this.length; i < len; i++){
23159                 if(this[i] == o) {
23160                     return i;
23161                 }
23162             }
23163             return -1;
23164         };
23165     }
23166     /**
23167      * The parent node for this node. @type Node
23168      */
23169     this.parentNode = null;
23170     /**
23171      * The first direct child node of this node, or null if this node has no child nodes. @type Node
23172      */
23173     this.firstChild = null;
23174     /**
23175      * The last direct child node of this node, or null if this node has no child nodes. @type Node
23176      */
23177     this.lastChild = null;
23178     /**
23179      * The node immediately preceding this node in the tree, or null if there is no sibling node. @type Node
23180      */
23181     this.previousSibling = null;
23182     /**
23183      * The node immediately following this node in the tree, or null if there is no sibling node. @type Node
23184      */
23185     this.nextSibling = null;
23186
23187     this.addEvents({
23188        /**
23189         * @event append
23190         * Fires when a new child node is appended
23191         * @param {Tree} tree The owner tree
23192         * @param {Node} this This node
23193         * @param {Node} node The newly appended node
23194         * @param {Number} index The index of the newly appended node
23195         */
23196        "append" : true,
23197        /**
23198         * @event remove
23199         * Fires when a child node is removed
23200         * @param {Tree} tree The owner tree
23201         * @param {Node} this This node
23202         * @param {Node} node The removed node
23203         */
23204        "remove" : true,
23205        /**
23206         * @event move
23207         * Fires when this node is moved to a new location in the tree
23208         * @param {Tree} tree The owner tree
23209         * @param {Node} this This node
23210         * @param {Node} oldParent The old parent of this node
23211         * @param {Node} newParent The new parent of this node
23212         * @param {Number} index The index it was moved to
23213         */
23214        "move" : true,
23215        /**
23216         * @event insert
23217         * Fires when a new child node is inserted.
23218         * @param {Tree} tree The owner tree
23219         * @param {Node} this This node
23220         * @param {Node} node The child node inserted
23221         * @param {Node} refNode The child node the node was inserted before
23222         */
23223        "insert" : true,
23224        /**
23225         * @event beforeappend
23226         * Fires before a new child is appended, return false to cancel the append.
23227         * @param {Tree} tree The owner tree
23228         * @param {Node} this This node
23229         * @param {Node} node The child node to be appended
23230         */
23231        "beforeappend" : true,
23232        /**
23233         * @event beforeremove
23234         * Fires before a child is removed, return false to cancel the remove.
23235         * @param {Tree} tree The owner tree
23236         * @param {Node} this This node
23237         * @param {Node} node The child node to be removed
23238         */
23239        "beforeremove" : true,
23240        /**
23241         * @event beforemove
23242         * Fires before this node is moved to a new location in the tree. Return false to cancel the move.
23243         * @param {Tree} tree The owner tree
23244         * @param {Node} this This node
23245         * @param {Node} oldParent The parent of this node
23246         * @param {Node} newParent The new parent this node is moving to
23247         * @param {Number} index The index it is being moved to
23248         */
23249        "beforemove" : true,
23250        /**
23251         * @event beforeinsert
23252         * Fires before a new child is inserted, return false to cancel the insert.
23253         * @param {Tree} tree The owner tree
23254         * @param {Node} this This node
23255         * @param {Node} node The child node to be inserted
23256         * @param {Node} refNode The child node the node is being inserted before
23257         */
23258        "beforeinsert" : true
23259    });
23260     this.listeners = this.attributes.listeners;
23261     Roo.data.Node.superclass.constructor.call(this);
23262 };
23263
23264 Roo.extend(Roo.data.Node, Roo.util.Observable, {
23265     fireEvent : function(evtName){
23266         // first do standard event for this node
23267         if(Roo.data.Node.superclass.fireEvent.apply(this, arguments) === false){
23268             return false;
23269         }
23270         // then bubble it up to the tree if the event wasn't cancelled
23271         var ot = this.getOwnerTree();
23272         if(ot){
23273             if(ot.proxyNodeEvent.apply(ot, arguments) === false){
23274                 return false;
23275             }
23276         }
23277         return true;
23278     },
23279
23280     /**
23281      * Returns true if this node is a leaf
23282      * @return {Boolean}
23283      */
23284     isLeaf : function(){
23285         return this.leaf === true;
23286     },
23287
23288     // private
23289     setFirstChild : function(node){
23290         this.firstChild = node;
23291     },
23292
23293     //private
23294     setLastChild : function(node){
23295         this.lastChild = node;
23296     },
23297
23298
23299     /**
23300      * Returns true if this node is the last child of its parent
23301      * @return {Boolean}
23302      */
23303     isLast : function(){
23304        return (!this.parentNode ? true : this.parentNode.lastChild == this);
23305     },
23306
23307     /**
23308      * Returns true if this node is the first child of its parent
23309      * @return {Boolean}
23310      */
23311     isFirst : function(){
23312        return (!this.parentNode ? true : this.parentNode.firstChild == this);
23313     },
23314
23315     hasChildNodes : function(){
23316         return !this.isLeaf() && this.childNodes.length > 0;
23317     },
23318
23319     /**
23320      * Insert node(s) as the last child node of this node.
23321      * @param {Node/Array} node The node or Array of nodes to append
23322      * @return {Node} The appended node if single append, or null if an array was passed
23323      */
23324     appendChild : function(node){
23325         var multi = false;
23326         if(node instanceof Array){
23327             multi = node;
23328         }else if(arguments.length > 1){
23329             multi = arguments;
23330         }
23331         // if passed an array or multiple args do them one by one
23332         if(multi){
23333             for(var i = 0, len = multi.length; i < len; i++) {
23334                 this.appendChild(multi[i]);
23335             }
23336         }else{
23337             if(this.fireEvent("beforeappend", this.ownerTree, this, node) === false){
23338                 return false;
23339             }
23340             var index = this.childNodes.length;
23341             var oldParent = node.parentNode;
23342             // it's a move, make sure we move it cleanly
23343             if(oldParent){
23344                 if(node.fireEvent("beforemove", node.getOwnerTree(), node, oldParent, this, index) === false){
23345                     return false;
23346                 }
23347                 oldParent.removeChild(node);
23348             }
23349             index = this.childNodes.length;
23350             if(index == 0){
23351                 this.setFirstChild(node);
23352             }
23353             this.childNodes.push(node);
23354             node.parentNode = this;
23355             var ps = this.childNodes[index-1];
23356             if(ps){
23357                 node.previousSibling = ps;
23358                 ps.nextSibling = node;
23359             }else{
23360                 node.previousSibling = null;
23361             }
23362             node.nextSibling = null;
23363             this.setLastChild(node);
23364             node.setOwnerTree(this.getOwnerTree());
23365             this.fireEvent("append", this.ownerTree, this, node, index);
23366             if(oldParent){
23367                 node.fireEvent("move", this.ownerTree, node, oldParent, this, index);
23368             }
23369             return node;
23370         }
23371     },
23372
23373     /**
23374      * Removes a child node from this node.
23375      * @param {Node} node The node to remove
23376      * @return {Node} The removed node
23377      */
23378     removeChild : function(node){
23379         var index = this.childNodes.indexOf(node);
23380         if(index == -1){
23381             return false;
23382         }
23383         if(this.fireEvent("beforeremove", this.ownerTree, this, node) === false){
23384             return false;
23385         }
23386
23387         // remove it from childNodes collection
23388         this.childNodes.splice(index, 1);
23389
23390         // update siblings
23391         if(node.previousSibling){
23392             node.previousSibling.nextSibling = node.nextSibling;
23393         }
23394         if(node.nextSibling){
23395             node.nextSibling.previousSibling = node.previousSibling;
23396         }
23397
23398         // update child refs
23399         if(this.firstChild == node){
23400             this.setFirstChild(node.nextSibling);
23401         }
23402         if(this.lastChild == node){
23403             this.setLastChild(node.previousSibling);
23404         }
23405
23406         node.setOwnerTree(null);
23407         // clear any references from the node
23408         node.parentNode = null;
23409         node.previousSibling = null;
23410         node.nextSibling = null;
23411         this.fireEvent("remove", this.ownerTree, this, node);
23412         return node;
23413     },
23414
23415     /**
23416      * Inserts the first node before the second node in this nodes childNodes collection.
23417      * @param {Node} node The node to insert
23418      * @param {Node} refNode The node to insert before (if null the node is appended)
23419      * @return {Node} The inserted node
23420      */
23421     insertBefore : function(node, refNode){
23422         if(!refNode){ // like standard Dom, refNode can be null for append
23423             return this.appendChild(node);
23424         }
23425         // nothing to do
23426         if(node == refNode){
23427             return false;
23428         }
23429
23430         if(this.fireEvent("beforeinsert", this.ownerTree, this, node, refNode) === false){
23431             return false;
23432         }
23433         var index = this.childNodes.indexOf(refNode);
23434         var oldParent = node.parentNode;
23435         var refIndex = index;
23436
23437         // when moving internally, indexes will change after remove
23438         if(oldParent == this && this.childNodes.indexOf(node) < index){
23439             refIndex--;
23440         }
23441
23442         // it's a move, make sure we move it cleanly
23443         if(oldParent){
23444             if(node.fireEvent("beforemove", node.getOwnerTree(), node, oldParent, this, index, refNode) === false){
23445                 return false;
23446             }
23447             oldParent.removeChild(node);
23448         }
23449         if(refIndex == 0){
23450             this.setFirstChild(node);
23451         }
23452         this.childNodes.splice(refIndex, 0, node);
23453         node.parentNode = this;
23454         var ps = this.childNodes[refIndex-1];
23455         if(ps){
23456             node.previousSibling = ps;
23457             ps.nextSibling = node;
23458         }else{
23459             node.previousSibling = null;
23460         }
23461         node.nextSibling = refNode;
23462         refNode.previousSibling = node;
23463         node.setOwnerTree(this.getOwnerTree());
23464         this.fireEvent("insert", this.ownerTree, this, node, refNode);
23465         if(oldParent){
23466             node.fireEvent("move", this.ownerTree, node, oldParent, this, refIndex, refNode);
23467         }
23468         return node;
23469     },
23470
23471     /**
23472      * Returns the child node at the specified index.
23473      * @param {Number} index
23474      * @return {Node}
23475      */
23476     item : function(index){
23477         return this.childNodes[index];
23478     },
23479
23480     /**
23481      * Replaces one child node in this node with another.
23482      * @param {Node} newChild The replacement node
23483      * @param {Node} oldChild The node to replace
23484      * @return {Node} The replaced node
23485      */
23486     replaceChild : function(newChild, oldChild){
23487         this.insertBefore(newChild, oldChild);
23488         this.removeChild(oldChild);
23489         return oldChild;
23490     },
23491
23492     /**
23493      * Returns the index of a child node
23494      * @param {Node} node
23495      * @return {Number} The index of the node or -1 if it was not found
23496      */
23497     indexOf : function(child){
23498         return this.childNodes.indexOf(child);
23499     },
23500
23501     /**
23502      * Returns the tree this node is in.
23503      * @return {Tree}
23504      */
23505     getOwnerTree : function(){
23506         // if it doesn't have one, look for one
23507         if(!this.ownerTree){
23508             var p = this;
23509             while(p){
23510                 if(p.ownerTree){
23511                     this.ownerTree = p.ownerTree;
23512                     break;
23513                 }
23514                 p = p.parentNode;
23515             }
23516         }
23517         return this.ownerTree;
23518     },
23519
23520     /**
23521      * Returns depth of this node (the root node has a depth of 0)
23522      * @return {Number}
23523      */
23524     getDepth : function(){
23525         var depth = 0;
23526         var p = this;
23527         while(p.parentNode){
23528             ++depth;
23529             p = p.parentNode;
23530         }
23531         return depth;
23532     },
23533
23534     // private
23535     setOwnerTree : function(tree){
23536         // if it's move, we need to update everyone
23537         if(tree != this.ownerTree){
23538             if(this.ownerTree){
23539                 this.ownerTree.unregisterNode(this);
23540             }
23541             this.ownerTree = tree;
23542             var cs = this.childNodes;
23543             for(var i = 0, len = cs.length; i < len; i++) {
23544                 cs[i].setOwnerTree(tree);
23545             }
23546             if(tree){
23547                 tree.registerNode(this);
23548             }
23549         }
23550     },
23551
23552     /**
23553      * Returns the path for this node. The path can be used to expand or select this node programmatically.
23554      * @param {String} attr (optional) The attr to use for the path (defaults to the node's id)
23555      * @return {String} The path
23556      */
23557     getPath : function(attr){
23558         attr = attr || "id";
23559         var p = this.parentNode;
23560         var b = [this.attributes[attr]];
23561         while(p){
23562             b.unshift(p.attributes[attr]);
23563             p = p.parentNode;
23564         }
23565         var sep = this.getOwnerTree().pathSeparator;
23566         return sep + b.join(sep);
23567     },
23568
23569     /**
23570      * Bubbles up the tree from this node, calling the specified function with each node. The scope (<i>this</i>) of
23571      * function call will be the scope provided or the current node. The arguments to the function
23572      * will be the args provided or the current node. If the function returns false at any point,
23573      * the bubble is stopped.
23574      * @param {Function} fn The function to call
23575      * @param {Object} scope (optional) The scope of the function (defaults to current node)
23576      * @param {Array} args (optional) The args to call the function with (default to passing the current node)
23577      */
23578     bubble : function(fn, scope, args){
23579         var p = this;
23580         while(p){
23581             if(fn.call(scope || p, args || p) === false){
23582                 break;
23583             }
23584             p = p.parentNode;
23585         }
23586     },
23587
23588     /**
23589      * Cascades down the tree from this node, calling the specified function with each node. The scope (<i>this</i>) of
23590      * function call will be the scope provided or the current node. The arguments to the function
23591      * will be the args provided or the current node. If the function returns false at any point,
23592      * the cascade is stopped on that branch.
23593      * @param {Function} fn The function to call
23594      * @param {Object} scope (optional) The scope of the function (defaults to current node)
23595      * @param {Array} args (optional) The args to call the function with (default to passing the current node)
23596      */
23597     cascade : function(fn, scope, args){
23598         if(fn.call(scope || this, args || this) !== false){
23599             var cs = this.childNodes;
23600             for(var i = 0, len = cs.length; i < len; i++) {
23601                 cs[i].cascade(fn, scope, args);
23602             }
23603         }
23604     },
23605
23606     /**
23607      * Interates the child nodes of this node, calling the specified function with each node. The scope (<i>this</i>) of
23608      * function call will be the scope provided or the current node. The arguments to the function
23609      * will be the args provided or the current node. If the function returns false at any point,
23610      * the iteration stops.
23611      * @param {Function} fn The function to call
23612      * @param {Object} scope (optional) The scope of the function (defaults to current node)
23613      * @param {Array} args (optional) The args to call the function with (default to passing the current node)
23614      */
23615     eachChild : function(fn, scope, args){
23616         var cs = this.childNodes;
23617         for(var i = 0, len = cs.length; i < len; i++) {
23618                 if(fn.call(scope || this, args || cs[i]) === false){
23619                     break;
23620                 }
23621         }
23622     },
23623
23624     /**
23625      * Finds the first child that has the attribute with the specified value.
23626      * @param {String} attribute The attribute name
23627      * @param {Mixed} value The value to search for
23628      * @return {Node} The found child or null if none was found
23629      */
23630     findChild : function(attribute, value){
23631         var cs = this.childNodes;
23632         for(var i = 0, len = cs.length; i < len; i++) {
23633                 if(cs[i].attributes[attribute] == value){
23634                     return cs[i];
23635                 }
23636         }
23637         return null;
23638     },
23639
23640     /**
23641      * Finds the first child by a custom function. The child matches if the function passed
23642      * returns true.
23643      * @param {Function} fn
23644      * @param {Object} scope (optional)
23645      * @return {Node} The found child or null if none was found
23646      */
23647     findChildBy : function(fn, scope){
23648         var cs = this.childNodes;
23649         for(var i = 0, len = cs.length; i < len; i++) {
23650                 if(fn.call(scope||cs[i], cs[i]) === true){
23651                     return cs[i];
23652                 }
23653         }
23654         return null;
23655     },
23656
23657     /**
23658      * Sorts this nodes children using the supplied sort function
23659      * @param {Function} fn
23660      * @param {Object} scope (optional)
23661      */
23662     sort : function(fn, scope){
23663         var cs = this.childNodes;
23664         var len = cs.length;
23665         if(len > 0){
23666             var sortFn = scope ? function(){fn.apply(scope, arguments);} : fn;
23667             cs.sort(sortFn);
23668             for(var i = 0; i < len; i++){
23669                 var n = cs[i];
23670                 n.previousSibling = cs[i-1];
23671                 n.nextSibling = cs[i+1];
23672                 if(i == 0){
23673                     this.setFirstChild(n);
23674                 }
23675                 if(i == len-1){
23676                     this.setLastChild(n);
23677                 }
23678             }
23679         }
23680     },
23681
23682     /**
23683      * Returns true if this node is an ancestor (at any point) of the passed node.
23684      * @param {Node} node
23685      * @return {Boolean}
23686      */
23687     contains : function(node){
23688         return node.isAncestor(this);
23689     },
23690
23691     /**
23692      * Returns true if the passed node is an ancestor (at any point) of this node.
23693      * @param {Node} node
23694      * @return {Boolean}
23695      */
23696     isAncestor : function(node){
23697         var p = this.parentNode;
23698         while(p){
23699             if(p == node){
23700                 return true;
23701             }
23702             p = p.parentNode;
23703         }
23704         return false;
23705     },
23706
23707     toString : function(){
23708         return "[Node"+(this.id?" "+this.id:"")+"]";
23709     }
23710 });/*
23711  * Based on:
23712  * Ext JS Library 1.1.1
23713  * Copyright(c) 2006-2007, Ext JS, LLC.
23714  *
23715  * Originally Released Under LGPL - original licence link has changed is not relivant.
23716  *
23717  * Fork - LGPL
23718  * <script type="text/javascript">
23719  */
23720  (function(){ 
23721 /**
23722  * @class Roo.Layer
23723  * @extends Roo.Element
23724  * An extended {@link Roo.Element} object that supports a shadow and shim, constrain to viewport and
23725  * automatic maintaining of shadow/shim positions.
23726  * @cfg {Boolean} shim False to disable the iframe shim in browsers which need one (defaults to true)
23727  * @cfg {String/Boolean} shadow True to create a shadow element with default class "x-layer-shadow", or
23728  * you can pass a string with a CSS class name. False turns off the shadow.
23729  * @cfg {Object} dh DomHelper object config to create element with (defaults to {tag: "div", cls: "x-layer"}).
23730  * @cfg {Boolean} constrain False to disable constrain to viewport (defaults to true)
23731  * @cfg {String} cls CSS class to add to the element
23732  * @cfg {Number} zindex Starting z-index (defaults to 11000)
23733  * @cfg {Number} shadowOffset Number of pixels to offset the shadow (defaults to 3)
23734  * @constructor
23735  * @param {Object} config An object with config options.
23736  * @param {String/HTMLElement} existingEl (optional) Uses an existing DOM element. If the element is not found it creates it.
23737  */
23738
23739 Roo.Layer = function(config, existingEl){
23740     config = config || {};
23741     var dh = Roo.DomHelper;
23742     var cp = config.parentEl, pel = cp ? Roo.getDom(cp) : document.body;
23743     if(existingEl){
23744         this.dom = Roo.getDom(existingEl);
23745     }
23746     if(!this.dom){
23747         var o = config.dh || {tag: "div", cls: "x-layer"};
23748         this.dom = dh.append(pel, o);
23749     }
23750     if(config.cls){
23751         this.addClass(config.cls);
23752     }
23753     this.constrain = config.constrain !== false;
23754     this.visibilityMode = Roo.Element.VISIBILITY;
23755     if(config.id){
23756         this.id = this.dom.id = config.id;
23757     }else{
23758         this.id = Roo.id(this.dom);
23759     }
23760     this.zindex = config.zindex || this.getZIndex();
23761     this.position("absolute", this.zindex);
23762     if(config.shadow){
23763         this.shadowOffset = config.shadowOffset || 4;
23764         this.shadow = new Roo.Shadow({
23765             offset : this.shadowOffset,
23766             mode : config.shadow
23767         });
23768     }else{
23769         this.shadowOffset = 0;
23770     }
23771     this.useShim = config.shim !== false && Roo.useShims;
23772     this.useDisplay = config.useDisplay;
23773     this.hide();
23774 };
23775
23776 var supr = Roo.Element.prototype;
23777
23778 // shims are shared among layer to keep from having 100 iframes
23779 var shims = [];
23780
23781 Roo.extend(Roo.Layer, Roo.Element, {
23782
23783     getZIndex : function(){
23784         return this.zindex || parseInt(this.getStyle("z-index"), 10) || 11000;
23785     },
23786
23787     getShim : function(){
23788         if(!this.useShim){
23789             return null;
23790         }
23791         if(this.shim){
23792             return this.shim;
23793         }
23794         var shim = shims.shift();
23795         if(!shim){
23796             shim = this.createShim();
23797             shim.enableDisplayMode('block');
23798             shim.dom.style.display = 'none';
23799             shim.dom.style.visibility = 'visible';
23800         }
23801         var pn = this.dom.parentNode;
23802         if(shim.dom.parentNode != pn){
23803             pn.insertBefore(shim.dom, this.dom);
23804         }
23805         shim.setStyle('z-index', this.getZIndex()-2);
23806         this.shim = shim;
23807         return shim;
23808     },
23809
23810     hideShim : function(){
23811         if(this.shim){
23812             this.shim.setDisplayed(false);
23813             shims.push(this.shim);
23814             delete this.shim;
23815         }
23816     },
23817
23818     disableShadow : function(){
23819         if(this.shadow){
23820             this.shadowDisabled = true;
23821             this.shadow.hide();
23822             this.lastShadowOffset = this.shadowOffset;
23823             this.shadowOffset = 0;
23824         }
23825     },
23826
23827     enableShadow : function(show){
23828         if(this.shadow){
23829             this.shadowDisabled = false;
23830             this.shadowOffset = this.lastShadowOffset;
23831             delete this.lastShadowOffset;
23832             if(show){
23833                 this.sync(true);
23834             }
23835         }
23836     },
23837
23838     // private
23839     // this code can execute repeatedly in milliseconds (i.e. during a drag) so
23840     // code size was sacrificed for effeciency (e.g. no getBox/setBox, no XY calls)
23841     sync : function(doShow){
23842         var sw = this.shadow;
23843         if(!this.updating && this.isVisible() && (sw || this.useShim)){
23844             var sh = this.getShim();
23845
23846             var w = this.getWidth(),
23847                 h = this.getHeight();
23848
23849             var l = this.getLeft(true),
23850                 t = this.getTop(true);
23851
23852             if(sw && !this.shadowDisabled){
23853                 if(doShow && !sw.isVisible()){
23854                     sw.show(this);
23855                 }else{
23856                     sw.realign(l, t, w, h);
23857                 }
23858                 if(sh){
23859                     if(doShow){
23860                        sh.show();
23861                     }
23862                     // fit the shim behind the shadow, so it is shimmed too
23863                     var a = sw.adjusts, s = sh.dom.style;
23864                     s.left = (Math.min(l, l+a.l))+"px";
23865                     s.top = (Math.min(t, t+a.t))+"px";
23866                     s.width = (w+a.w)+"px";
23867                     s.height = (h+a.h)+"px";
23868                 }
23869             }else if(sh){
23870                 if(doShow){
23871                    sh.show();
23872                 }
23873                 sh.setSize(w, h);
23874                 sh.setLeftTop(l, t);
23875             }
23876             
23877         }
23878     },
23879
23880     // private
23881     destroy : function(){
23882         this.hideShim();
23883         if(this.shadow){
23884             this.shadow.hide();
23885         }
23886         this.removeAllListeners();
23887         var pn = this.dom.parentNode;
23888         if(pn){
23889             pn.removeChild(this.dom);
23890         }
23891         Roo.Element.uncache(this.id);
23892     },
23893
23894     remove : function(){
23895         this.destroy();
23896     },
23897
23898     // private
23899     beginUpdate : function(){
23900         this.updating = true;
23901     },
23902
23903     // private
23904     endUpdate : function(){
23905         this.updating = false;
23906         this.sync(true);
23907     },
23908
23909     // private
23910     hideUnders : function(negOffset){
23911         if(this.shadow){
23912             this.shadow.hide();
23913         }
23914         this.hideShim();
23915     },
23916
23917     // private
23918     constrainXY : function(){
23919         if(this.constrain){
23920             var vw = Roo.lib.Dom.getViewWidth(),
23921                 vh = Roo.lib.Dom.getViewHeight();
23922             var s = Roo.get(document).getScroll();
23923
23924             var xy = this.getXY();
23925             var x = xy[0], y = xy[1];   
23926             var w = this.dom.offsetWidth+this.shadowOffset, h = this.dom.offsetHeight+this.shadowOffset;
23927             // only move it if it needs it
23928             var moved = false;
23929             // first validate right/bottom
23930             if((x + w) > vw+s.left){
23931                 x = vw - w - this.shadowOffset;
23932                 moved = true;
23933             }
23934             if((y + h) > vh+s.top){
23935                 y = vh - h - this.shadowOffset;
23936                 moved = true;
23937             }
23938             // then make sure top/left isn't negative
23939             if(x < s.left){
23940                 x = s.left;
23941                 moved = true;
23942             }
23943             if(y < s.top){
23944                 y = s.top;
23945                 moved = true;
23946             }
23947             if(moved){
23948                 if(this.avoidY){
23949                     var ay = this.avoidY;
23950                     if(y <= ay && (y+h) >= ay){
23951                         y = ay-h-5;   
23952                     }
23953                 }
23954                 xy = [x, y];
23955                 this.storeXY(xy);
23956                 supr.setXY.call(this, xy);
23957                 this.sync();
23958             }
23959         }
23960     },
23961
23962     isVisible : function(){
23963         return this.visible;    
23964     },
23965
23966     // private
23967     showAction : function(){
23968         this.visible = true; // track visibility to prevent getStyle calls
23969         if(this.useDisplay === true){
23970             this.setDisplayed("");
23971         }else if(this.lastXY){
23972             supr.setXY.call(this, this.lastXY);
23973         }else if(this.lastLT){
23974             supr.setLeftTop.call(this, this.lastLT[0], this.lastLT[1]);
23975         }
23976     },
23977
23978     // private
23979     hideAction : function(){
23980         this.visible = false;
23981         if(this.useDisplay === true){
23982             this.setDisplayed(false);
23983         }else{
23984             this.setLeftTop(-10000,-10000);
23985         }
23986     },
23987
23988     // overridden Element method
23989     setVisible : function(v, a, d, c, e){
23990         if(v){
23991             this.showAction();
23992         }
23993         if(a && v){
23994             var cb = function(){
23995                 this.sync(true);
23996                 if(c){
23997                     c();
23998                 }
23999             }.createDelegate(this);
24000             supr.setVisible.call(this, true, true, d, cb, e);
24001         }else{
24002             if(!v){
24003                 this.hideUnders(true);
24004             }
24005             var cb = c;
24006             if(a){
24007                 cb = function(){
24008                     this.hideAction();
24009                     if(c){
24010                         c();
24011                     }
24012                 }.createDelegate(this);
24013             }
24014             supr.setVisible.call(this, v, a, d, cb, e);
24015             if(v){
24016                 this.sync(true);
24017             }else if(!a){
24018                 this.hideAction();
24019             }
24020         }
24021     },
24022
24023     storeXY : function(xy){
24024         delete this.lastLT;
24025         this.lastXY = xy;
24026     },
24027
24028     storeLeftTop : function(left, top){
24029         delete this.lastXY;
24030         this.lastLT = [left, top];
24031     },
24032
24033     // private
24034     beforeFx : function(){
24035         this.beforeAction();
24036         return Roo.Layer.superclass.beforeFx.apply(this, arguments);
24037     },
24038
24039     // private
24040     afterFx : function(){
24041         Roo.Layer.superclass.afterFx.apply(this, arguments);
24042         this.sync(this.isVisible());
24043     },
24044
24045     // private
24046     beforeAction : function(){
24047         if(!this.updating && this.shadow){
24048             this.shadow.hide();
24049         }
24050     },
24051
24052     // overridden Element method
24053     setLeft : function(left){
24054         this.storeLeftTop(left, this.getTop(true));
24055         supr.setLeft.apply(this, arguments);
24056         this.sync();
24057     },
24058
24059     setTop : function(top){
24060         this.storeLeftTop(this.getLeft(true), top);
24061         supr.setTop.apply(this, arguments);
24062         this.sync();
24063     },
24064
24065     setLeftTop : function(left, top){
24066         this.storeLeftTop(left, top);
24067         supr.setLeftTop.apply(this, arguments);
24068         this.sync();
24069     },
24070
24071     setXY : function(xy, a, d, c, e){
24072         this.fixDisplay();
24073         this.beforeAction();
24074         this.storeXY(xy);
24075         var cb = this.createCB(c);
24076         supr.setXY.call(this, xy, a, d, cb, e);
24077         if(!a){
24078             cb();
24079         }
24080     },
24081
24082     // private
24083     createCB : function(c){
24084         var el = this;
24085         return function(){
24086             el.constrainXY();
24087             el.sync(true);
24088             if(c){
24089                 c();
24090             }
24091         };
24092     },
24093
24094     // overridden Element method
24095     setX : function(x, a, d, c, e){
24096         this.setXY([x, this.getY()], a, d, c, e);
24097     },
24098
24099     // overridden Element method
24100     setY : function(y, a, d, c, e){
24101         this.setXY([this.getX(), y], a, d, c, e);
24102     },
24103
24104     // overridden Element method
24105     setSize : function(w, h, a, d, c, e){
24106         this.beforeAction();
24107         var cb = this.createCB(c);
24108         supr.setSize.call(this, w, h, a, d, cb, e);
24109         if(!a){
24110             cb();
24111         }
24112     },
24113
24114     // overridden Element method
24115     setWidth : function(w, a, d, c, e){
24116         this.beforeAction();
24117         var cb = this.createCB(c);
24118         supr.setWidth.call(this, w, a, d, cb, e);
24119         if(!a){
24120             cb();
24121         }
24122     },
24123
24124     // overridden Element method
24125     setHeight : function(h, a, d, c, e){
24126         this.beforeAction();
24127         var cb = this.createCB(c);
24128         supr.setHeight.call(this, h, a, d, cb, e);
24129         if(!a){
24130             cb();
24131         }
24132     },
24133
24134     // overridden Element method
24135     setBounds : function(x, y, w, h, a, d, c, e){
24136         this.beforeAction();
24137         var cb = this.createCB(c);
24138         if(!a){
24139             this.storeXY([x, y]);
24140             supr.setXY.call(this, [x, y]);
24141             supr.setSize.call(this, w, h, a, d, cb, e);
24142             cb();
24143         }else{
24144             supr.setBounds.call(this, x, y, w, h, a, d, cb, e);
24145         }
24146         return this;
24147     },
24148     
24149     /**
24150      * Sets the z-index of this layer and adjusts any shadow and shim z-indexes. The layer z-index is automatically
24151      * incremented by two more than the value passed in so that it always shows above any shadow or shim (the shadow
24152      * element, if any, will be assigned z-index + 1, and the shim element, if any, will be assigned the unmodified z-index).
24153      * @param {Number} zindex The new z-index to set
24154      * @return {this} The Layer
24155      */
24156     setZIndex : function(zindex){
24157         this.zindex = zindex;
24158         this.setStyle("z-index", zindex + 2);
24159         if(this.shadow){
24160             this.shadow.setZIndex(zindex + 1);
24161         }
24162         if(this.shim){
24163             this.shim.setStyle("z-index", zindex);
24164         }
24165     }
24166 });
24167 })();/*
24168  * Based on:
24169  * Ext JS Library 1.1.1
24170  * Copyright(c) 2006-2007, Ext JS, LLC.
24171  *
24172  * Originally Released Under LGPL - original licence link has changed is not relivant.
24173  *
24174  * Fork - LGPL
24175  * <script type="text/javascript">
24176  */
24177
24178
24179 /**
24180  * @class Roo.Shadow
24181  * Simple class that can provide a shadow effect for any element.  Note that the element MUST be absolutely positioned,
24182  * and the shadow does not provide any shimming.  This should be used only in simple cases -- for more advanced
24183  * functionality that can also provide the same shadow effect, see the {@link Roo.Layer} class.
24184  * @constructor
24185  * Create a new Shadow
24186  * @param {Object} config The config object
24187  */
24188 Roo.Shadow = function(config){
24189     Roo.apply(this, config);
24190     if(typeof this.mode != "string"){
24191         this.mode = this.defaultMode;
24192     }
24193     var o = this.offset, a = {h: 0};
24194     var rad = Math.floor(this.offset/2);
24195     switch(this.mode.toLowerCase()){ // all this hideous nonsense calculates the various offsets for shadows
24196         case "drop":
24197             a.w = 0;
24198             a.l = a.t = o;
24199             a.t -= 1;
24200             if(Roo.isIE){
24201                 a.l -= this.offset + rad;
24202                 a.t -= this.offset + rad;
24203                 a.w -= rad;
24204                 a.h -= rad;
24205                 a.t += 1;
24206             }
24207         break;
24208         case "sides":
24209             a.w = (o*2);
24210             a.l = -o;
24211             a.t = o-1;
24212             if(Roo.isIE){
24213                 a.l -= (this.offset - rad);
24214                 a.t -= this.offset + rad;
24215                 a.l += 1;
24216                 a.w -= (this.offset - rad)*2;
24217                 a.w -= rad + 1;
24218                 a.h -= 1;
24219             }
24220         break;
24221         case "frame":
24222             a.w = a.h = (o*2);
24223             a.l = a.t = -o;
24224             a.t += 1;
24225             a.h -= 2;
24226             if(Roo.isIE){
24227                 a.l -= (this.offset - rad);
24228                 a.t -= (this.offset - rad);
24229                 a.l += 1;
24230                 a.w -= (this.offset + rad + 1);
24231                 a.h -= (this.offset + rad);
24232                 a.h += 1;
24233             }
24234         break;
24235     };
24236
24237     this.adjusts = a;
24238 };
24239
24240 Roo.Shadow.prototype = {
24241     /**
24242      * @cfg {String} mode
24243      * The shadow display mode.  Supports the following options:<br />
24244      * sides: Shadow displays on both sides and bottom only<br />
24245      * frame: Shadow displays equally on all four sides<br />
24246      * drop: Traditional bottom-right drop shadow (default)
24247      */
24248     /**
24249      * @cfg {String} offset
24250      * The number of pixels to offset the shadow from the element (defaults to 4)
24251      */
24252     offset: 4,
24253
24254     // private
24255     defaultMode: "drop",
24256
24257     /**
24258      * Displays the shadow under the target element
24259      * @param {String/HTMLElement/Element} targetEl The id or element under which the shadow should display
24260      */
24261     show : function(target){
24262         target = Roo.get(target);
24263         if(!this.el){
24264             this.el = Roo.Shadow.Pool.pull();
24265             if(this.el.dom.nextSibling != target.dom){
24266                 this.el.insertBefore(target);
24267             }
24268         }
24269         this.el.setStyle("z-index", this.zIndex || parseInt(target.getStyle("z-index"), 10)-1);
24270         if(Roo.isIE){
24271             this.el.dom.style.filter="progid:DXImageTransform.Microsoft.alpha(opacity=50) progid:DXImageTransform.Microsoft.Blur(pixelradius="+(this.offset)+")";
24272         }
24273         this.realign(
24274             target.getLeft(true),
24275             target.getTop(true),
24276             target.getWidth(),
24277             target.getHeight()
24278         );
24279         this.el.dom.style.display = "block";
24280     },
24281
24282     /**
24283      * Returns true if the shadow is visible, else false
24284      */
24285     isVisible : function(){
24286         return this.el ? true : false;  
24287     },
24288
24289     /**
24290      * Direct alignment when values are already available. Show must be called at least once before
24291      * calling this method to ensure it is initialized.
24292      * @param {Number} left The target element left position
24293      * @param {Number} top The target element top position
24294      * @param {Number} width The target element width
24295      * @param {Number} height The target element height
24296      */
24297     realign : function(l, t, w, h){
24298         if(!this.el){
24299             return;
24300         }
24301         var a = this.adjusts, d = this.el.dom, s = d.style;
24302         var iea = 0;
24303         s.left = (l+a.l)+"px";
24304         s.top = (t+a.t)+"px";
24305         var sw = (w+a.w), sh = (h+a.h), sws = sw +"px", shs = sh + "px";
24306  
24307         if(s.width != sws || s.height != shs){
24308             s.width = sws;
24309             s.height = shs;
24310             if(!Roo.isIE){
24311                 var cn = d.childNodes;
24312                 var sww = Math.max(0, (sw-12))+"px";
24313                 cn[0].childNodes[1].style.width = sww;
24314                 cn[1].childNodes[1].style.width = sww;
24315                 cn[2].childNodes[1].style.width = sww;
24316                 cn[1].style.height = Math.max(0, (sh-12))+"px";
24317             }
24318         }
24319     },
24320
24321     /**
24322      * Hides this shadow
24323      */
24324     hide : function(){
24325         if(this.el){
24326             this.el.dom.style.display = "none";
24327             Roo.Shadow.Pool.push(this.el);
24328             delete this.el;
24329         }
24330     },
24331
24332     /**
24333      * Adjust the z-index of this shadow
24334      * @param {Number} zindex The new z-index
24335      */
24336     setZIndex : function(z){
24337         this.zIndex = z;
24338         if(this.el){
24339             this.el.setStyle("z-index", z);
24340         }
24341     }
24342 };
24343
24344 // Private utility class that manages the internal Shadow cache
24345 Roo.Shadow.Pool = function(){
24346     var p = [];
24347     var markup = Roo.isIE ?
24348                  '<div class="x-ie-shadow"></div>' :
24349                  '<div class="x-shadow"><div class="xst"><div class="xstl"></div><div class="xstc"></div><div class="xstr"></div></div><div class="xsc"><div class="xsml"></div><div class="xsmc"></div><div class="xsmr"></div></div><div class="xsb"><div class="xsbl"></div><div class="xsbc"></div><div class="xsbr"></div></div></div>';
24350     return {
24351         pull : function(){
24352             var sh = p.shift();
24353             if(!sh){
24354                 sh = Roo.get(Roo.DomHelper.insertHtml("beforeBegin", document.body.firstChild, markup));
24355                 sh.autoBoxAdjust = false;
24356             }
24357             return sh;
24358         },
24359
24360         push : function(sh){
24361             p.push(sh);
24362         }
24363     };
24364 }();/*
24365  * Based on:
24366  * Ext JS Library 1.1.1
24367  * Copyright(c) 2006-2007, Ext JS, LLC.
24368  *
24369  * Originally Released Under LGPL - original licence link has changed is not relivant.
24370  *
24371  * Fork - LGPL
24372  * <script type="text/javascript">
24373  */
24374
24375
24376 /**
24377  * @class Roo.SplitBar
24378  * @extends Roo.util.Observable
24379  * Creates draggable splitter bar functionality from two elements (element to be dragged and element to be resized).
24380  * <br><br>
24381  * Usage:
24382  * <pre><code>
24383 var split = new Roo.SplitBar("elementToDrag", "elementToSize",
24384                    Roo.SplitBar.HORIZONTAL, Roo.SplitBar.LEFT);
24385 split.setAdapter(new Roo.SplitBar.AbsoluteLayoutAdapter("container"));
24386 split.minSize = 100;
24387 split.maxSize = 600;
24388 split.animate = true;
24389 split.on('moved', splitterMoved);
24390 </code></pre>
24391  * @constructor
24392  * Create a new SplitBar
24393  * @param {String/HTMLElement/Roo.Element} dragElement The element to be dragged and act as the SplitBar. 
24394  * @param {String/HTMLElement/Roo.Element} resizingElement The element to be resized based on where the SplitBar element is dragged 
24395  * @param {Number} orientation (optional) Either Roo.SplitBar.HORIZONTAL or Roo.SplitBar.VERTICAL. (Defaults to HORIZONTAL)
24396  * @param {Number} placement (optional) Either Roo.SplitBar.LEFT or Roo.SplitBar.RIGHT for horizontal or  
24397                         Roo.SplitBar.TOP or Roo.SplitBar.BOTTOM for vertical. (By default, this is determined automatically by the initial
24398                         position of the SplitBar).
24399  */
24400 Roo.SplitBar = function(dragElement, resizingElement, orientation, placement, existingProxy){
24401     
24402     /** @private */
24403     this.el = Roo.get(dragElement, true);
24404     this.el.dom.unselectable = "on";
24405     /** @private */
24406     this.resizingEl = Roo.get(resizingElement, true);
24407
24408     /**
24409      * @private
24410      * The orientation of the split. Either Roo.SplitBar.HORIZONTAL or Roo.SplitBar.VERTICAL. (Defaults to HORIZONTAL)
24411      * Note: If this is changed after creating the SplitBar, the placement property must be manually updated
24412      * @type Number
24413      */
24414     this.orientation = orientation || Roo.SplitBar.HORIZONTAL;
24415     
24416     /**
24417      * The minimum size of the resizing element. (Defaults to 0)
24418      * @type Number
24419      */
24420     this.minSize = 0;
24421     
24422     /**
24423      * The maximum size of the resizing element. (Defaults to 2000)
24424      * @type Number
24425      */
24426     this.maxSize = 2000;
24427     
24428     /**
24429      * Whether to animate the transition to the new size
24430      * @type Boolean
24431      */
24432     this.animate = false;
24433     
24434     /**
24435      * Whether to create a transparent shim that overlays the page when dragging, enables dragging across iframes.
24436      * @type Boolean
24437      */
24438     this.useShim = false;
24439     
24440     /** @private */
24441     this.shim = null;
24442     
24443     if(!existingProxy){
24444         /** @private */
24445         this.proxy = Roo.SplitBar.createProxy(this.orientation);
24446     }else{
24447         this.proxy = Roo.get(existingProxy).dom;
24448     }
24449     /** @private */
24450     this.dd = new Roo.dd.DDProxy(this.el.dom.id, "XSplitBars", {dragElId : this.proxy.id});
24451     
24452     /** @private */
24453     this.dd.b4StartDrag = this.onStartProxyDrag.createDelegate(this);
24454     
24455     /** @private */
24456     this.dd.endDrag = this.onEndProxyDrag.createDelegate(this);
24457     
24458     /** @private */
24459     this.dragSpecs = {};
24460     
24461     /**
24462      * @private The adapter to use to positon and resize elements
24463      */
24464     this.adapter = new Roo.SplitBar.BasicLayoutAdapter();
24465     this.adapter.init(this);
24466     
24467     if(this.orientation == Roo.SplitBar.HORIZONTAL){
24468         /** @private */
24469         this.placement = placement || (this.el.getX() > this.resizingEl.getX() ? Roo.SplitBar.LEFT : Roo.SplitBar.RIGHT);
24470         this.el.addClass("x-splitbar-h");
24471     }else{
24472         /** @private */
24473         this.placement = placement || (this.el.getY() > this.resizingEl.getY() ? Roo.SplitBar.TOP : Roo.SplitBar.BOTTOM);
24474         this.el.addClass("x-splitbar-v");
24475     }
24476     
24477     this.addEvents({
24478         /**
24479          * @event resize
24480          * Fires when the splitter is moved (alias for {@link #event-moved})
24481          * @param {Roo.SplitBar} this
24482          * @param {Number} newSize the new width or height
24483          */
24484         "resize" : true,
24485         /**
24486          * @event moved
24487          * Fires when the splitter is moved
24488          * @param {Roo.SplitBar} this
24489          * @param {Number} newSize the new width or height
24490          */
24491         "moved" : true,
24492         /**
24493          * @event beforeresize
24494          * Fires before the splitter is dragged
24495          * @param {Roo.SplitBar} this
24496          */
24497         "beforeresize" : true,
24498
24499         "beforeapply" : true
24500     });
24501
24502     Roo.util.Observable.call(this);
24503 };
24504
24505 Roo.extend(Roo.SplitBar, Roo.util.Observable, {
24506     onStartProxyDrag : function(x, y){
24507         this.fireEvent("beforeresize", this);
24508         if(!this.overlay){
24509             var o = Roo.DomHelper.insertFirst(document.body,  {cls: "x-drag-overlay", html: "&#160;"}, true);
24510             o.unselectable();
24511             o.enableDisplayMode("block");
24512             // all splitbars share the same overlay
24513             Roo.SplitBar.prototype.overlay = o;
24514         }
24515         this.overlay.setSize(Roo.lib.Dom.getViewWidth(true), Roo.lib.Dom.getViewHeight(true));
24516         this.overlay.show();
24517         Roo.get(this.proxy).setDisplayed("block");
24518         var size = this.adapter.getElementSize(this);
24519         this.activeMinSize = this.getMinimumSize();;
24520         this.activeMaxSize = this.getMaximumSize();;
24521         var c1 = size - this.activeMinSize;
24522         var c2 = Math.max(this.activeMaxSize - size, 0);
24523         if(this.orientation == Roo.SplitBar.HORIZONTAL){
24524             this.dd.resetConstraints();
24525             this.dd.setXConstraint(
24526                 this.placement == Roo.SplitBar.LEFT ? c1 : c2, 
24527                 this.placement == Roo.SplitBar.LEFT ? c2 : c1
24528             );
24529             this.dd.setYConstraint(0, 0);
24530         }else{
24531             this.dd.resetConstraints();
24532             this.dd.setXConstraint(0, 0);
24533             this.dd.setYConstraint(
24534                 this.placement == Roo.SplitBar.TOP ? c1 : c2, 
24535                 this.placement == Roo.SplitBar.TOP ? c2 : c1
24536             );
24537          }
24538         this.dragSpecs.startSize = size;
24539         this.dragSpecs.startPoint = [x, y];
24540         Roo.dd.DDProxy.prototype.b4StartDrag.call(this.dd, x, y);
24541     },
24542     
24543     /** 
24544      * @private Called after the drag operation by the DDProxy
24545      */
24546     onEndProxyDrag : function(e){
24547         Roo.get(this.proxy).setDisplayed(false);
24548         var endPoint = Roo.lib.Event.getXY(e);
24549         if(this.overlay){
24550             this.overlay.hide();
24551         }
24552         var newSize;
24553         if(this.orientation == Roo.SplitBar.HORIZONTAL){
24554             newSize = this.dragSpecs.startSize + 
24555                 (this.placement == Roo.SplitBar.LEFT ?
24556                     endPoint[0] - this.dragSpecs.startPoint[0] :
24557                     this.dragSpecs.startPoint[0] - endPoint[0]
24558                 );
24559         }else{
24560             newSize = this.dragSpecs.startSize + 
24561                 (this.placement == Roo.SplitBar.TOP ?
24562                     endPoint[1] - this.dragSpecs.startPoint[1] :
24563                     this.dragSpecs.startPoint[1] - endPoint[1]
24564                 );
24565         }
24566         newSize = Math.min(Math.max(newSize, this.activeMinSize), this.activeMaxSize);
24567         if(newSize != this.dragSpecs.startSize){
24568             if(this.fireEvent('beforeapply', this, newSize) !== false){
24569                 this.adapter.setElementSize(this, newSize);
24570                 this.fireEvent("moved", this, newSize);
24571                 this.fireEvent("resize", this, newSize);
24572             }
24573         }
24574     },
24575     
24576     /**
24577      * Get the adapter this SplitBar uses
24578      * @return The adapter object
24579      */
24580     getAdapter : function(){
24581         return this.adapter;
24582     },
24583     
24584     /**
24585      * Set the adapter this SplitBar uses
24586      * @param {Object} adapter A SplitBar adapter object
24587      */
24588     setAdapter : function(adapter){
24589         this.adapter = adapter;
24590         this.adapter.init(this);
24591     },
24592     
24593     /**
24594      * Gets the minimum size for the resizing element
24595      * @return {Number} The minimum size
24596      */
24597     getMinimumSize : function(){
24598         return this.minSize;
24599     },
24600     
24601     /**
24602      * Sets the minimum size for the resizing element
24603      * @param {Number} minSize The minimum size
24604      */
24605     setMinimumSize : function(minSize){
24606         this.minSize = minSize;
24607     },
24608     
24609     /**
24610      * Gets the maximum size for the resizing element
24611      * @return {Number} The maximum size
24612      */
24613     getMaximumSize : function(){
24614         return this.maxSize;
24615     },
24616     
24617     /**
24618      * Sets the maximum size for the resizing element
24619      * @param {Number} maxSize The maximum size
24620      */
24621     setMaximumSize : function(maxSize){
24622         this.maxSize = maxSize;
24623     },
24624     
24625     /**
24626      * Sets the initialize size for the resizing element
24627      * @param {Number} size The initial size
24628      */
24629     setCurrentSize : function(size){
24630         var oldAnimate = this.animate;
24631         this.animate = false;
24632         this.adapter.setElementSize(this, size);
24633         this.animate = oldAnimate;
24634     },
24635     
24636     /**
24637      * Destroy this splitbar. 
24638      * @param {Boolean} removeEl True to remove the element
24639      */
24640     destroy : function(removeEl){
24641         if(this.shim){
24642             this.shim.remove();
24643         }
24644         this.dd.unreg();
24645         this.proxy.parentNode.removeChild(this.proxy);
24646         if(removeEl){
24647             this.el.remove();
24648         }
24649     }
24650 });
24651
24652 /**
24653  * @private static Create our own proxy element element. So it will be the same same size on all browsers, we won't use borders. Instead we use a background color.
24654  */
24655 Roo.SplitBar.createProxy = function(dir){
24656     var proxy = new Roo.Element(document.createElement("div"));
24657     proxy.unselectable();
24658     var cls = 'x-splitbar-proxy';
24659     proxy.addClass(cls + ' ' + (dir == Roo.SplitBar.HORIZONTAL ? cls +'-h' : cls + '-v'));
24660     document.body.appendChild(proxy.dom);
24661     return proxy.dom;
24662 };
24663
24664 /** 
24665  * @class Roo.SplitBar.BasicLayoutAdapter
24666  * Default Adapter. It assumes the splitter and resizing element are not positioned
24667  * elements and only gets/sets the width of the element. Generally used for table based layouts.
24668  */
24669 Roo.SplitBar.BasicLayoutAdapter = function(){
24670 };
24671
24672 Roo.SplitBar.BasicLayoutAdapter.prototype = {
24673     // do nothing for now
24674     init : function(s){
24675     
24676     },
24677     /**
24678      * Called before drag operations to get the current size of the resizing element. 
24679      * @param {Roo.SplitBar} s The SplitBar using this adapter
24680      */
24681      getElementSize : function(s){
24682         if(s.orientation == Roo.SplitBar.HORIZONTAL){
24683             return s.resizingEl.getWidth();
24684         }else{
24685             return s.resizingEl.getHeight();
24686         }
24687     },
24688     
24689     /**
24690      * Called after drag operations to set the size of the resizing element.
24691      * @param {Roo.SplitBar} s The SplitBar using this adapter
24692      * @param {Number} newSize The new size to set
24693      * @param {Function} onComplete A function to be invoked when resizing is complete
24694      */
24695     setElementSize : function(s, newSize, onComplete){
24696         if(s.orientation == Roo.SplitBar.HORIZONTAL){
24697             if(!s.animate){
24698                 s.resizingEl.setWidth(newSize);
24699                 if(onComplete){
24700                     onComplete(s, newSize);
24701                 }
24702             }else{
24703                 s.resizingEl.setWidth(newSize, true, .1, onComplete, 'easeOut');
24704             }
24705         }else{
24706             
24707             if(!s.animate){
24708                 s.resizingEl.setHeight(newSize);
24709                 if(onComplete){
24710                     onComplete(s, newSize);
24711                 }
24712             }else{
24713                 s.resizingEl.setHeight(newSize, true, .1, onComplete, 'easeOut');
24714             }
24715         }
24716     }
24717 };
24718
24719 /** 
24720  *@class Roo.SplitBar.AbsoluteLayoutAdapter
24721  * @extends Roo.SplitBar.BasicLayoutAdapter
24722  * Adapter that  moves the splitter element to align with the resized sizing element. 
24723  * Used with an absolute positioned SplitBar.
24724  * @param {String/HTMLElement/Roo.Element} container The container that wraps around the absolute positioned content. If it's
24725  * document.body, make sure you assign an id to the body element.
24726  */
24727 Roo.SplitBar.AbsoluteLayoutAdapter = function(container){
24728     this.basic = new Roo.SplitBar.BasicLayoutAdapter();
24729     this.container = Roo.get(container);
24730 };
24731
24732 Roo.SplitBar.AbsoluteLayoutAdapter.prototype = {
24733     init : function(s){
24734         this.basic.init(s);
24735     },
24736     
24737     getElementSize : function(s){
24738         return this.basic.getElementSize(s);
24739     },
24740     
24741     setElementSize : function(s, newSize, onComplete){
24742         this.basic.setElementSize(s, newSize, this.moveSplitter.createDelegate(this, [s]));
24743     },
24744     
24745     moveSplitter : function(s){
24746         var yes = Roo.SplitBar;
24747         switch(s.placement){
24748             case yes.LEFT:
24749                 s.el.setX(s.resizingEl.getRight());
24750                 break;
24751             case yes.RIGHT:
24752                 s.el.setStyle("right", (this.container.getWidth() - s.resizingEl.getLeft()) + "px");
24753                 break;
24754             case yes.TOP:
24755                 s.el.setY(s.resizingEl.getBottom());
24756                 break;
24757             case yes.BOTTOM:
24758                 s.el.setY(s.resizingEl.getTop() - s.el.getHeight());
24759                 break;
24760         }
24761     }
24762 };
24763
24764 /**
24765  * Orientation constant - Create a vertical SplitBar
24766  * @static
24767  * @type Number
24768  */
24769 Roo.SplitBar.VERTICAL = 1;
24770
24771 /**
24772  * Orientation constant - Create a horizontal SplitBar
24773  * @static
24774  * @type Number
24775  */
24776 Roo.SplitBar.HORIZONTAL = 2;
24777
24778 /**
24779  * Placement constant - The resizing element is to the left of the splitter element
24780  * @static
24781  * @type Number
24782  */
24783 Roo.SplitBar.LEFT = 1;
24784
24785 /**
24786  * Placement constant - The resizing element is to the right of the splitter element
24787  * @static
24788  * @type Number
24789  */
24790 Roo.SplitBar.RIGHT = 2;
24791
24792 /**
24793  * Placement constant - The resizing element is positioned above the splitter element
24794  * @static
24795  * @type Number
24796  */
24797 Roo.SplitBar.TOP = 3;
24798
24799 /**
24800  * Placement constant - The resizing element is positioned under splitter element
24801  * @static
24802  * @type Number
24803  */
24804 Roo.SplitBar.BOTTOM = 4;
24805 /*
24806  * Based on:
24807  * Ext JS Library 1.1.1
24808  * Copyright(c) 2006-2007, Ext JS, LLC.
24809  *
24810  * Originally Released Under LGPL - original licence link has changed is not relivant.
24811  *
24812  * Fork - LGPL
24813  * <script type="text/javascript">
24814  */
24815
24816 /**
24817  * @class Roo.View
24818  * @extends Roo.util.Observable
24819  * Create a "View" for an element based on a data model or UpdateManager and the supplied DomHelper template. 
24820  * This class also supports single and multi selection modes. <br>
24821  * Create a data model bound view:
24822  <pre><code>
24823  var store = new Roo.data.Store(...);
24824
24825  var view = new Roo.View({
24826     el : "my-element",
24827     tpl : '&lt;div id="{0}"&gt;{2} - {1}&lt;/div&gt;', // auto create template
24828  
24829     singleSelect: true,
24830     selectedClass: "ydataview-selected",
24831     store: store
24832  });
24833
24834  // listen for node click?
24835  view.on("click", function(vw, index, node, e){
24836  alert('Node "' + node.id + '" at index: ' + index + " was clicked.");
24837  });
24838
24839  // load XML data
24840  dataModel.load("foobar.xml");
24841  </code></pre>
24842  For an example of creating a JSON/UpdateManager view, see {@link Roo.JsonView}.
24843  * <br><br>
24844  * <b>Note: The root of your template must be a single node. Table/row implementations may work but are not supported due to
24845  * IE"s limited insertion support with tables and Opera"s faulty event bubbling.</b>
24846  * 
24847  * Note: old style constructor is still suported (container, template, config)
24848  * 
24849  * @constructor
24850  * Create a new View
24851  * @param {Object} config The config object
24852  * 
24853  */
24854 Roo.View = function(config, depreciated_tpl, depreciated_config){
24855     
24856     this.parent = false;
24857     
24858     if (typeof(depreciated_tpl) == 'undefined') {
24859         // new way.. - universal constructor.
24860         Roo.apply(this, config);
24861         this.el  = Roo.get(this.el);
24862     } else {
24863         // old format..
24864         this.el  = Roo.get(config);
24865         this.tpl = depreciated_tpl;
24866         Roo.apply(this, depreciated_config);
24867     }
24868     this.wrapEl  = this.el.wrap().wrap();
24869     ///this.el = this.wrapEla.appendChild(document.createElement("div"));
24870     
24871     
24872     if(typeof(this.tpl) == "string"){
24873         this.tpl = new Roo.Template(this.tpl);
24874     } else {
24875         // support xtype ctors..
24876         this.tpl = new Roo.factory(this.tpl, Roo);
24877     }
24878     
24879     
24880     this.tpl.compile();
24881     
24882     /** @private */
24883     this.addEvents({
24884         /**
24885          * @event beforeclick
24886          * Fires before a click is processed. Returns false to cancel the default action.
24887          * @param {Roo.View} this
24888          * @param {Number} index The index of the target node
24889          * @param {HTMLElement} node The target node
24890          * @param {Roo.EventObject} e The raw event object
24891          */
24892             "beforeclick" : true,
24893         /**
24894          * @event click
24895          * Fires when a template node is clicked.
24896          * @param {Roo.View} this
24897          * @param {Number} index The index of the target node
24898          * @param {HTMLElement} node The target node
24899          * @param {Roo.EventObject} e The raw event object
24900          */
24901             "click" : true,
24902         /**
24903          * @event dblclick
24904          * Fires when a template node is double clicked.
24905          * @param {Roo.View} this
24906          * @param {Number} index The index of the target node
24907          * @param {HTMLElement} node The target node
24908          * @param {Roo.EventObject} e The raw event object
24909          */
24910             "dblclick" : true,
24911         /**
24912          * @event contextmenu
24913          * Fires when a template node is right clicked.
24914          * @param {Roo.View} this
24915          * @param {Number} index The index of the target node
24916          * @param {HTMLElement} node The target node
24917          * @param {Roo.EventObject} e The raw event object
24918          */
24919             "contextmenu" : true,
24920         /**
24921          * @event selectionchange
24922          * Fires when the selected nodes change.
24923          * @param {Roo.View} this
24924          * @param {Array} selections Array of the selected nodes
24925          */
24926             "selectionchange" : true,
24927     
24928         /**
24929          * @event beforeselect
24930          * Fires before a selection is made. If any handlers return false, the selection is cancelled.
24931          * @param {Roo.View} this
24932          * @param {HTMLElement} node The node to be selected
24933          * @param {Array} selections Array of currently selected nodes
24934          */
24935             "beforeselect" : true,
24936         /**
24937          * @event preparedata
24938          * Fires on every row to render, to allow you to change the data.
24939          * @param {Roo.View} this
24940          * @param {Object} data to be rendered (change this)
24941          */
24942           "preparedata" : true
24943           
24944           
24945         });
24946
24947
24948
24949     this.el.on({
24950         "click": this.onClick,
24951         "dblclick": this.onDblClick,
24952         "contextmenu": this.onContextMenu,
24953         scope:this
24954     });
24955
24956     this.selections = [];
24957     this.nodes = [];
24958     this.cmp = new Roo.CompositeElementLite([]);
24959     if(this.store){
24960         this.store = Roo.factory(this.store, Roo.data);
24961         this.setStore(this.store, true);
24962     }
24963     
24964     if ( this.footer && this.footer.xtype) {
24965            
24966          var fctr = this.wrapEl.appendChild(document.createElement("div"));
24967         
24968         this.footer.dataSource = this.store
24969         this.footer.container = fctr;
24970         this.footer = Roo.factory(this.footer, Roo);
24971         fctr.insertFirst(this.el);
24972         
24973         // this is a bit insane - as the paging toolbar seems to detach the el..
24974 //        dom.parentNode.parentNode.parentNode
24975          // they get detached?
24976     }
24977     
24978     
24979     Roo.View.superclass.constructor.call(this);
24980     
24981     
24982 };
24983
24984 Roo.extend(Roo.View, Roo.util.Observable, {
24985     
24986      /**
24987      * @cfg {Roo.data.Store} store Data store to load data from.
24988      */
24989     store : false,
24990     
24991     /**
24992      * @cfg {String|Roo.Element} el The container element.
24993      */
24994     el : '',
24995     
24996     /**
24997      * @cfg {String|Roo.Template} tpl The template used by this View 
24998      */
24999     tpl : false,
25000     /**
25001      * @cfg {String} dataName the named area of the template to use as the data area
25002      *                          Works with domtemplates roo-name="name"
25003      */
25004     dataName: false,
25005     /**
25006      * @cfg {String} selectedClass The css class to add to selected nodes
25007      */
25008     selectedClass : "x-view-selected",
25009      /**
25010      * @cfg {String} emptyText The empty text to show when nothing is loaded.
25011      */
25012     emptyText : "",
25013     
25014     /**
25015      * @cfg {String} text to display on mask (default Loading)
25016      */
25017     mask : false,
25018     /**
25019      * @cfg {Boolean} multiSelect Allow multiple selection
25020      */
25021     multiSelect : false,
25022     /**
25023      * @cfg {Boolean} singleSelect Allow single selection
25024      */
25025     singleSelect:  false,
25026     
25027     /**
25028      * @cfg {Boolean} toggleSelect - selecting 
25029      */
25030     toggleSelect : false,
25031     
25032     /**
25033      * @cfg {Boolean} tickable - selecting 
25034      */
25035     tickable : false,
25036     
25037     /**
25038      * Returns the element this view is bound to.
25039      * @return {Roo.Element}
25040      */
25041     getEl : function(){
25042         return this.wrapEl;
25043     },
25044     
25045     
25046
25047     /**
25048      * Refreshes the view. - called by datachanged on the store. - do not call directly.
25049      */
25050     refresh : function(){
25051         Roo.log('refresh');
25052         var t = this.tpl;
25053         
25054         // if we are using something like 'domtemplate', then
25055         // the what gets used is:
25056         // t.applySubtemplate(NAME, data, wrapping data..)
25057         // the outer template then get' applied with
25058         //     the store 'extra data'
25059         // and the body get's added to the
25060         //      roo-name="data" node?
25061         //      <span class='roo-tpl-{name}'></span> ?????
25062         
25063         
25064         
25065         this.clearSelections();
25066         this.el.update("");
25067         var html = [];
25068         var records = this.store.getRange();
25069         if(records.length < 1) {
25070             
25071             // is this valid??  = should it render a template??
25072             
25073             this.el.update(this.emptyText);
25074             return;
25075         }
25076         var el = this.el;
25077         if (this.dataName) {
25078             this.el.update(t.apply(this.store.meta)); //????
25079             el = this.el.child('.roo-tpl-' + this.dataName);
25080         }
25081         
25082         for(var i = 0, len = records.length; i < len; i++){
25083             var data = this.prepareData(records[i].data, i, records[i]);
25084             this.fireEvent("preparedata", this, data, i, records[i]);
25085             
25086             var d = Roo.apply({}, data);
25087             
25088             if(this.tickable){
25089                 Roo.apply(d, {'roo-id' : Roo.id()});
25090                 
25091                 var _this = this;
25092             
25093                 Roo.each(this.parent.item, function(item){
25094                     if(item[_this.parent.valueField] != data[_this.parent.valueField]){
25095                         return;
25096                     }
25097                     Roo.apply(d, {'roo-data-checked' : 'checked'});
25098                 });
25099             }
25100             
25101             html[html.length] = Roo.util.Format.trim(
25102                 this.dataName ?
25103                     t.applySubtemplate(this.dataName, d, this.store.meta) :
25104                     t.apply(d)
25105             );
25106         }
25107         
25108         
25109         
25110         el.update(html.join(""));
25111         this.nodes = el.dom.childNodes;
25112         this.updateIndexes(0);
25113     },
25114     
25115
25116     /**
25117      * Function to override to reformat the data that is sent to
25118      * the template for each node.
25119      * DEPRICATED - use the preparedata event handler.
25120      * @param {Array/Object} data The raw data (array of colData for a data model bound view or
25121      * a JSON object for an UpdateManager bound view).
25122      */
25123     prepareData : function(data, index, record)
25124     {
25125         this.fireEvent("preparedata", this, data, index, record);
25126         return data;
25127     },
25128
25129     onUpdate : function(ds, record){
25130          Roo.log('on update');   
25131         this.clearSelections();
25132         var index = this.store.indexOf(record);
25133         var n = this.nodes[index];
25134         this.tpl.insertBefore(n, this.prepareData(record.data, index, record));
25135         n.parentNode.removeChild(n);
25136         this.updateIndexes(index, index);
25137     },
25138
25139     
25140     
25141 // --------- FIXME     
25142     onAdd : function(ds, records, index)
25143     {
25144         Roo.log(['on Add', ds, records, index] );        
25145         this.clearSelections();
25146         if(this.nodes.length == 0){
25147             this.refresh();
25148             return;
25149         }
25150         var n = this.nodes[index];
25151         for(var i = 0, len = records.length; i < len; i++){
25152             var d = this.prepareData(records[i].data, i, records[i]);
25153             if(n){
25154                 this.tpl.insertBefore(n, d);
25155             }else{
25156                 
25157                 this.tpl.append(this.el, d);
25158             }
25159         }
25160         this.updateIndexes(index);
25161     },
25162
25163     onRemove : function(ds, record, index){
25164         Roo.log('onRemove');
25165         this.clearSelections();
25166         var el = this.dataName  ?
25167             this.el.child('.roo-tpl-' + this.dataName) :
25168             this.el; 
25169         
25170         el.dom.removeChild(this.nodes[index]);
25171         this.updateIndexes(index);
25172     },
25173
25174     /**
25175      * Refresh an individual node.
25176      * @param {Number} index
25177      */
25178     refreshNode : function(index){
25179         this.onUpdate(this.store, this.store.getAt(index));
25180     },
25181
25182     updateIndexes : function(startIndex, endIndex){
25183         var ns = this.nodes;
25184         startIndex = startIndex || 0;
25185         endIndex = endIndex || ns.length - 1;
25186         for(var i = startIndex; i <= endIndex; i++){
25187             ns[i].nodeIndex = i;
25188         }
25189     },
25190
25191     /**
25192      * Changes the data store this view uses and refresh the view.
25193      * @param {Store} store
25194      */
25195     setStore : function(store, initial){
25196         if(!initial && this.store){
25197             this.store.un("datachanged", this.refresh);
25198             this.store.un("add", this.onAdd);
25199             this.store.un("remove", this.onRemove);
25200             this.store.un("update", this.onUpdate);
25201             this.store.un("clear", this.refresh);
25202             this.store.un("beforeload", this.onBeforeLoad);
25203             this.store.un("load", this.onLoad);
25204             this.store.un("loadexception", this.onLoad);
25205         }
25206         if(store){
25207           
25208             store.on("datachanged", this.refresh, this);
25209             store.on("add", this.onAdd, this);
25210             store.on("remove", this.onRemove, this);
25211             store.on("update", this.onUpdate, this);
25212             store.on("clear", this.refresh, this);
25213             store.on("beforeload", this.onBeforeLoad, this);
25214             store.on("load", this.onLoad, this);
25215             store.on("loadexception", this.onLoad, this);
25216         }
25217         
25218         if(store){
25219             this.refresh();
25220         }
25221     },
25222     /**
25223      * onbeforeLoad - masks the loading area.
25224      *
25225      */
25226     onBeforeLoad : function(store,opts)
25227     {
25228          Roo.log('onBeforeLoad');   
25229         if (!opts.add) {
25230             this.el.update("");
25231         }
25232         this.el.mask(this.mask ? this.mask : "Loading" ); 
25233     },
25234     onLoad : function ()
25235     {
25236         this.el.unmask();
25237     },
25238     
25239
25240     /**
25241      * Returns the template node the passed child belongs to or null if it doesn't belong to one.
25242      * @param {HTMLElement} node
25243      * @return {HTMLElement} The template node
25244      */
25245     findItemFromChild : function(node){
25246         var el = this.dataName  ?
25247             this.el.child('.roo-tpl-' + this.dataName,true) :
25248             this.el.dom; 
25249         
25250         if(!node || node.parentNode == el){
25251                     return node;
25252             }
25253             var p = node.parentNode;
25254             while(p && p != el){
25255             if(p.parentNode == el){
25256                 return p;
25257             }
25258             p = p.parentNode;
25259         }
25260             return null;
25261     },
25262
25263     /** @ignore */
25264     onClick : function(e){
25265         var item = this.findItemFromChild(e.getTarget());
25266         if(item){
25267             var index = this.indexOf(item);
25268             if(this.onItemClick(item, index, e) !== false){
25269                 this.fireEvent("click", this, index, item, e);
25270             }
25271         }else{
25272             this.clearSelections();
25273         }
25274     },
25275
25276     /** @ignore */
25277     onContextMenu : function(e){
25278         var item = this.findItemFromChild(e.getTarget());
25279         if(item){
25280             this.fireEvent("contextmenu", this, this.indexOf(item), item, e);
25281         }
25282     },
25283
25284     /** @ignore */
25285     onDblClick : function(e){
25286         var item = this.findItemFromChild(e.getTarget());
25287         if(item){
25288             this.fireEvent("dblclick", this, this.indexOf(item), item, e);
25289         }
25290     },
25291
25292     onItemClick : function(item, index, e)
25293     {
25294         if(this.fireEvent("beforeclick", this, index, item, e) === false){
25295             return false;
25296         }
25297         if (this.toggleSelect) {
25298             var m = this.isSelected(item) ? 'unselect' : 'select';
25299             Roo.log(m);
25300             var _t = this;
25301             _t[m](item, true, false);
25302             return true;
25303         }
25304         if(this.multiSelect || this.singleSelect){
25305             if(this.multiSelect && e.shiftKey && this.lastSelection){
25306                 this.select(this.getNodes(this.indexOf(this.lastSelection), index), false);
25307             }else{
25308                 this.select(item, this.multiSelect && e.ctrlKey);
25309                 this.lastSelection = item;
25310             }
25311             
25312             if(!this.tickable){
25313                 e.preventDefault();
25314             }
25315             
25316         }
25317         return true;
25318     },
25319
25320     /**
25321      * Get the number of selected nodes.
25322      * @return {Number}
25323      */
25324     getSelectionCount : function(){
25325         return this.selections.length;
25326     },
25327
25328     /**
25329      * Get the currently selected nodes.
25330      * @return {Array} An array of HTMLElements
25331      */
25332     getSelectedNodes : function(){
25333         return this.selections;
25334     },
25335
25336     /**
25337      * Get the indexes of the selected nodes.
25338      * @return {Array}
25339      */
25340     getSelectedIndexes : function(){
25341         var indexes = [], s = this.selections;
25342         for(var i = 0, len = s.length; i < len; i++){
25343             indexes.push(s[i].nodeIndex);
25344         }
25345         return indexes;
25346     },
25347
25348     /**
25349      * Clear all selections
25350      * @param {Boolean} suppressEvent (optional) true to skip firing of the selectionchange event
25351      */
25352     clearSelections : function(suppressEvent){
25353         if(this.nodes && (this.multiSelect || this.singleSelect) && this.selections.length > 0){
25354             this.cmp.elements = this.selections;
25355             this.cmp.removeClass(this.selectedClass);
25356             this.selections = [];
25357             if(!suppressEvent){
25358                 this.fireEvent("selectionchange", this, this.selections);
25359             }
25360         }
25361     },
25362
25363     /**
25364      * Returns true if the passed node is selected
25365      * @param {HTMLElement/Number} node The node or node index
25366      * @return {Boolean}
25367      */
25368     isSelected : function(node){
25369         var s = this.selections;
25370         if(s.length < 1){
25371             return false;
25372         }
25373         node = this.getNode(node);
25374         return s.indexOf(node) !== -1;
25375     },
25376
25377     /**
25378      * Selects nodes.
25379      * @param {Array/HTMLElement/String/Number} nodeInfo An HTMLElement template node, index of a template node, id of a template node or an array of any of those to select
25380      * @param {Boolean} keepExisting (optional) true to keep existing selections
25381      * @param {Boolean} suppressEvent (optional) true to skip firing of the selectionchange vent
25382      */
25383     select : function(nodeInfo, keepExisting, suppressEvent){
25384         if(nodeInfo instanceof Array){
25385             if(!keepExisting){
25386                 this.clearSelections(true);
25387             }
25388             for(var i = 0, len = nodeInfo.length; i < len; i++){
25389                 this.select(nodeInfo[i], true, true);
25390             }
25391             return;
25392         } 
25393         var node = this.getNode(nodeInfo);
25394         if(!node || this.isSelected(node)){
25395             return; // already selected.
25396         }
25397         if(!keepExisting){
25398             this.clearSelections(true);
25399         }
25400         if(this.fireEvent("beforeselect", this, node, this.selections) !== false){
25401             Roo.fly(node).addClass(this.selectedClass);
25402             this.selections.push(node);
25403             if(!suppressEvent){
25404                 this.fireEvent("selectionchange", this, this.selections);
25405             }
25406         }
25407         
25408         
25409     },
25410       /**
25411      * Unselects nodes.
25412      * @param {Array/HTMLElement/String/Number} nodeInfo An HTMLElement template node, index of a template node, id of a template node or an array of any of those to select
25413      * @param {Boolean} keepExisting (optional) true IGNORED (for campatibility with select)
25414      * @param {Boolean} suppressEvent (optional) true to skip firing of the selectionchange vent
25415      */
25416     unselect : function(nodeInfo, keepExisting, suppressEvent)
25417     {
25418         if(nodeInfo instanceof Array){
25419             Roo.each(this.selections, function(s) {
25420                 this.unselect(s, nodeInfo);
25421             }, this);
25422             return;
25423         }
25424         var node = this.getNode(nodeInfo);
25425         if(!node || !this.isSelected(node)){
25426             Roo.log("not selected");
25427             return; // not selected.
25428         }
25429         // fireevent???
25430         var ns = [];
25431         Roo.each(this.selections, function(s) {
25432             if (s == node ) {
25433                 Roo.fly(node).removeClass(this.selectedClass);
25434
25435                 return;
25436             }
25437             ns.push(s);
25438         },this);
25439         
25440         this.selections= ns;
25441         this.fireEvent("selectionchange", this, this.selections);
25442     },
25443
25444     /**
25445      * Gets a template node.
25446      * @param {HTMLElement/String/Number} nodeInfo An HTMLElement template node, index of a template node or the id of a template node
25447      * @return {HTMLElement} The node or null if it wasn't found
25448      */
25449     getNode : function(nodeInfo){
25450         if(typeof nodeInfo == "string"){
25451             return document.getElementById(nodeInfo);
25452         }else if(typeof nodeInfo == "number"){
25453             return this.nodes[nodeInfo];
25454         }
25455         return nodeInfo;
25456     },
25457
25458     /**
25459      * Gets a range template nodes.
25460      * @param {Number} startIndex
25461      * @param {Number} endIndex
25462      * @return {Array} An array of nodes
25463      */
25464     getNodes : function(start, end){
25465         var ns = this.nodes;
25466         start = start || 0;
25467         end = typeof end == "undefined" ? ns.length - 1 : end;
25468         var nodes = [];
25469         if(start <= end){
25470             for(var i = start; i <= end; i++){
25471                 nodes.push(ns[i]);
25472             }
25473         } else{
25474             for(var i = start; i >= end; i--){
25475                 nodes.push(ns[i]);
25476             }
25477         }
25478         return nodes;
25479     },
25480
25481     /**
25482      * Finds the index of the passed node
25483      * @param {HTMLElement/String/Number} nodeInfo An HTMLElement template node, index of a template node or the id of a template node
25484      * @return {Number} The index of the node or -1
25485      */
25486     indexOf : function(node){
25487         node = this.getNode(node);
25488         if(typeof node.nodeIndex == "number"){
25489             return node.nodeIndex;
25490         }
25491         var ns = this.nodes;
25492         for(var i = 0, len = ns.length; i < len; i++){
25493             if(ns[i] == node){
25494                 return i;
25495             }
25496         }
25497         return -1;
25498     }
25499 });
25500 /*
25501  * Based on:
25502  * Ext JS Library 1.1.1
25503  * Copyright(c) 2006-2007, Ext JS, LLC.
25504  *
25505  * Originally Released Under LGPL - original licence link has changed is not relivant.
25506  *
25507  * Fork - LGPL
25508  * <script type="text/javascript">
25509  */
25510
25511 /**
25512  * @class Roo.JsonView
25513  * @extends Roo.View
25514  * Shortcut class to create a JSON + {@link Roo.UpdateManager} template view. Usage:
25515 <pre><code>
25516 var view = new Roo.JsonView({
25517     container: "my-element",
25518     tpl: '&lt;div id="{id}"&gt;{foo} - {bar}&lt;/div&gt;', // auto create template
25519     multiSelect: true, 
25520     jsonRoot: "data" 
25521 });
25522
25523 // listen for node click?
25524 view.on("click", function(vw, index, node, e){
25525     alert('Node "' + node.id + '" at index: ' + index + " was clicked.");
25526 });
25527
25528 // direct load of JSON data
25529 view.load("foobar.php");
25530
25531 // Example from my blog list
25532 var tpl = new Roo.Template(
25533     '&lt;div class="entry"&gt;' +
25534     '&lt;a class="entry-title" href="{link}"&gt;{title}&lt;/a&gt;' +
25535     "&lt;h4&gt;{date} by {author} | {comments} Comments&lt;/h4&gt;{description}" +
25536     "&lt;/div&gt;&lt;hr /&gt;"
25537 );
25538
25539 var moreView = new Roo.JsonView({
25540     container :  "entry-list", 
25541     template : tpl,
25542     jsonRoot: "posts"
25543 });
25544 moreView.on("beforerender", this.sortEntries, this);
25545 moreView.load({
25546     url: "/blog/get-posts.php",
25547     params: "allposts=true",
25548     text: "Loading Blog Entries..."
25549 });
25550 </code></pre>
25551
25552 * Note: old code is supported with arguments : (container, template, config)
25553
25554
25555  * @constructor
25556  * Create a new JsonView
25557  * 
25558  * @param {Object} config The config object
25559  * 
25560  */
25561 Roo.JsonView = function(config, depreciated_tpl, depreciated_config){
25562     
25563     
25564     Roo.JsonView.superclass.constructor.call(this, config, depreciated_tpl, depreciated_config);
25565
25566     var um = this.el.getUpdateManager();
25567     um.setRenderer(this);
25568     um.on("update", this.onLoad, this);
25569     um.on("failure", this.onLoadException, this);
25570
25571     /**
25572      * @event beforerender
25573      * Fires before rendering of the downloaded JSON data.
25574      * @param {Roo.JsonView} this
25575      * @param {Object} data The JSON data loaded
25576      */
25577     /**
25578      * @event load
25579      * Fires when data is loaded.
25580      * @param {Roo.JsonView} this
25581      * @param {Object} data The JSON data loaded
25582      * @param {Object} response The raw Connect response object
25583      */
25584     /**
25585      * @event loadexception
25586      * Fires when loading fails.
25587      * @param {Roo.JsonView} this
25588      * @param {Object} response The raw Connect response object
25589      */
25590     this.addEvents({
25591         'beforerender' : true,
25592         'load' : true,
25593         'loadexception' : true
25594     });
25595 };
25596 Roo.extend(Roo.JsonView, Roo.View, {
25597     /**
25598      * @type {String} The root property in the loaded JSON object that contains the data
25599      */
25600     jsonRoot : "",
25601
25602     /**
25603      * Refreshes the view.
25604      */
25605     refresh : function(){
25606         this.clearSelections();
25607         this.el.update("");
25608         var html = [];
25609         var o = this.jsonData;
25610         if(o && o.length > 0){
25611             for(var i = 0, len = o.length; i < len; i++){
25612                 var data = this.prepareData(o[i], i, o);
25613                 html[html.length] = this.tpl.apply(data);
25614             }
25615         }else{
25616             html.push(this.emptyText);
25617         }
25618         this.el.update(html.join(""));
25619         this.nodes = this.el.dom.childNodes;
25620         this.updateIndexes(0);
25621     },
25622
25623     /**
25624      * Performs an async HTTP request, and loads the JSON from the response. If <i>params</i> are specified it uses POST, otherwise it uses GET.
25625      * @param {Object/String/Function} url The URL for this request, or a function to call to get the URL, or a config object containing any of the following options:
25626      <pre><code>
25627      view.load({
25628          url: "your-url.php",
25629          params: {param1: "foo", param2: "bar"}, // or a URL encoded string
25630          callback: yourFunction,
25631          scope: yourObject, //(optional scope)
25632          discardUrl: false,
25633          nocache: false,
25634          text: "Loading...",
25635          timeout: 30,
25636          scripts: false
25637      });
25638      </code></pre>
25639      * The only required property is <i>url</i>. The optional properties <i>nocache</i>, <i>text</i> and <i>scripts</i>
25640      * are respectively shorthand for <i>disableCaching</i>, <i>indicatorText</i>, and <i>loadScripts</i> and are used to set their associated property on this UpdateManager instance.
25641      * @param {String/Object} params (optional) The parameters to pass, as either a URL encoded string "param1=1&amp;param2=2" or an object {param1: 1, param2: 2}
25642      * @param {Function} callback (optional) Callback when transaction is complete - called with signature (oElement, bSuccess)
25643      * @param {Boolean} discardUrl (optional) By default when you execute an update the defaultUrl is changed to the last used URL. If true, it will not store the URL.
25644      */
25645     load : function(){
25646         var um = this.el.getUpdateManager();
25647         um.update.apply(um, arguments);
25648     },
25649
25650     render : function(el, response){
25651         this.clearSelections();
25652         this.el.update("");
25653         var o;
25654         try{
25655             o = Roo.util.JSON.decode(response.responseText);
25656             if(this.jsonRoot){
25657                 
25658                 o = o[this.jsonRoot];
25659             }
25660         } catch(e){
25661         }
25662         /**
25663          * The current JSON data or null
25664          */
25665         this.jsonData = o;
25666         this.beforeRender();
25667         this.refresh();
25668     },
25669
25670 /**
25671  * Get the number of records in the current JSON dataset
25672  * @return {Number}
25673  */
25674     getCount : function(){
25675         return this.jsonData ? this.jsonData.length : 0;
25676     },
25677
25678 /**
25679  * Returns the JSON object for the specified node(s)
25680  * @param {HTMLElement/Array} node The node or an array of nodes
25681  * @return {Object/Array} If you pass in an array, you get an array back, otherwise
25682  * you get the JSON object for the node
25683  */
25684     getNodeData : function(node){
25685         if(node instanceof Array){
25686             var data = [];
25687             for(var i = 0, len = node.length; i < len; i++){
25688                 data.push(this.getNodeData(node[i]));
25689             }
25690             return data;
25691         }
25692         return this.jsonData[this.indexOf(node)] || null;
25693     },
25694
25695     beforeRender : function(){
25696         this.snapshot = this.jsonData;
25697         if(this.sortInfo){
25698             this.sort.apply(this, this.sortInfo);
25699         }
25700         this.fireEvent("beforerender", this, this.jsonData);
25701     },
25702
25703     onLoad : function(el, o){
25704         this.fireEvent("load", this, this.jsonData, o);
25705     },
25706
25707     onLoadException : function(el, o){
25708         this.fireEvent("loadexception", this, o);
25709     },
25710
25711 /**
25712  * Filter the data by a specific property.
25713  * @param {String} property A property on your JSON objects
25714  * @param {String/RegExp} value Either string that the property values
25715  * should start with, or a RegExp to test against the property
25716  */
25717     filter : function(property, value){
25718         if(this.jsonData){
25719             var data = [];
25720             var ss = this.snapshot;
25721             if(typeof value == "string"){
25722                 var vlen = value.length;
25723                 if(vlen == 0){
25724                     this.clearFilter();
25725                     return;
25726                 }
25727                 value = value.toLowerCase();
25728                 for(var i = 0, len = ss.length; i < len; i++){
25729                     var o = ss[i];
25730                     if(o[property].substr(0, vlen).toLowerCase() == value){
25731                         data.push(o);
25732                     }
25733                 }
25734             } else if(value.exec){ // regex?
25735                 for(var i = 0, len = ss.length; i < len; i++){
25736                     var o = ss[i];
25737                     if(value.test(o[property])){
25738                         data.push(o);
25739                     }
25740                 }
25741             } else{
25742                 return;
25743             }
25744             this.jsonData = data;
25745             this.refresh();
25746         }
25747     },
25748
25749 /**
25750  * Filter by a function. The passed function will be called with each
25751  * object in the current dataset. If the function returns true the value is kept,
25752  * otherwise it is filtered.
25753  * @param {Function} fn
25754  * @param {Object} scope (optional) The scope of the function (defaults to this JsonView)
25755  */
25756     filterBy : function(fn, scope){
25757         if(this.jsonData){
25758             var data = [];
25759             var ss = this.snapshot;
25760             for(var i = 0, len = ss.length; i < len; i++){
25761                 var o = ss[i];
25762                 if(fn.call(scope || this, o)){
25763                     data.push(o);
25764                 }
25765             }
25766             this.jsonData = data;
25767             this.refresh();
25768         }
25769     },
25770
25771 /**
25772  * Clears the current filter.
25773  */
25774     clearFilter : function(){
25775         if(this.snapshot && this.jsonData != this.snapshot){
25776             this.jsonData = this.snapshot;
25777             this.refresh();
25778         }
25779     },
25780
25781
25782 /**
25783  * Sorts the data for this view and refreshes it.
25784  * @param {String} property A property on your JSON objects to sort on
25785  * @param {String} direction (optional) "desc" or "asc" (defaults to "asc")
25786  * @param {Function} sortType (optional) A function to call to convert the data to a sortable value.
25787  */
25788     sort : function(property, dir, sortType){
25789         this.sortInfo = Array.prototype.slice.call(arguments, 0);
25790         if(this.jsonData){
25791             var p = property;
25792             var dsc = dir && dir.toLowerCase() == "desc";
25793             var f = function(o1, o2){
25794                 var v1 = sortType ? sortType(o1[p]) : o1[p];
25795                 var v2 = sortType ? sortType(o2[p]) : o2[p];
25796                 ;
25797                 if(v1 < v2){
25798                     return dsc ? +1 : -1;
25799                 } else if(v1 > v2){
25800                     return dsc ? -1 : +1;
25801                 } else{
25802                     return 0;
25803                 }
25804             };
25805             this.jsonData.sort(f);
25806             this.refresh();
25807             if(this.jsonData != this.snapshot){
25808                 this.snapshot.sort(f);
25809             }
25810         }
25811     }
25812 });/*
25813  * Based on:
25814  * Ext JS Library 1.1.1
25815  * Copyright(c) 2006-2007, Ext JS, LLC.
25816  *
25817  * Originally Released Under LGPL - original licence link has changed is not relivant.
25818  *
25819  * Fork - LGPL
25820  * <script type="text/javascript">
25821  */
25822  
25823
25824 /**
25825  * @class Roo.ColorPalette
25826  * @extends Roo.Component
25827  * Simple color palette class for choosing colors.  The palette can be rendered to any container.<br />
25828  * Here's an example of typical usage:
25829  * <pre><code>
25830 var cp = new Roo.ColorPalette({value:'993300'});  // initial selected color
25831 cp.render('my-div');
25832
25833 cp.on('select', function(palette, selColor){
25834     // do something with selColor
25835 });
25836 </code></pre>
25837  * @constructor
25838  * Create a new ColorPalette
25839  * @param {Object} config The config object
25840  */
25841 Roo.ColorPalette = function(config){
25842     Roo.ColorPalette.superclass.constructor.call(this, config);
25843     this.addEvents({
25844         /**
25845              * @event select
25846              * Fires when a color is selected
25847              * @param {ColorPalette} this
25848              * @param {String} color The 6-digit color hex code (without the # symbol)
25849              */
25850         select: true
25851     });
25852
25853     if(this.handler){
25854         this.on("select", this.handler, this.scope, true);
25855     }
25856 };
25857 Roo.extend(Roo.ColorPalette, Roo.Component, {
25858     /**
25859      * @cfg {String} itemCls
25860      * The CSS class to apply to the containing element (defaults to "x-color-palette")
25861      */
25862     itemCls : "x-color-palette",
25863     /**
25864      * @cfg {String} value
25865      * The initial color to highlight (should be a valid 6-digit color hex code without the # symbol).  Note that
25866      * the hex codes are case-sensitive.
25867      */
25868     value : null,
25869     clickEvent:'click',
25870     // private
25871     ctype: "Roo.ColorPalette",
25872
25873     /**
25874      * @cfg {Boolean} allowReselect If set to true then reselecting a color that is already selected fires the selection event
25875      */
25876     allowReselect : false,
25877
25878     /**
25879      * <p>An array of 6-digit color hex code strings (without the # symbol).  This array can contain any number
25880      * of colors, and each hex code should be unique.  The width of the palette is controlled via CSS by adjusting
25881      * the width property of the 'x-color-palette' class (or assigning a custom class), so you can balance the number
25882      * of colors with the width setting until the box is symmetrical.</p>
25883      * <p>You can override individual colors if needed:</p>
25884      * <pre><code>
25885 var cp = new Roo.ColorPalette();
25886 cp.colors[0] = "FF0000";  // change the first box to red
25887 </code></pre>
25888
25889 Or you can provide a custom array of your own for complete control:
25890 <pre><code>
25891 var cp = new Roo.ColorPalette();
25892 cp.colors = ["000000", "993300", "333300"];
25893 </code></pre>
25894      * @type Array
25895      */
25896     colors : [
25897         "000000", "993300", "333300", "003300", "003366", "000080", "333399", "333333",
25898         "800000", "FF6600", "808000", "008000", "008080", "0000FF", "666699", "808080",
25899         "FF0000", "FF9900", "99CC00", "339966", "33CCCC", "3366FF", "800080", "969696",
25900         "FF00FF", "FFCC00", "FFFF00", "00FF00", "00FFFF", "00CCFF", "993366", "C0C0C0",
25901         "FF99CC", "FFCC99", "FFFF99", "CCFFCC", "CCFFFF", "99CCFF", "CC99FF", "FFFFFF"
25902     ],
25903
25904     // private
25905     onRender : function(container, position){
25906         var t = new Roo.MasterTemplate(
25907             '<tpl><a href="#" class="color-{0}" hidefocus="on"><em><span style="background:#{0}" unselectable="on">&#160;</span></em></a></tpl>'
25908         );
25909         var c = this.colors;
25910         for(var i = 0, len = c.length; i < len; i++){
25911             t.add([c[i]]);
25912         }
25913         var el = document.createElement("div");
25914         el.className = this.itemCls;
25915         t.overwrite(el);
25916         container.dom.insertBefore(el, position);
25917         this.el = Roo.get(el);
25918         this.el.on(this.clickEvent, this.handleClick,  this, {delegate: "a"});
25919         if(this.clickEvent != 'click'){
25920             this.el.on('click', Roo.emptyFn,  this, {delegate: "a", preventDefault:true});
25921         }
25922     },
25923
25924     // private
25925     afterRender : function(){
25926         Roo.ColorPalette.superclass.afterRender.call(this);
25927         if(this.value){
25928             var s = this.value;
25929             this.value = null;
25930             this.select(s);
25931         }
25932     },
25933
25934     // private
25935     handleClick : function(e, t){
25936         e.preventDefault();
25937         if(!this.disabled){
25938             var c = t.className.match(/(?:^|\s)color-(.{6})(?:\s|$)/)[1];
25939             this.select(c.toUpperCase());
25940         }
25941     },
25942
25943     /**
25944      * Selects the specified color in the palette (fires the select event)
25945      * @param {String} color A valid 6-digit color hex code (# will be stripped if included)
25946      */
25947     select : function(color){
25948         color = color.replace("#", "");
25949         if(color != this.value || this.allowReselect){
25950             var el = this.el;
25951             if(this.value){
25952                 el.child("a.color-"+this.value).removeClass("x-color-palette-sel");
25953             }
25954             el.child("a.color-"+color).addClass("x-color-palette-sel");
25955             this.value = color;
25956             this.fireEvent("select", this, color);
25957         }
25958     }
25959 });/*
25960  * Based on:
25961  * Ext JS Library 1.1.1
25962  * Copyright(c) 2006-2007, Ext JS, LLC.
25963  *
25964  * Originally Released Under LGPL - original licence link has changed is not relivant.
25965  *
25966  * Fork - LGPL
25967  * <script type="text/javascript">
25968  */
25969  
25970 /**
25971  * @class Roo.DatePicker
25972  * @extends Roo.Component
25973  * Simple date picker class.
25974  * @constructor
25975  * Create a new DatePicker
25976  * @param {Object} config The config object
25977  */
25978 Roo.DatePicker = function(config){
25979     Roo.DatePicker.superclass.constructor.call(this, config);
25980
25981     this.value = config && config.value ?
25982                  config.value.clearTime() : new Date().clearTime();
25983
25984     this.addEvents({
25985         /**
25986              * @event select
25987              * Fires when a date is selected
25988              * @param {DatePicker} this
25989              * @param {Date} date The selected date
25990              */
25991         'select': true,
25992         /**
25993              * @event monthchange
25994              * Fires when the displayed month changes 
25995              * @param {DatePicker} this
25996              * @param {Date} date The selected month
25997              */
25998         'monthchange': true
25999     });
26000
26001     if(this.handler){
26002         this.on("select", this.handler,  this.scope || this);
26003     }
26004     // build the disabledDatesRE
26005     if(!this.disabledDatesRE && this.disabledDates){
26006         var dd = this.disabledDates;
26007         var re = "(?:";
26008         for(var i = 0; i < dd.length; i++){
26009             re += dd[i];
26010             if(i != dd.length-1) re += "|";
26011         }
26012         this.disabledDatesRE = new RegExp(re + ")");
26013     }
26014 };
26015
26016 Roo.extend(Roo.DatePicker, Roo.Component, {
26017     /**
26018      * @cfg {String} todayText
26019      * The text to display on the button that selects the current date (defaults to "Today")
26020      */
26021     todayText : "Today",
26022     /**
26023      * @cfg {String} okText
26024      * The text to display on the ok button
26025      */
26026     okText : "&#160;OK&#160;", // &#160; to give the user extra clicking room
26027     /**
26028      * @cfg {String} cancelText
26029      * The text to display on the cancel button
26030      */
26031     cancelText : "Cancel",
26032     /**
26033      * @cfg {String} todayTip
26034      * The tooltip to display for the button that selects the current date (defaults to "{current date} (Spacebar)")
26035      */
26036     todayTip : "{0} (Spacebar)",
26037     /**
26038      * @cfg {Date} minDate
26039      * Minimum allowable date (JavaScript date object, defaults to null)
26040      */
26041     minDate : null,
26042     /**
26043      * @cfg {Date} maxDate
26044      * Maximum allowable date (JavaScript date object, defaults to null)
26045      */
26046     maxDate : null,
26047     /**
26048      * @cfg {String} minText
26049      * The error text to display if the minDate validation fails (defaults to "This date is before the minimum date")
26050      */
26051     minText : "This date is before the minimum date",
26052     /**
26053      * @cfg {String} maxText
26054      * The error text to display if the maxDate validation fails (defaults to "This date is after the maximum date")
26055      */
26056     maxText : "This date is after the maximum date",
26057     /**
26058      * @cfg {String} format
26059      * The default date format string which can be overriden for localization support.  The format must be
26060      * valid according to {@link Date#parseDate} (defaults to 'm/d/y').
26061      */
26062     format : "m/d/y",
26063     /**
26064      * @cfg {Array} disabledDays
26065      * An array of days to disable, 0-based. For example, [0, 6] disables Sunday and Saturday (defaults to null).
26066      */
26067     disabledDays : null,
26068     /**
26069      * @cfg {String} disabledDaysText
26070      * The tooltip to display when the date falls on a disabled day (defaults to "")
26071      */
26072     disabledDaysText : "",
26073     /**
26074      * @cfg {RegExp} disabledDatesRE
26075      * JavaScript regular expression used to disable a pattern of dates (defaults to null)
26076      */
26077     disabledDatesRE : null,
26078     /**
26079      * @cfg {String} disabledDatesText
26080      * The tooltip text to display when the date falls on a disabled date (defaults to "")
26081      */
26082     disabledDatesText : "",
26083     /**
26084      * @cfg {Boolean} constrainToViewport
26085      * True to constrain the date picker to the viewport (defaults to true)
26086      */
26087     constrainToViewport : true,
26088     /**
26089      * @cfg {Array} monthNames
26090      * An array of textual month names which can be overriden for localization support (defaults to Date.monthNames)
26091      */
26092     monthNames : Date.monthNames,
26093     /**
26094      * @cfg {Array} dayNames
26095      * An array of textual day names which can be overriden for localization support (defaults to Date.dayNames)
26096      */
26097     dayNames : Date.dayNames,
26098     /**
26099      * @cfg {String} nextText
26100      * The next month navigation button tooltip (defaults to 'Next Month (Control+Right)')
26101      */
26102     nextText: 'Next Month (Control+Right)',
26103     /**
26104      * @cfg {String} prevText
26105      * The previous month navigation button tooltip (defaults to 'Previous Month (Control+Left)')
26106      */
26107     prevText: 'Previous Month (Control+Left)',
26108     /**
26109      * @cfg {String} monthYearText
26110      * The header month selector tooltip (defaults to 'Choose a month (Control+Up/Down to move years)')
26111      */
26112     monthYearText: 'Choose a month (Control+Up/Down to move years)',
26113     /**
26114      * @cfg {Number} startDay
26115      * Day index at which the week should begin, 0-based (defaults to 0, which is Sunday)
26116      */
26117     startDay : 0,
26118     /**
26119      * @cfg {Bool} showClear
26120      * Show a clear button (usefull for date form elements that can be blank.)
26121      */
26122     
26123     showClear: false,
26124     
26125     /**
26126      * Sets the value of the date field
26127      * @param {Date} value The date to set
26128      */
26129     setValue : function(value){
26130         var old = this.value;
26131         
26132         if (typeof(value) == 'string') {
26133          
26134             value = Date.parseDate(value, this.format);
26135         }
26136         if (!value) {
26137             value = new Date();
26138         }
26139         
26140         this.value = value.clearTime(true);
26141         if(this.el){
26142             this.update(this.value);
26143         }
26144     },
26145
26146     /**
26147      * Gets the current selected value of the date field
26148      * @return {Date} The selected date
26149      */
26150     getValue : function(){
26151         return this.value;
26152     },
26153
26154     // private
26155     focus : function(){
26156         if(this.el){
26157             this.update(this.activeDate);
26158         }
26159     },
26160
26161     // privateval
26162     onRender : function(container, position){
26163         
26164         var m = [
26165              '<table cellspacing="0">',
26166                 '<tr><td class="x-date-left"><a href="#" title="', this.prevText ,'">&#160;</a></td><td class="x-date-middle" align="center"></td><td class="x-date-right"><a href="#" title="', this.nextText ,'">&#160;</a></td></tr>',
26167                 '<tr><td colspan="3"><table class="x-date-inner" cellspacing="0"><thead><tr>'];
26168         var dn = this.dayNames;
26169         for(var i = 0; i < 7; i++){
26170             var d = this.startDay+i;
26171             if(d > 6){
26172                 d = d-7;
26173             }
26174             m.push("<th><span>", dn[d].substr(0,1), "</span></th>");
26175         }
26176         m[m.length] = "</tr></thead><tbody><tr>";
26177         for(var i = 0; i < 42; i++) {
26178             if(i % 7 == 0 && i != 0){
26179                 m[m.length] = "</tr><tr>";
26180             }
26181             m[m.length] = '<td><a href="#" hidefocus="on" class="x-date-date" tabIndex="1"><em><span></span></em></a></td>';
26182         }
26183         m[m.length] = '</tr></tbody></table></td></tr><tr>'+
26184             '<td colspan="3" class="x-date-bottom" align="center"></td></tr></table><div class="x-date-mp"></div>';
26185
26186         var el = document.createElement("div");
26187         el.className = "x-date-picker";
26188         el.innerHTML = m.join("");
26189
26190         container.dom.insertBefore(el, position);
26191
26192         this.el = Roo.get(el);
26193         this.eventEl = Roo.get(el.firstChild);
26194
26195         new Roo.util.ClickRepeater(this.el.child("td.x-date-left a"), {
26196             handler: this.showPrevMonth,
26197             scope: this,
26198             preventDefault:true,
26199             stopDefault:true
26200         });
26201
26202         new Roo.util.ClickRepeater(this.el.child("td.x-date-right a"), {
26203             handler: this.showNextMonth,
26204             scope: this,
26205             preventDefault:true,
26206             stopDefault:true
26207         });
26208
26209         this.eventEl.on("mousewheel", this.handleMouseWheel,  this);
26210
26211         this.monthPicker = this.el.down('div.x-date-mp');
26212         this.monthPicker.enableDisplayMode('block');
26213         
26214         var kn = new Roo.KeyNav(this.eventEl, {
26215             "left" : function(e){
26216                 e.ctrlKey ?
26217                     this.showPrevMonth() :
26218                     this.update(this.activeDate.add("d", -1));
26219             },
26220
26221             "right" : function(e){
26222                 e.ctrlKey ?
26223                     this.showNextMonth() :
26224                     this.update(this.activeDate.add("d", 1));
26225             },
26226
26227             "up" : function(e){
26228                 e.ctrlKey ?
26229                     this.showNextYear() :
26230                     this.update(this.activeDate.add("d", -7));
26231             },
26232
26233             "down" : function(e){
26234                 e.ctrlKey ?
26235                     this.showPrevYear() :
26236                     this.update(this.activeDate.add("d", 7));
26237             },
26238
26239             "pageUp" : function(e){
26240                 this.showNextMonth();
26241             },
26242
26243             "pageDown" : function(e){
26244                 this.showPrevMonth();
26245             },
26246
26247             "enter" : function(e){
26248                 e.stopPropagation();
26249                 return true;
26250             },
26251
26252             scope : this
26253         });
26254
26255         this.eventEl.on("click", this.handleDateClick,  this, {delegate: "a.x-date-date"});
26256
26257         this.eventEl.addKeyListener(Roo.EventObject.SPACE, this.selectToday,  this);
26258
26259         this.el.unselectable();
26260         
26261         this.cells = this.el.select("table.x-date-inner tbody td");
26262         this.textNodes = this.el.query("table.x-date-inner tbody span");
26263
26264         this.mbtn = new Roo.Button(this.el.child("td.x-date-middle", true), {
26265             text: "&#160;",
26266             tooltip: this.monthYearText
26267         });
26268
26269         this.mbtn.on('click', this.showMonthPicker, this);
26270         this.mbtn.el.child(this.mbtn.menuClassTarget).addClass("x-btn-with-menu");
26271
26272
26273         var today = (new Date()).dateFormat(this.format);
26274         
26275         var baseTb = new Roo.Toolbar(this.el.child("td.x-date-bottom", true));
26276         if (this.showClear) {
26277             baseTb.add( new Roo.Toolbar.Fill());
26278         }
26279         baseTb.add({
26280             text: String.format(this.todayText, today),
26281             tooltip: String.format(this.todayTip, today),
26282             handler: this.selectToday,
26283             scope: this
26284         });
26285         
26286         //var todayBtn = new Roo.Button(this.el.child("td.x-date-bottom", true), {
26287             
26288         //});
26289         if (this.showClear) {
26290             
26291             baseTb.add( new Roo.Toolbar.Fill());
26292             baseTb.add({
26293                 text: '&#160;',
26294                 cls: 'x-btn-icon x-btn-clear',
26295                 handler: function() {
26296                     //this.value = '';
26297                     this.fireEvent("select", this, '');
26298                 },
26299                 scope: this
26300             });
26301         }
26302         
26303         
26304         if(Roo.isIE){
26305             this.el.repaint();
26306         }
26307         this.update(this.value);
26308     },
26309
26310     createMonthPicker : function(){
26311         if(!this.monthPicker.dom.firstChild){
26312             var buf = ['<table border="0" cellspacing="0">'];
26313             for(var i = 0; i < 6; i++){
26314                 buf.push(
26315                     '<tr><td class="x-date-mp-month"><a href="#">', this.monthNames[i].substr(0, 3), '</a></td>',
26316                     '<td class="x-date-mp-month x-date-mp-sep"><a href="#">', this.monthNames[i+6].substr(0, 3), '</a></td>',
26317                     i == 0 ?
26318                     '<td class="x-date-mp-ybtn" align="center"><a class="x-date-mp-prev"></a></td><td class="x-date-mp-ybtn" align="center"><a class="x-date-mp-next"></a></td></tr>' :
26319                     '<td class="x-date-mp-year"><a href="#"></a></td><td class="x-date-mp-year"><a href="#"></a></td></tr>'
26320                 );
26321             }
26322             buf.push(
26323                 '<tr class="x-date-mp-btns"><td colspan="4"><button type="button" class="x-date-mp-ok">',
26324                     this.okText,
26325                     '</button><button type="button" class="x-date-mp-cancel">',
26326                     this.cancelText,
26327                     '</button></td></tr>',
26328                 '</table>'
26329             );
26330             this.monthPicker.update(buf.join(''));
26331             this.monthPicker.on('click', this.onMonthClick, this);
26332             this.monthPicker.on('dblclick', this.onMonthDblClick, this);
26333
26334             this.mpMonths = this.monthPicker.select('td.x-date-mp-month');
26335             this.mpYears = this.monthPicker.select('td.x-date-mp-year');
26336
26337             this.mpMonths.each(function(m, a, i){
26338                 i += 1;
26339                 if((i%2) == 0){
26340                     m.dom.xmonth = 5 + Math.round(i * .5);
26341                 }else{
26342                     m.dom.xmonth = Math.round((i-1) * .5);
26343                 }
26344             });
26345         }
26346     },
26347
26348     showMonthPicker : function(){
26349         this.createMonthPicker();
26350         var size = this.el.getSize();
26351         this.monthPicker.setSize(size);
26352         this.monthPicker.child('table').setSize(size);
26353
26354         this.mpSelMonth = (this.activeDate || this.value).getMonth();
26355         this.updateMPMonth(this.mpSelMonth);
26356         this.mpSelYear = (this.activeDate || this.value).getFullYear();
26357         this.updateMPYear(this.mpSelYear);
26358
26359         this.monthPicker.slideIn('t', {duration:.2});
26360     },
26361
26362     updateMPYear : function(y){
26363         this.mpyear = y;
26364         var ys = this.mpYears.elements;
26365         for(var i = 1; i <= 10; i++){
26366             var td = ys[i-1], y2;
26367             if((i%2) == 0){
26368                 y2 = y + Math.round(i * .5);
26369                 td.firstChild.innerHTML = y2;
26370                 td.xyear = y2;
26371             }else{
26372                 y2 = y - (5-Math.round(i * .5));
26373                 td.firstChild.innerHTML = y2;
26374                 td.xyear = y2;
26375             }
26376             this.mpYears.item(i-1)[y2 == this.mpSelYear ? 'addClass' : 'removeClass']('x-date-mp-sel');
26377         }
26378     },
26379
26380     updateMPMonth : function(sm){
26381         this.mpMonths.each(function(m, a, i){
26382             m[m.dom.xmonth == sm ? 'addClass' : 'removeClass']('x-date-mp-sel');
26383         });
26384     },
26385
26386     selectMPMonth: function(m){
26387         
26388     },
26389
26390     onMonthClick : function(e, t){
26391         e.stopEvent();
26392         var el = new Roo.Element(t), pn;
26393         if(el.is('button.x-date-mp-cancel')){
26394             this.hideMonthPicker();
26395         }
26396         else if(el.is('button.x-date-mp-ok')){
26397             this.update(new Date(this.mpSelYear, this.mpSelMonth, (this.activeDate || this.value).getDate()));
26398             this.hideMonthPicker();
26399         }
26400         else if(pn = el.up('td.x-date-mp-month', 2)){
26401             this.mpMonths.removeClass('x-date-mp-sel');
26402             pn.addClass('x-date-mp-sel');
26403             this.mpSelMonth = pn.dom.xmonth;
26404         }
26405         else if(pn = el.up('td.x-date-mp-year', 2)){
26406             this.mpYears.removeClass('x-date-mp-sel');
26407             pn.addClass('x-date-mp-sel');
26408             this.mpSelYear = pn.dom.xyear;
26409         }
26410         else if(el.is('a.x-date-mp-prev')){
26411             this.updateMPYear(this.mpyear-10);
26412         }
26413         else if(el.is('a.x-date-mp-next')){
26414             this.updateMPYear(this.mpyear+10);
26415         }
26416     },
26417
26418     onMonthDblClick : function(e, t){
26419         e.stopEvent();
26420         var el = new Roo.Element(t), pn;
26421         if(pn = el.up('td.x-date-mp-month', 2)){
26422             this.update(new Date(this.mpSelYear, pn.dom.xmonth, (this.activeDate || this.value).getDate()));
26423             this.hideMonthPicker();
26424         }
26425         else if(pn = el.up('td.x-date-mp-year', 2)){
26426             this.update(new Date(pn.dom.xyear, this.mpSelMonth, (this.activeDate || this.value).getDate()));
26427             this.hideMonthPicker();
26428         }
26429     },
26430
26431     hideMonthPicker : function(disableAnim){
26432         if(this.monthPicker){
26433             if(disableAnim === true){
26434                 this.monthPicker.hide();
26435             }else{
26436                 this.monthPicker.slideOut('t', {duration:.2});
26437             }
26438         }
26439     },
26440
26441     // private
26442     showPrevMonth : function(e){
26443         this.update(this.activeDate.add("mo", -1));
26444     },
26445
26446     // private
26447     showNextMonth : function(e){
26448         this.update(this.activeDate.add("mo", 1));
26449     },
26450
26451     // private
26452     showPrevYear : function(){
26453         this.update(this.activeDate.add("y", -1));
26454     },
26455
26456     // private
26457     showNextYear : function(){
26458         this.update(this.activeDate.add("y", 1));
26459     },
26460
26461     // private
26462     handleMouseWheel : function(e){
26463         var delta = e.getWheelDelta();
26464         if(delta > 0){
26465             this.showPrevMonth();
26466             e.stopEvent();
26467         } else if(delta < 0){
26468             this.showNextMonth();
26469             e.stopEvent();
26470         }
26471     },
26472
26473     // private
26474     handleDateClick : function(e, t){
26475         e.stopEvent();
26476         if(t.dateValue && !Roo.fly(t.parentNode).hasClass("x-date-disabled")){
26477             this.setValue(new Date(t.dateValue));
26478             this.fireEvent("select", this, this.value);
26479         }
26480     },
26481
26482     // private
26483     selectToday : function(){
26484         this.setValue(new Date().clearTime());
26485         this.fireEvent("select", this, this.value);
26486     },
26487
26488     // private
26489     update : function(date)
26490     {
26491         var vd = this.activeDate;
26492         this.activeDate = date;
26493         if(vd && this.el){
26494             var t = date.getTime();
26495             if(vd.getMonth() == date.getMonth() && vd.getFullYear() == date.getFullYear()){
26496                 this.cells.removeClass("x-date-selected");
26497                 this.cells.each(function(c){
26498                    if(c.dom.firstChild.dateValue == t){
26499                        c.addClass("x-date-selected");
26500                        setTimeout(function(){
26501                             try{c.dom.firstChild.focus();}catch(e){}
26502                        }, 50);
26503                        return false;
26504                    }
26505                 });
26506                 return;
26507             }
26508         }
26509         
26510         var days = date.getDaysInMonth();
26511         var firstOfMonth = date.getFirstDateOfMonth();
26512         var startingPos = firstOfMonth.getDay()-this.startDay;
26513
26514         if(startingPos <= this.startDay){
26515             startingPos += 7;
26516         }
26517
26518         var pm = date.add("mo", -1);
26519         var prevStart = pm.getDaysInMonth()-startingPos;
26520
26521         var cells = this.cells.elements;
26522         var textEls = this.textNodes;
26523         days += startingPos;
26524
26525         // convert everything to numbers so it's fast
26526         var day = 86400000;
26527         var d = (new Date(pm.getFullYear(), pm.getMonth(), prevStart)).clearTime();
26528         var today = new Date().clearTime().getTime();
26529         var sel = date.clearTime().getTime();
26530         var min = this.minDate ? this.minDate.clearTime() : Number.NEGATIVE_INFINITY;
26531         var max = this.maxDate ? this.maxDate.clearTime() : Number.POSITIVE_INFINITY;
26532         var ddMatch = this.disabledDatesRE;
26533         var ddText = this.disabledDatesText;
26534         var ddays = this.disabledDays ? this.disabledDays.join("") : false;
26535         var ddaysText = this.disabledDaysText;
26536         var format = this.format;
26537
26538         var setCellClass = function(cal, cell){
26539             cell.title = "";
26540             var t = d.getTime();
26541             cell.firstChild.dateValue = t;
26542             if(t == today){
26543                 cell.className += " x-date-today";
26544                 cell.title = cal.todayText;
26545             }
26546             if(t == sel){
26547                 cell.className += " x-date-selected";
26548                 setTimeout(function(){
26549                     try{cell.firstChild.focus();}catch(e){}
26550                 }, 50);
26551             }
26552             // disabling
26553             if(t < min) {
26554                 cell.className = " x-date-disabled";
26555                 cell.title = cal.minText;
26556                 return;
26557             }
26558             if(t > max) {
26559                 cell.className = " x-date-disabled";
26560                 cell.title = cal.maxText;
26561                 return;
26562             }
26563             if(ddays){
26564                 if(ddays.indexOf(d.getDay()) != -1){
26565                     cell.title = ddaysText;
26566                     cell.className = " x-date-disabled";
26567                 }
26568             }
26569             if(ddMatch && format){
26570                 var fvalue = d.dateFormat(format);
26571                 if(ddMatch.test(fvalue)){
26572                     cell.title = ddText.replace("%0", fvalue);
26573                     cell.className = " x-date-disabled";
26574                 }
26575             }
26576         };
26577
26578         var i = 0;
26579         for(; i < startingPos; i++) {
26580             textEls[i].innerHTML = (++prevStart);
26581             d.setDate(d.getDate()+1);
26582             cells[i].className = "x-date-prevday";
26583             setCellClass(this, cells[i]);
26584         }
26585         for(; i < days; i++){
26586             intDay = i - startingPos + 1;
26587             textEls[i].innerHTML = (intDay);
26588             d.setDate(d.getDate()+1);
26589             cells[i].className = "x-date-active";
26590             setCellClass(this, cells[i]);
26591         }
26592         var extraDays = 0;
26593         for(; i < 42; i++) {
26594              textEls[i].innerHTML = (++extraDays);
26595              d.setDate(d.getDate()+1);
26596              cells[i].className = "x-date-nextday";
26597              setCellClass(this, cells[i]);
26598         }
26599
26600         this.mbtn.setText(this.monthNames[date.getMonth()] + " " + date.getFullYear());
26601         this.fireEvent('monthchange', this, date);
26602         
26603         if(!this.internalRender){
26604             var main = this.el.dom.firstChild;
26605             var w = main.offsetWidth;
26606             this.el.setWidth(w + this.el.getBorderWidth("lr"));
26607             Roo.fly(main).setWidth(w);
26608             this.internalRender = true;
26609             // opera does not respect the auto grow header center column
26610             // then, after it gets a width opera refuses to recalculate
26611             // without a second pass
26612             if(Roo.isOpera && !this.secondPass){
26613                 main.rows[0].cells[1].style.width = (w - (main.rows[0].cells[0].offsetWidth+main.rows[0].cells[2].offsetWidth)) + "px";
26614                 this.secondPass = true;
26615                 this.update.defer(10, this, [date]);
26616             }
26617         }
26618         
26619         
26620     }
26621 });        /*
26622  * Based on:
26623  * Ext JS Library 1.1.1
26624  * Copyright(c) 2006-2007, Ext JS, LLC.
26625  *
26626  * Originally Released Under LGPL - original licence link has changed is not relivant.
26627  *
26628  * Fork - LGPL
26629  * <script type="text/javascript">
26630  */
26631 /**
26632  * @class Roo.TabPanel
26633  * @extends Roo.util.Observable
26634  * A lightweight tab container.
26635  * <br><br>
26636  * Usage:
26637  * <pre><code>
26638 // basic tabs 1, built from existing content
26639 var tabs = new Roo.TabPanel("tabs1");
26640 tabs.addTab("script", "View Script");
26641 tabs.addTab("markup", "View Markup");
26642 tabs.activate("script");
26643
26644 // more advanced tabs, built from javascript
26645 var jtabs = new Roo.TabPanel("jtabs");
26646 jtabs.addTab("jtabs-1", "Normal Tab", "My content was added during construction.");
26647
26648 // set up the UpdateManager
26649 var tab2 = jtabs.addTab("jtabs-2", "Ajax Tab 1");
26650 var updater = tab2.getUpdateManager();
26651 updater.setDefaultUrl("ajax1.htm");
26652 tab2.on('activate', updater.refresh, updater, true);
26653
26654 // Use setUrl for Ajax loading
26655 var tab3 = jtabs.addTab("jtabs-3", "Ajax Tab 2");
26656 tab3.setUrl("ajax2.htm", null, true);
26657
26658 // Disabled tab
26659 var tab4 = jtabs.addTab("tabs1-5", "Disabled Tab", "Can't see me cause I'm disabled");
26660 tab4.disable();
26661
26662 jtabs.activate("jtabs-1");
26663  * </code></pre>
26664  * @constructor
26665  * Create a new TabPanel.
26666  * @param {String/HTMLElement/Roo.Element} container The id, DOM element or Roo.Element container where this TabPanel is to be rendered.
26667  * @param {Object/Boolean} config Config object to set any properties for this TabPanel, or true to render the tabs on the bottom.
26668  */
26669 Roo.TabPanel = function(container, config){
26670     /**
26671     * The container element for this TabPanel.
26672     * @type Roo.Element
26673     */
26674     this.el = Roo.get(container, true);
26675     if(config){
26676         if(typeof config == "boolean"){
26677             this.tabPosition = config ? "bottom" : "top";
26678         }else{
26679             Roo.apply(this, config);
26680         }
26681     }
26682     if(this.tabPosition == "bottom"){
26683         this.bodyEl = Roo.get(this.createBody(this.el.dom));
26684         this.el.addClass("x-tabs-bottom");
26685     }
26686     this.stripWrap = Roo.get(this.createStrip(this.el.dom), true);
26687     this.stripEl = Roo.get(this.createStripList(this.stripWrap.dom), true);
26688     this.stripBody = Roo.get(this.stripWrap.dom.firstChild.firstChild, true);
26689     if(Roo.isIE){
26690         Roo.fly(this.stripWrap.dom.firstChild).setStyle("overflow-x", "hidden");
26691     }
26692     if(this.tabPosition != "bottom"){
26693         /** The body element that contains {@link Roo.TabPanelItem} bodies. +
26694          * @type Roo.Element
26695          */
26696         this.bodyEl = Roo.get(this.createBody(this.el.dom));
26697         this.el.addClass("x-tabs-top");
26698     }
26699     this.items = [];
26700
26701     this.bodyEl.setStyle("position", "relative");
26702
26703     this.active = null;
26704     this.activateDelegate = this.activate.createDelegate(this);
26705
26706     this.addEvents({
26707         /**
26708          * @event tabchange
26709          * Fires when the active tab changes
26710          * @param {Roo.TabPanel} this
26711          * @param {Roo.TabPanelItem} activePanel The new active tab
26712          */
26713         "tabchange": true,
26714         /**
26715          * @event beforetabchange
26716          * Fires before the active tab changes, set cancel to true on the "e" parameter to cancel the change
26717          * @param {Roo.TabPanel} this
26718          * @param {Object} e Set cancel to true on this object to cancel the tab change
26719          * @param {Roo.TabPanelItem} tab The tab being changed to
26720          */
26721         "beforetabchange" : true
26722     });
26723
26724     Roo.EventManager.onWindowResize(this.onResize, this);
26725     this.cpad = this.el.getPadding("lr");
26726     this.hiddenCount = 0;
26727
26728
26729     // toolbar on the tabbar support...
26730     if (this.toolbar) {
26731         var tcfg = this.toolbar;
26732         tcfg.container = this.stripEl.child('td.x-tab-strip-toolbar');  
26733         this.toolbar = new Roo.Toolbar(tcfg);
26734         if (Roo.isSafari) {
26735             var tbl = tcfg.container.child('table', true);
26736             tbl.setAttribute('width', '100%');
26737         }
26738         
26739     }
26740    
26741
26742
26743     Roo.TabPanel.superclass.constructor.call(this);
26744 };
26745
26746 Roo.extend(Roo.TabPanel, Roo.util.Observable, {
26747     /*
26748      *@cfg {String} tabPosition "top" or "bottom" (defaults to "top")
26749      */
26750     tabPosition : "top",
26751     /*
26752      *@cfg {Number} currentTabWidth The width of the current tab (defaults to 0)
26753      */
26754     currentTabWidth : 0,
26755     /*
26756      *@cfg {Number} minTabWidth The minimum width of a tab (defaults to 40) (ignored if {@link #resizeTabs} is not true)
26757      */
26758     minTabWidth : 40,
26759     /*
26760      *@cfg {Number} maxTabWidth The maximum width of a tab (defaults to 250) (ignored if {@link #resizeTabs} is not true)
26761      */
26762     maxTabWidth : 250,
26763     /*
26764      *@cfg {Number} preferredTabWidth The preferred (default) width of a tab (defaults to 175) (ignored if {@link #resizeTabs} is not true)
26765      */
26766     preferredTabWidth : 175,
26767     /*
26768      *@cfg {Boolean} resizeTabs True to enable dynamic tab resizing (defaults to false)
26769      */
26770     resizeTabs : false,
26771     /*
26772      *@cfg {Boolean} monitorResize Set this to true to turn on window resize monitoring (ignored if {@link #resizeTabs} is not true) (defaults to true)
26773      */
26774     monitorResize : true,
26775     /*
26776      *@cfg {Object} toolbar xtype description of toolbar to show at the right of the tab bar. 
26777      */
26778     toolbar : false,
26779
26780     /**
26781      * Creates a new {@link Roo.TabPanelItem} by looking for an existing element with the provided id -- if it's not found it creates one.
26782      * @param {String} id The id of the div to use <b>or create</b>
26783      * @param {String} text The text for the tab
26784      * @param {String} content (optional) Content to put in the TabPanelItem body
26785      * @param {Boolean} closable (optional) True to create a close icon on the tab
26786      * @return {Roo.TabPanelItem} The created TabPanelItem
26787      */
26788     addTab : function(id, text, content, closable){
26789         var item = new Roo.TabPanelItem(this, id, text, closable);
26790         this.addTabItem(item);
26791         if(content){
26792             item.setContent(content);
26793         }
26794         return item;
26795     },
26796
26797     /**
26798      * Returns the {@link Roo.TabPanelItem} with the specified id/index
26799      * @param {String/Number} id The id or index of the TabPanelItem to fetch.
26800      * @return {Roo.TabPanelItem}
26801      */
26802     getTab : function(id){
26803         return this.items[id];
26804     },
26805
26806     /**
26807      * Hides the {@link Roo.TabPanelItem} with the specified id/index
26808      * @param {String/Number} id The id or index of the TabPanelItem to hide.
26809      */
26810     hideTab : function(id){
26811         var t = this.items[id];
26812         if(!t.isHidden()){
26813            t.setHidden(true);
26814            this.hiddenCount++;
26815            this.autoSizeTabs();
26816         }
26817     },
26818
26819     /**
26820      * "Unhides" the {@link Roo.TabPanelItem} with the specified id/index.
26821      * @param {String/Number} id The id or index of the TabPanelItem to unhide.
26822      */
26823     unhideTab : function(id){
26824         var t = this.items[id];
26825         if(t.isHidden()){
26826            t.setHidden(false);
26827            this.hiddenCount--;
26828            this.autoSizeTabs();
26829         }
26830     },
26831
26832     /**
26833      * Adds an existing {@link Roo.TabPanelItem}.
26834      * @param {Roo.TabPanelItem} item The TabPanelItem to add
26835      */
26836     addTabItem : function(item){
26837         this.items[item.id] = item;
26838         this.items.push(item);
26839         if(this.resizeTabs){
26840            item.setWidth(this.currentTabWidth || this.preferredTabWidth);
26841            this.autoSizeTabs();
26842         }else{
26843             item.autoSize();
26844         }
26845     },
26846
26847     /**
26848      * Removes a {@link Roo.TabPanelItem}.
26849      * @param {String/Number} id The id or index of the TabPanelItem to remove.
26850      */
26851     removeTab : function(id){
26852         var items = this.items;
26853         var tab = items[id];
26854         if(!tab) { return; }
26855         var index = items.indexOf(tab);
26856         if(this.active == tab && items.length > 1){
26857             var newTab = this.getNextAvailable(index);
26858             if(newTab) {
26859                 newTab.activate();
26860             }
26861         }
26862         this.stripEl.dom.removeChild(tab.pnode.dom);
26863         if(tab.bodyEl.dom.parentNode == this.bodyEl.dom){ // if it was moved already prevent error
26864             this.bodyEl.dom.removeChild(tab.bodyEl.dom);
26865         }
26866         items.splice(index, 1);
26867         delete this.items[tab.id];
26868         tab.fireEvent("close", tab);
26869         tab.purgeListeners();
26870         this.autoSizeTabs();
26871     },
26872
26873     getNextAvailable : function(start){
26874         var items = this.items;
26875         var index = start;
26876         // look for a next tab that will slide over to
26877         // replace the one being removed
26878         while(index < items.length){
26879             var item = items[++index];
26880             if(item && !item.isHidden()){
26881                 return item;
26882             }
26883         }
26884         // if one isn't found select the previous tab (on the left)
26885         index = start;
26886         while(index >= 0){
26887             var item = items[--index];
26888             if(item && !item.isHidden()){
26889                 return item;
26890             }
26891         }
26892         return null;
26893     },
26894
26895     /**
26896      * Disables a {@link Roo.TabPanelItem}. It cannot be the active tab, if it is this call is ignored.
26897      * @param {String/Number} id The id or index of the TabPanelItem to disable.
26898      */
26899     disableTab : function(id){
26900         var tab = this.items[id];
26901         if(tab && this.active != tab){
26902             tab.disable();
26903         }
26904     },
26905
26906     /**
26907      * Enables a {@link Roo.TabPanelItem} that is disabled.
26908      * @param {String/Number} id The id or index of the TabPanelItem to enable.
26909      */
26910     enableTab : function(id){
26911         var tab = this.items[id];
26912         tab.enable();
26913     },
26914
26915     /**
26916      * Activates a {@link Roo.TabPanelItem}. The currently active one will be deactivated.
26917      * @param {String/Number} id The id or index of the TabPanelItem to activate.
26918      * @return {Roo.TabPanelItem} The TabPanelItem.
26919      */
26920     activate : function(id){
26921         var tab = this.items[id];
26922         if(!tab){
26923             return null;
26924         }
26925         if(tab == this.active || tab.disabled){
26926             return tab;
26927         }
26928         var e = {};
26929         this.fireEvent("beforetabchange", this, e, tab);
26930         if(e.cancel !== true && !tab.disabled){
26931             if(this.active){
26932                 this.active.hide();
26933             }
26934             this.active = this.items[id];
26935             this.active.show();
26936             this.fireEvent("tabchange", this, this.active);
26937         }
26938         return tab;
26939     },
26940
26941     /**
26942      * Gets the active {@link Roo.TabPanelItem}.
26943      * @return {Roo.TabPanelItem} The active TabPanelItem or null if none are active.
26944      */
26945     getActiveTab : function(){
26946         return this.active;
26947     },
26948
26949     /**
26950      * Updates the tab body element to fit the height of the container element
26951      * for overflow scrolling
26952      * @param {Number} targetHeight (optional) Override the starting height from the elements height
26953      */
26954     syncHeight : function(targetHeight){
26955         var height = (targetHeight || this.el.getHeight())-this.el.getBorderWidth("tb")-this.el.getPadding("tb");
26956         var bm = this.bodyEl.getMargins();
26957         var newHeight = height-(this.stripWrap.getHeight()||0)-(bm.top+bm.bottom);
26958         this.bodyEl.setHeight(newHeight);
26959         return newHeight;
26960     },
26961
26962     onResize : function(){
26963         if(this.monitorResize){
26964             this.autoSizeTabs();
26965         }
26966     },
26967
26968     /**
26969      * Disables tab resizing while tabs are being added (if {@link #resizeTabs} is false this does nothing)
26970      */
26971     beginUpdate : function(){
26972         this.updating = true;
26973     },
26974
26975     /**
26976      * Stops an update and resizes the tabs (if {@link #resizeTabs} is false this does nothing)
26977      */
26978     endUpdate : function(){
26979         this.updating = false;
26980         this.autoSizeTabs();
26981     },
26982
26983     /**
26984      * Manual call to resize the tabs (if {@link #resizeTabs} is false this does nothing)
26985      */
26986     autoSizeTabs : function(){
26987         var count = this.items.length;
26988         var vcount = count - this.hiddenCount;
26989         if(!this.resizeTabs || count < 1 || vcount < 1 || this.updating) return;
26990         var w = Math.max(this.el.getWidth() - this.cpad, 10);
26991         var availWidth = Math.floor(w / vcount);
26992         var b = this.stripBody;
26993         if(b.getWidth() > w){
26994             var tabs = this.items;
26995             this.setTabWidth(Math.max(availWidth, this.minTabWidth)-2);
26996             if(availWidth < this.minTabWidth){
26997                 /*if(!this.sleft){    // incomplete scrolling code
26998                     this.createScrollButtons();
26999                 }
27000                 this.showScroll();
27001                 this.stripClip.setWidth(w - (this.sleft.getWidth()+this.sright.getWidth()));*/
27002             }
27003         }else{
27004             if(this.currentTabWidth < this.preferredTabWidth){
27005                 this.setTabWidth(Math.min(availWidth, this.preferredTabWidth)-2);
27006             }
27007         }
27008     },
27009
27010     /**
27011      * Returns the number of tabs in this TabPanel.
27012      * @return {Number}
27013      */
27014      getCount : function(){
27015          return this.items.length;
27016      },
27017
27018     /**
27019      * Resizes all the tabs to the passed width
27020      * @param {Number} The new width
27021      */
27022     setTabWidth : function(width){
27023         this.currentTabWidth = width;
27024         for(var i = 0, len = this.items.length; i < len; i++) {
27025                 if(!this.items[i].isHidden())this.items[i].setWidth(width);
27026         }
27027     },
27028
27029     /**
27030      * Destroys this TabPanel
27031      * @param {Boolean} removeEl (optional) True to remove the element from the DOM as well (defaults to undefined)
27032      */
27033     destroy : function(removeEl){
27034         Roo.EventManager.removeResizeListener(this.onResize, this);
27035         for(var i = 0, len = this.items.length; i < len; i++){
27036             this.items[i].purgeListeners();
27037         }
27038         if(removeEl === true){
27039             this.el.update("");
27040             this.el.remove();
27041         }
27042     }
27043 });
27044
27045 /**
27046  * @class Roo.TabPanelItem
27047  * @extends Roo.util.Observable
27048  * Represents an individual item (tab plus body) in a TabPanel.
27049  * @param {Roo.TabPanel} tabPanel The {@link Roo.TabPanel} this TabPanelItem belongs to
27050  * @param {String} id The id of this TabPanelItem
27051  * @param {String} text The text for the tab of this TabPanelItem
27052  * @param {Boolean} closable True to allow this TabPanelItem to be closable (defaults to false)
27053  */
27054 Roo.TabPanelItem = function(tabPanel, id, text, closable){
27055     /**
27056      * The {@link Roo.TabPanel} this TabPanelItem belongs to
27057      * @type Roo.TabPanel
27058      */
27059     this.tabPanel = tabPanel;
27060     /**
27061      * The id for this TabPanelItem
27062      * @type String
27063      */
27064     this.id = id;
27065     /** @private */
27066     this.disabled = false;
27067     /** @private */
27068     this.text = text;
27069     /** @private */
27070     this.loaded = false;
27071     this.closable = closable;
27072
27073     /**
27074      * The body element for this TabPanelItem.
27075      * @type Roo.Element
27076      */
27077     this.bodyEl = Roo.get(tabPanel.createItemBody(tabPanel.bodyEl.dom, id));
27078     this.bodyEl.setVisibilityMode(Roo.Element.VISIBILITY);
27079     this.bodyEl.setStyle("display", "block");
27080     this.bodyEl.setStyle("zoom", "1");
27081     this.hideAction();
27082
27083     var els = tabPanel.createStripElements(tabPanel.stripEl.dom, text, closable);
27084     /** @private */
27085     this.el = Roo.get(els.el, true);
27086     this.inner = Roo.get(els.inner, true);
27087     this.textEl = Roo.get(this.el.dom.firstChild.firstChild.firstChild, true);
27088     this.pnode = Roo.get(els.el.parentNode, true);
27089     this.el.on("mousedown", this.onTabMouseDown, this);
27090     this.el.on("click", this.onTabClick, this);
27091     /** @private */
27092     if(closable){
27093         var c = Roo.get(els.close, true);
27094         c.dom.title = this.closeText;
27095         c.addClassOnOver("close-over");
27096         c.on("click", this.closeClick, this);
27097      }
27098
27099     this.addEvents({
27100          /**
27101          * @event activate
27102          * Fires when this tab becomes the active tab.
27103          * @param {Roo.TabPanel} tabPanel The parent TabPanel
27104          * @param {Roo.TabPanelItem} this
27105          */
27106         "activate": true,
27107         /**
27108          * @event beforeclose
27109          * Fires before this tab is closed. To cancel the close, set cancel to true on e (e.cancel = true).
27110          * @param {Roo.TabPanelItem} this
27111          * @param {Object} e Set cancel to true on this object to cancel the close.
27112          */
27113         "beforeclose": true,
27114         /**
27115          * @event close
27116          * Fires when this tab is closed.
27117          * @param {Roo.TabPanelItem} this
27118          */
27119          "close": true,
27120         /**
27121          * @event deactivate
27122          * Fires when this tab is no longer the active tab.
27123          * @param {Roo.TabPanel} tabPanel The parent TabPanel
27124          * @param {Roo.TabPanelItem} this
27125          */
27126          "deactivate" : true
27127     });
27128     this.hidden = false;
27129
27130     Roo.TabPanelItem.superclass.constructor.call(this);
27131 };
27132
27133 Roo.extend(Roo.TabPanelItem, Roo.util.Observable, {
27134     purgeListeners : function(){
27135        Roo.util.Observable.prototype.purgeListeners.call(this);
27136        this.el.removeAllListeners();
27137     },
27138     /**
27139      * Shows this TabPanelItem -- this <b>does not</b> deactivate the currently active TabPanelItem.
27140      */
27141     show : function(){
27142         this.pnode.addClass("on");
27143         this.showAction();
27144         if(Roo.isOpera){
27145             this.tabPanel.stripWrap.repaint();
27146         }
27147         this.fireEvent("activate", this.tabPanel, this);
27148     },
27149
27150     /**
27151      * Returns true if this tab is the active tab.
27152      * @return {Boolean}
27153      */
27154     isActive : function(){
27155         return this.tabPanel.getActiveTab() == this;
27156     },
27157
27158     /**
27159      * Hides this TabPanelItem -- if you don't activate another TabPanelItem this could look odd.
27160      */
27161     hide : function(){
27162         this.pnode.removeClass("on");
27163         this.hideAction();
27164         this.fireEvent("deactivate", this.tabPanel, this);
27165     },
27166
27167     hideAction : function(){
27168         this.bodyEl.hide();
27169         this.bodyEl.setStyle("position", "absolute");
27170         this.bodyEl.setLeft("-20000px");
27171         this.bodyEl.setTop("-20000px");
27172     },
27173
27174     showAction : function(){
27175         this.bodyEl.setStyle("position", "relative");
27176         this.bodyEl.setTop("");
27177         this.bodyEl.setLeft("");
27178         this.bodyEl.show();
27179     },
27180
27181     /**
27182      * Set the tooltip for the tab.
27183      * @param {String} tooltip The tab's tooltip
27184      */
27185     setTooltip : function(text){
27186         if(Roo.QuickTips && Roo.QuickTips.isEnabled()){
27187             this.textEl.dom.qtip = text;
27188             this.textEl.dom.removeAttribute('title');
27189         }else{
27190             this.textEl.dom.title = text;
27191         }
27192     },
27193
27194     onTabClick : function(e){
27195         e.preventDefault();
27196         this.tabPanel.activate(this.id);
27197     },
27198
27199     onTabMouseDown : function(e){
27200         e.preventDefault();
27201         this.tabPanel.activate(this.id);
27202     },
27203
27204     getWidth : function(){
27205         return this.inner.getWidth();
27206     },
27207
27208     setWidth : function(width){
27209         var iwidth = width - this.pnode.getPadding("lr");
27210         this.inner.setWidth(iwidth);
27211         this.textEl.setWidth(iwidth-this.inner.getPadding("lr"));
27212         this.pnode.setWidth(width);
27213     },
27214
27215     /**
27216      * Show or hide the tab
27217      * @param {Boolean} hidden True to hide or false to show.
27218      */
27219     setHidden : function(hidden){
27220         this.hidden = hidden;
27221         this.pnode.setStyle("display", hidden ? "none" : "");
27222     },
27223
27224     /**
27225      * Returns true if this tab is "hidden"
27226      * @return {Boolean}
27227      */
27228     isHidden : function(){
27229         return this.hidden;
27230     },
27231
27232     /**
27233      * Returns the text for this tab
27234      * @return {String}
27235      */
27236     getText : function(){
27237         return this.text;
27238     },
27239
27240     autoSize : function(){
27241         //this.el.beginMeasure();
27242         this.textEl.setWidth(1);
27243         /*
27244          *  #2804 [new] Tabs in Roojs
27245          *  increase the width by 2-4 pixels to prevent the ellipssis showing in chrome
27246          */
27247         this.setWidth(this.textEl.dom.scrollWidth+this.pnode.getPadding("lr")+this.inner.getPadding("lr") + 2);
27248         //this.el.endMeasure();
27249     },
27250
27251     /**
27252      * Sets the text for the tab (Note: this also sets the tooltip text)
27253      * @param {String} text The tab's text and tooltip
27254      */
27255     setText : function(text){
27256         this.text = text;
27257         this.textEl.update(text);
27258         this.setTooltip(text);
27259         if(!this.tabPanel.resizeTabs){
27260             this.autoSize();
27261         }
27262     },
27263     /**
27264      * Activates this TabPanelItem -- this <b>does</b> deactivate the currently active TabPanelItem.
27265      */
27266     activate : function(){
27267         this.tabPanel.activate(this.id);
27268     },
27269
27270     /**
27271      * Disables this TabPanelItem -- this does nothing if this is the active TabPanelItem.
27272      */
27273     disable : function(){
27274         if(this.tabPanel.active != this){
27275             this.disabled = true;
27276             this.pnode.addClass("disabled");
27277         }
27278     },
27279
27280     /**
27281      * Enables this TabPanelItem if it was previously disabled.
27282      */
27283     enable : function(){
27284         this.disabled = false;
27285         this.pnode.removeClass("disabled");
27286     },
27287
27288     /**
27289      * Sets the content for this TabPanelItem.
27290      * @param {String} content The content
27291      * @param {Boolean} loadScripts true to look for and load scripts
27292      */
27293     setContent : function(content, loadScripts){
27294         this.bodyEl.update(content, loadScripts);
27295     },
27296
27297     /**
27298      * Gets the {@link Roo.UpdateManager} for the body of this TabPanelItem. Enables you to perform Ajax updates.
27299      * @return {Roo.UpdateManager} The UpdateManager
27300      */
27301     getUpdateManager : function(){
27302         return this.bodyEl.getUpdateManager();
27303     },
27304
27305     /**
27306      * Set a URL to be used to load the content for this TabPanelItem.
27307      * @param {String/Function} url The URL to load the content from, or a function to call to get the URL
27308      * @param {String/Object} params (optional) The string params for the update call or an object of the params. See {@link Roo.UpdateManager#update} for more details. (Defaults to null)
27309      * @param {Boolean} loadOnce (optional) Whether to only load the content once. If this is false it makes the Ajax call every time this TabPanelItem is activated. (Defaults to false)
27310      * @return {Roo.UpdateManager} The UpdateManager
27311      */
27312     setUrl : function(url, params, loadOnce){
27313         if(this.refreshDelegate){
27314             this.un('activate', this.refreshDelegate);
27315         }
27316         this.refreshDelegate = this._handleRefresh.createDelegate(this, [url, params, loadOnce]);
27317         this.on("activate", this.refreshDelegate);
27318         return this.bodyEl.getUpdateManager();
27319     },
27320
27321     /** @private */
27322     _handleRefresh : function(url, params, loadOnce){
27323         if(!loadOnce || !this.loaded){
27324             var updater = this.bodyEl.getUpdateManager();
27325             updater.update(url, params, this._setLoaded.createDelegate(this));
27326         }
27327     },
27328
27329     /**
27330      *   Forces a content refresh from the URL specified in the {@link #setUrl} method.
27331      *   Will fail silently if the setUrl method has not been called.
27332      *   This does not activate the panel, just updates its content.
27333      */
27334     refresh : function(){
27335         if(this.refreshDelegate){
27336            this.loaded = false;
27337            this.refreshDelegate();
27338         }
27339     },
27340
27341     /** @private */
27342     _setLoaded : function(){
27343         this.loaded = true;
27344     },
27345
27346     /** @private */
27347     closeClick : function(e){
27348         var o = {};
27349         e.stopEvent();
27350         this.fireEvent("beforeclose", this, o);
27351         if(o.cancel !== true){
27352             this.tabPanel.removeTab(this.id);
27353         }
27354     },
27355     /**
27356      * The text displayed in the tooltip for the close icon.
27357      * @type String
27358      */
27359     closeText : "Close this tab"
27360 });
27361
27362 /** @private */
27363 Roo.TabPanel.prototype.createStrip = function(container){
27364     var strip = document.createElement("div");
27365     strip.className = "x-tabs-wrap";
27366     container.appendChild(strip);
27367     return strip;
27368 };
27369 /** @private */
27370 Roo.TabPanel.prototype.createStripList = function(strip){
27371     // div wrapper for retard IE
27372     // returns the "tr" element.
27373     strip.innerHTML = '<div class="x-tabs-strip-wrap">'+
27374         '<table class="x-tabs-strip" cellspacing="0" cellpadding="0" border="0"><tbody><tr>'+
27375         '<td class="x-tab-strip-toolbar"></td></tr></tbody></table></div>';
27376     return strip.firstChild.firstChild.firstChild.firstChild;
27377 };
27378 /** @private */
27379 Roo.TabPanel.prototype.createBody = function(container){
27380     var body = document.createElement("div");
27381     Roo.id(body, "tab-body");
27382     Roo.fly(body).addClass("x-tabs-body");
27383     container.appendChild(body);
27384     return body;
27385 };
27386 /** @private */
27387 Roo.TabPanel.prototype.createItemBody = function(bodyEl, id){
27388     var body = Roo.getDom(id);
27389     if(!body){
27390         body = document.createElement("div");
27391         body.id = id;
27392     }
27393     Roo.fly(body).addClass("x-tabs-item-body");
27394     bodyEl.insertBefore(body, bodyEl.firstChild);
27395     return body;
27396 };
27397 /** @private */
27398 Roo.TabPanel.prototype.createStripElements = function(stripEl, text, closable){
27399     var td = document.createElement("td");
27400     stripEl.insertBefore(td, stripEl.childNodes[stripEl.childNodes.length-1]);
27401     //stripEl.appendChild(td);
27402     if(closable){
27403         td.className = "x-tabs-closable";
27404         if(!this.closeTpl){
27405             this.closeTpl = new Roo.Template(
27406                '<a href="#" class="x-tabs-right"><span class="x-tabs-left"><em class="x-tabs-inner">' +
27407                '<span unselectable="on"' + (this.disableTooltips ? '' : ' title="{text}"') +' class="x-tabs-text">{text}</span>' +
27408                '<div unselectable="on" class="close-icon">&#160;</div></em></span></a>'
27409             );
27410         }
27411         var el = this.closeTpl.overwrite(td, {"text": text});
27412         var close = el.getElementsByTagName("div")[0];
27413         var inner = el.getElementsByTagName("em")[0];
27414         return {"el": el, "close": close, "inner": inner};
27415     } else {
27416         if(!this.tabTpl){
27417             this.tabTpl = new Roo.Template(
27418                '<a href="#" class="x-tabs-right"><span class="x-tabs-left"><em class="x-tabs-inner">' +
27419                '<span unselectable="on"' + (this.disableTooltips ? '' : ' title="{text}"') +' class="x-tabs-text">{text}</span></em></span></a>'
27420             );
27421         }
27422         var el = this.tabTpl.overwrite(td, {"text": text});
27423         var inner = el.getElementsByTagName("em")[0];
27424         return {"el": el, "inner": inner};
27425     }
27426 };/*
27427  * Based on:
27428  * Ext JS Library 1.1.1
27429  * Copyright(c) 2006-2007, Ext JS, LLC.
27430  *
27431  * Originally Released Under LGPL - original licence link has changed is not relivant.
27432  *
27433  * Fork - LGPL
27434  * <script type="text/javascript">
27435  */
27436
27437 /**
27438  * @class Roo.Button
27439  * @extends Roo.util.Observable
27440  * Simple Button class
27441  * @cfg {String} text The button text
27442  * @cfg {String} icon The path to an image to display in the button (the image will be set as the background-image
27443  * CSS property of the button by default, so if you want a mixed icon/text button, set cls:"x-btn-text-icon")
27444  * @cfg {Function} handler A function called when the button is clicked (can be used instead of click event)
27445  * @cfg {Object} scope The scope of the handler
27446  * @cfg {Number} minWidth The minimum width for this button (used to give a set of buttons a common width)
27447  * @cfg {String/Object} tooltip The tooltip for the button - can be a string or QuickTips config object
27448  * @cfg {Boolean} hidden True to start hidden (defaults to false)
27449  * @cfg {Boolean} disabled True to start disabled (defaults to false)
27450  * @cfg {Boolean} pressed True to start pressed (only if enableToggle = true)
27451  * @cfg {String} toggleGroup The group this toggle button is a member of (only 1 per group can be pressed, only
27452    applies if enableToggle = true)
27453  * @cfg {String/HTMLElement/Element} renderTo The element to append the button to
27454  * @cfg {Boolean/Object} repeat True to repeat fire the click event while the mouse is down. This can also be
27455   an {@link Roo.util.ClickRepeater} config object (defaults to false).
27456  * @constructor
27457  * Create a new button
27458  * @param {Object} config The config object
27459  */
27460 Roo.Button = function(renderTo, config)
27461 {
27462     if (!config) {
27463         config = renderTo;
27464         renderTo = config.renderTo || false;
27465     }
27466     
27467     Roo.apply(this, config);
27468     this.addEvents({
27469         /**
27470              * @event click
27471              * Fires when this button is clicked
27472              * @param {Button} this
27473              * @param {EventObject} e The click event
27474              */
27475             "click" : true,
27476         /**
27477              * @event toggle
27478              * Fires when the "pressed" state of this button changes (only if enableToggle = true)
27479              * @param {Button} this
27480              * @param {Boolean} pressed
27481              */
27482             "toggle" : true,
27483         /**
27484              * @event mouseover
27485              * Fires when the mouse hovers over the button
27486              * @param {Button} this
27487              * @param {Event} e The event object
27488              */
27489         'mouseover' : true,
27490         /**
27491              * @event mouseout
27492              * Fires when the mouse exits the button
27493              * @param {Button} this
27494              * @param {Event} e The event object
27495              */
27496         'mouseout': true,
27497          /**
27498              * @event render
27499              * Fires when the button is rendered
27500              * @param {Button} this
27501              */
27502         'render': true
27503     });
27504     if(this.menu){
27505         this.menu = Roo.menu.MenuMgr.get(this.menu);
27506     }
27507     // register listeners first!!  - so render can be captured..
27508     Roo.util.Observable.call(this);
27509     if(renderTo){
27510         this.render(renderTo);
27511     }
27512     
27513   
27514 };
27515
27516 Roo.extend(Roo.Button, Roo.util.Observable, {
27517     /**
27518      * 
27519      */
27520     
27521     /**
27522      * Read-only. True if this button is hidden
27523      * @type Boolean
27524      */
27525     hidden : false,
27526     /**
27527      * Read-only. True if this button is disabled
27528      * @type Boolean
27529      */
27530     disabled : false,
27531     /**
27532      * Read-only. True if this button is pressed (only if enableToggle = true)
27533      * @type Boolean
27534      */
27535     pressed : false,
27536
27537     /**
27538      * @cfg {Number} tabIndex 
27539      * The DOM tabIndex for this button (defaults to undefined)
27540      */
27541     tabIndex : undefined,
27542
27543     /**
27544      * @cfg {Boolean} enableToggle
27545      * True to enable pressed/not pressed toggling (defaults to false)
27546      */
27547     enableToggle: false,
27548     /**
27549      * @cfg {Mixed} menu
27550      * Standard menu attribute consisting of a reference to a menu object, a menu id or a menu config blob (defaults to undefined).
27551      */
27552     menu : undefined,
27553     /**
27554      * @cfg {String} menuAlign
27555      * The position to align the menu to (see {@link Roo.Element#alignTo} for more details, defaults to 'tl-bl?').
27556      */
27557     menuAlign : "tl-bl?",
27558
27559     /**
27560      * @cfg {String} iconCls
27561      * A css class which sets a background image to be used as the icon for this button (defaults to undefined).
27562      */
27563     iconCls : undefined,
27564     /**
27565      * @cfg {String} type
27566      * The button's type, corresponding to the DOM input element type attribute.  Either "submit," "reset" or "button" (default).
27567      */
27568     type : 'button',
27569
27570     // private
27571     menuClassTarget: 'tr',
27572
27573     /**
27574      * @cfg {String} clickEvent
27575      * The type of event to map to the button's event handler (defaults to 'click')
27576      */
27577     clickEvent : 'click',
27578
27579     /**
27580      * @cfg {Boolean} handleMouseEvents
27581      * False to disable visual cues on mouseover, mouseout and mousedown (defaults to true)
27582      */
27583     handleMouseEvents : true,
27584
27585     /**
27586      * @cfg {String} tooltipType
27587      * The type of tooltip to use. Either "qtip" (default) for QuickTips or "title" for title attribute.
27588      */
27589     tooltipType : 'qtip',
27590
27591     /**
27592      * @cfg {String} cls
27593      * A CSS class to apply to the button's main element.
27594      */
27595     
27596     /**
27597      * @cfg {Roo.Template} template (Optional)
27598      * An {@link Roo.Template} with which to create the Button's main element. This Template must
27599      * contain numeric substitution parameter 0 if it is to display the tRoo property. Changing the template could
27600      * require code modifications if required elements (e.g. a button) aren't present.
27601      */
27602
27603     // private
27604     render : function(renderTo){
27605         var btn;
27606         if(this.hideParent){
27607             this.parentEl = Roo.get(renderTo);
27608         }
27609         if(!this.dhconfig){
27610             if(!this.template){
27611                 if(!Roo.Button.buttonTemplate){
27612                     // hideous table template
27613                     Roo.Button.buttonTemplate = new Roo.Template(
27614                         '<table border="0" cellpadding="0" cellspacing="0" class="x-btn-wrap"><tbody><tr>',
27615                         '<td class="x-btn-left"><i>&#160;</i></td><td class="x-btn-center"><em unselectable="on"><button class="x-btn-text" type="{1}">{0}</button></em></td><td class="x-btn-right"><i>&#160;</i></td>',
27616                         "</tr></tbody></table>");
27617                 }
27618                 this.template = Roo.Button.buttonTemplate;
27619             }
27620             btn = this.template.append(renderTo, [this.text || '&#160;', this.type], true);
27621             var btnEl = btn.child("button:first");
27622             btnEl.on('focus', this.onFocus, this);
27623             btnEl.on('blur', this.onBlur, this);
27624             if(this.cls){
27625                 btn.addClass(this.cls);
27626             }
27627             if(this.icon){
27628                 btnEl.setStyle('background-image', 'url(' +this.icon +')');
27629             }
27630             if(this.iconCls){
27631                 btnEl.addClass(this.iconCls);
27632                 if(!this.cls){
27633                     btn.addClass(this.text ? 'x-btn-text-icon' : 'x-btn-icon');
27634                 }
27635             }
27636             if(this.tabIndex !== undefined){
27637                 btnEl.dom.tabIndex = this.tabIndex;
27638             }
27639             if(this.tooltip){
27640                 if(typeof this.tooltip == 'object'){
27641                     Roo.QuickTips.tips(Roo.apply({
27642                           target: btnEl.id
27643                     }, this.tooltip));
27644                 } else {
27645                     btnEl.dom[this.tooltipType] = this.tooltip;
27646                 }
27647             }
27648         }else{
27649             btn = Roo.DomHelper.append(Roo.get(renderTo).dom, this.dhconfig, true);
27650         }
27651         this.el = btn;
27652         if(this.id){
27653             this.el.dom.id = this.el.id = this.id;
27654         }
27655         if(this.menu){
27656             this.el.child(this.menuClassTarget).addClass("x-btn-with-menu");
27657             this.menu.on("show", this.onMenuShow, this);
27658             this.menu.on("hide", this.onMenuHide, this);
27659         }
27660         btn.addClass("x-btn");
27661         if(Roo.isIE && !Roo.isIE7){
27662             this.autoWidth.defer(1, this);
27663         }else{
27664             this.autoWidth();
27665         }
27666         if(this.handleMouseEvents){
27667             btn.on("mouseover", this.onMouseOver, this);
27668             btn.on("mouseout", this.onMouseOut, this);
27669             btn.on("mousedown", this.onMouseDown, this);
27670         }
27671         btn.on(this.clickEvent, this.onClick, this);
27672         //btn.on("mouseup", this.onMouseUp, this);
27673         if(this.hidden){
27674             this.hide();
27675         }
27676         if(this.disabled){
27677             this.disable();
27678         }
27679         Roo.ButtonToggleMgr.register(this);
27680         if(this.pressed){
27681             this.el.addClass("x-btn-pressed");
27682         }
27683         if(this.repeat){
27684             var repeater = new Roo.util.ClickRepeater(btn,
27685                 typeof this.repeat == "object" ? this.repeat : {}
27686             );
27687             repeater.on("click", this.onClick,  this);
27688         }
27689         
27690         this.fireEvent('render', this);
27691         
27692     },
27693     /**
27694      * Returns the button's underlying element
27695      * @return {Roo.Element} The element
27696      */
27697     getEl : function(){
27698         return this.el;  
27699     },
27700     
27701     /**
27702      * Destroys this Button and removes any listeners.
27703      */
27704     destroy : function(){
27705         Roo.ButtonToggleMgr.unregister(this);
27706         this.el.removeAllListeners();
27707         this.purgeListeners();
27708         this.el.remove();
27709     },
27710
27711     // private
27712     autoWidth : function(){
27713         if(this.el){
27714             this.el.setWidth("auto");
27715             if(Roo.isIE7 && Roo.isStrict){
27716                 var ib = this.el.child('button');
27717                 if(ib && ib.getWidth() > 20){
27718                     ib.clip();
27719                     ib.setWidth(Roo.util.TextMetrics.measure(ib, this.text).width+ib.getFrameWidth('lr'));
27720                 }
27721             }
27722             if(this.minWidth){
27723                 if(this.hidden){
27724                     this.el.beginMeasure();
27725                 }
27726                 if(this.el.getWidth() < this.minWidth){
27727                     this.el.setWidth(this.minWidth);
27728                 }
27729                 if(this.hidden){
27730                     this.el.endMeasure();
27731                 }
27732             }
27733         }
27734     },
27735
27736     /**
27737      * Assigns this button's click handler
27738      * @param {Function} handler The function to call when the button is clicked
27739      * @param {Object} scope (optional) Scope for the function passed in
27740      */
27741     setHandler : function(handler, scope){
27742         this.handler = handler;
27743         this.scope = scope;  
27744     },
27745     
27746     /**
27747      * Sets this button's text
27748      * @param {String} text The button text
27749      */
27750     setText : function(text){
27751         this.text = text;
27752         if(this.el){
27753             this.el.child("td.x-btn-center button.x-btn-text").update(text);
27754         }
27755         this.autoWidth();
27756     },
27757     
27758     /**
27759      * Gets the text for this button
27760      * @return {String} The button text
27761      */
27762     getText : function(){
27763         return this.text;  
27764     },
27765     
27766     /**
27767      * Show this button
27768      */
27769     show: function(){
27770         this.hidden = false;
27771         if(this.el){
27772             this[this.hideParent? 'parentEl' : 'el'].setStyle("display", "");
27773         }
27774     },
27775     
27776     /**
27777      * Hide this button
27778      */
27779     hide: function(){
27780         this.hidden = true;
27781         if(this.el){
27782             this[this.hideParent? 'parentEl' : 'el'].setStyle("display", "none");
27783         }
27784     },
27785     
27786     /**
27787      * Convenience function for boolean show/hide
27788      * @param {Boolean} visible True to show, false to hide
27789      */
27790     setVisible: function(visible){
27791         if(visible) {
27792             this.show();
27793         }else{
27794             this.hide();
27795         }
27796     },
27797     
27798     /**
27799      * If a state it passed, it becomes the pressed state otherwise the current state is toggled.
27800      * @param {Boolean} state (optional) Force a particular state
27801      */
27802     toggle : function(state){
27803         state = state === undefined ? !this.pressed : state;
27804         if(state != this.pressed){
27805             if(state){
27806                 this.el.addClass("x-btn-pressed");
27807                 this.pressed = true;
27808                 this.fireEvent("toggle", this, true);
27809             }else{
27810                 this.el.removeClass("x-btn-pressed");
27811                 this.pressed = false;
27812                 this.fireEvent("toggle", this, false);
27813             }
27814             if(this.toggleHandler){
27815                 this.toggleHandler.call(this.scope || this, this, state);
27816             }
27817         }
27818     },
27819     
27820     /**
27821      * Focus the button
27822      */
27823     focus : function(){
27824         this.el.child('button:first').focus();
27825     },
27826     
27827     /**
27828      * Disable this button
27829      */
27830     disable : function(){
27831         if(this.el){
27832             this.el.addClass("x-btn-disabled");
27833         }
27834         this.disabled = true;
27835     },
27836     
27837     /**
27838      * Enable this button
27839      */
27840     enable : function(){
27841         if(this.el){
27842             this.el.removeClass("x-btn-disabled");
27843         }
27844         this.disabled = false;
27845     },
27846
27847     /**
27848      * Convenience function for boolean enable/disable
27849      * @param {Boolean} enabled True to enable, false to disable
27850      */
27851     setDisabled : function(v){
27852         this[v !== true ? "enable" : "disable"]();
27853     },
27854
27855     // private
27856     onClick : function(e){
27857         if(e){
27858             e.preventDefault();
27859         }
27860         if(e.button != 0){
27861             return;
27862         }
27863         if(!this.disabled){
27864             if(this.enableToggle){
27865                 this.toggle();
27866             }
27867             if(this.menu && !this.menu.isVisible()){
27868                 this.menu.show(this.el, this.menuAlign);
27869             }
27870             this.fireEvent("click", this, e);
27871             if(this.handler){
27872                 this.el.removeClass("x-btn-over");
27873                 this.handler.call(this.scope || this, this, e);
27874             }
27875         }
27876     },
27877     // private
27878     onMouseOver : function(e){
27879         if(!this.disabled){
27880             this.el.addClass("x-btn-over");
27881             this.fireEvent('mouseover', this, e);
27882         }
27883     },
27884     // private
27885     onMouseOut : function(e){
27886         if(!e.within(this.el,  true)){
27887             this.el.removeClass("x-btn-over");
27888             this.fireEvent('mouseout', this, e);
27889         }
27890     },
27891     // private
27892     onFocus : function(e){
27893         if(!this.disabled){
27894             this.el.addClass("x-btn-focus");
27895         }
27896     },
27897     // private
27898     onBlur : function(e){
27899         this.el.removeClass("x-btn-focus");
27900     },
27901     // private
27902     onMouseDown : function(e){
27903         if(!this.disabled && e.button == 0){
27904             this.el.addClass("x-btn-click");
27905             Roo.get(document).on('mouseup', this.onMouseUp, this);
27906         }
27907     },
27908     // private
27909     onMouseUp : function(e){
27910         if(e.button == 0){
27911             this.el.removeClass("x-btn-click");
27912             Roo.get(document).un('mouseup', this.onMouseUp, this);
27913         }
27914     },
27915     // private
27916     onMenuShow : function(e){
27917         this.el.addClass("x-btn-menu-active");
27918     },
27919     // private
27920     onMenuHide : function(e){
27921         this.el.removeClass("x-btn-menu-active");
27922     }   
27923 });
27924
27925 // Private utility class used by Button
27926 Roo.ButtonToggleMgr = function(){
27927    var groups = {};
27928    
27929    function toggleGroup(btn, state){
27930        if(state){
27931            var g = groups[btn.toggleGroup];
27932            for(var i = 0, l = g.length; i < l; i++){
27933                if(g[i] != btn){
27934                    g[i].toggle(false);
27935                }
27936            }
27937        }
27938    }
27939    
27940    return {
27941        register : function(btn){
27942            if(!btn.toggleGroup){
27943                return;
27944            }
27945            var g = groups[btn.toggleGroup];
27946            if(!g){
27947                g = groups[btn.toggleGroup] = [];
27948            }
27949            g.push(btn);
27950            btn.on("toggle", toggleGroup);
27951        },
27952        
27953        unregister : function(btn){
27954            if(!btn.toggleGroup){
27955                return;
27956            }
27957            var g = groups[btn.toggleGroup];
27958            if(g){
27959                g.remove(btn);
27960                btn.un("toggle", toggleGroup);
27961            }
27962        }
27963    };
27964 }();/*
27965  * Based on:
27966  * Ext JS Library 1.1.1
27967  * Copyright(c) 2006-2007, Ext JS, LLC.
27968  *
27969  * Originally Released Under LGPL - original licence link has changed is not relivant.
27970  *
27971  * Fork - LGPL
27972  * <script type="text/javascript">
27973  */
27974  
27975 /**
27976  * @class Roo.SplitButton
27977  * @extends Roo.Button
27978  * A split button that provides a built-in dropdown arrow that can fire an event separately from the default
27979  * click event of the button.  Typically this would be used to display a dropdown menu that provides additional
27980  * options to the primary button action, but any custom handler can provide the arrowclick implementation.
27981  * @cfg {Function} arrowHandler A function called when the arrow button is clicked (can be used instead of click event)
27982  * @cfg {String} arrowTooltip The title attribute of the arrow
27983  * @constructor
27984  * Create a new menu button
27985  * @param {String/HTMLElement/Element} renderTo The element to append the button to
27986  * @param {Object} config The config object
27987  */
27988 Roo.SplitButton = function(renderTo, config){
27989     Roo.SplitButton.superclass.constructor.call(this, renderTo, config);
27990     /**
27991      * @event arrowclick
27992      * Fires when this button's arrow is clicked
27993      * @param {SplitButton} this
27994      * @param {EventObject} e The click event
27995      */
27996     this.addEvents({"arrowclick":true});
27997 };
27998
27999 Roo.extend(Roo.SplitButton, Roo.Button, {
28000     render : function(renderTo){
28001         // this is one sweet looking template!
28002         var tpl = new Roo.Template(
28003             '<table cellspacing="0" class="x-btn-menu-wrap x-btn"><tr><td>',
28004             '<table cellspacing="0" class="x-btn-wrap x-btn-menu-text-wrap"><tbody>',
28005             '<tr><td class="x-btn-left"><i>&#160;</i></td><td class="x-btn-center"><button class="x-btn-text" type="{1}">{0}</button></td></tr>',
28006             "</tbody></table></td><td>",
28007             '<table cellspacing="0" class="x-btn-wrap x-btn-menu-arrow-wrap"><tbody>',
28008             '<tr><td class="x-btn-center"><button class="x-btn-menu-arrow-el" type="button">&#160;</button></td><td class="x-btn-right"><i>&#160;</i></td></tr>',
28009             "</tbody></table></td></tr></table>"
28010         );
28011         var btn = tpl.append(renderTo, [this.text, this.type], true);
28012         var btnEl = btn.child("button");
28013         if(this.cls){
28014             btn.addClass(this.cls);
28015         }
28016         if(this.icon){
28017             btnEl.setStyle('background-image', 'url(' +this.icon +')');
28018         }
28019         if(this.iconCls){
28020             btnEl.addClass(this.iconCls);
28021             if(!this.cls){
28022                 btn.addClass(this.text ? 'x-btn-text-icon' : 'x-btn-icon');
28023             }
28024         }
28025         this.el = btn;
28026         if(this.handleMouseEvents){
28027             btn.on("mouseover", this.onMouseOver, this);
28028             btn.on("mouseout", this.onMouseOut, this);
28029             btn.on("mousedown", this.onMouseDown, this);
28030             btn.on("mouseup", this.onMouseUp, this);
28031         }
28032         btn.on(this.clickEvent, this.onClick, this);
28033         if(this.tooltip){
28034             if(typeof this.tooltip == 'object'){
28035                 Roo.QuickTips.tips(Roo.apply({
28036                       target: btnEl.id
28037                 }, this.tooltip));
28038             } else {
28039                 btnEl.dom[this.tooltipType] = this.tooltip;
28040             }
28041         }
28042         if(this.arrowTooltip){
28043             btn.child("button:nth(2)").dom[this.tooltipType] = this.arrowTooltip;
28044         }
28045         if(this.hidden){
28046             this.hide();
28047         }
28048         if(this.disabled){
28049             this.disable();
28050         }
28051         if(this.pressed){
28052             this.el.addClass("x-btn-pressed");
28053         }
28054         if(Roo.isIE && !Roo.isIE7){
28055             this.autoWidth.defer(1, this);
28056         }else{
28057             this.autoWidth();
28058         }
28059         if(this.menu){
28060             this.menu.on("show", this.onMenuShow, this);
28061             this.menu.on("hide", this.onMenuHide, this);
28062         }
28063         this.fireEvent('render', this);
28064     },
28065
28066     // private
28067     autoWidth : function(){
28068         if(this.el){
28069             var tbl = this.el.child("table:first");
28070             var tbl2 = this.el.child("table:last");
28071             this.el.setWidth("auto");
28072             tbl.setWidth("auto");
28073             if(Roo.isIE7 && Roo.isStrict){
28074                 var ib = this.el.child('button:first');
28075                 if(ib && ib.getWidth() > 20){
28076                     ib.clip();
28077                     ib.setWidth(Roo.util.TextMetrics.measure(ib, this.text).width+ib.getFrameWidth('lr'));
28078                 }
28079             }
28080             if(this.minWidth){
28081                 if(this.hidden){
28082                     this.el.beginMeasure();
28083                 }
28084                 if((tbl.getWidth()+tbl2.getWidth()) < this.minWidth){
28085                     tbl.setWidth(this.minWidth-tbl2.getWidth());
28086                 }
28087                 if(this.hidden){
28088                     this.el.endMeasure();
28089                 }
28090             }
28091             this.el.setWidth(tbl.getWidth()+tbl2.getWidth());
28092         } 
28093     },
28094     /**
28095      * Sets this button's click handler
28096      * @param {Function} handler The function to call when the button is clicked
28097      * @param {Object} scope (optional) Scope for the function passed above
28098      */
28099     setHandler : function(handler, scope){
28100         this.handler = handler;
28101         this.scope = scope;  
28102     },
28103     
28104     /**
28105      * Sets this button's arrow click handler
28106      * @param {Function} handler The function to call when the arrow is clicked
28107      * @param {Object} scope (optional) Scope for the function passed above
28108      */
28109     setArrowHandler : function(handler, scope){
28110         this.arrowHandler = handler;
28111         this.scope = scope;  
28112     },
28113     
28114     /**
28115      * Focus the button
28116      */
28117     focus : function(){
28118         if(this.el){
28119             this.el.child("button:first").focus();
28120         }
28121     },
28122
28123     // private
28124     onClick : function(e){
28125         e.preventDefault();
28126         if(!this.disabled){
28127             if(e.getTarget(".x-btn-menu-arrow-wrap")){
28128                 if(this.menu && !this.menu.isVisible()){
28129                     this.menu.show(this.el, this.menuAlign);
28130                 }
28131                 this.fireEvent("arrowclick", this, e);
28132                 if(this.arrowHandler){
28133                     this.arrowHandler.call(this.scope || this, this, e);
28134                 }
28135             }else{
28136                 this.fireEvent("click", this, e);
28137                 if(this.handler){
28138                     this.handler.call(this.scope || this, this, e);
28139                 }
28140             }
28141         }
28142     },
28143     // private
28144     onMouseDown : function(e){
28145         if(!this.disabled){
28146             Roo.fly(e.getTarget("table")).addClass("x-btn-click");
28147         }
28148     },
28149     // private
28150     onMouseUp : function(e){
28151         Roo.fly(e.getTarget("table")).removeClass("x-btn-click");
28152     }   
28153 });
28154
28155
28156 // backwards compat
28157 Roo.MenuButton = Roo.SplitButton;/*
28158  * Based on:
28159  * Ext JS Library 1.1.1
28160  * Copyright(c) 2006-2007, Ext JS, LLC.
28161  *
28162  * Originally Released Under LGPL - original licence link has changed is not relivant.
28163  *
28164  * Fork - LGPL
28165  * <script type="text/javascript">
28166  */
28167
28168 /**
28169  * @class Roo.Toolbar
28170  * Basic Toolbar class.
28171  * @constructor
28172  * Creates a new Toolbar
28173  * @param {Object} container The config object
28174  */ 
28175 Roo.Toolbar = function(container, buttons, config)
28176 {
28177     /// old consturctor format still supported..
28178     if(container instanceof Array){ // omit the container for later rendering
28179         buttons = container;
28180         config = buttons;
28181         container = null;
28182     }
28183     if (typeof(container) == 'object' && container.xtype) {
28184         config = container;
28185         container = config.container;
28186         buttons = config.buttons || []; // not really - use items!!
28187     }
28188     var xitems = [];
28189     if (config && config.items) {
28190         xitems = config.items;
28191         delete config.items;
28192     }
28193     Roo.apply(this, config);
28194     this.buttons = buttons;
28195     
28196     if(container){
28197         this.render(container);
28198     }
28199     this.xitems = xitems;
28200     Roo.each(xitems, function(b) {
28201         this.add(b);
28202     }, this);
28203     
28204 };
28205
28206 Roo.Toolbar.prototype = {
28207     /**
28208      * @cfg {Array} items
28209      * array of button configs or elements to add (will be converted to a MixedCollection)
28210      */
28211     
28212     /**
28213      * @cfg {String/HTMLElement/Element} container
28214      * The id or element that will contain the toolbar
28215      */
28216     // private
28217     render : function(ct){
28218         this.el = Roo.get(ct);
28219         if(this.cls){
28220             this.el.addClass(this.cls);
28221         }
28222         // using a table allows for vertical alignment
28223         // 100% width is needed by Safari...
28224         this.el.update('<div class="x-toolbar x-small-editor"><table cellspacing="0"><tr></tr></table></div>');
28225         this.tr = this.el.child("tr", true);
28226         var autoId = 0;
28227         this.items = new Roo.util.MixedCollection(false, function(o){
28228             return o.id || ("item" + (++autoId));
28229         });
28230         if(this.buttons){
28231             this.add.apply(this, this.buttons);
28232             delete this.buttons;
28233         }
28234     },
28235
28236     /**
28237      * Adds element(s) to the toolbar -- this function takes a variable number of 
28238      * arguments of mixed type and adds them to the toolbar.
28239      * @param {Mixed} arg1 The following types of arguments are all valid:<br />
28240      * <ul>
28241      * <li>{@link Roo.Toolbar.Button} config: A valid button config object (equivalent to {@link #addButton})</li>
28242      * <li>HtmlElement: Any standard HTML element (equivalent to {@link #addElement})</li>
28243      * <li>Field: Any form field (equivalent to {@link #addField})</li>
28244      * <li>Item: Any subclass of {@link Roo.Toolbar.Item} (equivalent to {@link #addItem})</li>
28245      * <li>String: Any generic string (gets wrapped in a {@link Roo.Toolbar.TextItem}, equivalent to {@link #addText}).
28246      * Note that there are a few special strings that are treated differently as explained nRoo.</li>
28247      * <li>'separator' or '-': Creates a separator element (equivalent to {@link #addSeparator})</li>
28248      * <li>' ': Creates a spacer element (equivalent to {@link #addSpacer})</li>
28249      * <li>'->': Creates a fill element (equivalent to {@link #addFill})</li>
28250      * </ul>
28251      * @param {Mixed} arg2
28252      * @param {Mixed} etc.
28253      */
28254     add : function(){
28255         var a = arguments, l = a.length;
28256         for(var i = 0; i < l; i++){
28257             this._add(a[i]);
28258         }
28259     },
28260     // private..
28261     _add : function(el) {
28262         
28263         if (el.xtype) {
28264             el = Roo.factory(el, typeof(Roo.Toolbar[el.xtype]) == 'undefined' ? Roo.form : Roo.Toolbar);
28265         }
28266         
28267         if (el.applyTo){ // some kind of form field
28268             return this.addField(el);
28269         } 
28270         if (el.render){ // some kind of Toolbar.Item
28271             return this.addItem(el);
28272         }
28273         if (typeof el == "string"){ // string
28274             if(el == "separator" || el == "-"){
28275                 return this.addSeparator();
28276             }
28277             if (el == " "){
28278                 return this.addSpacer();
28279             }
28280             if(el == "->"){
28281                 return this.addFill();
28282             }
28283             return this.addText(el);
28284             
28285         }
28286         if(el.tagName){ // element
28287             return this.addElement(el);
28288         }
28289         if(typeof el == "object"){ // must be button config?
28290             return this.addButton(el);
28291         }
28292         // and now what?!?!
28293         return false;
28294         
28295     },
28296     
28297     /**
28298      * Add an Xtype element
28299      * @param {Object} xtype Xtype Object
28300      * @return {Object} created Object
28301      */
28302     addxtype : function(e){
28303         return this.add(e);  
28304     },
28305     
28306     /**
28307      * Returns the Element for this toolbar.
28308      * @return {Roo.Element}
28309      */
28310     getEl : function(){
28311         return this.el;  
28312     },
28313     
28314     /**
28315      * Adds a separator
28316      * @return {Roo.Toolbar.Item} The separator item
28317      */
28318     addSeparator : function(){
28319         return this.addItem(new Roo.Toolbar.Separator());
28320     },
28321
28322     /**
28323      * Adds a spacer element
28324      * @return {Roo.Toolbar.Spacer} The spacer item
28325      */
28326     addSpacer : function(){
28327         return this.addItem(new Roo.Toolbar.Spacer());
28328     },
28329
28330     /**
28331      * Adds a fill element that forces subsequent additions to the right side of the toolbar
28332      * @return {Roo.Toolbar.Fill} The fill item
28333      */
28334     addFill : function(){
28335         return this.addItem(new Roo.Toolbar.Fill());
28336     },
28337
28338     /**
28339      * Adds any standard HTML element to the toolbar
28340      * @param {String/HTMLElement/Element} el The element or id of the element to add
28341      * @return {Roo.Toolbar.Item} The element's item
28342      */
28343     addElement : function(el){
28344         return this.addItem(new Roo.Toolbar.Item(el));
28345     },
28346     /**
28347      * Collection of items on the toolbar.. (only Toolbar Items, so use fields to retrieve fields)
28348      * @type Roo.util.MixedCollection  
28349      */
28350     items : false,
28351      
28352     /**
28353      * Adds any Toolbar.Item or subclass
28354      * @param {Roo.Toolbar.Item} item
28355      * @return {Roo.Toolbar.Item} The item
28356      */
28357     addItem : function(item){
28358         var td = this.nextBlock();
28359         item.render(td);
28360         this.items.add(item);
28361         return item;
28362     },
28363     
28364     /**
28365      * Adds a button (or buttons). See {@link Roo.Toolbar.Button} for more info on the config.
28366      * @param {Object/Array} config A button config or array of configs
28367      * @return {Roo.Toolbar.Button/Array}
28368      */
28369     addButton : function(config){
28370         if(config instanceof Array){
28371             var buttons = [];
28372             for(var i = 0, len = config.length; i < len; i++) {
28373                 buttons.push(this.addButton(config[i]));
28374             }
28375             return buttons;
28376         }
28377         var b = config;
28378         if(!(config instanceof Roo.Toolbar.Button)){
28379             b = config.split ?
28380                 new Roo.Toolbar.SplitButton(config) :
28381                 new Roo.Toolbar.Button(config);
28382         }
28383         var td = this.nextBlock();
28384         b.render(td);
28385         this.items.add(b);
28386         return b;
28387     },
28388     
28389     /**
28390      * Adds text to the toolbar
28391      * @param {String} text The text to add
28392      * @return {Roo.Toolbar.Item} The element's item
28393      */
28394     addText : function(text){
28395         return this.addItem(new Roo.Toolbar.TextItem(text));
28396     },
28397     
28398     /**
28399      * Inserts any {@link Roo.Toolbar.Item}/{@link Roo.Toolbar.Button} at the specified index.
28400      * @param {Number} index The index where the item is to be inserted
28401      * @param {Object/Roo.Toolbar.Item/Roo.Toolbar.Button (may be Array)} item The button, or button config object to be inserted.
28402      * @return {Roo.Toolbar.Button/Item}
28403      */
28404     insertButton : function(index, item){
28405         if(item instanceof Array){
28406             var buttons = [];
28407             for(var i = 0, len = item.length; i < len; i++) {
28408                buttons.push(this.insertButton(index + i, item[i]));
28409             }
28410             return buttons;
28411         }
28412         if (!(item instanceof Roo.Toolbar.Button)){
28413            item = new Roo.Toolbar.Button(item);
28414         }
28415         var td = document.createElement("td");
28416         this.tr.insertBefore(td, this.tr.childNodes[index]);
28417         item.render(td);
28418         this.items.insert(index, item);
28419         return item;
28420     },
28421     
28422     /**
28423      * Adds a new element to the toolbar from the passed {@link Roo.DomHelper} config.
28424      * @param {Object} config
28425      * @return {Roo.Toolbar.Item} The element's item
28426      */
28427     addDom : function(config, returnEl){
28428         var td = this.nextBlock();
28429         Roo.DomHelper.overwrite(td, config);
28430         var ti = new Roo.Toolbar.Item(td.firstChild);
28431         ti.render(td);
28432         this.items.add(ti);
28433         return ti;
28434     },
28435
28436     /**
28437      * Collection of fields on the toolbar.. usefull for quering (value is false if there are no fields)
28438      * @type Roo.util.MixedCollection  
28439      */
28440     fields : false,
28441     
28442     /**
28443      * Adds a dynamically rendered Roo.form field (TextField, ComboBox, etc).
28444      * Note: the field should not have been rendered yet. For a field that has already been
28445      * rendered, use {@link #addElement}.
28446      * @param {Roo.form.Field} field
28447      * @return {Roo.ToolbarItem}
28448      */
28449      
28450       
28451     addField : function(field) {
28452         if (!this.fields) {
28453             var autoId = 0;
28454             this.fields = new Roo.util.MixedCollection(false, function(o){
28455                 return o.id || ("item" + (++autoId));
28456             });
28457
28458         }
28459         
28460         var td = this.nextBlock();
28461         field.render(td);
28462         var ti = new Roo.Toolbar.Item(td.firstChild);
28463         ti.render(td);
28464         this.items.add(ti);
28465         this.fields.add(field);
28466         return ti;
28467     },
28468     /**
28469      * Hide the toolbar
28470      * @method hide
28471      */
28472      
28473       
28474     hide : function()
28475     {
28476         this.el.child('div').setVisibilityMode(Roo.Element.DISPLAY);
28477         this.el.child('div').hide();
28478     },
28479     /**
28480      * Show the toolbar
28481      * @method show
28482      */
28483     show : function()
28484     {
28485         this.el.child('div').show();
28486     },
28487       
28488     // private
28489     nextBlock : function(){
28490         var td = document.createElement("td");
28491         this.tr.appendChild(td);
28492         return td;
28493     },
28494
28495     // private
28496     destroy : function(){
28497         if(this.items){ // rendered?
28498             Roo.destroy.apply(Roo, this.items.items);
28499         }
28500         if(this.fields){ // rendered?
28501             Roo.destroy.apply(Roo, this.fields.items);
28502         }
28503         Roo.Element.uncache(this.el, this.tr);
28504     }
28505 };
28506
28507 /**
28508  * @class Roo.Toolbar.Item
28509  * The base class that other classes should extend in order to get some basic common toolbar item functionality.
28510  * @constructor
28511  * Creates a new Item
28512  * @param {HTMLElement} el 
28513  */
28514 Roo.Toolbar.Item = function(el){
28515     this.el = Roo.getDom(el);
28516     this.id = Roo.id(this.el);
28517     this.hidden = false;
28518 };
28519
28520 Roo.Toolbar.Item.prototype = {
28521     
28522     /**
28523      * Get this item's HTML Element
28524      * @return {HTMLElement}
28525      */
28526     getEl : function(){
28527        return this.el;  
28528     },
28529
28530     // private
28531     render : function(td){
28532         this.td = td;
28533         td.appendChild(this.el);
28534     },
28535     
28536     /**
28537      * Removes and destroys this item.
28538      */
28539     destroy : function(){
28540         this.td.parentNode.removeChild(this.td);
28541     },
28542     
28543     /**
28544      * Shows this item.
28545      */
28546     show: function(){
28547         this.hidden = false;
28548         this.td.style.display = "";
28549     },
28550     
28551     /**
28552      * Hides this item.
28553      */
28554     hide: function(){
28555         this.hidden = true;
28556         this.td.style.display = "none";
28557     },
28558     
28559     /**
28560      * Convenience function for boolean show/hide.
28561      * @param {Boolean} visible true to show/false to hide
28562      */
28563     setVisible: function(visible){
28564         if(visible) {
28565             this.show();
28566         }else{
28567             this.hide();
28568         }
28569     },
28570     
28571     /**
28572      * Try to focus this item.
28573      */
28574     focus : function(){
28575         Roo.fly(this.el).focus();
28576     },
28577     
28578     /**
28579      * Disables this item.
28580      */
28581     disable : function(){
28582         Roo.fly(this.td).addClass("x-item-disabled");
28583         this.disabled = true;
28584         this.el.disabled = true;
28585     },
28586     
28587     /**
28588      * Enables this item.
28589      */
28590     enable : function(){
28591         Roo.fly(this.td).removeClass("x-item-disabled");
28592         this.disabled = false;
28593         this.el.disabled = false;
28594     }
28595 };
28596
28597
28598 /**
28599  * @class Roo.Toolbar.Separator
28600  * @extends Roo.Toolbar.Item
28601  * A simple toolbar separator class
28602  * @constructor
28603  * Creates a new Separator
28604  */
28605 Roo.Toolbar.Separator = function(){
28606     var s = document.createElement("span");
28607     s.className = "ytb-sep";
28608     Roo.Toolbar.Separator.superclass.constructor.call(this, s);
28609 };
28610 Roo.extend(Roo.Toolbar.Separator, Roo.Toolbar.Item, {
28611     enable:Roo.emptyFn,
28612     disable:Roo.emptyFn,
28613     focus:Roo.emptyFn
28614 });
28615
28616 /**
28617  * @class Roo.Toolbar.Spacer
28618  * @extends Roo.Toolbar.Item
28619  * A simple element that adds extra horizontal space to a toolbar.
28620  * @constructor
28621  * Creates a new Spacer
28622  */
28623 Roo.Toolbar.Spacer = function(){
28624     var s = document.createElement("div");
28625     s.className = "ytb-spacer";
28626     Roo.Toolbar.Spacer.superclass.constructor.call(this, s);
28627 };
28628 Roo.extend(Roo.Toolbar.Spacer, Roo.Toolbar.Item, {
28629     enable:Roo.emptyFn,
28630     disable:Roo.emptyFn,
28631     focus:Roo.emptyFn
28632 });
28633
28634 /**
28635  * @class Roo.Toolbar.Fill
28636  * @extends Roo.Toolbar.Spacer
28637  * A simple element that adds a greedy (100% width) horizontal space to a toolbar.
28638  * @constructor
28639  * Creates a new Spacer
28640  */
28641 Roo.Toolbar.Fill = Roo.extend(Roo.Toolbar.Spacer, {
28642     // private
28643     render : function(td){
28644         td.style.width = '100%';
28645         Roo.Toolbar.Fill.superclass.render.call(this, td);
28646     }
28647 });
28648
28649 /**
28650  * @class Roo.Toolbar.TextItem
28651  * @extends Roo.Toolbar.Item
28652  * A simple class that renders text directly into a toolbar.
28653  * @constructor
28654  * Creates a new TextItem
28655  * @param {String} text
28656  */
28657 Roo.Toolbar.TextItem = function(text){
28658     if (typeof(text) == 'object') {
28659         text = text.text;
28660     }
28661     var s = document.createElement("span");
28662     s.className = "ytb-text";
28663     s.innerHTML = text;
28664     Roo.Toolbar.TextItem.superclass.constructor.call(this, s);
28665 };
28666 Roo.extend(Roo.Toolbar.TextItem, Roo.Toolbar.Item, {
28667     enable:Roo.emptyFn,
28668     disable:Roo.emptyFn,
28669     focus:Roo.emptyFn
28670 });
28671
28672 /**
28673  * @class Roo.Toolbar.Button
28674  * @extends Roo.Button
28675  * A button that renders into a toolbar.
28676  * @constructor
28677  * Creates a new Button
28678  * @param {Object} config A standard {@link Roo.Button} config object
28679  */
28680 Roo.Toolbar.Button = function(config){
28681     Roo.Toolbar.Button.superclass.constructor.call(this, null, config);
28682 };
28683 Roo.extend(Roo.Toolbar.Button, Roo.Button, {
28684     render : function(td){
28685         this.td = td;
28686         Roo.Toolbar.Button.superclass.render.call(this, td);
28687     },
28688     
28689     /**
28690      * Removes and destroys this button
28691      */
28692     destroy : function(){
28693         Roo.Toolbar.Button.superclass.destroy.call(this);
28694         this.td.parentNode.removeChild(this.td);
28695     },
28696     
28697     /**
28698      * Shows this button
28699      */
28700     show: function(){
28701         this.hidden = false;
28702         this.td.style.display = "";
28703     },
28704     
28705     /**
28706      * Hides this button
28707      */
28708     hide: function(){
28709         this.hidden = true;
28710         this.td.style.display = "none";
28711     },
28712
28713     /**
28714      * Disables this item
28715      */
28716     disable : function(){
28717         Roo.fly(this.td).addClass("x-item-disabled");
28718         this.disabled = true;
28719     },
28720
28721     /**
28722      * Enables this item
28723      */
28724     enable : function(){
28725         Roo.fly(this.td).removeClass("x-item-disabled");
28726         this.disabled = false;
28727     }
28728 });
28729 // backwards compat
28730 Roo.ToolbarButton = Roo.Toolbar.Button;
28731
28732 /**
28733  * @class Roo.Toolbar.SplitButton
28734  * @extends Roo.SplitButton
28735  * A menu button that renders into a toolbar.
28736  * @constructor
28737  * Creates a new SplitButton
28738  * @param {Object} config A standard {@link Roo.SplitButton} config object
28739  */
28740 Roo.Toolbar.SplitButton = function(config){
28741     Roo.Toolbar.SplitButton.superclass.constructor.call(this, null, config);
28742 };
28743 Roo.extend(Roo.Toolbar.SplitButton, Roo.SplitButton, {
28744     render : function(td){
28745         this.td = td;
28746         Roo.Toolbar.SplitButton.superclass.render.call(this, td);
28747     },
28748     
28749     /**
28750      * Removes and destroys this button
28751      */
28752     destroy : function(){
28753         Roo.Toolbar.SplitButton.superclass.destroy.call(this);
28754         this.td.parentNode.removeChild(this.td);
28755     },
28756     
28757     /**
28758      * Shows this button
28759      */
28760     show: function(){
28761         this.hidden = false;
28762         this.td.style.display = "";
28763     },
28764     
28765     /**
28766      * Hides this button
28767      */
28768     hide: function(){
28769         this.hidden = true;
28770         this.td.style.display = "none";
28771     }
28772 });
28773
28774 // backwards compat
28775 Roo.Toolbar.MenuButton = Roo.Toolbar.SplitButton;/*
28776  * Based on:
28777  * Ext JS Library 1.1.1
28778  * Copyright(c) 2006-2007, Ext JS, LLC.
28779  *
28780  * Originally Released Under LGPL - original licence link has changed is not relivant.
28781  *
28782  * Fork - LGPL
28783  * <script type="text/javascript">
28784  */
28785  
28786 /**
28787  * @class Roo.PagingToolbar
28788  * @extends Roo.Toolbar
28789  * A specialized toolbar that is bound to a {@link Roo.data.Store} and provides automatic paging controls.
28790  * @constructor
28791  * Create a new PagingToolbar
28792  * @param {Object} config The config object
28793  */
28794 Roo.PagingToolbar = function(el, ds, config)
28795 {
28796     // old args format still supported... - xtype is prefered..
28797     if (typeof(el) == 'object' && el.xtype) {
28798         // created from xtype...
28799         config = el;
28800         ds = el.dataSource;
28801         el = config.container;
28802     }
28803     var items = [];
28804     if (config.items) {
28805         items = config.items;
28806         config.items = [];
28807     }
28808     
28809     Roo.PagingToolbar.superclass.constructor.call(this, el, null, config);
28810     this.ds = ds;
28811     this.cursor = 0;
28812     this.renderButtons(this.el);
28813     this.bind(ds);
28814     
28815     // supprot items array.
28816    
28817     Roo.each(items, function(e) {
28818         this.add(Roo.factory(e));
28819     },this);
28820     
28821 };
28822
28823 Roo.extend(Roo.PagingToolbar, Roo.Toolbar, {
28824     /**
28825      * @cfg {Roo.data.Store} dataSource
28826      * The underlying data store providing the paged data
28827      */
28828     /**
28829      * @cfg {String/HTMLElement/Element} container
28830      * container The id or element that will contain the toolbar
28831      */
28832     /**
28833      * @cfg {Boolean} displayInfo
28834      * True to display the displayMsg (defaults to false)
28835      */
28836     /**
28837      * @cfg {Number} pageSize
28838      * The number of records to display per page (defaults to 20)
28839      */
28840     pageSize: 20,
28841     /**
28842      * @cfg {String} displayMsg
28843      * The paging status message to display (defaults to "Displaying {start} - {end} of {total}")
28844      */
28845     displayMsg : 'Displaying {0} - {1} of {2}',
28846     /**
28847      * @cfg {String} emptyMsg
28848      * The message to display when no records are found (defaults to "No data to display")
28849      */
28850     emptyMsg : 'No data to display',
28851     /**
28852      * Customizable piece of the default paging text (defaults to "Page")
28853      * @type String
28854      */
28855     beforePageText : "Page",
28856     /**
28857      * Customizable piece of the default paging text (defaults to "of %0")
28858      * @type String
28859      */
28860     afterPageText : "of {0}",
28861     /**
28862      * Customizable piece of the default paging text (defaults to "First Page")
28863      * @type String
28864      */
28865     firstText : "First Page",
28866     /**
28867      * Customizable piece of the default paging text (defaults to "Previous Page")
28868      * @type String
28869      */
28870     prevText : "Previous Page",
28871     /**
28872      * Customizable piece of the default paging text (defaults to "Next Page")
28873      * @type String
28874      */
28875     nextText : "Next Page",
28876     /**
28877      * Customizable piece of the default paging text (defaults to "Last Page")
28878      * @type String
28879      */
28880     lastText : "Last Page",
28881     /**
28882      * Customizable piece of the default paging text (defaults to "Refresh")
28883      * @type String
28884      */
28885     refreshText : "Refresh",
28886
28887     // private
28888     renderButtons : function(el){
28889         Roo.PagingToolbar.superclass.render.call(this, el);
28890         this.first = this.addButton({
28891             tooltip: this.firstText,
28892             cls: "x-btn-icon x-grid-page-first",
28893             disabled: true,
28894             handler: this.onClick.createDelegate(this, ["first"])
28895         });
28896         this.prev = this.addButton({
28897             tooltip: this.prevText,
28898             cls: "x-btn-icon x-grid-page-prev",
28899             disabled: true,
28900             handler: this.onClick.createDelegate(this, ["prev"])
28901         });
28902         //this.addSeparator();
28903         this.add(this.beforePageText);
28904         this.field = Roo.get(this.addDom({
28905            tag: "input",
28906            type: "text",
28907            size: "3",
28908            value: "1",
28909            cls: "x-grid-page-number"
28910         }).el);
28911         this.field.on("keydown", this.onPagingKeydown, this);
28912         this.field.on("focus", function(){this.dom.select();});
28913         this.afterTextEl = this.addText(String.format(this.afterPageText, 1));
28914         this.field.setHeight(18);
28915         //this.addSeparator();
28916         this.next = this.addButton({
28917             tooltip: this.nextText,
28918             cls: "x-btn-icon x-grid-page-next",
28919             disabled: true,
28920             handler: this.onClick.createDelegate(this, ["next"])
28921         });
28922         this.last = this.addButton({
28923             tooltip: this.lastText,
28924             cls: "x-btn-icon x-grid-page-last",
28925             disabled: true,
28926             handler: this.onClick.createDelegate(this, ["last"])
28927         });
28928         //this.addSeparator();
28929         this.loading = this.addButton({
28930             tooltip: this.refreshText,
28931             cls: "x-btn-icon x-grid-loading",
28932             handler: this.onClick.createDelegate(this, ["refresh"])
28933         });
28934
28935         if(this.displayInfo){
28936             this.displayEl = Roo.fly(this.el.dom.firstChild).createChild({cls:'x-paging-info'});
28937         }
28938     },
28939
28940     // private
28941     updateInfo : function(){
28942         if(this.displayEl){
28943             var count = this.ds.getCount();
28944             var msg = count == 0 ?
28945                 this.emptyMsg :
28946                 String.format(
28947                     this.displayMsg,
28948                     this.cursor+1, this.cursor+count, this.ds.getTotalCount()    
28949                 );
28950             this.displayEl.update(msg);
28951         }
28952     },
28953
28954     // private
28955     onLoad : function(ds, r, o){
28956        this.cursor = o.params ? o.params.start : 0;
28957        var d = this.getPageData(), ap = d.activePage, ps = d.pages;
28958
28959        this.afterTextEl.el.innerHTML = String.format(this.afterPageText, d.pages);
28960        this.field.dom.value = ap;
28961        this.first.setDisabled(ap == 1);
28962        this.prev.setDisabled(ap == 1);
28963        this.next.setDisabled(ap == ps);
28964        this.last.setDisabled(ap == ps);
28965        this.loading.enable();
28966        this.updateInfo();
28967     },
28968
28969     // private
28970     getPageData : function(){
28971         var total = this.ds.getTotalCount();
28972         return {
28973             total : total,
28974             activePage : Math.ceil((this.cursor+this.pageSize)/this.pageSize),
28975             pages :  total < this.pageSize ? 1 : Math.ceil(total/this.pageSize)
28976         };
28977     },
28978
28979     // private
28980     onLoadError : function(){
28981         this.loading.enable();
28982     },
28983
28984     // private
28985     onPagingKeydown : function(e){
28986         var k = e.getKey();
28987         var d = this.getPageData();
28988         if(k == e.RETURN){
28989             var v = this.field.dom.value, pageNum;
28990             if(!v || isNaN(pageNum = parseInt(v, 10))){
28991                 this.field.dom.value = d.activePage;
28992                 return;
28993             }
28994             pageNum = Math.min(Math.max(1, pageNum), d.pages) - 1;
28995             this.ds.load({params:{start: pageNum * this.pageSize, limit: this.pageSize}});
28996             e.stopEvent();
28997         }
28998         else if(k == e.HOME || (k == e.UP && e.ctrlKey) || (k == e.PAGEUP && e.ctrlKey) || (k == e.RIGHT && e.ctrlKey) || k == e.END || (k == e.DOWN && e.ctrlKey) || (k == e.LEFT && e.ctrlKey) || (k == e.PAGEDOWN && e.ctrlKey))
28999         {
29000           var pageNum = (k == e.HOME || (k == e.DOWN && e.ctrlKey) || (k == e.LEFT && e.ctrlKey) || (k == e.PAGEDOWN && e.ctrlKey)) ? 1 : d.pages;
29001           this.field.dom.value = pageNum;
29002           this.ds.load({params:{start: (pageNum - 1) * this.pageSize, limit: this.pageSize}});
29003           e.stopEvent();
29004         }
29005         else if(k == e.UP || k == e.RIGHT || k == e.PAGEUP || k == e.DOWN || k == e.LEFT || k == e.PAGEDOWN)
29006         {
29007           var v = this.field.dom.value, pageNum; 
29008           var increment = (e.shiftKey) ? 10 : 1;
29009           if(k == e.DOWN || k == e.LEFT || k == e.PAGEDOWN)
29010             increment *= -1;
29011           if(!v || isNaN(pageNum = parseInt(v, 10))) {
29012             this.field.dom.value = d.activePage;
29013             return;
29014           }
29015           else if(parseInt(v, 10) + increment >= 1 & parseInt(v, 10) + increment <= d.pages)
29016           {
29017             this.field.dom.value = parseInt(v, 10) + increment;
29018             pageNum = Math.min(Math.max(1, pageNum + increment), d.pages) - 1;
29019             this.ds.load({params:{start: pageNum * this.pageSize, limit: this.pageSize}});
29020           }
29021           e.stopEvent();
29022         }
29023     },
29024
29025     // private
29026     beforeLoad : function(){
29027         if(this.loading){
29028             this.loading.disable();
29029         }
29030     },
29031
29032     // private
29033     onClick : function(which){
29034         var ds = this.ds;
29035         switch(which){
29036             case "first":
29037                 ds.load({params:{start: 0, limit: this.pageSize}});
29038             break;
29039             case "prev":
29040                 ds.load({params:{start: Math.max(0, this.cursor-this.pageSize), limit: this.pageSize}});
29041             break;
29042             case "next":
29043                 ds.load({params:{start: this.cursor+this.pageSize, limit: this.pageSize}});
29044             break;
29045             case "last":
29046                 var total = ds.getTotalCount();
29047                 var extra = total % this.pageSize;
29048                 var lastStart = extra ? (total - extra) : total-this.pageSize;
29049                 ds.load({params:{start: lastStart, limit: this.pageSize}});
29050             break;
29051             case "refresh":
29052                 ds.load({params:{start: this.cursor, limit: this.pageSize}});
29053             break;
29054         }
29055     },
29056
29057     /**
29058      * Unbinds the paging toolbar from the specified {@link Roo.data.Store}
29059      * @param {Roo.data.Store} store The data store to unbind
29060      */
29061     unbind : function(ds){
29062         ds.un("beforeload", this.beforeLoad, this);
29063         ds.un("load", this.onLoad, this);
29064         ds.un("loadexception", this.onLoadError, this);
29065         ds.un("remove", this.updateInfo, this);
29066         ds.un("add", this.updateInfo, this);
29067         this.ds = undefined;
29068     },
29069
29070     /**
29071      * Binds the paging toolbar to the specified {@link Roo.data.Store}
29072      * @param {Roo.data.Store} store The data store to bind
29073      */
29074     bind : function(ds){
29075         ds.on("beforeload", this.beforeLoad, this);
29076         ds.on("load", this.onLoad, this);
29077         ds.on("loadexception", this.onLoadError, this);
29078         ds.on("remove", this.updateInfo, this);
29079         ds.on("add", this.updateInfo, this);
29080         this.ds = ds;
29081     }
29082 });/*
29083  * Based on:
29084  * Ext JS Library 1.1.1
29085  * Copyright(c) 2006-2007, Ext JS, LLC.
29086  *
29087  * Originally Released Under LGPL - original licence link has changed is not relivant.
29088  *
29089  * Fork - LGPL
29090  * <script type="text/javascript">
29091  */
29092
29093 /**
29094  * @class Roo.Resizable
29095  * @extends Roo.util.Observable
29096  * <p>Applies drag handles to an element to make it resizable. The drag handles are inserted into the element
29097  * and positioned absolute. Some elements, such as a textarea or image, don't support this. To overcome that, you can wrap
29098  * the textarea in a div and set "resizeChild" to true (or to the id of the element), <b>or</b> set wrap:true in your config and
29099  * the element will be wrapped for you automatically.</p>
29100  * <p>Here is the list of valid resize handles:</p>
29101  * <pre>
29102 Value   Description
29103 ------  -------------------
29104  'n'     north
29105  's'     south
29106  'e'     east
29107  'w'     west
29108  'nw'    northwest
29109  'sw'    southwest
29110  'se'    southeast
29111  'ne'    northeast
29112  'hd'    horizontal drag
29113  'all'   all
29114 </pre>
29115  * <p>Here's an example showing the creation of a typical Resizable:</p>
29116  * <pre><code>
29117 var resizer = new Roo.Resizable("element-id", {
29118     handles: 'all',
29119     minWidth: 200,
29120     minHeight: 100,
29121     maxWidth: 500,
29122     maxHeight: 400,
29123     pinned: true
29124 });
29125 resizer.on("resize", myHandler);
29126 </code></pre>
29127  * <p>To hide a particular handle, set its display to none in CSS, or through script:<br>
29128  * resizer.east.setDisplayed(false);</p>
29129  * @cfg {Boolean/String/Element} resizeChild True to resize the first child, or id/element to resize (defaults to false)
29130  * @cfg {Array/String} adjustments String "auto" or an array [width, height] with values to be <b>added</b> to the
29131  * resize operation's new size (defaults to [0, 0])
29132  * @cfg {Number} minWidth The minimum width for the element (defaults to 5)
29133  * @cfg {Number} minHeight The minimum height for the element (defaults to 5)
29134  * @cfg {Number} maxWidth The maximum width for the element (defaults to 10000)
29135  * @cfg {Number} maxHeight The maximum height for the element (defaults to 10000)
29136  * @cfg {Boolean} enabled False to disable resizing (defaults to true)
29137  * @cfg {Boolean} wrap True to wrap an element with a div if needed (required for textareas and images, defaults to false)
29138  * @cfg {Number} width The width of the element in pixels (defaults to null)
29139  * @cfg {Number} height The height of the element in pixels (defaults to null)
29140  * @cfg {Boolean} animate True to animate the resize (not compatible with dynamic sizing, defaults to false)
29141  * @cfg {Number} duration Animation duration if animate = true (defaults to .35)
29142  * @cfg {Boolean} dynamic True to resize the element while dragging instead of using a proxy (defaults to false)
29143  * @cfg {String} handles String consisting of the resize handles to display (defaults to undefined)
29144  * @cfg {Boolean} multiDirectional <b>Deprecated</b>.  The old style of adding multi-direction resize handles, deprecated
29145  * in favor of the handles config option (defaults to false)
29146  * @cfg {Boolean} disableTrackOver True to disable mouse tracking. This is only applied at config time. (defaults to false)
29147  * @cfg {String} easing Animation easing if animate = true (defaults to 'easingOutStrong')
29148  * @cfg {Number} widthIncrement The increment to snap the width resize in pixels (dynamic must be true, defaults to 0)
29149  * @cfg {Number} heightIncrement The increment to snap the height resize in pixels (dynamic must be true, defaults to 0)
29150  * @cfg {Boolean} pinned True to ensure that the resize handles are always visible, false to display them only when the
29151  * user mouses over the resizable borders. This is only applied at config time. (defaults to false)
29152  * @cfg {Boolean} preserveRatio True to preserve the original ratio between height and width during resize (defaults to false)
29153  * @cfg {Boolean} transparent True for transparent handles. This is only applied at config time. (defaults to false)
29154  * @cfg {Number} minX The minimum allowed page X for the element (only used for west resizing, defaults to 0)
29155  * @cfg {Number} minY The minimum allowed page Y for the element (only used for north resizing, defaults to 0)
29156  * @cfg {Boolean} draggable Convenience to initialize drag drop (defaults to false)
29157  * @constructor
29158  * Create a new resizable component
29159  * @param {String/HTMLElement/Roo.Element} el The id or element to resize
29160  * @param {Object} config configuration options
29161   */
29162 Roo.Resizable = function(el, config)
29163 {
29164     this.el = Roo.get(el);
29165
29166     if(config && config.wrap){
29167         config.resizeChild = this.el;
29168         this.el = this.el.wrap(typeof config.wrap == "object" ? config.wrap : {cls:"xresizable-wrap"});
29169         this.el.id = this.el.dom.id = config.resizeChild.id + "-rzwrap";
29170         this.el.setStyle("overflow", "hidden");
29171         this.el.setPositioning(config.resizeChild.getPositioning());
29172         config.resizeChild.clearPositioning();
29173         if(!config.width || !config.height){
29174             var csize = config.resizeChild.getSize();
29175             this.el.setSize(csize.width, csize.height);
29176         }
29177         if(config.pinned && !config.adjustments){
29178             config.adjustments = "auto";
29179         }
29180     }
29181
29182     this.proxy = this.el.createProxy({tag: "div", cls: "x-resizable-proxy", id: this.el.id + "-rzproxy"});
29183     this.proxy.unselectable();
29184     this.proxy.enableDisplayMode('block');
29185
29186     Roo.apply(this, config);
29187
29188     if(this.pinned){
29189         this.disableTrackOver = true;
29190         this.el.addClass("x-resizable-pinned");
29191     }
29192     // if the element isn't positioned, make it relative
29193     var position = this.el.getStyle("position");
29194     if(position != "absolute" && position != "fixed"){
29195         this.el.setStyle("position", "relative");
29196     }
29197     if(!this.handles){ // no handles passed, must be legacy style
29198         this.handles = 's,e,se';
29199         if(this.multiDirectional){
29200             this.handles += ',n,w';
29201         }
29202     }
29203     if(this.handles == "all"){
29204         this.handles = "n s e w ne nw se sw";
29205     }
29206     var hs = this.handles.split(/\s*?[,;]\s*?| /);
29207     var ps = Roo.Resizable.positions;
29208     for(var i = 0, len = hs.length; i < len; i++){
29209         if(hs[i] && ps[hs[i]]){
29210             var pos = ps[hs[i]];
29211             this[pos] = new Roo.Resizable.Handle(this, pos, this.disableTrackOver, this.transparent);
29212         }
29213     }
29214     // legacy
29215     this.corner = this.southeast;
29216     
29217     // updateBox = the box can move..
29218     if(this.handles.indexOf("n") != -1 || this.handles.indexOf("w") != -1 || this.handles.indexOf("hd") != -1) {
29219         this.updateBox = true;
29220     }
29221
29222     this.activeHandle = null;
29223
29224     if(this.resizeChild){
29225         if(typeof this.resizeChild == "boolean"){
29226             this.resizeChild = Roo.get(this.el.dom.firstChild, true);
29227         }else{
29228             this.resizeChild = Roo.get(this.resizeChild, true);
29229         }
29230     }
29231     
29232     if(this.adjustments == "auto"){
29233         var rc = this.resizeChild;
29234         var hw = this.west, he = this.east, hn = this.north, hs = this.south;
29235         if(rc && (hw || hn)){
29236             rc.position("relative");
29237             rc.setLeft(hw ? hw.el.getWidth() : 0);
29238             rc.setTop(hn ? hn.el.getHeight() : 0);
29239         }
29240         this.adjustments = [
29241             (he ? -he.el.getWidth() : 0) + (hw ? -hw.el.getWidth() : 0),
29242             (hn ? -hn.el.getHeight() : 0) + (hs ? -hs.el.getHeight() : 0) -1
29243         ];
29244     }
29245
29246     if(this.draggable){
29247         this.dd = this.dynamic ?
29248             this.el.initDD(null) : this.el.initDDProxy(null, {dragElId: this.proxy.id});
29249         this.dd.setHandleElId(this.resizeChild ? this.resizeChild.id : this.el.id);
29250     }
29251
29252     // public events
29253     this.addEvents({
29254         /**
29255          * @event beforeresize
29256          * Fired before resize is allowed. Set enabled to false to cancel resize.
29257          * @param {Roo.Resizable} this
29258          * @param {Roo.EventObject} e The mousedown event
29259          */
29260         "beforeresize" : true,
29261         /**
29262          * @event resizing
29263          * Fired a resizing.
29264          * @param {Roo.Resizable} this
29265          * @param {Number} x The new x position
29266          * @param {Number} y The new y position
29267          * @param {Number} w The new w width
29268          * @param {Number} h The new h hight
29269          * @param {Roo.EventObject} e The mouseup event
29270          */
29271         "resizing" : true,
29272         /**
29273          * @event resize
29274          * Fired after a resize.
29275          * @param {Roo.Resizable} this
29276          * @param {Number} width The new width
29277          * @param {Number} height The new height
29278          * @param {Roo.EventObject} e The mouseup event
29279          */
29280         "resize" : true
29281     });
29282
29283     if(this.width !== null && this.height !== null){
29284         this.resizeTo(this.width, this.height);
29285     }else{
29286         this.updateChildSize();
29287     }
29288     if(Roo.isIE){
29289         this.el.dom.style.zoom = 1;
29290     }
29291     Roo.Resizable.superclass.constructor.call(this);
29292 };
29293
29294 Roo.extend(Roo.Resizable, Roo.util.Observable, {
29295         resizeChild : false,
29296         adjustments : [0, 0],
29297         minWidth : 5,
29298         minHeight : 5,
29299         maxWidth : 10000,
29300         maxHeight : 10000,
29301         enabled : true,
29302         animate : false,
29303         duration : .35,
29304         dynamic : false,
29305         handles : false,
29306         multiDirectional : false,
29307         disableTrackOver : false,
29308         easing : 'easeOutStrong',
29309         widthIncrement : 0,
29310         heightIncrement : 0,
29311         pinned : false,
29312         width : null,
29313         height : null,
29314         preserveRatio : false,
29315         transparent: false,
29316         minX: 0,
29317         minY: 0,
29318         draggable: false,
29319
29320         /**
29321          * @cfg {String/HTMLElement/Element} constrainTo Constrain the resize to a particular element
29322          */
29323         constrainTo: undefined,
29324         /**
29325          * @cfg {Roo.lib.Region} resizeRegion Constrain the resize to a particular region
29326          */
29327         resizeRegion: undefined,
29328
29329
29330     /**
29331      * Perform a manual resize
29332      * @param {Number} width
29333      * @param {Number} height
29334      */
29335     resizeTo : function(width, height){
29336         this.el.setSize(width, height);
29337         this.updateChildSize();
29338         this.fireEvent("resize", this, width, height, null);
29339     },
29340
29341     // private
29342     startSizing : function(e, handle){
29343         this.fireEvent("beforeresize", this, e);
29344         if(this.enabled){ // 2nd enabled check in case disabled before beforeresize handler
29345
29346             if(!this.overlay){
29347                 this.overlay = this.el.createProxy({tag: "div", cls: "x-resizable-overlay", html: "&#160;"});
29348                 this.overlay.unselectable();
29349                 this.overlay.enableDisplayMode("block");
29350                 this.overlay.on("mousemove", this.onMouseMove, this);
29351                 this.overlay.on("mouseup", this.onMouseUp, this);
29352             }
29353             this.overlay.setStyle("cursor", handle.el.getStyle("cursor"));
29354
29355             this.resizing = true;
29356             this.startBox = this.el.getBox();
29357             this.startPoint = e.getXY();
29358             this.offsets = [(this.startBox.x + this.startBox.width) - this.startPoint[0],
29359                             (this.startBox.y + this.startBox.height) - this.startPoint[1]];
29360
29361             this.overlay.setSize(Roo.lib.Dom.getViewWidth(true), Roo.lib.Dom.getViewHeight(true));
29362             this.overlay.show();
29363
29364             if(this.constrainTo) {
29365                 var ct = Roo.get(this.constrainTo);
29366                 this.resizeRegion = ct.getRegion().adjust(
29367                     ct.getFrameWidth('t'),
29368                     ct.getFrameWidth('l'),
29369                     -ct.getFrameWidth('b'),
29370                     -ct.getFrameWidth('r')
29371                 );
29372             }
29373
29374             this.proxy.setStyle('visibility', 'hidden'); // workaround display none
29375             this.proxy.show();
29376             this.proxy.setBox(this.startBox);
29377             if(!this.dynamic){
29378                 this.proxy.setStyle('visibility', 'visible');
29379             }
29380         }
29381     },
29382
29383     // private
29384     onMouseDown : function(handle, e){
29385         if(this.enabled){
29386             e.stopEvent();
29387             this.activeHandle = handle;
29388             this.startSizing(e, handle);
29389         }
29390     },
29391
29392     // private
29393     onMouseUp : function(e){
29394         var size = this.resizeElement();
29395         this.resizing = false;
29396         this.handleOut();
29397         this.overlay.hide();
29398         this.proxy.hide();
29399         this.fireEvent("resize", this, size.width, size.height, e);
29400     },
29401
29402     // private
29403     updateChildSize : function(){
29404         
29405         if(this.resizeChild){
29406             var el = this.el;
29407             var child = this.resizeChild;
29408             var adj = this.adjustments;
29409             if(el.dom.offsetWidth){
29410                 var b = el.getSize(true);
29411                 child.setSize(b.width+adj[0], b.height+adj[1]);
29412             }
29413             // Second call here for IE
29414             // The first call enables instant resizing and
29415             // the second call corrects scroll bars if they
29416             // exist
29417             if(Roo.isIE){
29418                 setTimeout(function(){
29419                     if(el.dom.offsetWidth){
29420                         var b = el.getSize(true);
29421                         child.setSize(b.width+adj[0], b.height+adj[1]);
29422                     }
29423                 }, 10);
29424             }
29425         }
29426     },
29427
29428     // private
29429     snap : function(value, inc, min){
29430         if(!inc || !value) return value;
29431         var newValue = value;
29432         var m = value % inc;
29433         if(m > 0){
29434             if(m > (inc/2)){
29435                 newValue = value + (inc-m);
29436             }else{
29437                 newValue = value - m;
29438             }
29439         }
29440         return Math.max(min, newValue);
29441     },
29442
29443     // private
29444     resizeElement : function(){
29445         var box = this.proxy.getBox();
29446         if(this.updateBox){
29447             this.el.setBox(box, false, this.animate, this.duration, null, this.easing);
29448         }else{
29449             this.el.setSize(box.width, box.height, this.animate, this.duration, null, this.easing);
29450         }
29451         this.updateChildSize();
29452         if(!this.dynamic){
29453             this.proxy.hide();
29454         }
29455         return box;
29456     },
29457
29458     // private
29459     constrain : function(v, diff, m, mx){
29460         if(v - diff < m){
29461             diff = v - m;
29462         }else if(v - diff > mx){
29463             diff = mx - v;
29464         }
29465         return diff;
29466     },
29467
29468     // private
29469     onMouseMove : function(e){
29470         
29471         if(this.enabled){
29472             try{// try catch so if something goes wrong the user doesn't get hung
29473
29474             if(this.resizeRegion && !this.resizeRegion.contains(e.getPoint())) {
29475                 return;
29476             }
29477
29478             //var curXY = this.startPoint;
29479             var curSize = this.curSize || this.startBox;
29480             var x = this.startBox.x, y = this.startBox.y;
29481             var ox = x, oy = y;
29482             var w = curSize.width, h = curSize.height;
29483             var ow = w, oh = h;
29484             var mw = this.minWidth, mh = this.minHeight;
29485             var mxw = this.maxWidth, mxh = this.maxHeight;
29486             var wi = this.widthIncrement;
29487             var hi = this.heightIncrement;
29488
29489             var eventXY = e.getXY();
29490             var diffX = -(this.startPoint[0] - Math.max(this.minX, eventXY[0]));
29491             var diffY = -(this.startPoint[1] - Math.max(this.minY, eventXY[1]));
29492
29493             var pos = this.activeHandle.position;
29494
29495             switch(pos){
29496                 case "east":
29497                     w += diffX;
29498                     w = Math.min(Math.max(mw, w), mxw);
29499                     break;
29500              
29501                 case "south":
29502                     h += diffY;
29503                     h = Math.min(Math.max(mh, h), mxh);
29504                     break;
29505                 case "southeast":
29506                     w += diffX;
29507                     h += diffY;
29508                     w = Math.min(Math.max(mw, w), mxw);
29509                     h = Math.min(Math.max(mh, h), mxh);
29510                     break;
29511                 case "north":
29512                     diffY = this.constrain(h, diffY, mh, mxh);
29513                     y += diffY;
29514                     h -= diffY;
29515                     break;
29516                 case "hdrag":
29517                     
29518                     if (wi) {
29519                         var adiffX = Math.abs(diffX);
29520                         var sub = (adiffX % wi); // how much 
29521                         if (sub > (wi/2)) { // far enough to snap
29522                             diffX = (diffX > 0) ? diffX-sub + wi : diffX+sub - wi;
29523                         } else {
29524                             // remove difference.. 
29525                             diffX = (diffX > 0) ? diffX-sub : diffX+sub;
29526                         }
29527                     }
29528                     x += diffX;
29529                     x = Math.max(this.minX, x);
29530                     break;
29531                 case "west":
29532                     diffX = this.constrain(w, diffX, mw, mxw);
29533                     x += diffX;
29534                     w -= diffX;
29535                     break;
29536                 case "northeast":
29537                     w += diffX;
29538                     w = Math.min(Math.max(mw, w), mxw);
29539                     diffY = this.constrain(h, diffY, mh, mxh);
29540                     y += diffY;
29541                     h -= diffY;
29542                     break;
29543                 case "northwest":
29544                     diffX = this.constrain(w, diffX, mw, mxw);
29545                     diffY = this.constrain(h, diffY, mh, mxh);
29546                     y += diffY;
29547                     h -= diffY;
29548                     x += diffX;
29549                     w -= diffX;
29550                     break;
29551                case "southwest":
29552                     diffX = this.constrain(w, diffX, mw, mxw);
29553                     h += diffY;
29554                     h = Math.min(Math.max(mh, h), mxh);
29555                     x += diffX;
29556                     w -= diffX;
29557                     break;
29558             }
29559
29560             var sw = this.snap(w, wi, mw);
29561             var sh = this.snap(h, hi, mh);
29562             if(sw != w || sh != h){
29563                 switch(pos){
29564                     case "northeast":
29565                         y -= sh - h;
29566                     break;
29567                     case "north":
29568                         y -= sh - h;
29569                         break;
29570                     case "southwest":
29571                         x -= sw - w;
29572                     break;
29573                     case "west":
29574                         x -= sw - w;
29575                         break;
29576                     case "northwest":
29577                         x -= sw - w;
29578                         y -= sh - h;
29579                     break;
29580                 }
29581                 w = sw;
29582                 h = sh;
29583             }
29584
29585             if(this.preserveRatio){
29586                 switch(pos){
29587                     case "southeast":
29588                     case "east":
29589                         h = oh * (w/ow);
29590                         h = Math.min(Math.max(mh, h), mxh);
29591                         w = ow * (h/oh);
29592                        break;
29593                     case "south":
29594                         w = ow * (h/oh);
29595                         w = Math.min(Math.max(mw, w), mxw);
29596                         h = oh * (w/ow);
29597                         break;
29598                     case "northeast":
29599                         w = ow * (h/oh);
29600                         w = Math.min(Math.max(mw, w), mxw);
29601                         h = oh * (w/ow);
29602                     break;
29603                     case "north":
29604                         var tw = w;
29605                         w = ow * (h/oh);
29606                         w = Math.min(Math.max(mw, w), mxw);
29607                         h = oh * (w/ow);
29608                         x += (tw - w) / 2;
29609                         break;
29610                     case "southwest":
29611                         h = oh * (w/ow);
29612                         h = Math.min(Math.max(mh, h), mxh);
29613                         var tw = w;
29614                         w = ow * (h/oh);
29615                         x += tw - w;
29616                         break;
29617                     case "west":
29618                         var th = h;
29619                         h = oh * (w/ow);
29620                         h = Math.min(Math.max(mh, h), mxh);
29621                         y += (th - h) / 2;
29622                         var tw = w;
29623                         w = ow * (h/oh);
29624                         x += tw - w;
29625                        break;
29626                     case "northwest":
29627                         var tw = w;
29628                         var th = h;
29629                         h = oh * (w/ow);
29630                         h = Math.min(Math.max(mh, h), mxh);
29631                         w = ow * (h/oh);
29632                         y += th - h;
29633                         x += tw - w;
29634                        break;
29635
29636                 }
29637             }
29638             if (pos == 'hdrag') {
29639                 w = ow;
29640             }
29641             this.proxy.setBounds(x, y, w, h);
29642             if(this.dynamic){
29643                 this.resizeElement();
29644             }
29645             }catch(e){}
29646         }
29647         this.fireEvent("resizing", this, x, y, w, h, e);
29648     },
29649
29650     // private
29651     handleOver : function(){
29652         if(this.enabled){
29653             this.el.addClass("x-resizable-over");
29654         }
29655     },
29656
29657     // private
29658     handleOut : function(){
29659         if(!this.resizing){
29660             this.el.removeClass("x-resizable-over");
29661         }
29662     },
29663
29664     /**
29665      * Returns the element this component is bound to.
29666      * @return {Roo.Element}
29667      */
29668     getEl : function(){
29669         return this.el;
29670     },
29671
29672     /**
29673      * Returns the resizeChild element (or null).
29674      * @return {Roo.Element}
29675      */
29676     getResizeChild : function(){
29677         return this.resizeChild;
29678     },
29679     groupHandler : function()
29680     {
29681         
29682     },
29683     /**
29684      * Destroys this resizable. If the element was wrapped and
29685      * removeEl is not true then the element remains.
29686      * @param {Boolean} removeEl (optional) true to remove the element from the DOM
29687      */
29688     destroy : function(removeEl){
29689         this.proxy.remove();
29690         if(this.overlay){
29691             this.overlay.removeAllListeners();
29692             this.overlay.remove();
29693         }
29694         var ps = Roo.Resizable.positions;
29695         for(var k in ps){
29696             if(typeof ps[k] != "function" && this[ps[k]]){
29697                 var h = this[ps[k]];
29698                 h.el.removeAllListeners();
29699                 h.el.remove();
29700             }
29701         }
29702         if(removeEl){
29703             this.el.update("");
29704             this.el.remove();
29705         }
29706     }
29707 });
29708
29709 // private
29710 // hash to map config positions to true positions
29711 Roo.Resizable.positions = {
29712     n: "north", s: "south", e: "east", w: "west", se: "southeast", sw: "southwest", nw: "northwest", ne: "northeast", 
29713     hd: "hdrag"
29714 };
29715
29716 // private
29717 Roo.Resizable.Handle = function(rz, pos, disableTrackOver, transparent){
29718     if(!this.tpl){
29719         // only initialize the template if resizable is used
29720         var tpl = Roo.DomHelper.createTemplate(
29721             {tag: "div", cls: "x-resizable-handle x-resizable-handle-{0}"}
29722         );
29723         tpl.compile();
29724         Roo.Resizable.Handle.prototype.tpl = tpl;
29725     }
29726     this.position = pos;
29727     this.rz = rz;
29728     // show north drag fro topdra
29729     var handlepos = pos == 'hdrag' ? 'north' : pos;
29730     
29731     this.el = this.tpl.append(rz.el.dom, [handlepos], true);
29732     if (pos == 'hdrag') {
29733         this.el.setStyle('cursor', 'pointer');
29734     }
29735     this.el.unselectable();
29736     if(transparent){
29737         this.el.setOpacity(0);
29738     }
29739     this.el.on("mousedown", this.onMouseDown, this);
29740     if(!disableTrackOver){
29741         this.el.on("mouseover", this.onMouseOver, this);
29742         this.el.on("mouseout", this.onMouseOut, this);
29743     }
29744 };
29745
29746 // private
29747 Roo.Resizable.Handle.prototype = {
29748     afterResize : function(rz){
29749         Roo.log('after?');
29750         // do nothing
29751     },
29752     // private
29753     onMouseDown : function(e){
29754         this.rz.onMouseDown(this, e);
29755     },
29756     // private
29757     onMouseOver : function(e){
29758         this.rz.handleOver(this, e);
29759     },
29760     // private
29761     onMouseOut : function(e){
29762         this.rz.handleOut(this, e);
29763     }
29764 };/*
29765  * Based on:
29766  * Ext JS Library 1.1.1
29767  * Copyright(c) 2006-2007, Ext JS, LLC.
29768  *
29769  * Originally Released Under LGPL - original licence link has changed is not relivant.
29770  *
29771  * Fork - LGPL
29772  * <script type="text/javascript">
29773  */
29774
29775 /**
29776  * @class Roo.Editor
29777  * @extends Roo.Component
29778  * A base editor field that handles displaying/hiding on demand and has some built-in sizing and event handling logic.
29779  * @constructor
29780  * Create a new Editor
29781  * @param {Roo.form.Field} field The Field object (or descendant)
29782  * @param {Object} config The config object
29783  */
29784 Roo.Editor = function(field, config){
29785     Roo.Editor.superclass.constructor.call(this, config);
29786     this.field = field;
29787     this.addEvents({
29788         /**
29789              * @event beforestartedit
29790              * Fires when editing is initiated, but before the value changes.  Editing can be canceled by returning
29791              * false from the handler of this event.
29792              * @param {Editor} this
29793              * @param {Roo.Element} boundEl The underlying element bound to this editor
29794              * @param {Mixed} value The field value being set
29795              */
29796         "beforestartedit" : true,
29797         /**
29798              * @event startedit
29799              * Fires when this editor is displayed
29800              * @param {Roo.Element} boundEl The underlying element bound to this editor
29801              * @param {Mixed} value The starting field value
29802              */
29803         "startedit" : true,
29804         /**
29805              * @event beforecomplete
29806              * Fires after a change has been made to the field, but before the change is reflected in the underlying
29807              * field.  Saving the change to the field can be canceled by returning false from the handler of this event.
29808              * Note that if the value has not changed and ignoreNoChange = true, the editing will still end but this
29809              * event will not fire since no edit actually occurred.
29810              * @param {Editor} this
29811              * @param {Mixed} value The current field value
29812              * @param {Mixed} startValue The original field value
29813              */
29814         "beforecomplete" : true,
29815         /**
29816              * @event complete
29817              * Fires after editing is complete and any changed value has been written to the underlying field.
29818              * @param {Editor} this
29819              * @param {Mixed} value The current field value
29820              * @param {Mixed} startValue The original field value
29821              */
29822         "complete" : true,
29823         /**
29824          * @event specialkey
29825          * Fires when any key related to navigation (arrows, tab, enter, esc, etc.) is pressed.  You can check
29826          * {@link Roo.EventObject#getKey} to determine which key was pressed.
29827          * @param {Roo.form.Field} this
29828          * @param {Roo.EventObject} e The event object
29829          */
29830         "specialkey" : true
29831     });
29832 };
29833
29834 Roo.extend(Roo.Editor, Roo.Component, {
29835     /**
29836      * @cfg {Boolean/String} autosize
29837      * True for the editor to automatically adopt the size of the underlying field, "width" to adopt the width only,
29838      * or "height" to adopt the height only (defaults to false)
29839      */
29840     /**
29841      * @cfg {Boolean} revertInvalid
29842      * True to automatically revert the field value and cancel the edit when the user completes an edit and the field
29843      * validation fails (defaults to true)
29844      */
29845     /**
29846      * @cfg {Boolean} ignoreNoChange
29847      * True to skip the the edit completion process (no save, no events fired) if the user completes an edit and
29848      * the value has not changed (defaults to false).  Applies only to string values - edits for other data types
29849      * will never be ignored.
29850      */
29851     /**
29852      * @cfg {Boolean} hideEl
29853      * False to keep the bound element visible while the editor is displayed (defaults to true)
29854      */
29855     /**
29856      * @cfg {Mixed} value
29857      * The data value of the underlying field (defaults to "")
29858      */
29859     value : "",
29860     /**
29861      * @cfg {String} alignment
29862      * The position to align to (see {@link Roo.Element#alignTo} for more details, defaults to "c-c?").
29863      */
29864     alignment: "c-c?",
29865     /**
29866      * @cfg {Boolean/String} shadow "sides" for sides/bottom only, "frame" for 4-way shadow, and "drop"
29867      * for bottom-right shadow (defaults to "frame")
29868      */
29869     shadow : "frame",
29870     /**
29871      * @cfg {Boolean} constrain True to constrain the editor to the viewport
29872      */
29873     constrain : false,
29874     /**
29875      * @cfg {Boolean} completeOnEnter True to complete the edit when the enter key is pressed (defaults to false)
29876      */
29877     completeOnEnter : false,
29878     /**
29879      * @cfg {Boolean} cancelOnEsc True to cancel the edit when the escape key is pressed (defaults to false)
29880      */
29881     cancelOnEsc : false,
29882     /**
29883      * @cfg {Boolean} updateEl True to update the innerHTML of the bound element when the update completes (defaults to false)
29884      */
29885     updateEl : false,
29886
29887     // private
29888     onRender : function(ct, position){
29889         this.el = new Roo.Layer({
29890             shadow: this.shadow,
29891             cls: "x-editor",
29892             parentEl : ct,
29893             shim : this.shim,
29894             shadowOffset:4,
29895             id: this.id,
29896             constrain: this.constrain
29897         });
29898         this.el.setStyle("overflow", Roo.isGecko ? "auto" : "hidden");
29899         if(this.field.msgTarget != 'title'){
29900             this.field.msgTarget = 'qtip';
29901         }
29902         this.field.render(this.el);
29903         if(Roo.isGecko){
29904             this.field.el.dom.setAttribute('autocomplete', 'off');
29905         }
29906         this.field.on("specialkey", this.onSpecialKey, this);
29907         if(this.swallowKeys){
29908             this.field.el.swallowEvent(['keydown','keypress']);
29909         }
29910         this.field.show();
29911         this.field.on("blur", this.onBlur, this);
29912         if(this.field.grow){
29913             this.field.on("autosize", this.el.sync,  this.el, {delay:1});
29914         }
29915     },
29916
29917     onSpecialKey : function(field, e)
29918     {
29919         //Roo.log('editor onSpecialKey');
29920         if(this.completeOnEnter && e.getKey() == e.ENTER){
29921             e.stopEvent();
29922             this.completeEdit();
29923             return;
29924         }
29925         // do not fire special key otherwise it might hide close the editor...
29926         if(e.getKey() == e.ENTER){    
29927             return;
29928         }
29929         if(this.cancelOnEsc && e.getKey() == e.ESC){
29930             this.cancelEdit();
29931             return;
29932         } 
29933         this.fireEvent('specialkey', field, e);
29934     
29935     },
29936
29937     /**
29938      * Starts the editing process and shows the editor.
29939      * @param {String/HTMLElement/Element} el The element to edit
29940      * @param {String} value (optional) A value to initialize the editor with. If a value is not provided, it defaults
29941       * to the innerHTML of el.
29942      */
29943     startEdit : function(el, value){
29944         if(this.editing){
29945             this.completeEdit();
29946         }
29947         this.boundEl = Roo.get(el);
29948         var v = value !== undefined ? value : this.boundEl.dom.innerHTML;
29949         if(!this.rendered){
29950             this.render(this.parentEl || document.body);
29951         }
29952         if(this.fireEvent("beforestartedit", this, this.boundEl, v) === false){
29953             return;
29954         }
29955         this.startValue = v;
29956         this.field.setValue(v);
29957         if(this.autoSize){
29958             var sz = this.boundEl.getSize();
29959             switch(this.autoSize){
29960                 case "width":
29961                 this.setSize(sz.width,  "");
29962                 break;
29963                 case "height":
29964                 this.setSize("",  sz.height);
29965                 break;
29966                 default:
29967                 this.setSize(sz.width,  sz.height);
29968             }
29969         }
29970         this.el.alignTo(this.boundEl, this.alignment);
29971         this.editing = true;
29972         if(Roo.QuickTips){
29973             Roo.QuickTips.disable();
29974         }
29975         this.show();
29976     },
29977
29978     /**
29979      * Sets the height and width of this editor.
29980      * @param {Number} width The new width
29981      * @param {Number} height The new height
29982      */
29983     setSize : function(w, h){
29984         this.field.setSize(w, h);
29985         if(this.el){
29986             this.el.sync();
29987         }
29988     },
29989
29990     /**
29991      * Realigns the editor to the bound field based on the current alignment config value.
29992      */
29993     realign : function(){
29994         this.el.alignTo(this.boundEl, this.alignment);
29995     },
29996
29997     /**
29998      * Ends the editing process, persists the changed value to the underlying field, and hides the editor.
29999      * @param {Boolean} remainVisible Override the default behavior and keep the editor visible after edit (defaults to false)
30000      */
30001     completeEdit : function(remainVisible){
30002         if(!this.editing){
30003             return;
30004         }
30005         var v = this.getValue();
30006         if(this.revertInvalid !== false && !this.field.isValid()){
30007             v = this.startValue;
30008             this.cancelEdit(true);
30009         }
30010         if(String(v) === String(this.startValue) && this.ignoreNoChange){
30011             this.editing = false;
30012             this.hide();
30013             return;
30014         }
30015         if(this.fireEvent("beforecomplete", this, v, this.startValue) !== false){
30016             this.editing = false;
30017             if(this.updateEl && this.boundEl){
30018                 this.boundEl.update(v);
30019             }
30020             if(remainVisible !== true){
30021                 this.hide();
30022             }
30023             this.fireEvent("complete", this, v, this.startValue);
30024         }
30025     },
30026
30027     // private
30028     onShow : function(){
30029         this.el.show();
30030         if(this.hideEl !== false){
30031             this.boundEl.hide();
30032         }
30033         this.field.show();
30034         if(Roo.isIE && !this.fixIEFocus){ // IE has problems with focusing the first time
30035             this.fixIEFocus = true;
30036             this.deferredFocus.defer(50, this);
30037         }else{
30038             this.field.focus();
30039         }
30040         this.fireEvent("startedit", this.boundEl, this.startValue);
30041     },
30042
30043     deferredFocus : function(){
30044         if(this.editing){
30045             this.field.focus();
30046         }
30047     },
30048
30049     /**
30050      * Cancels the editing process and hides the editor without persisting any changes.  The field value will be
30051      * reverted to the original starting value.
30052      * @param {Boolean} remainVisible Override the default behavior and keep the editor visible after
30053      * cancel (defaults to false)
30054      */
30055     cancelEdit : function(remainVisible){
30056         if(this.editing){
30057             this.setValue(this.startValue);
30058             if(remainVisible !== true){
30059                 this.hide();
30060             }
30061         }
30062     },
30063
30064     // private
30065     onBlur : function(){
30066         if(this.allowBlur !== true && this.editing){
30067             this.completeEdit();
30068         }
30069     },
30070
30071     // private
30072     onHide : function(){
30073         if(this.editing){
30074             this.completeEdit();
30075             return;
30076         }
30077         this.field.blur();
30078         if(this.field.collapse){
30079             this.field.collapse();
30080         }
30081         this.el.hide();
30082         if(this.hideEl !== false){
30083             this.boundEl.show();
30084         }
30085         if(Roo.QuickTips){
30086             Roo.QuickTips.enable();
30087         }
30088     },
30089
30090     /**
30091      * Sets the data value of the editor
30092      * @param {Mixed} value Any valid value supported by the underlying field
30093      */
30094     setValue : function(v){
30095         this.field.setValue(v);
30096     },
30097
30098     /**
30099      * Gets the data value of the editor
30100      * @return {Mixed} The data value
30101      */
30102     getValue : function(){
30103         return this.field.getValue();
30104     }
30105 });/*
30106  * Based on:
30107  * Ext JS Library 1.1.1
30108  * Copyright(c) 2006-2007, Ext JS, LLC.
30109  *
30110  * Originally Released Under LGPL - original licence link has changed is not relivant.
30111  *
30112  * Fork - LGPL
30113  * <script type="text/javascript">
30114  */
30115  
30116 /**
30117  * @class Roo.BasicDialog
30118  * @extends Roo.util.Observable
30119  * Lightweight Dialog Class.  The code below shows the creation of a typical dialog using existing HTML markup:
30120  * <pre><code>
30121 var dlg = new Roo.BasicDialog("my-dlg", {
30122     height: 200,
30123     width: 300,
30124     minHeight: 100,
30125     minWidth: 150,
30126     modal: true,
30127     proxyDrag: true,
30128     shadow: true
30129 });
30130 dlg.addKeyListener(27, dlg.hide, dlg); // ESC can also close the dialog
30131 dlg.addButton('OK', dlg.hide, dlg);    // Could call a save function instead of hiding
30132 dlg.addButton('Cancel', dlg.hide, dlg);
30133 dlg.show();
30134 </code></pre>
30135   <b>A Dialog should always be a direct child of the body element.</b>
30136  * @cfg {Boolean/DomHelper} autoCreate True to auto create from scratch, or using a DomHelper Object (defaults to false)
30137  * @cfg {String} title Default text to display in the title bar (defaults to null)
30138  * @cfg {Number} width Width of the dialog in pixels (can also be set via CSS).  Determined by browser if unspecified.
30139  * @cfg {Number} height Height of the dialog in pixels (can also be set via CSS).  Determined by browser if unspecified.
30140  * @cfg {Number} x The default left page coordinate of the dialog (defaults to center screen)
30141  * @cfg {Number} y The default top page coordinate of the dialog (defaults to center screen)
30142  * @cfg {String/Element} animateTarget Id or element from which the dialog should animate while opening
30143  * (defaults to null with no animation)
30144  * @cfg {Boolean} resizable False to disable manual dialog resizing (defaults to true)
30145  * @cfg {String} resizeHandles Which resize handles to display - see the {@link Roo.Resizable} handles config
30146  * property for valid values (defaults to 'all')
30147  * @cfg {Number} minHeight The minimum allowable height for a resizable dialog (defaults to 80)
30148  * @cfg {Number} minWidth The minimum allowable width for a resizable dialog (defaults to 200)
30149  * @cfg {Boolean} modal True to show the dialog modally, preventing user interaction with the rest of the page (defaults to false)
30150  * @cfg {Boolean} autoScroll True to allow the dialog body contents to overflow and display scrollbars (defaults to false)
30151  * @cfg {Boolean} closable False to remove the built-in top-right corner close button (defaults to true)
30152  * @cfg {Boolean} collapsible False to remove the built-in top-right corner collapse button (defaults to true)
30153  * @cfg {Boolean} constraintoviewport True to keep the dialog constrained within the visible viewport boundaries (defaults to true)
30154  * @cfg {Boolean} syncHeightBeforeShow True to cause the dimensions to be recalculated before the dialog is shown (defaults to false)
30155  * @cfg {Boolean} draggable False to disable dragging of the dialog within the viewport (defaults to true)
30156  * @cfg {Boolean} autoTabs If true, all elements with class 'x-dlg-tab' will get automatically converted to tabs (defaults to false)
30157  * @cfg {String} tabTag The tag name of tab elements, used when autoTabs = true (defaults to 'div')
30158  * @cfg {Boolean} proxyDrag True to drag a lightweight proxy element rather than the dialog itself, used when
30159  * draggable = true (defaults to false)
30160  * @cfg {Boolean} fixedcenter True to ensure that anytime the dialog is shown or resized it gets centered (defaults to false)
30161  * @cfg {Boolean/String} shadow True or "sides" for the default effect, "frame" for 4-way shadow, and "drop" for bottom-right
30162  * shadow (defaults to false)
30163  * @cfg {Number} shadowOffset The number of pixels to offset the shadow if displayed (defaults to 5)
30164  * @cfg {String} buttonAlign Valid values are "left," "center" and "right" (defaults to "right")
30165  * @cfg {Number} minButtonWidth Minimum width of all dialog buttons (defaults to 75)
30166  * @cfg {Array} buttons Array of buttons
30167  * @cfg {Boolean} shim True to create an iframe shim that prevents selects from showing through (defaults to false)
30168  * @constructor
30169  * Create a new BasicDialog.
30170  * @param {String/HTMLElement/Roo.Element} el The container element or DOM node, or its id
30171  * @param {Object} config Configuration options
30172  */
30173 Roo.BasicDialog = function(el, config){
30174     this.el = Roo.get(el);
30175     var dh = Roo.DomHelper;
30176     if(!this.el && config && config.autoCreate){
30177         if(typeof config.autoCreate == "object"){
30178             if(!config.autoCreate.id){
30179                 config.autoCreate.id = el;
30180             }
30181             this.el = dh.append(document.body,
30182                         config.autoCreate, true);
30183         }else{
30184             this.el = dh.append(document.body,
30185                         {tag: "div", id: el, style:'visibility:hidden;'}, true);
30186         }
30187     }
30188     el = this.el;
30189     el.setDisplayed(true);
30190     el.hide = this.hideAction;
30191     this.id = el.id;
30192     el.addClass("x-dlg");
30193
30194     Roo.apply(this, config);
30195
30196     this.proxy = el.createProxy("x-dlg-proxy");
30197     this.proxy.hide = this.hideAction;
30198     this.proxy.setOpacity(.5);
30199     this.proxy.hide();
30200
30201     if(config.width){
30202         el.setWidth(config.width);
30203     }
30204     if(config.height){
30205         el.setHeight(config.height);
30206     }
30207     this.size = el.getSize();
30208     if(typeof config.x != "undefined" && typeof config.y != "undefined"){
30209         this.xy = [config.x,config.y];
30210     }else{
30211         this.xy = el.getCenterXY(true);
30212     }
30213     /** The header element @type Roo.Element */
30214     this.header = el.child("> .x-dlg-hd");
30215     /** The body element @type Roo.Element */
30216     this.body = el.child("> .x-dlg-bd");
30217     /** The footer element @type Roo.Element */
30218     this.footer = el.child("> .x-dlg-ft");
30219
30220     if(!this.header){
30221         this.header = el.createChild({tag: "div", cls:"x-dlg-hd", html: "&#160;"}, this.body ? this.body.dom : null);
30222     }
30223     if(!this.body){
30224         this.body = el.createChild({tag: "div", cls:"x-dlg-bd"});
30225     }
30226
30227     this.header.unselectable();
30228     if(this.title){
30229         this.header.update(this.title);
30230     }
30231     // this element allows the dialog to be focused for keyboard event
30232     this.focusEl = el.createChild({tag: "a", href:"#", cls:"x-dlg-focus", tabIndex:"-1"});
30233     this.focusEl.swallowEvent("click", true);
30234
30235     this.header.wrap({cls:"x-dlg-hd-right"}).wrap({cls:"x-dlg-hd-left"}, true);
30236
30237     // wrap the body and footer for special rendering
30238     this.bwrap = this.body.wrap({tag: "div", cls:"x-dlg-dlg-body"});
30239     if(this.footer){
30240         this.bwrap.dom.appendChild(this.footer.dom);
30241     }
30242
30243     this.bg = this.el.createChild({
30244         tag: "div", cls:"x-dlg-bg",
30245         html: '<div class="x-dlg-bg-left"><div class="x-dlg-bg-right"><div class="x-dlg-bg-center">&#160;</div></div></div>'
30246     });
30247     this.centerBg = this.bg.child("div.x-dlg-bg-center");
30248
30249
30250     if(this.autoScroll !== false && !this.autoTabs){
30251         this.body.setStyle("overflow", "auto");
30252     }
30253
30254     this.toolbox = this.el.createChild({cls: "x-dlg-toolbox"});
30255
30256     if(this.closable !== false){
30257         this.el.addClass("x-dlg-closable");
30258         this.close = this.toolbox.createChild({cls:"x-dlg-close"});
30259         this.close.on("click", this.closeClick, this);
30260         this.close.addClassOnOver("x-dlg-close-over");
30261     }
30262     if(this.collapsible !== false){
30263         this.collapseBtn = this.toolbox.createChild({cls:"x-dlg-collapse"});
30264         this.collapseBtn.on("click", this.collapseClick, this);
30265         this.collapseBtn.addClassOnOver("x-dlg-collapse-over");
30266         this.header.on("dblclick", this.collapseClick, this);
30267     }
30268     if(this.resizable !== false){
30269         this.el.addClass("x-dlg-resizable");
30270         this.resizer = new Roo.Resizable(el, {
30271             minWidth: this.minWidth || 80,
30272             minHeight:this.minHeight || 80,
30273             handles: this.resizeHandles || "all",
30274             pinned: true
30275         });
30276         this.resizer.on("beforeresize", this.beforeResize, this);
30277         this.resizer.on("resize", this.onResize, this);
30278     }
30279     if(this.draggable !== false){
30280         el.addClass("x-dlg-draggable");
30281         if (!this.proxyDrag) {
30282             var dd = new Roo.dd.DD(el.dom.id, "WindowDrag");
30283         }
30284         else {
30285             var dd = new Roo.dd.DDProxy(el.dom.id, "WindowDrag", {dragElId: this.proxy.id});
30286         }
30287         dd.setHandleElId(this.header.id);
30288         dd.endDrag = this.endMove.createDelegate(this);
30289         dd.startDrag = this.startMove.createDelegate(this);
30290         dd.onDrag = this.onDrag.createDelegate(this);
30291         dd.scroll = false;
30292         this.dd = dd;
30293     }
30294     if(this.modal){
30295         this.mask = dh.append(document.body, {tag: "div", cls:"x-dlg-mask"}, true);
30296         this.mask.enableDisplayMode("block");
30297         this.mask.hide();
30298         this.el.addClass("x-dlg-modal");
30299     }
30300     if(this.shadow){
30301         this.shadow = new Roo.Shadow({
30302             mode : typeof this.shadow == "string" ? this.shadow : "sides",
30303             offset : this.shadowOffset
30304         });
30305     }else{
30306         this.shadowOffset = 0;
30307     }
30308     if(Roo.useShims && this.shim !== false){
30309         this.shim = this.el.createShim();
30310         this.shim.hide = this.hideAction;
30311         this.shim.hide();
30312     }else{
30313         this.shim = false;
30314     }
30315     if(this.autoTabs){
30316         this.initTabs();
30317     }
30318     if (this.buttons) { 
30319         var bts= this.buttons;
30320         this.buttons = [];
30321         Roo.each(bts, function(b) {
30322             this.addButton(b);
30323         }, this);
30324     }
30325     
30326     
30327     this.addEvents({
30328         /**
30329          * @event keydown
30330          * Fires when a key is pressed
30331          * @param {Roo.BasicDialog} this
30332          * @param {Roo.EventObject} e
30333          */
30334         "keydown" : true,
30335         /**
30336          * @event move
30337          * Fires when this dialog is moved by the user.
30338          * @param {Roo.BasicDialog} this
30339          * @param {Number} x The new page X
30340          * @param {Number} y The new page Y
30341          */
30342         "move" : true,
30343         /**
30344          * @event resize
30345          * Fires when this dialog is resized by the user.
30346          * @param {Roo.BasicDialog} this
30347          * @param {Number} width The new width
30348          * @param {Number} height The new height
30349          */
30350         "resize" : true,
30351         /**
30352          * @event beforehide
30353          * Fires before this dialog is hidden.
30354          * @param {Roo.BasicDialog} this
30355          */
30356         "beforehide" : true,
30357         /**
30358          * @event hide
30359          * Fires when this dialog is hidden.
30360          * @param {Roo.BasicDialog} this
30361          */
30362         "hide" : true,
30363         /**
30364          * @event beforeshow
30365          * Fires before this dialog is shown.
30366          * @param {Roo.BasicDialog} this
30367          */
30368         "beforeshow" : true,
30369         /**
30370          * @event show
30371          * Fires when this dialog is shown.
30372          * @param {Roo.BasicDialog} this
30373          */
30374         "show" : true
30375     });
30376     el.on("keydown", this.onKeyDown, this);
30377     el.on("mousedown", this.toFront, this);
30378     Roo.EventManager.onWindowResize(this.adjustViewport, this, true);
30379     this.el.hide();
30380     Roo.DialogManager.register(this);
30381     Roo.BasicDialog.superclass.constructor.call(this);
30382 };
30383
30384 Roo.extend(Roo.BasicDialog, Roo.util.Observable, {
30385     shadowOffset: Roo.isIE ? 6 : 5,
30386     minHeight: 80,
30387     minWidth: 200,
30388     minButtonWidth: 75,
30389     defaultButton: null,
30390     buttonAlign: "right",
30391     tabTag: 'div',
30392     firstShow: true,
30393
30394     /**
30395      * Sets the dialog title text
30396      * @param {String} text The title text to display
30397      * @return {Roo.BasicDialog} this
30398      */
30399     setTitle : function(text){
30400         this.header.update(text);
30401         return this;
30402     },
30403
30404     // private
30405     closeClick : function(){
30406         this.hide();
30407     },
30408
30409     // private
30410     collapseClick : function(){
30411         this[this.collapsed ? "expand" : "collapse"]();
30412     },
30413
30414     /**
30415      * Collapses the dialog to its minimized state (only the title bar is visible).
30416      * Equivalent to the user clicking the collapse dialog button.
30417      */
30418     collapse : function(){
30419         if(!this.collapsed){
30420             this.collapsed = true;
30421             this.el.addClass("x-dlg-collapsed");
30422             this.restoreHeight = this.el.getHeight();
30423             this.resizeTo(this.el.getWidth(), this.header.getHeight());
30424         }
30425     },
30426
30427     /**
30428      * Expands a collapsed dialog back to its normal state.  Equivalent to the user
30429      * clicking the expand dialog button.
30430      */
30431     expand : function(){
30432         if(this.collapsed){
30433             this.collapsed = false;
30434             this.el.removeClass("x-dlg-collapsed");
30435             this.resizeTo(this.el.getWidth(), this.restoreHeight);
30436         }
30437     },
30438
30439     /**
30440      * Reinitializes the tabs component, clearing out old tabs and finding new ones.
30441      * @return {Roo.TabPanel} The tabs component
30442      */
30443     initTabs : function(){
30444         var tabs = this.getTabs();
30445         while(tabs.getTab(0)){
30446             tabs.removeTab(0);
30447         }
30448         this.el.select(this.tabTag+'.x-dlg-tab').each(function(el){
30449             var dom = el.dom;
30450             tabs.addTab(Roo.id(dom), dom.title);
30451             dom.title = "";
30452         });
30453         tabs.activate(0);
30454         return tabs;
30455     },
30456
30457     // private
30458     beforeResize : function(){
30459         this.resizer.minHeight = Math.max(this.minHeight, this.getHeaderFooterHeight(true)+40);
30460     },
30461
30462     // private
30463     onResize : function(){
30464         this.refreshSize();
30465         this.syncBodyHeight();
30466         this.adjustAssets();
30467         this.focus();
30468         this.fireEvent("resize", this, this.size.width, this.size.height);
30469     },
30470
30471     // private
30472     onKeyDown : function(e){
30473         if(this.isVisible()){
30474             this.fireEvent("keydown", this, e);
30475         }
30476     },
30477
30478     /**
30479      * Resizes the dialog.
30480      * @param {Number} width
30481      * @param {Number} height
30482      * @return {Roo.BasicDialog} this
30483      */
30484     resizeTo : function(width, height){
30485         this.el.setSize(width, height);
30486         this.size = {width: width, height: height};
30487         this.syncBodyHeight();
30488         if(this.fixedcenter){
30489             this.center();
30490         }
30491         if(this.isVisible()){
30492             this.constrainXY();
30493             this.adjustAssets();
30494         }
30495         this.fireEvent("resize", this, width, height);
30496         return this;
30497     },
30498
30499
30500     /**
30501      * Resizes the dialog to fit the specified content size.
30502      * @param {Number} width
30503      * @param {Number} height
30504      * @return {Roo.BasicDialog} this
30505      */
30506     setContentSize : function(w, h){
30507         h += this.getHeaderFooterHeight() + this.body.getMargins("tb");
30508         w += this.body.getMargins("lr") + this.bwrap.getMargins("lr") + this.centerBg.getPadding("lr");
30509         //if(!this.el.isBorderBox()){
30510             h +=  this.body.getPadding("tb") + this.bwrap.getBorderWidth("tb") + this.body.getBorderWidth("tb") + this.el.getBorderWidth("tb");
30511             w += this.body.getPadding("lr") + this.bwrap.getBorderWidth("lr") + this.body.getBorderWidth("lr") + this.bwrap.getPadding("lr") + this.el.getBorderWidth("lr");
30512         //}
30513         if(this.tabs){
30514             h += this.tabs.stripWrap.getHeight() + this.tabs.bodyEl.getMargins("tb") + this.tabs.bodyEl.getPadding("tb");
30515             w += this.tabs.bodyEl.getMargins("lr") + this.tabs.bodyEl.getPadding("lr");
30516         }
30517         this.resizeTo(w, h);
30518         return this;
30519     },
30520
30521     /**
30522      * Adds a key listener for when this dialog is displayed.  This allows you to hook in a function that will be
30523      * executed in response to a particular key being pressed while the dialog is active.
30524      * @param {Number/Array/Object} key Either the numeric key code, array of key codes or an object with the following options:
30525      *                                  {key: (number or array), shift: (true/false), ctrl: (true/false), alt: (true/false)}
30526      * @param {Function} fn The function to call
30527      * @param {Object} scope (optional) The scope of the function
30528      * @return {Roo.BasicDialog} this
30529      */
30530     addKeyListener : function(key, fn, scope){
30531         var keyCode, shift, ctrl, alt;
30532         if(typeof key == "object" && !(key instanceof Array)){
30533             keyCode = key["key"];
30534             shift = key["shift"];
30535             ctrl = key["ctrl"];
30536             alt = key["alt"];
30537         }else{
30538             keyCode = key;
30539         }
30540         var handler = function(dlg, e){
30541             if((!shift || e.shiftKey) && (!ctrl || e.ctrlKey) &&  (!alt || e.altKey)){
30542                 var k = e.getKey();
30543                 if(keyCode instanceof Array){
30544                     for(var i = 0, len = keyCode.length; i < len; i++){
30545                         if(keyCode[i] == k){
30546                           fn.call(scope || window, dlg, k, e);
30547                           return;
30548                         }
30549                     }
30550                 }else{
30551                     if(k == keyCode){
30552                         fn.call(scope || window, dlg, k, e);
30553                     }
30554                 }
30555             }
30556         };
30557         this.on("keydown", handler);
30558         return this;
30559     },
30560
30561     /**
30562      * Returns the TabPanel component (creates it if it doesn't exist).
30563      * Note: If you wish to simply check for the existence of tabs without creating them,
30564      * check for a null 'tabs' property.
30565      * @return {Roo.TabPanel} The tabs component
30566      */
30567     getTabs : function(){
30568         if(!this.tabs){
30569             this.el.addClass("x-dlg-auto-tabs");
30570             this.body.addClass(this.tabPosition == "bottom" ? "x-tabs-bottom" : "x-tabs-top");
30571             this.tabs = new Roo.TabPanel(this.body.dom, this.tabPosition == "bottom");
30572         }
30573         return this.tabs;
30574     },
30575
30576     /**
30577      * Adds a button to the footer section of the dialog.
30578      * @param {String/Object} config A string becomes the button text, an object can either be a Button config
30579      * object or a valid Roo.DomHelper element config
30580      * @param {Function} handler The function called when the button is clicked
30581      * @param {Object} scope (optional) The scope of the handler function (accepts position as a property)
30582      * @return {Roo.Button} The new button
30583      */
30584     addButton : function(config, handler, scope){
30585         var dh = Roo.DomHelper;
30586         if(!this.footer){
30587             this.footer = dh.append(this.bwrap, {tag: "div", cls:"x-dlg-ft"}, true);
30588         }
30589         if(!this.btnContainer){
30590             var tb = this.footer.createChild({
30591
30592                 cls:"x-dlg-btns x-dlg-btns-"+this.buttonAlign,
30593                 html:'<table cellspacing="0"><tbody><tr></tr></tbody></table><div class="x-clear"></div>'
30594             }, null, true);
30595             this.btnContainer = tb.firstChild.firstChild.firstChild;
30596         }
30597         var bconfig = {
30598             handler: handler,
30599             scope: scope,
30600             minWidth: this.minButtonWidth,
30601             hideParent:true
30602         };
30603         if(typeof config == "string"){
30604             bconfig.text = config;
30605         }else{
30606             if(config.tag){
30607                 bconfig.dhconfig = config;
30608             }else{
30609                 Roo.apply(bconfig, config);
30610             }
30611         }
30612         var fc = false;
30613         if ((typeof(bconfig.position) != 'undefined') && bconfig.position < this.btnContainer.childNodes.length-1) {
30614             bconfig.position = Math.max(0, bconfig.position);
30615             fc = this.btnContainer.childNodes[bconfig.position];
30616         }
30617          
30618         var btn = new Roo.Button(
30619             fc ? 
30620                 this.btnContainer.insertBefore(document.createElement("td"),fc)
30621                 : this.btnContainer.appendChild(document.createElement("td")),
30622             //Roo.get(this.btnContainer).createChild( { tag: 'td'},  fc ),
30623             bconfig
30624         );
30625         this.syncBodyHeight();
30626         if(!this.buttons){
30627             /**
30628              * Array of all the buttons that have been added to this dialog via addButton
30629              * @type Array
30630              */
30631             this.buttons = [];
30632         }
30633         this.buttons.push(btn);
30634         return btn;
30635     },
30636
30637     /**
30638      * Sets the default button to be focused when the dialog is displayed.
30639      * @param {Roo.BasicDialog.Button} btn The button object returned by {@link #addButton}
30640      * @return {Roo.BasicDialog} this
30641      */
30642     setDefaultButton : function(btn){
30643         this.defaultButton = btn;
30644         return this;
30645     },
30646
30647     // private
30648     getHeaderFooterHeight : function(safe){
30649         var height = 0;
30650         if(this.header){
30651            height += this.header.getHeight();
30652         }
30653         if(this.footer){
30654            var fm = this.footer.getMargins();
30655             height += (this.footer.getHeight()+fm.top+fm.bottom);
30656         }
30657         height += this.bwrap.getPadding("tb")+this.bwrap.getBorderWidth("tb");
30658         height += this.centerBg.getPadding("tb");
30659         return height;
30660     },
30661
30662     // private
30663     syncBodyHeight : function()
30664     {
30665         var bd = this.body, // the text
30666             cb = this.centerBg, // wrapper around bottom.. but does not seem to be used..
30667             bw = this.bwrap;
30668         var height = this.size.height - this.getHeaderFooterHeight(false);
30669         bd.setHeight(height-bd.getMargins("tb"));
30670         var hh = this.header.getHeight();
30671         var h = this.size.height-hh;
30672         cb.setHeight(h);
30673         
30674         bw.setLeftTop(cb.getPadding("l"), hh+cb.getPadding("t"));
30675         bw.setHeight(h-cb.getPadding("tb"));
30676         
30677         bw.setWidth(this.el.getWidth(true)-cb.getPadding("lr"));
30678         bd.setWidth(bw.getWidth(true));
30679         if(this.tabs){
30680             this.tabs.syncHeight();
30681             if(Roo.isIE){
30682                 this.tabs.el.repaint();
30683             }
30684         }
30685     },
30686
30687     /**
30688      * Restores the previous state of the dialog if Roo.state is configured.
30689      * @return {Roo.BasicDialog} this
30690      */
30691     restoreState : function(){
30692         var box = Roo.state.Manager.get(this.stateId || (this.el.id + "-state"));
30693         if(box && box.width){
30694             this.xy = [box.x, box.y];
30695             this.resizeTo(box.width, box.height);
30696         }
30697         return this;
30698     },
30699
30700     // private
30701     beforeShow : function(){
30702         this.expand();
30703         if(this.fixedcenter){
30704             this.xy = this.el.getCenterXY(true);
30705         }
30706         if(this.modal){
30707             Roo.get(document.body).addClass("x-body-masked");
30708             this.mask.setSize(Roo.lib.Dom.getViewWidth(true), Roo.lib.Dom.getViewHeight(true));
30709             this.mask.show();
30710         }
30711         this.constrainXY();
30712     },
30713
30714     // private
30715     animShow : function(){
30716         var b = Roo.get(this.animateTarget).getBox();
30717         this.proxy.setSize(b.width, b.height);
30718         this.proxy.setLocation(b.x, b.y);
30719         this.proxy.show();
30720         this.proxy.setBounds(this.xy[0], this.xy[1], this.size.width, this.size.height,
30721                     true, .35, this.showEl.createDelegate(this));
30722     },
30723
30724     /**
30725      * Shows the dialog.
30726      * @param {String/HTMLElement/Roo.Element} animateTarget (optional) Reset the animation target
30727      * @return {Roo.BasicDialog} this
30728      */
30729     show : function(animateTarget){
30730         if (this.fireEvent("beforeshow", this) === false){
30731             return;
30732         }
30733         if(this.syncHeightBeforeShow){
30734             this.syncBodyHeight();
30735         }else if(this.firstShow){
30736             this.firstShow = false;
30737             this.syncBodyHeight(); // sync the height on the first show instead of in the constructor
30738         }
30739         this.animateTarget = animateTarget || this.animateTarget;
30740         if(!this.el.isVisible()){
30741             this.beforeShow();
30742             if(this.animateTarget && Roo.get(this.animateTarget)){
30743                 this.animShow();
30744             }else{
30745                 this.showEl();
30746             }
30747         }
30748         return this;
30749     },
30750
30751     // private
30752     showEl : function(){
30753         this.proxy.hide();
30754         this.el.setXY(this.xy);
30755         this.el.show();
30756         this.adjustAssets(true);
30757         this.toFront();
30758         this.focus();
30759         // IE peekaboo bug - fix found by Dave Fenwick
30760         if(Roo.isIE){
30761             this.el.repaint();
30762         }
30763         this.fireEvent("show", this);
30764     },
30765
30766     /**
30767      * Focuses the dialog.  If a defaultButton is set, it will receive focus, otherwise the
30768      * dialog itself will receive focus.
30769      */
30770     focus : function(){
30771         if(this.defaultButton){
30772             this.defaultButton.focus();
30773         }else{
30774             this.focusEl.focus();
30775         }
30776     },
30777
30778     // private
30779     constrainXY : function(){
30780         if(this.constraintoviewport !== false){
30781             if(!this.viewSize){
30782                 if(this.container){
30783                     var s = this.container.getSize();
30784                     this.viewSize = [s.width, s.height];
30785                 }else{
30786                     this.viewSize = [Roo.lib.Dom.getViewWidth(),Roo.lib.Dom.getViewHeight()];
30787                 }
30788             }
30789             var s = Roo.get(this.container||document).getScroll();
30790
30791             var x = this.xy[0], y = this.xy[1];
30792             var w = this.size.width, h = this.size.height;
30793             var vw = this.viewSize[0], vh = this.viewSize[1];
30794             // only move it if it needs it
30795             var moved = false;
30796             // first validate right/bottom
30797             if(x + w > vw+s.left){
30798                 x = vw - w;
30799                 moved = true;
30800             }
30801             if(y + h > vh+s.top){
30802                 y = vh - h;
30803                 moved = true;
30804             }
30805             // then make sure top/left isn't negative
30806             if(x < s.left){
30807                 x = s.left;
30808                 moved = true;
30809             }
30810             if(y < s.top){
30811                 y = s.top;
30812                 moved = true;
30813             }
30814             if(moved){
30815                 // cache xy
30816                 this.xy = [x, y];
30817                 if(this.isVisible()){
30818                     this.el.setLocation(x, y);
30819                     this.adjustAssets();
30820                 }
30821             }
30822         }
30823     },
30824
30825     // private
30826     onDrag : function(){
30827         if(!this.proxyDrag){
30828             this.xy = this.el.getXY();
30829             this.adjustAssets();
30830         }
30831     },
30832
30833     // private
30834     adjustAssets : function(doShow){
30835         var x = this.xy[0], y = this.xy[1];
30836         var w = this.size.width, h = this.size.height;
30837         if(doShow === true){
30838             if(this.shadow){
30839                 this.shadow.show(this.el);
30840             }
30841             if(this.shim){
30842                 this.shim.show();
30843             }
30844         }
30845         if(this.shadow && this.shadow.isVisible()){
30846             this.shadow.show(this.el);
30847         }
30848         if(this.shim && this.shim.isVisible()){
30849             this.shim.setBounds(x, y, w, h);
30850         }
30851     },
30852
30853     // private
30854     adjustViewport : function(w, h){
30855         if(!w || !h){
30856             w = Roo.lib.Dom.getViewWidth();
30857             h = Roo.lib.Dom.getViewHeight();
30858         }
30859         // cache the size
30860         this.viewSize = [w, h];
30861         if(this.modal && this.mask.isVisible()){
30862             this.mask.setSize(w, h); // first make sure the mask isn't causing overflow
30863             this.mask.setSize(Roo.lib.Dom.getViewWidth(true), Roo.lib.Dom.getViewHeight(true));
30864         }
30865         if(this.isVisible()){
30866             this.constrainXY();
30867         }
30868     },
30869
30870     /**
30871      * Destroys this dialog and all its supporting elements (including any tabs, shim,
30872      * shadow, proxy, mask, etc.)  Also removes all event listeners.
30873      * @param {Boolean} removeEl (optional) true to remove the element from the DOM
30874      */
30875     destroy : function(removeEl){
30876         if(this.isVisible()){
30877             this.animateTarget = null;
30878             this.hide();
30879         }
30880         Roo.EventManager.removeResizeListener(this.adjustViewport, this);
30881         if(this.tabs){
30882             this.tabs.destroy(removeEl);
30883         }
30884         Roo.destroy(
30885              this.shim,
30886              this.proxy,
30887              this.resizer,
30888              this.close,
30889              this.mask
30890         );
30891         if(this.dd){
30892             this.dd.unreg();
30893         }
30894         if(this.buttons){
30895            for(var i = 0, len = this.buttons.length; i < len; i++){
30896                this.buttons[i].destroy();
30897            }
30898         }
30899         this.el.removeAllListeners();
30900         if(removeEl === true){
30901             this.el.update("");
30902             this.el.remove();
30903         }
30904         Roo.DialogManager.unregister(this);
30905     },
30906
30907     // private
30908     startMove : function(){
30909         if(this.proxyDrag){
30910             this.proxy.show();
30911         }
30912         if(this.constraintoviewport !== false){
30913             this.dd.constrainTo(document.body, {right: this.shadowOffset, bottom: this.shadowOffset});
30914         }
30915     },
30916
30917     // private
30918     endMove : function(){
30919         if(!this.proxyDrag){
30920             Roo.dd.DD.prototype.endDrag.apply(this.dd, arguments);
30921         }else{
30922             Roo.dd.DDProxy.prototype.endDrag.apply(this.dd, arguments);
30923             this.proxy.hide();
30924         }
30925         this.refreshSize();
30926         this.adjustAssets();
30927         this.focus();
30928         this.fireEvent("move", this, this.xy[0], this.xy[1]);
30929     },
30930
30931     /**
30932      * Brings this dialog to the front of any other visible dialogs
30933      * @return {Roo.BasicDialog} this
30934      */
30935     toFront : function(){
30936         Roo.DialogManager.bringToFront(this);
30937         return this;
30938     },
30939
30940     /**
30941      * Sends this dialog to the back (under) of any other visible dialogs
30942      * @return {Roo.BasicDialog} this
30943      */
30944     toBack : function(){
30945         Roo.DialogManager.sendToBack(this);
30946         return this;
30947     },
30948
30949     /**
30950      * Centers this dialog in the viewport
30951      * @return {Roo.BasicDialog} this
30952      */
30953     center : function(){
30954         var xy = this.el.getCenterXY(true);
30955         this.moveTo(xy[0], xy[1]);
30956         return this;
30957     },
30958
30959     /**
30960      * Moves the dialog's top-left corner to the specified point
30961      * @param {Number} x
30962      * @param {Number} y
30963      * @return {Roo.BasicDialog} this
30964      */
30965     moveTo : function(x, y){
30966         this.xy = [x,y];
30967         if(this.isVisible()){
30968             this.el.setXY(this.xy);
30969             this.adjustAssets();
30970         }
30971         return this;
30972     },
30973
30974     /**
30975      * Aligns the dialog to the specified element
30976      * @param {String/HTMLElement/Roo.Element} element The element to align to.
30977      * @param {String} position The position to align to (see {@link Roo.Element#alignTo} for more details).
30978      * @param {Array} offsets (optional) Offset the positioning by [x, y]
30979      * @return {Roo.BasicDialog} this
30980      */
30981     alignTo : function(element, position, offsets){
30982         this.xy = this.el.getAlignToXY(element, position, offsets);
30983         if(this.isVisible()){
30984             this.el.setXY(this.xy);
30985             this.adjustAssets();
30986         }
30987         return this;
30988     },
30989
30990     /**
30991      * Anchors an element to another element and realigns it when the window is resized.
30992      * @param {String/HTMLElement/Roo.Element} element The element to align to.
30993      * @param {String} position The position to align to (see {@link Roo.Element#alignTo} for more details)
30994      * @param {Array} offsets (optional) Offset the positioning by [x, y]
30995      * @param {Boolean/Number} monitorScroll (optional) true to monitor body scroll and reposition. If this parameter
30996      * is a number, it is used as the buffer delay (defaults to 50ms).
30997      * @return {Roo.BasicDialog} this
30998      */
30999     anchorTo : function(el, alignment, offsets, monitorScroll){
31000         var action = function(){
31001             this.alignTo(el, alignment, offsets);
31002         };
31003         Roo.EventManager.onWindowResize(action, this);
31004         var tm = typeof monitorScroll;
31005         if(tm != 'undefined'){
31006             Roo.EventManager.on(window, 'scroll', action, this,
31007                 {buffer: tm == 'number' ? monitorScroll : 50});
31008         }
31009         action.call(this);
31010         return this;
31011     },
31012
31013     /**
31014      * Returns true if the dialog is visible
31015      * @return {Boolean}
31016      */
31017     isVisible : function(){
31018         return this.el.isVisible();
31019     },
31020
31021     // private
31022     animHide : function(callback){
31023         var b = Roo.get(this.animateTarget).getBox();
31024         this.proxy.show();
31025         this.proxy.setBounds(this.xy[0], this.xy[1], this.size.width, this.size.height);
31026         this.el.hide();
31027         this.proxy.setBounds(b.x, b.y, b.width, b.height, true, .35,
31028                     this.hideEl.createDelegate(this, [callback]));
31029     },
31030
31031     /**
31032      * Hides the dialog.
31033      * @param {Function} callback (optional) Function to call when the dialog is hidden
31034      * @return {Roo.BasicDialog} this
31035      */
31036     hide : function(callback){
31037         if (this.fireEvent("beforehide", this) === false){
31038             return;
31039         }
31040         if(this.shadow){
31041             this.shadow.hide();
31042         }
31043         if(this.shim) {
31044           this.shim.hide();
31045         }
31046         // sometimes animateTarget seems to get set.. causing problems...
31047         // this just double checks..
31048         if(this.animateTarget && Roo.get(this.animateTarget)) {
31049            this.animHide(callback);
31050         }else{
31051             this.el.hide();
31052             this.hideEl(callback);
31053         }
31054         return this;
31055     },
31056
31057     // private
31058     hideEl : function(callback){
31059         this.proxy.hide();
31060         if(this.modal){
31061             this.mask.hide();
31062             Roo.get(document.body).removeClass("x-body-masked");
31063         }
31064         this.fireEvent("hide", this);
31065         if(typeof callback == "function"){
31066             callback();
31067         }
31068     },
31069
31070     // private
31071     hideAction : function(){
31072         this.setLeft("-10000px");
31073         this.setTop("-10000px");
31074         this.setStyle("visibility", "hidden");
31075     },
31076
31077     // private
31078     refreshSize : function(){
31079         this.size = this.el.getSize();
31080         this.xy = this.el.getXY();
31081         Roo.state.Manager.set(this.stateId || this.el.id + "-state", this.el.getBox());
31082     },
31083
31084     // private
31085     // z-index is managed by the DialogManager and may be overwritten at any time
31086     setZIndex : function(index){
31087         if(this.modal){
31088             this.mask.setStyle("z-index", index);
31089         }
31090         if(this.shim){
31091             this.shim.setStyle("z-index", ++index);
31092         }
31093         if(this.shadow){
31094             this.shadow.setZIndex(++index);
31095         }
31096         this.el.setStyle("z-index", ++index);
31097         if(this.proxy){
31098             this.proxy.setStyle("z-index", ++index);
31099         }
31100         if(this.resizer){
31101             this.resizer.proxy.setStyle("z-index", ++index);
31102         }
31103
31104         this.lastZIndex = index;
31105     },
31106
31107     /**
31108      * Returns the element for this dialog
31109      * @return {Roo.Element} The underlying dialog Element
31110      */
31111     getEl : function(){
31112         return this.el;
31113     }
31114 });
31115
31116 /**
31117  * @class Roo.DialogManager
31118  * Provides global access to BasicDialogs that have been created and
31119  * support for z-indexing (layering) multiple open dialogs.
31120  */
31121 Roo.DialogManager = function(){
31122     var list = {};
31123     var accessList = [];
31124     var front = null;
31125
31126     // private
31127     var sortDialogs = function(d1, d2){
31128         return (!d1._lastAccess || d1._lastAccess < d2._lastAccess) ? -1 : 1;
31129     };
31130
31131     // private
31132     var orderDialogs = function(){
31133         accessList.sort(sortDialogs);
31134         var seed = Roo.DialogManager.zseed;
31135         for(var i = 0, len = accessList.length; i < len; i++){
31136             var dlg = accessList[i];
31137             if(dlg){
31138                 dlg.setZIndex(seed + (i*10));
31139             }
31140         }
31141     };
31142
31143     return {
31144         /**
31145          * The starting z-index for BasicDialogs (defaults to 9000)
31146          * @type Number The z-index value
31147          */
31148         zseed : 9000,
31149
31150         // private
31151         register : function(dlg){
31152             list[dlg.id] = dlg;
31153             accessList.push(dlg);
31154         },
31155
31156         // private
31157         unregister : function(dlg){
31158             delete list[dlg.id];
31159             var i=0;
31160             var len=0;
31161             if(!accessList.indexOf){
31162                 for(  i = 0, len = accessList.length; i < len; i++){
31163                     if(accessList[i] == dlg){
31164                         accessList.splice(i, 1);
31165                         return;
31166                     }
31167                 }
31168             }else{
31169                  i = accessList.indexOf(dlg);
31170                 if(i != -1){
31171                     accessList.splice(i, 1);
31172                 }
31173             }
31174         },
31175
31176         /**
31177          * Gets a registered dialog by id
31178          * @param {String/Object} id The id of the dialog or a dialog
31179          * @return {Roo.BasicDialog} this
31180          */
31181         get : function(id){
31182             return typeof id == "object" ? id : list[id];
31183         },
31184
31185         /**
31186          * Brings the specified dialog to the front
31187          * @param {String/Object} dlg The id of the dialog or a dialog
31188          * @return {Roo.BasicDialog} this
31189          */
31190         bringToFront : function(dlg){
31191             dlg = this.get(dlg);
31192             if(dlg != front){
31193                 front = dlg;
31194                 dlg._lastAccess = new Date().getTime();
31195                 orderDialogs();
31196             }
31197             return dlg;
31198         },
31199
31200         /**
31201          * Sends the specified dialog to the back
31202          * @param {String/Object} dlg The id of the dialog or a dialog
31203          * @return {Roo.BasicDialog} this
31204          */
31205         sendToBack : function(dlg){
31206             dlg = this.get(dlg);
31207             dlg._lastAccess = -(new Date().getTime());
31208             orderDialogs();
31209             return dlg;
31210         },
31211
31212         /**
31213          * Hides all dialogs
31214          */
31215         hideAll : function(){
31216             for(var id in list){
31217                 if(list[id] && typeof list[id] != "function" && list[id].isVisible()){
31218                     list[id].hide();
31219                 }
31220             }
31221         }
31222     };
31223 }();
31224
31225 /**
31226  * @class Roo.LayoutDialog
31227  * @extends Roo.BasicDialog
31228  * Dialog which provides adjustments for working with a layout in a Dialog.
31229  * Add your necessary layout config options to the dialog's config.<br>
31230  * Example usage (including a nested layout):
31231  * <pre><code>
31232 if(!dialog){
31233     dialog = new Roo.LayoutDialog("download-dlg", {
31234         modal: true,
31235         width:600,
31236         height:450,
31237         shadow:true,
31238         minWidth:500,
31239         minHeight:350,
31240         autoTabs:true,
31241         proxyDrag:true,
31242         // layout config merges with the dialog config
31243         center:{
31244             tabPosition: "top",
31245             alwaysShowTabs: true
31246         }
31247     });
31248     dialog.addKeyListener(27, dialog.hide, dialog);
31249     dialog.setDefaultButton(dialog.addButton("Close", dialog.hide, dialog));
31250     dialog.addButton("Build It!", this.getDownload, this);
31251
31252     // we can even add nested layouts
31253     var innerLayout = new Roo.BorderLayout("dl-inner", {
31254         east: {
31255             initialSize: 200,
31256             autoScroll:true,
31257             split:true
31258         },
31259         center: {
31260             autoScroll:true
31261         }
31262     });
31263     innerLayout.beginUpdate();
31264     innerLayout.add("east", new Roo.ContentPanel("dl-details"));
31265     innerLayout.add("center", new Roo.ContentPanel("selection-panel"));
31266     innerLayout.endUpdate(true);
31267
31268     var layout = dialog.getLayout();
31269     layout.beginUpdate();
31270     layout.add("center", new Roo.ContentPanel("standard-panel",
31271                         {title: "Download the Source", fitToFrame:true}));
31272     layout.add("center", new Roo.NestedLayoutPanel(innerLayout,
31273                {title: "Build your own roo.js"}));
31274     layout.getRegion("center").showPanel(sp);
31275     layout.endUpdate();
31276 }
31277 </code></pre>
31278     * @constructor
31279     * @param {String/HTMLElement/Roo.Element} el The id of or container element, or config
31280     * @param {Object} config configuration options
31281   */
31282 Roo.LayoutDialog = function(el, cfg){
31283     
31284     var config=  cfg;
31285     if (typeof(cfg) == 'undefined') {
31286         config = Roo.apply({}, el);
31287         // not sure why we use documentElement here.. - it should always be body.
31288         // IE7 borks horribly if we use documentElement.
31289         // webkit also does not like documentElement - it creates a body element...
31290         el = Roo.get( document.body || document.documentElement ).createChild();
31291         //config.autoCreate = true;
31292     }
31293     
31294     
31295     config.autoTabs = false;
31296     Roo.LayoutDialog.superclass.constructor.call(this, el, config);
31297     this.body.setStyle({overflow:"hidden", position:"relative"});
31298     this.layout = new Roo.BorderLayout(this.body.dom, config);
31299     this.layout.monitorWindowResize = false;
31300     this.el.addClass("x-dlg-auto-layout");
31301     // fix case when center region overwrites center function
31302     this.center = Roo.BasicDialog.prototype.center;
31303     this.on("show", this.layout.layout, this.layout, true);
31304     if (config.items) {
31305         var xitems = config.items;
31306         delete config.items;
31307         Roo.each(xitems, this.addxtype, this);
31308     }
31309     
31310     
31311 };
31312 Roo.extend(Roo.LayoutDialog, Roo.BasicDialog, {
31313     /**
31314      * Ends update of the layout <strike>and resets display to none</strike>. Use standard beginUpdate/endUpdate on the layout.
31315      * @deprecated
31316      */
31317     endUpdate : function(){
31318         this.layout.endUpdate();
31319     },
31320
31321     /**
31322      * Begins an update of the layout <strike>and sets display to block and visibility to hidden</strike>. Use standard beginUpdate/endUpdate on the layout.
31323      *  @deprecated
31324      */
31325     beginUpdate : function(){
31326         this.layout.beginUpdate();
31327     },
31328
31329     /**
31330      * Get the BorderLayout for this dialog
31331      * @return {Roo.BorderLayout}
31332      */
31333     getLayout : function(){
31334         return this.layout;
31335     },
31336
31337     showEl : function(){
31338         Roo.LayoutDialog.superclass.showEl.apply(this, arguments);
31339         if(Roo.isIE7){
31340             this.layout.layout();
31341         }
31342     },
31343
31344     // private
31345     // Use the syncHeightBeforeShow config option to control this automatically
31346     syncBodyHeight : function(){
31347         Roo.LayoutDialog.superclass.syncBodyHeight.call(this);
31348         if(this.layout){this.layout.layout();}
31349     },
31350     
31351       /**
31352      * Add an xtype element (actually adds to the layout.)
31353      * @return {Object} xdata xtype object data.
31354      */
31355     
31356     addxtype : function(c) {
31357         return this.layout.addxtype(c);
31358     }
31359 });/*
31360  * Based on:
31361  * Ext JS Library 1.1.1
31362  * Copyright(c) 2006-2007, Ext JS, LLC.
31363  *
31364  * Originally Released Under LGPL - original licence link has changed is not relivant.
31365  *
31366  * Fork - LGPL
31367  * <script type="text/javascript">
31368  */
31369  
31370 /**
31371  * @class Roo.MessageBox
31372  * Utility class for generating different styles of message boxes.  The alias Roo.Msg can also be used.
31373  * Example usage:
31374  *<pre><code>
31375 // Basic alert:
31376 Roo.Msg.alert('Status', 'Changes saved successfully.');
31377
31378 // Prompt for user data:
31379 Roo.Msg.prompt('Name', 'Please enter your name:', function(btn, text){
31380     if (btn == 'ok'){
31381         // process text value...
31382     }
31383 });
31384
31385 // Show a dialog using config options:
31386 Roo.Msg.show({
31387    title:'Save Changes?',
31388    msg: 'Your are closing a tab that has unsaved changes. Would you like to save your changes?',
31389    buttons: Roo.Msg.YESNOCANCEL,
31390    fn: processResult,
31391    animEl: 'elId'
31392 });
31393 </code></pre>
31394  * @singleton
31395  */
31396 Roo.MessageBox = function(){
31397     var dlg, opt, mask, waitTimer;
31398     var bodyEl, msgEl, textboxEl, textareaEl, progressEl, pp;
31399     var buttons, activeTextEl, bwidth;
31400
31401     // private
31402     var handleButton = function(button){
31403         dlg.hide();
31404         Roo.callback(opt.fn, opt.scope||window, [button, activeTextEl.dom.value], 1);
31405     };
31406
31407     // private
31408     var handleHide = function(){
31409         if(opt && opt.cls){
31410             dlg.el.removeClass(opt.cls);
31411         }
31412         if(waitTimer){
31413             Roo.TaskMgr.stop(waitTimer);
31414             waitTimer = null;
31415         }
31416     };
31417
31418     // private
31419     var updateButtons = function(b){
31420         var width = 0;
31421         if(!b){
31422             buttons["ok"].hide();
31423             buttons["cancel"].hide();
31424             buttons["yes"].hide();
31425             buttons["no"].hide();
31426             dlg.footer.dom.style.display = 'none';
31427             return width;
31428         }
31429         dlg.footer.dom.style.display = '';
31430         for(var k in buttons){
31431             if(typeof buttons[k] != "function"){
31432                 if(b[k]){
31433                     buttons[k].show();
31434                     buttons[k].setText(typeof b[k] == "string" ? b[k] : Roo.MessageBox.buttonText[k]);
31435                     width += buttons[k].el.getWidth()+15;
31436                 }else{
31437                     buttons[k].hide();
31438                 }
31439             }
31440         }
31441         return width;
31442     };
31443
31444     // private
31445     var handleEsc = function(d, k, e){
31446         if(opt && opt.closable !== false){
31447             dlg.hide();
31448         }
31449         if(e){
31450             e.stopEvent();
31451         }
31452     };
31453
31454     return {
31455         /**
31456          * Returns a reference to the underlying {@link Roo.BasicDialog} element
31457          * @return {Roo.BasicDialog} The BasicDialog element
31458          */
31459         getDialog : function(){
31460            if(!dlg){
31461                 dlg = new Roo.BasicDialog("x-msg-box", {
31462                     autoCreate : true,
31463                     shadow: true,
31464                     draggable: true,
31465                     resizable:false,
31466                     constraintoviewport:false,
31467                     fixedcenter:true,
31468                     collapsible : false,
31469                     shim:true,
31470                     modal: true,
31471                     width:400, height:100,
31472                     buttonAlign:"center",
31473                     closeClick : function(){
31474                         if(opt && opt.buttons && opt.buttons.no && !opt.buttons.cancel){
31475                             handleButton("no");
31476                         }else{
31477                             handleButton("cancel");
31478                         }
31479                     }
31480                 });
31481                 dlg.on("hide", handleHide);
31482                 mask = dlg.mask;
31483                 dlg.addKeyListener(27, handleEsc);
31484                 buttons = {};
31485                 var bt = this.buttonText;
31486                 buttons["ok"] = dlg.addButton(bt["ok"], handleButton.createCallback("ok"));
31487                 buttons["yes"] = dlg.addButton(bt["yes"], handleButton.createCallback("yes"));
31488                 buttons["no"] = dlg.addButton(bt["no"], handleButton.createCallback("no"));
31489                 buttons["cancel"] = dlg.addButton(bt["cancel"], handleButton.createCallback("cancel"));
31490                 bodyEl = dlg.body.createChild({
31491
31492                     html:'<span class="roo-mb-text"></span><br /><input type="text" class="roo-mb-input" /><textarea class="roo-mb-textarea"></textarea><div class="roo-mb-progress-wrap"><div class="roo-mb-progress"><div class="roo-mb-progress-bar">&#160;</div></div></div>'
31493                 });
31494                 msgEl = bodyEl.dom.firstChild;
31495                 textboxEl = Roo.get(bodyEl.dom.childNodes[2]);
31496                 textboxEl.enableDisplayMode();
31497                 textboxEl.addKeyListener([10,13], function(){
31498                     if(dlg.isVisible() && opt && opt.buttons){
31499                         if(opt.buttons.ok){
31500                             handleButton("ok");
31501                         }else if(opt.buttons.yes){
31502                             handleButton("yes");
31503                         }
31504                     }
31505                 });
31506                 textareaEl = Roo.get(bodyEl.dom.childNodes[3]);
31507                 textareaEl.enableDisplayMode();
31508                 progressEl = Roo.get(bodyEl.dom.childNodes[4]);
31509                 progressEl.enableDisplayMode();
31510                 var pf = progressEl.dom.firstChild;
31511                 if (pf) {
31512                     pp = Roo.get(pf.firstChild);
31513                     pp.setHeight(pf.offsetHeight);
31514                 }
31515                 
31516             }
31517             return dlg;
31518         },
31519
31520         /**
31521          * Updates the message box body text
31522          * @param {String} text (optional) Replaces the message box element's innerHTML with the specified string (defaults to
31523          * the XHTML-compliant non-breaking space character '&amp;#160;')
31524          * @return {Roo.MessageBox} This message box
31525          */
31526         updateText : function(text){
31527             if(!dlg.isVisible() && !opt.width){
31528                 dlg.resizeTo(this.maxWidth, 100); // resize first so content is never clipped from previous shows
31529             }
31530             msgEl.innerHTML = text || '&#160;';
31531       
31532             var cw =  Math.max(msgEl.offsetWidth, msgEl.parentNode.scrollWidth);
31533             //Roo.log("guesed size: " + JSON.stringify([cw,msgEl.offsetWidth, msgEl.parentNode.scrollWidth]));
31534             var w = Math.max(
31535                     Math.min(opt.width || cw , this.maxWidth), 
31536                     Math.max(opt.minWidth || this.minWidth, bwidth)
31537             );
31538             if(opt.prompt){
31539                 activeTextEl.setWidth(w);
31540             }
31541             if(dlg.isVisible()){
31542                 dlg.fixedcenter = false;
31543             }
31544             // to big, make it scroll. = But as usual stupid IE does not support
31545             // !important..
31546             
31547             if ( bodyEl.getHeight() > (Roo.lib.Dom.getViewHeight() - 100)) {
31548                 bodyEl.setHeight ( Roo.lib.Dom.getViewHeight() - 100 );
31549                 bodyEl.dom.style.overflowY = 'auto' + ( Roo.isIE ? '' : ' !important');
31550             } else {
31551                 bodyEl.dom.style.height = '';
31552                 bodyEl.dom.style.overflowY = '';
31553             }
31554             if (cw > w) {
31555                 bodyEl.dom.style.get = 'auto' + ( Roo.isIE ? '' : ' !important');
31556             } else {
31557                 bodyEl.dom.style.overflowX = '';
31558             }
31559             
31560             dlg.setContentSize(w, bodyEl.getHeight());
31561             if(dlg.isVisible()){
31562                 dlg.fixedcenter = true;
31563             }
31564             return this;
31565         },
31566
31567         /**
31568          * Updates a progress-style message box's text and progress bar.  Only relevant on message boxes
31569          * initiated via {@link Roo.MessageBox#progress} or by calling {@link Roo.MessageBox#show} with progress: true.
31570          * @param {Number} value Any number between 0 and 1 (e.g., .5)
31571          * @param {String} text (optional) If defined, the message box's body text is replaced with the specified string (defaults to undefined)
31572          * @return {Roo.MessageBox} This message box
31573          */
31574         updateProgress : function(value, text){
31575             if(text){
31576                 this.updateText(text);
31577             }
31578             if (pp) { // weird bug on my firefox - for some reason this is not defined
31579                 pp.setWidth(Math.floor(value*progressEl.dom.firstChild.offsetWidth));
31580             }
31581             return this;
31582         },        
31583
31584         /**
31585          * Returns true if the message box is currently displayed
31586          * @return {Boolean} True if the message box is visible, else false
31587          */
31588         isVisible : function(){
31589             return dlg && dlg.isVisible();  
31590         },
31591
31592         /**
31593          * Hides the message box if it is displayed
31594          */
31595         hide : function(){
31596             if(this.isVisible()){
31597                 dlg.hide();
31598             }  
31599         },
31600
31601         /**
31602          * Displays a new message box, or reinitializes an existing message box, based on the config options
31603          * passed in. All functions (e.g. prompt, alert, etc) on MessageBox call this function internally.
31604          * The following config object properties are supported:
31605          * <pre>
31606 Property    Type             Description
31607 ----------  ---------------  ------------------------------------------------------------------------------------
31608 animEl            String/Element   An id or Element from which the message box should animate as it opens and
31609                                    closes (defaults to undefined)
31610 buttons           Object/Boolean   A button config object (e.g., Roo.MessageBox.OKCANCEL or {ok:'Foo',
31611                                    cancel:'Bar'}), or false to not show any buttons (defaults to false)
31612 closable          Boolean          False to hide the top-right close button (defaults to true).  Note that
31613                                    progress and wait dialogs will ignore this property and always hide the
31614                                    close button as they can only be closed programmatically.
31615 cls               String           A custom CSS class to apply to the message box element
31616 defaultTextHeight Number           The default height in pixels of the message box's multiline textarea if
31617                                    displayed (defaults to 75)
31618 fn                Function         A callback function to execute after closing the dialog.  The arguments to the
31619                                    function will be btn (the name of the button that was clicked, if applicable,
31620                                    e.g. "ok"), and text (the value of the active text field, if applicable).
31621                                    Progress and wait dialogs will ignore this option since they do not respond to
31622                                    user actions and can only be closed programmatically, so any required function
31623                                    should be called by the same code after it closes the dialog.
31624 icon              String           A CSS class that provides a background image to be used as an icon for
31625                                    the dialog (e.g., Roo.MessageBox.WARNING or 'custom-class', defaults to '')
31626 maxWidth          Number           The maximum width in pixels of the message box (defaults to 600)
31627 minWidth          Number           The minimum width in pixels of the message box (defaults to 100)
31628 modal             Boolean          False to allow user interaction with the page while the message box is
31629                                    displayed (defaults to true)
31630 msg               String           A string that will replace the existing message box body text (defaults
31631                                    to the XHTML-compliant non-breaking space character '&#160;')
31632 multiline         Boolean          True to prompt the user to enter multi-line text (defaults to false)
31633 progress          Boolean          True to display a progress bar (defaults to false)
31634 progressText      String           The text to display inside the progress bar if progress = true (defaults to '')
31635 prompt            Boolean          True to prompt the user to enter single-line text (defaults to false)
31636 proxyDrag         Boolean          True to display a lightweight proxy while dragging (defaults to false)
31637 title             String           The title text
31638 value             String           The string value to set into the active textbox element if displayed
31639 wait              Boolean          True to display a progress bar (defaults to false)
31640 width             Number           The width of the dialog in pixels
31641 </pre>
31642          *
31643          * Example usage:
31644          * <pre><code>
31645 Roo.Msg.show({
31646    title: 'Address',
31647    msg: 'Please enter your address:',
31648    width: 300,
31649    buttons: Roo.MessageBox.OKCANCEL,
31650    multiline: true,
31651    fn: saveAddress,
31652    animEl: 'addAddressBtn'
31653 });
31654 </code></pre>
31655          * @param {Object} config Configuration options
31656          * @return {Roo.MessageBox} This message box
31657          */
31658         show : function(options)
31659         {
31660             
31661             // this causes nightmares if you show one dialog after another
31662             // especially on callbacks..
31663              
31664             if(this.isVisible()){
31665                 
31666                 this.hide();
31667                 Roo.log("[Roo.Messagebox] Show called while message displayed:" );
31668                 Roo.log("Old Dialog Message:" +  msgEl.innerHTML );
31669                 Roo.log("New Dialog Message:" +  options.msg )
31670                 //this.alert("ERROR", "Multiple dialogs where displayed at the same time");
31671                 //throw "Roo.MessageBox ERROR : Multiple dialogs where displayed at the same time";
31672                 
31673             }
31674             var d = this.getDialog();
31675             opt = options;
31676             d.setTitle(opt.title || "&#160;");
31677             d.close.setDisplayed(opt.closable !== false);
31678             activeTextEl = textboxEl;
31679             opt.prompt = opt.prompt || (opt.multiline ? true : false);
31680             if(opt.prompt){
31681                 if(opt.multiline){
31682                     textboxEl.hide();
31683                     textareaEl.show();
31684                     textareaEl.setHeight(typeof opt.multiline == "number" ?
31685                         opt.multiline : this.defaultTextHeight);
31686                     activeTextEl = textareaEl;
31687                 }else{
31688                     textboxEl.show();
31689                     textareaEl.hide();
31690                 }
31691             }else{
31692                 textboxEl.hide();
31693                 textareaEl.hide();
31694             }
31695             progressEl.setDisplayed(opt.progress === true);
31696             this.updateProgress(0);
31697             activeTextEl.dom.value = opt.value || "";
31698             if(opt.prompt){
31699                 dlg.setDefaultButton(activeTextEl);
31700             }else{
31701                 var bs = opt.buttons;
31702                 var db = null;
31703                 if(bs && bs.ok){
31704                     db = buttons["ok"];
31705                 }else if(bs && bs.yes){
31706                     db = buttons["yes"];
31707                 }
31708                 dlg.setDefaultButton(db);
31709             }
31710             bwidth = updateButtons(opt.buttons);
31711             this.updateText(opt.msg);
31712             if(opt.cls){
31713                 d.el.addClass(opt.cls);
31714             }
31715             d.proxyDrag = opt.proxyDrag === true;
31716             d.modal = opt.modal !== false;
31717             d.mask = opt.modal !== false ? mask : false;
31718             if(!d.isVisible()){
31719                 // force it to the end of the z-index stack so it gets a cursor in FF
31720                 document.body.appendChild(dlg.el.dom);
31721                 d.animateTarget = null;
31722                 d.show(options.animEl);
31723             }
31724             return this;
31725         },
31726
31727         /**
31728          * Displays a message box with a progress bar.  This message box has no buttons and is not closeable by
31729          * the user.  You are responsible for updating the progress bar as needed via {@link Roo.MessageBox#updateProgress}
31730          * and closing the message box when the process is complete.
31731          * @param {String} title The title bar text
31732          * @param {String} msg The message box body text
31733          * @return {Roo.MessageBox} This message box
31734          */
31735         progress : function(title, msg){
31736             this.show({
31737                 title : title,
31738                 msg : msg,
31739                 buttons: false,
31740                 progress:true,
31741                 closable:false,
31742                 minWidth: this.minProgressWidth,
31743                 modal : true
31744             });
31745             return this;
31746         },
31747
31748         /**
31749          * Displays a standard read-only message box with an OK button (comparable to the basic JavaScript Window.alert).
31750          * If a callback function is passed it will be called after the user clicks the button, and the
31751          * id of the button that was clicked will be passed as the only parameter to the callback
31752          * (could also be the top-right close button).
31753          * @param {String} title The title bar text
31754          * @param {String} msg The message box body text
31755          * @param {Function} fn (optional) The callback function invoked after the message box is closed
31756          * @param {Object} scope (optional) The scope of the callback function
31757          * @return {Roo.MessageBox} This message box
31758          */
31759         alert : function(title, msg, fn, scope){
31760             this.show({
31761                 title : title,
31762                 msg : msg,
31763                 buttons: this.OK,
31764                 fn: fn,
31765                 scope : scope,
31766                 modal : true
31767             });
31768             return this;
31769         },
31770
31771         /**
31772          * Displays a message box with an infinitely auto-updating progress bar.  This can be used to block user
31773          * interaction while waiting for a long-running process to complete that does not have defined intervals.
31774          * You are responsible for closing the message box when the process is complete.
31775          * @param {String} msg The message box body text
31776          * @param {String} title (optional) The title bar text
31777          * @return {Roo.MessageBox} This message box
31778          */
31779         wait : function(msg, title){
31780             this.show({
31781                 title : title,
31782                 msg : msg,
31783                 buttons: false,
31784                 closable:false,
31785                 progress:true,
31786                 modal:true,
31787                 width:300,
31788                 wait:true
31789             });
31790             waitTimer = Roo.TaskMgr.start({
31791                 run: function(i){
31792                     Roo.MessageBox.updateProgress(((((i+20)%20)+1)*5)*.01);
31793                 },
31794                 interval: 1000
31795             });
31796             return this;
31797         },
31798
31799         /**
31800          * Displays a confirmation message box with Yes and No buttons (comparable to JavaScript's Window.confirm).
31801          * If a callback function is passed it will be called after the user clicks either button, and the id of the
31802          * button that was clicked will be passed as the only parameter to the callback (could also be the top-right close button).
31803          * @param {String} title The title bar text
31804          * @param {String} msg The message box body text
31805          * @param {Function} fn (optional) The callback function invoked after the message box is closed
31806          * @param {Object} scope (optional) The scope of the callback function
31807          * @return {Roo.MessageBox} This message box
31808          */
31809         confirm : function(title, msg, fn, scope){
31810             this.show({
31811                 title : title,
31812                 msg : msg,
31813                 buttons: this.YESNO,
31814                 fn: fn,
31815                 scope : scope,
31816                 modal : true
31817             });
31818             return this;
31819         },
31820
31821         /**
31822          * Displays a message box with OK and Cancel buttons prompting the user to enter some text (comparable to
31823          * JavaScript's Window.prompt).  The prompt can be a single-line or multi-line textbox.  If a callback function
31824          * is passed it will be called after the user clicks either button, and the id of the button that was clicked
31825          * (could also be the top-right close button) and the text that was entered will be passed as the two
31826          * parameters to the callback.
31827          * @param {String} title The title bar text
31828          * @param {String} msg The message box body text
31829          * @param {Function} fn (optional) The callback function invoked after the message box is closed
31830          * @param {Object} scope (optional) The scope of the callback function
31831          * @param {Boolean/Number} multiline (optional) True to create a multiline textbox using the defaultTextHeight
31832          * property, or the height in pixels to create the textbox (defaults to false / single-line)
31833          * @return {Roo.MessageBox} This message box
31834          */
31835         prompt : function(title, msg, fn, scope, multiline){
31836             this.show({
31837                 title : title,
31838                 msg : msg,
31839                 buttons: this.OKCANCEL,
31840                 fn: fn,
31841                 minWidth:250,
31842                 scope : scope,
31843                 prompt:true,
31844                 multiline: multiline,
31845                 modal : true
31846             });
31847             return this;
31848         },
31849
31850         /**
31851          * Button config that displays a single OK button
31852          * @type Object
31853          */
31854         OK : {ok:true},
31855         /**
31856          * Button config that displays Yes and No buttons
31857          * @type Object
31858          */
31859         YESNO : {yes:true, no:true},
31860         /**
31861          * Button config that displays OK and Cancel buttons
31862          * @type Object
31863          */
31864         OKCANCEL : {ok:true, cancel:true},
31865         /**
31866          * Button config that displays Yes, No and Cancel buttons
31867          * @type Object
31868          */
31869         YESNOCANCEL : {yes:true, no:true, cancel:true},
31870
31871         /**
31872          * The default height in pixels of the message box's multiline textarea if displayed (defaults to 75)
31873          * @type Number
31874          */
31875         defaultTextHeight : 75,
31876         /**
31877          * The maximum width in pixels of the message box (defaults to 600)
31878          * @type Number
31879          */
31880         maxWidth : 600,
31881         /**
31882          * The minimum width in pixels of the message box (defaults to 100)
31883          * @type Number
31884          */
31885         minWidth : 100,
31886         /**
31887          * The minimum width in pixels of the message box if it is a progress-style dialog.  This is useful
31888          * for setting a different minimum width than text-only dialogs may need (defaults to 250)
31889          * @type Number
31890          */
31891         minProgressWidth : 250,
31892         /**
31893          * An object containing the default button text strings that can be overriden for localized language support.
31894          * Supported properties are: ok, cancel, yes and no.
31895          * Customize the default text like so: Roo.MessageBox.buttonText.yes = "S?";
31896          * @type Object
31897          */
31898         buttonText : {
31899             ok : "OK",
31900             cancel : "Cancel",
31901             yes : "Yes",
31902             no : "No"
31903         }
31904     };
31905 }();
31906
31907 /**
31908  * Shorthand for {@link Roo.MessageBox}
31909  */
31910 Roo.Msg = Roo.MessageBox;/*
31911  * Based on:
31912  * Ext JS Library 1.1.1
31913  * Copyright(c) 2006-2007, Ext JS, LLC.
31914  *
31915  * Originally Released Under LGPL - original licence link has changed is not relivant.
31916  *
31917  * Fork - LGPL
31918  * <script type="text/javascript">
31919  */
31920 /**
31921  * @class Roo.QuickTips
31922  * Provides attractive and customizable tooltips for any element.
31923  * @singleton
31924  */
31925 Roo.QuickTips = function(){
31926     var el, tipBody, tipBodyText, tipTitle, tm, cfg, close, tagEls = {}, esc, removeCls = null, bdLeft, bdRight;
31927     var ce, bd, xy, dd;
31928     var visible = false, disabled = true, inited = false;
31929     var showProc = 1, hideProc = 1, dismissProc = 1, locks = [];
31930     
31931     var onOver = function(e){
31932         if(disabled){
31933             return;
31934         }
31935         var t = e.getTarget();
31936         if(!t || t.nodeType !== 1 || t == document || t == document.body){
31937             return;
31938         }
31939         if(ce && t == ce.el){
31940             clearTimeout(hideProc);
31941             return;
31942         }
31943         if(t && tagEls[t.id]){
31944             tagEls[t.id].el = t;
31945             showProc = show.defer(tm.showDelay, tm, [tagEls[t.id]]);
31946             return;
31947         }
31948         var ttp, et = Roo.fly(t);
31949         var ns = cfg.namespace;
31950         if(tm.interceptTitles && t.title){
31951             ttp = t.title;
31952             t.qtip = ttp;
31953             t.removeAttribute("title");
31954             e.preventDefault();
31955         }else{
31956             ttp = t.qtip || et.getAttributeNS(ns, cfg.attribute);
31957         }
31958         if(ttp){
31959             showProc = show.defer(tm.showDelay, tm, [{
31960                 el: t, 
31961                 text: ttp, 
31962                 width: et.getAttributeNS(ns, cfg.width),
31963                 autoHide: et.getAttributeNS(ns, cfg.hide) != "user",
31964                 title: et.getAttributeNS(ns, cfg.title),
31965                     cls: et.getAttributeNS(ns, cfg.cls)
31966             }]);
31967         }
31968     };
31969     
31970     var onOut = function(e){
31971         clearTimeout(showProc);
31972         var t = e.getTarget();
31973         if(t && ce && ce.el == t && (tm.autoHide && ce.autoHide !== false)){
31974             hideProc = setTimeout(hide, tm.hideDelay);
31975         }
31976     };
31977     
31978     var onMove = function(e){
31979         if(disabled){
31980             return;
31981         }
31982         xy = e.getXY();
31983         xy[1] += 18;
31984         if(tm.trackMouse && ce){
31985             el.setXY(xy);
31986         }
31987     };
31988     
31989     var onDown = function(e){
31990         clearTimeout(showProc);
31991         clearTimeout(hideProc);
31992         if(!e.within(el)){
31993             if(tm.hideOnClick){
31994                 hide();
31995                 tm.disable();
31996                 tm.enable.defer(100, tm);
31997             }
31998         }
31999     };
32000     
32001     var getPad = function(){
32002         return 2;//bdLeft.getPadding('l')+bdRight.getPadding('r');
32003     };
32004
32005     var show = function(o){
32006         if(disabled){
32007             return;
32008         }
32009         clearTimeout(dismissProc);
32010         ce = o;
32011         if(removeCls){ // in case manually hidden
32012             el.removeClass(removeCls);
32013             removeCls = null;
32014         }
32015         if(ce.cls){
32016             el.addClass(ce.cls);
32017             removeCls = ce.cls;
32018         }
32019         if(ce.title){
32020             tipTitle.update(ce.title);
32021             tipTitle.show();
32022         }else{
32023             tipTitle.update('');
32024             tipTitle.hide();
32025         }
32026         el.dom.style.width  = tm.maxWidth+'px';
32027         //tipBody.dom.style.width = '';
32028         tipBodyText.update(o.text);
32029         var p = getPad(), w = ce.width;
32030         if(!w){
32031             var td = tipBodyText.dom;
32032             var aw = Math.max(td.offsetWidth, td.clientWidth, td.scrollWidth);
32033             if(aw > tm.maxWidth){
32034                 w = tm.maxWidth;
32035             }else if(aw < tm.minWidth){
32036                 w = tm.minWidth;
32037             }else{
32038                 w = aw;
32039             }
32040         }
32041         //tipBody.setWidth(w);
32042         el.setWidth(parseInt(w, 10) + p);
32043         if(ce.autoHide === false){
32044             close.setDisplayed(true);
32045             if(dd){
32046                 dd.unlock();
32047             }
32048         }else{
32049             close.setDisplayed(false);
32050             if(dd){
32051                 dd.lock();
32052             }
32053         }
32054         if(xy){
32055             el.avoidY = xy[1]-18;
32056             el.setXY(xy);
32057         }
32058         if(tm.animate){
32059             el.setOpacity(.1);
32060             el.setStyle("visibility", "visible");
32061             el.fadeIn({callback: afterShow});
32062         }else{
32063             afterShow();
32064         }
32065     };
32066     
32067     var afterShow = function(){
32068         if(ce){
32069             el.show();
32070             esc.enable();
32071             if(tm.autoDismiss && ce.autoHide !== false){
32072                 dismissProc = setTimeout(hide, tm.autoDismissDelay);
32073             }
32074         }
32075     };
32076     
32077     var hide = function(noanim){
32078         clearTimeout(dismissProc);
32079         clearTimeout(hideProc);
32080         ce = null;
32081         if(el.isVisible()){
32082             esc.disable();
32083             if(noanim !== true && tm.animate){
32084                 el.fadeOut({callback: afterHide});
32085             }else{
32086                 afterHide();
32087             } 
32088         }
32089     };
32090     
32091     var afterHide = function(){
32092         el.hide();
32093         if(removeCls){
32094             el.removeClass(removeCls);
32095             removeCls = null;
32096         }
32097     };
32098     
32099     return {
32100         /**
32101         * @cfg {Number} minWidth
32102         * The minimum width of the quick tip (defaults to 40)
32103         */
32104        minWidth : 40,
32105         /**
32106         * @cfg {Number} maxWidth
32107         * The maximum width of the quick tip (defaults to 300)
32108         */
32109        maxWidth : 300,
32110         /**
32111         * @cfg {Boolean} interceptTitles
32112         * True to automatically use the element's DOM title value if available (defaults to false)
32113         */
32114        interceptTitles : false,
32115         /**
32116         * @cfg {Boolean} trackMouse
32117         * True to have the quick tip follow the mouse as it moves over the target element (defaults to false)
32118         */
32119        trackMouse : false,
32120         /**
32121         * @cfg {Boolean} hideOnClick
32122         * True to hide the quick tip if the user clicks anywhere in the document (defaults to true)
32123         */
32124        hideOnClick : true,
32125         /**
32126         * @cfg {Number} showDelay
32127         * Delay in milliseconds before the quick tip displays after the mouse enters the target element (defaults to 500)
32128         */
32129        showDelay : 500,
32130         /**
32131         * @cfg {Number} hideDelay
32132         * Delay in milliseconds before the quick tip hides when autoHide = true (defaults to 200)
32133         */
32134        hideDelay : 200,
32135         /**
32136         * @cfg {Boolean} autoHide
32137         * True to automatically hide the quick tip after the mouse exits the target element (defaults to true).
32138         * Used in conjunction with hideDelay.
32139         */
32140        autoHide : true,
32141         /**
32142         * @cfg {Boolean}
32143         * True to automatically hide the quick tip after a set period of time, regardless of the user's actions
32144         * (defaults to true).  Used in conjunction with autoDismissDelay.
32145         */
32146        autoDismiss : true,
32147         /**
32148         * @cfg {Number}
32149         * Delay in milliseconds before the quick tip hides when autoDismiss = true (defaults to 5000)
32150         */
32151        autoDismissDelay : 5000,
32152        /**
32153         * @cfg {Boolean} animate
32154         * True to turn on fade animation. Defaults to false (ClearType/scrollbar flicker issues in IE7).
32155         */
32156        animate : false,
32157
32158        /**
32159         * @cfg {String} title
32160         * Title text to display (defaults to '').  This can be any valid HTML markup.
32161         */
32162         title: '',
32163        /**
32164         * @cfg {String} text
32165         * Body text to display (defaults to '').  This can be any valid HTML markup.
32166         */
32167         text : '',
32168        /**
32169         * @cfg {String} cls
32170         * A CSS class to apply to the base quick tip element (defaults to '').
32171         */
32172         cls : '',
32173        /**
32174         * @cfg {Number} width
32175         * Width in pixels of the quick tip (defaults to auto).  Width will be ignored if it exceeds the bounds of
32176         * minWidth or maxWidth.
32177         */
32178         width : null,
32179
32180     /**
32181      * Initialize and enable QuickTips for first use.  This should be called once before the first attempt to access
32182      * or display QuickTips in a page.
32183      */
32184        init : function(){
32185           tm = Roo.QuickTips;
32186           cfg = tm.tagConfig;
32187           if(!inited){
32188               if(!Roo.isReady){ // allow calling of init() before onReady
32189                   Roo.onReady(Roo.QuickTips.init, Roo.QuickTips);
32190                   return;
32191               }
32192               el = new Roo.Layer({cls:"x-tip", shadow:"drop", shim: true, constrain:true, shadowOffset:4});
32193               el.fxDefaults = {stopFx: true};
32194               // maximum custom styling
32195               //el.update('<div class="x-tip-top-left"><div class="x-tip-top-right"><div class="x-tip-top"></div></div></div><div class="x-tip-bd-left"><div class="x-tip-bd-right"><div class="x-tip-bd"><div class="x-tip-close"></div><h3></h3><div class="x-tip-bd-inner"></div><div class="x-clear"></div></div></div></div><div class="x-tip-ft-left"><div class="x-tip-ft-right"><div class="x-tip-ft"></div></div></div>');
32196               el.update('<div class="x-tip-bd"><div class="x-tip-close"></div><h3></h3><div class="x-tip-bd-inner"></div><div class="x-clear"></div></div>');              
32197               tipTitle = el.child('h3');
32198               tipTitle.enableDisplayMode("block");
32199               tipBody = el.child('div.x-tip-bd');
32200               tipBodyText = el.child('div.x-tip-bd-inner');
32201               //bdLeft = el.child('div.x-tip-bd-left');
32202               //bdRight = el.child('div.x-tip-bd-right');
32203               close = el.child('div.x-tip-close');
32204               close.enableDisplayMode("block");
32205               close.on("click", hide);
32206               var d = Roo.get(document);
32207               d.on("mousedown", onDown);
32208               d.on("mouseover", onOver);
32209               d.on("mouseout", onOut);
32210               d.on("mousemove", onMove);
32211               esc = d.addKeyListener(27, hide);
32212               esc.disable();
32213               if(Roo.dd.DD){
32214                   dd = el.initDD("default", null, {
32215                       onDrag : function(){
32216                           el.sync();  
32217                       }
32218                   });
32219                   dd.setHandleElId(tipTitle.id);
32220                   dd.lock();
32221               }
32222               inited = true;
32223           }
32224           this.enable(); 
32225        },
32226
32227     /**
32228      * Configures a new quick tip instance and assigns it to a target element.  The following config options
32229      * are supported:
32230      * <pre>
32231 Property    Type                   Description
32232 ----------  ---------------------  ------------------------------------------------------------------------
32233 target      Element/String/Array   An Element, id or array of ids that this quick tip should be tied to
32234      * </ul>
32235      * @param {Object} config The config object
32236      */
32237        register : function(config){
32238            var cs = config instanceof Array ? config : arguments;
32239            for(var i = 0, len = cs.length; i < len; i++) {
32240                var c = cs[i];
32241                var target = c.target;
32242                if(target){
32243                    if(target instanceof Array){
32244                        for(var j = 0, jlen = target.length; j < jlen; j++){
32245                            tagEls[target[j]] = c;
32246                        }
32247                    }else{
32248                        tagEls[typeof target == 'string' ? target : Roo.id(target)] = c;
32249                    }
32250                }
32251            }
32252        },
32253
32254     /**
32255      * Removes this quick tip from its element and destroys it.
32256      * @param {String/HTMLElement/Element} el The element from which the quick tip is to be removed.
32257      */
32258        unregister : function(el){
32259            delete tagEls[Roo.id(el)];
32260        },
32261
32262     /**
32263      * Enable this quick tip.
32264      */
32265        enable : function(){
32266            if(inited && disabled){
32267                locks.pop();
32268                if(locks.length < 1){
32269                    disabled = false;
32270                }
32271            }
32272        },
32273
32274     /**
32275      * Disable this quick tip.
32276      */
32277        disable : function(){
32278           disabled = true;
32279           clearTimeout(showProc);
32280           clearTimeout(hideProc);
32281           clearTimeout(dismissProc);
32282           if(ce){
32283               hide(true);
32284           }
32285           locks.push(1);
32286        },
32287
32288     /**
32289      * Returns true if the quick tip is enabled, else false.
32290      */
32291        isEnabled : function(){
32292             return !disabled;
32293        },
32294
32295         // private
32296        tagConfig : {
32297            namespace : "ext",
32298            attribute : "qtip",
32299            width : "width",
32300            target : "target",
32301            title : "qtitle",
32302            hide : "hide",
32303            cls : "qclass"
32304        }
32305    };
32306 }();
32307
32308 // backwards compat
32309 Roo.QuickTips.tips = Roo.QuickTips.register;/*
32310  * Based on:
32311  * Ext JS Library 1.1.1
32312  * Copyright(c) 2006-2007, Ext JS, LLC.
32313  *
32314  * Originally Released Under LGPL - original licence link has changed is not relivant.
32315  *
32316  * Fork - LGPL
32317  * <script type="text/javascript">
32318  */
32319  
32320
32321 /**
32322  * @class Roo.tree.TreePanel
32323  * @extends Roo.data.Tree
32324
32325  * @cfg {Boolean} rootVisible false to hide the root node (defaults to true)
32326  * @cfg {Boolean} lines false to disable tree lines (defaults to true)
32327  * @cfg {Boolean} enableDD true to enable drag and drop
32328  * @cfg {Boolean} enableDrag true to enable just drag
32329  * @cfg {Boolean} enableDrop true to enable just drop
32330  * @cfg {Object} dragConfig Custom config to pass to the {@link Roo.tree.TreeDragZone} instance
32331  * @cfg {Object} dropConfig Custom config to pass to the {@link Roo.tree.TreeDropZone} instance
32332  * @cfg {String} ddGroup The DD group this TreePanel belongs to
32333  * @cfg {String} ddAppendOnly True if the tree should only allow append drops (use for trees which are sorted)
32334  * @cfg {Boolean} ddScroll true to enable YUI body scrolling
32335  * @cfg {Boolean} containerScroll true to register this container with ScrollManager
32336  * @cfg {Boolean} hlDrop false to disable node highlight on drop (defaults to the value of Roo.enableFx)
32337  * @cfg {String} hlColor The color of the node highlight (defaults to C3DAF9)
32338  * @cfg {Boolean} animate true to enable animated expand/collapse (defaults to the value of Roo.enableFx)
32339  * @cfg {Boolean} singleExpand true if only 1 node per branch may be expanded
32340  * @cfg {Boolean} selModel A tree selection model to use with this TreePanel (defaults to a {@link Roo.tree.DefaultSelectionModel})
32341  * @cfg {Boolean} loader A TreeLoader for use with this TreePanel
32342  * @cfg {Object|Roo.tree.TreeEditor} editor The TreeEditor or xtype data to display when clicked.
32343  * @cfg {String} pathSeparator The token used to separate sub-paths in path strings (defaults to '/')
32344  * @cfg {Function} renderer DEPRECATED - use TreeLoader:create event / Sets the rendering (formatting) function for the nodes. to return HTML markup for the tree view. The render function is called with  the following parameters:<ul><li>The {Object} The data for the node.</li></ul>
32345  * @cfg {Function} rendererTip DEPRECATED - use TreeLoader:create event / Sets the rendering (formatting) function for the nodes hovertip to return HTML markup for the tree view. The render function is called with  the following parameters:<ul><li>The {Object} The data for the node.</li></ul>
32346  * 
32347  * @constructor
32348  * @param {String/HTMLElement/Element} el The container element
32349  * @param {Object} config
32350  */
32351 Roo.tree.TreePanel = function(el, config){
32352     var root = false;
32353     var loader = false;
32354     if (config.root) {
32355         root = config.root;
32356         delete config.root;
32357     }
32358     if (config.loader) {
32359         loader = config.loader;
32360         delete config.loader;
32361     }
32362     
32363     Roo.apply(this, config);
32364     Roo.tree.TreePanel.superclass.constructor.call(this);
32365     this.el = Roo.get(el);
32366     this.el.addClass('x-tree');
32367     //console.log(root);
32368     if (root) {
32369         this.setRootNode( Roo.factory(root, Roo.tree));
32370     }
32371     if (loader) {
32372         this.loader = Roo.factory(loader, Roo.tree);
32373     }
32374    /**
32375     * Read-only. The id of the container element becomes this TreePanel's id.
32376     */
32377     this.id = this.el.id;
32378     this.addEvents({
32379         /**
32380         * @event beforeload
32381         * Fires before a node is loaded, return false to cancel
32382         * @param {Node} node The node being loaded
32383         */
32384         "beforeload" : true,
32385         /**
32386         * @event load
32387         * Fires when a node is loaded
32388         * @param {Node} node The node that was loaded
32389         */
32390         "load" : true,
32391         /**
32392         * @event textchange
32393         * Fires when the text for a node is changed
32394         * @param {Node} node The node
32395         * @param {String} text The new text
32396         * @param {String} oldText The old text
32397         */
32398         "textchange" : true,
32399         /**
32400         * @event beforeexpand
32401         * Fires before a node is expanded, return false to cancel.
32402         * @param {Node} node The node
32403         * @param {Boolean} deep
32404         * @param {Boolean} anim
32405         */
32406         "beforeexpand" : true,
32407         /**
32408         * @event beforecollapse
32409         * Fires before a node is collapsed, return false to cancel.
32410         * @param {Node} node The node
32411         * @param {Boolean} deep
32412         * @param {Boolean} anim
32413         */
32414         "beforecollapse" : true,
32415         /**
32416         * @event expand
32417         * Fires when a node is expanded
32418         * @param {Node} node The node
32419         */
32420         "expand" : true,
32421         /**
32422         * @event disabledchange
32423         * Fires when the disabled status of a node changes
32424         * @param {Node} node The node
32425         * @param {Boolean} disabled
32426         */
32427         "disabledchange" : true,
32428         /**
32429         * @event collapse
32430         * Fires when a node is collapsed
32431         * @param {Node} node The node
32432         */
32433         "collapse" : true,
32434         /**
32435         * @event beforeclick
32436         * Fires before click processing on a node. Return false to cancel the default action.
32437         * @param {Node} node The node
32438         * @param {Roo.EventObject} e The event object
32439         */
32440         "beforeclick":true,
32441         /**
32442         * @event checkchange
32443         * Fires when a node with a checkbox's checked property changes
32444         * @param {Node} this This node
32445         * @param {Boolean} checked
32446         */
32447         "checkchange":true,
32448         /**
32449         * @event click
32450         * Fires when a node is clicked
32451         * @param {Node} node The node
32452         * @param {Roo.EventObject} e The event object
32453         */
32454         "click":true,
32455         /**
32456         * @event dblclick
32457         * Fires when a node is double clicked
32458         * @param {Node} node The node
32459         * @param {Roo.EventObject} e The event object
32460         */
32461         "dblclick":true,
32462         /**
32463         * @event contextmenu
32464         * Fires when a node is right clicked
32465         * @param {Node} node The node
32466         * @param {Roo.EventObject} e The event object
32467         */
32468         "contextmenu":true,
32469         /**
32470         * @event beforechildrenrendered
32471         * Fires right before the child nodes for a node are rendered
32472         * @param {Node} node The node
32473         */
32474         "beforechildrenrendered":true,
32475         /**
32476         * @event startdrag
32477         * Fires when a node starts being dragged
32478         * @param {Roo.tree.TreePanel} this
32479         * @param {Roo.tree.TreeNode} node
32480         * @param {event} e The raw browser event
32481         */ 
32482        "startdrag" : true,
32483        /**
32484         * @event enddrag
32485         * Fires when a drag operation is complete
32486         * @param {Roo.tree.TreePanel} this
32487         * @param {Roo.tree.TreeNode} node
32488         * @param {event} e The raw browser event
32489         */
32490        "enddrag" : true,
32491        /**
32492         * @event dragdrop
32493         * Fires when a dragged node is dropped on a valid DD target
32494         * @param {Roo.tree.TreePanel} this
32495         * @param {Roo.tree.TreeNode} node
32496         * @param {DD} dd The dd it was dropped on
32497         * @param {event} e The raw browser event
32498         */
32499        "dragdrop" : true,
32500        /**
32501         * @event beforenodedrop
32502         * Fires when a DD object is dropped on a node in this tree for preprocessing. Return false to cancel the drop. The dropEvent
32503         * passed to handlers has the following properties:<br />
32504         * <ul style="padding:5px;padding-left:16px;">
32505         * <li>tree - The TreePanel</li>
32506         * <li>target - The node being targeted for the drop</li>
32507         * <li>data - The drag data from the drag source</li>
32508         * <li>point - The point of the drop - append, above or below</li>
32509         * <li>source - The drag source</li>
32510         * <li>rawEvent - Raw mouse event</li>
32511         * <li>dropNode - Drop node(s) provided by the source <b>OR</b> you can supply node(s)
32512         * to be inserted by setting them on this object.</li>
32513         * <li>cancel - Set this to true to cancel the drop.</li>
32514         * </ul>
32515         * @param {Object} dropEvent
32516         */
32517        "beforenodedrop" : true,
32518        /**
32519         * @event nodedrop
32520         * Fires after a DD object is dropped on a node in this tree. The dropEvent
32521         * passed to handlers has the following properties:<br />
32522         * <ul style="padding:5px;padding-left:16px;">
32523         * <li>tree - The TreePanel</li>
32524         * <li>target - The node being targeted for the drop</li>
32525         * <li>data - The drag data from the drag source</li>
32526         * <li>point - The point of the drop - append, above or below</li>
32527         * <li>source - The drag source</li>
32528         * <li>rawEvent - Raw mouse event</li>
32529         * <li>dropNode - Dropped node(s).</li>
32530         * </ul>
32531         * @param {Object} dropEvent
32532         */
32533        "nodedrop" : true,
32534         /**
32535         * @event nodedragover
32536         * Fires when a tree node is being targeted for a drag drop, return false to signal drop not allowed. The dragOverEvent
32537         * passed to handlers has the following properties:<br />
32538         * <ul style="padding:5px;padding-left:16px;">
32539         * <li>tree - The TreePanel</li>
32540         * <li>target - The node being targeted for the drop</li>
32541         * <li>data - The drag data from the drag source</li>
32542         * <li>point - The point of the drop - append, above or below</li>
32543         * <li>source - The drag source</li>
32544         * <li>rawEvent - Raw mouse event</li>
32545         * <li>dropNode - Drop node(s) provided by the source.</li>
32546         * <li>cancel - Set this to true to signal drop not allowed.</li>
32547         * </ul>
32548         * @param {Object} dragOverEvent
32549         */
32550        "nodedragover" : true
32551         
32552     });
32553     if(this.singleExpand){
32554        this.on("beforeexpand", this.restrictExpand, this);
32555     }
32556     if (this.editor) {
32557         this.editor.tree = this;
32558         this.editor = Roo.factory(this.editor, Roo.tree);
32559     }
32560     
32561     if (this.selModel) {
32562         this.selModel = Roo.factory(this.selModel, Roo.tree);
32563     }
32564    
32565 };
32566 Roo.extend(Roo.tree.TreePanel, Roo.data.Tree, {
32567     rootVisible : true,
32568     animate: Roo.enableFx,
32569     lines : true,
32570     enableDD : false,
32571     hlDrop : Roo.enableFx,
32572   
32573     renderer: false,
32574     
32575     rendererTip: false,
32576     // private
32577     restrictExpand : function(node){
32578         var p = node.parentNode;
32579         if(p){
32580             if(p.expandedChild && p.expandedChild.parentNode == p){
32581                 p.expandedChild.collapse();
32582             }
32583             p.expandedChild = node;
32584         }
32585     },
32586
32587     // private override
32588     setRootNode : function(node){
32589         Roo.tree.TreePanel.superclass.setRootNode.call(this, node);
32590         if(!this.rootVisible){
32591             node.ui = new Roo.tree.RootTreeNodeUI(node);
32592         }
32593         return node;
32594     },
32595
32596     /**
32597      * Returns the container element for this TreePanel
32598      */
32599     getEl : function(){
32600         return this.el;
32601     },
32602
32603     /**
32604      * Returns the default TreeLoader for this TreePanel
32605      */
32606     getLoader : function(){
32607         return this.loader;
32608     },
32609
32610     /**
32611      * Expand all nodes
32612      */
32613     expandAll : function(){
32614         this.root.expand(true);
32615     },
32616
32617     /**
32618      * Collapse all nodes
32619      */
32620     collapseAll : function(){
32621         this.root.collapse(true);
32622     },
32623
32624     /**
32625      * Returns the selection model used by this TreePanel
32626      */
32627     getSelectionModel : function(){
32628         if(!this.selModel){
32629             this.selModel = new Roo.tree.DefaultSelectionModel();
32630         }
32631         return this.selModel;
32632     },
32633
32634     /**
32635      * Retrieve an array of checked nodes, or an array of a specific attribute of checked nodes (e.g. "id")
32636      * @param {String} attribute (optional) Defaults to null (return the actual nodes)
32637      * @param {TreeNode} startNode (optional) The node to start from, defaults to the root
32638      * @return {Array}
32639      */
32640     getChecked : function(a, startNode){
32641         startNode = startNode || this.root;
32642         var r = [];
32643         var f = function(){
32644             if(this.attributes.checked){
32645                 r.push(!a ? this : (a == 'id' ? this.id : this.attributes[a]));
32646             }
32647         }
32648         startNode.cascade(f);
32649         return r;
32650     },
32651
32652     /**
32653      * Expands a specified path in this TreePanel. A path can be retrieved from a node with {@link Roo.data.Node#getPath}
32654      * @param {String} path
32655      * @param {String} attr (optional) The attribute used in the path (see {@link Roo.data.Node#getPath} for more info)
32656      * @param {Function} callback (optional) The callback to call when the expand is complete. The callback will be called with
32657      * (bSuccess, oLastNode) where bSuccess is if the expand was successful and oLastNode is the last node that was expanded.
32658      */
32659     expandPath : function(path, attr, callback){
32660         attr = attr || "id";
32661         var keys = path.split(this.pathSeparator);
32662         var curNode = this.root;
32663         if(curNode.attributes[attr] != keys[1]){ // invalid root
32664             if(callback){
32665                 callback(false, null);
32666             }
32667             return;
32668         }
32669         var index = 1;
32670         var f = function(){
32671             if(++index == keys.length){
32672                 if(callback){
32673                     callback(true, curNode);
32674                 }
32675                 return;
32676             }
32677             var c = curNode.findChild(attr, keys[index]);
32678             if(!c){
32679                 if(callback){
32680                     callback(false, curNode);
32681                 }
32682                 return;
32683             }
32684             curNode = c;
32685             c.expand(false, false, f);
32686         };
32687         curNode.expand(false, false, f);
32688     },
32689
32690     /**
32691      * Selects the node in this tree at the specified path. A path can be retrieved from a node with {@link Roo.data.Node#getPath}
32692      * @param {String} path
32693      * @param {String} attr (optional) The attribute used in the path (see {@link Roo.data.Node#getPath} for more info)
32694      * @param {Function} callback (optional) The callback to call when the selection is complete. The callback will be called with
32695      * (bSuccess, oSelNode) where bSuccess is if the selection was successful and oSelNode is the selected node.
32696      */
32697     selectPath : function(path, attr, callback){
32698         attr = attr || "id";
32699         var keys = path.split(this.pathSeparator);
32700         var v = keys.pop();
32701         if(keys.length > 0){
32702             var f = function(success, node){
32703                 if(success && node){
32704                     var n = node.findChild(attr, v);
32705                     if(n){
32706                         n.select();
32707                         if(callback){
32708                             callback(true, n);
32709                         }
32710                     }else if(callback){
32711                         callback(false, n);
32712                     }
32713                 }else{
32714                     if(callback){
32715                         callback(false, n);
32716                     }
32717                 }
32718             };
32719             this.expandPath(keys.join(this.pathSeparator), attr, f);
32720         }else{
32721             this.root.select();
32722             if(callback){
32723                 callback(true, this.root);
32724             }
32725         }
32726     },
32727
32728     getTreeEl : function(){
32729         return this.el;
32730     },
32731
32732     /**
32733      * Trigger rendering of this TreePanel
32734      */
32735     render : function(){
32736         if (this.innerCt) {
32737             return this; // stop it rendering more than once!!
32738         }
32739         
32740         this.innerCt = this.el.createChild({tag:"ul",
32741                cls:"x-tree-root-ct " +
32742                (this.lines ? "x-tree-lines" : "x-tree-no-lines")});
32743
32744         if(this.containerScroll){
32745             Roo.dd.ScrollManager.register(this.el);
32746         }
32747         if((this.enableDD || this.enableDrop) && !this.dropZone){
32748            /**
32749             * The dropZone used by this tree if drop is enabled
32750             * @type Roo.tree.TreeDropZone
32751             */
32752              this.dropZone = new Roo.tree.TreeDropZone(this, this.dropConfig || {
32753                ddGroup: this.ddGroup || "TreeDD", appendOnly: this.ddAppendOnly === true
32754            });
32755         }
32756         if((this.enableDD || this.enableDrag) && !this.dragZone){
32757            /**
32758             * The dragZone used by this tree if drag is enabled
32759             * @type Roo.tree.TreeDragZone
32760             */
32761             this.dragZone = new Roo.tree.TreeDragZone(this, this.dragConfig || {
32762                ddGroup: this.ddGroup || "TreeDD",
32763                scroll: this.ddScroll
32764            });
32765         }
32766         this.getSelectionModel().init(this);
32767         if (!this.root) {
32768             Roo.log("ROOT not set in tree");
32769             return this;
32770         }
32771         this.root.render();
32772         if(!this.rootVisible){
32773             this.root.renderChildren();
32774         }
32775         return this;
32776     }
32777 });/*
32778  * Based on:
32779  * Ext JS Library 1.1.1
32780  * Copyright(c) 2006-2007, Ext JS, LLC.
32781  *
32782  * Originally Released Under LGPL - original licence link has changed is not relivant.
32783  *
32784  * Fork - LGPL
32785  * <script type="text/javascript">
32786  */
32787  
32788
32789 /**
32790  * @class Roo.tree.DefaultSelectionModel
32791  * @extends Roo.util.Observable
32792  * The default single selection for a TreePanel.
32793  * @param {Object} cfg Configuration
32794  */
32795 Roo.tree.DefaultSelectionModel = function(cfg){
32796    this.selNode = null;
32797    
32798    
32799    
32800    this.addEvents({
32801        /**
32802         * @event selectionchange
32803         * Fires when the selected node changes
32804         * @param {DefaultSelectionModel} this
32805         * @param {TreeNode} node the new selection
32806         */
32807        "selectionchange" : true,
32808
32809        /**
32810         * @event beforeselect
32811         * Fires before the selected node changes, return false to cancel the change
32812         * @param {DefaultSelectionModel} this
32813         * @param {TreeNode} node the new selection
32814         * @param {TreeNode} node the old selection
32815         */
32816        "beforeselect" : true
32817    });
32818    
32819     Roo.tree.DefaultSelectionModel.superclass.constructor.call(this,cfg);
32820 };
32821
32822 Roo.extend(Roo.tree.DefaultSelectionModel, Roo.util.Observable, {
32823     init : function(tree){
32824         this.tree = tree;
32825         tree.getTreeEl().on("keydown", this.onKeyDown, this);
32826         tree.on("click", this.onNodeClick, this);
32827     },
32828     
32829     onNodeClick : function(node, e){
32830         if (e.ctrlKey && this.selNode == node)  {
32831             this.unselect(node);
32832             return;
32833         }
32834         this.select(node);
32835     },
32836     
32837     /**
32838      * Select a node.
32839      * @param {TreeNode} node The node to select
32840      * @return {TreeNode} The selected node
32841      */
32842     select : function(node){
32843         var last = this.selNode;
32844         if(last != node && this.fireEvent('beforeselect', this, node, last) !== false){
32845             if(last){
32846                 last.ui.onSelectedChange(false);
32847             }
32848             this.selNode = node;
32849             node.ui.onSelectedChange(true);
32850             this.fireEvent("selectionchange", this, node, last);
32851         }
32852         return node;
32853     },
32854     
32855     /**
32856      * Deselect a node.
32857      * @param {TreeNode} node The node to unselect
32858      */
32859     unselect : function(node){
32860         if(this.selNode == node){
32861             this.clearSelections();
32862         }    
32863     },
32864     
32865     /**
32866      * Clear all selections
32867      */
32868     clearSelections : function(){
32869         var n = this.selNode;
32870         if(n){
32871             n.ui.onSelectedChange(false);
32872             this.selNode = null;
32873             this.fireEvent("selectionchange", this, null);
32874         }
32875         return n;
32876     },
32877     
32878     /**
32879      * Get the selected node
32880      * @return {TreeNode} The selected node
32881      */
32882     getSelectedNode : function(){
32883         return this.selNode;    
32884     },
32885     
32886     /**
32887      * Returns true if the node is selected
32888      * @param {TreeNode} node The node to check
32889      * @return {Boolean}
32890      */
32891     isSelected : function(node){
32892         return this.selNode == node;  
32893     },
32894
32895     /**
32896      * Selects the node above the selected node in the tree, intelligently walking the nodes
32897      * @return TreeNode The new selection
32898      */
32899     selectPrevious : function(){
32900         var s = this.selNode || this.lastSelNode;
32901         if(!s){
32902             return null;
32903         }
32904         var ps = s.previousSibling;
32905         if(ps){
32906             if(!ps.isExpanded() || ps.childNodes.length < 1){
32907                 return this.select(ps);
32908             } else{
32909                 var lc = ps.lastChild;
32910                 while(lc && lc.isExpanded() && lc.childNodes.length > 0){
32911                     lc = lc.lastChild;
32912                 }
32913                 return this.select(lc);
32914             }
32915         } else if(s.parentNode && (this.tree.rootVisible || !s.parentNode.isRoot)){
32916             return this.select(s.parentNode);
32917         }
32918         return null;
32919     },
32920
32921     /**
32922      * Selects the node above the selected node in the tree, intelligently walking the nodes
32923      * @return TreeNode The new selection
32924      */
32925     selectNext : function(){
32926         var s = this.selNode || this.lastSelNode;
32927         if(!s){
32928             return null;
32929         }
32930         if(s.firstChild && s.isExpanded()){
32931              return this.select(s.firstChild);
32932          }else if(s.nextSibling){
32933              return this.select(s.nextSibling);
32934          }else if(s.parentNode){
32935             var newS = null;
32936             s.parentNode.bubble(function(){
32937                 if(this.nextSibling){
32938                     newS = this.getOwnerTree().selModel.select(this.nextSibling);
32939                     return false;
32940                 }
32941             });
32942             return newS;
32943          }
32944         return null;
32945     },
32946
32947     onKeyDown : function(e){
32948         var s = this.selNode || this.lastSelNode;
32949         // undesirable, but required
32950         var sm = this;
32951         if(!s){
32952             return;
32953         }
32954         var k = e.getKey();
32955         switch(k){
32956              case e.DOWN:
32957                  e.stopEvent();
32958                  this.selectNext();
32959              break;
32960              case e.UP:
32961                  e.stopEvent();
32962                  this.selectPrevious();
32963              break;
32964              case e.RIGHT:
32965                  e.preventDefault();
32966                  if(s.hasChildNodes()){
32967                      if(!s.isExpanded()){
32968                          s.expand();
32969                      }else if(s.firstChild){
32970                          this.select(s.firstChild, e);
32971                      }
32972                  }
32973              break;
32974              case e.LEFT:
32975                  e.preventDefault();
32976                  if(s.hasChildNodes() && s.isExpanded()){
32977                      s.collapse();
32978                  }else if(s.parentNode && (this.tree.rootVisible || s.parentNode != this.tree.getRootNode())){
32979                      this.select(s.parentNode, e);
32980                  }
32981              break;
32982         };
32983     }
32984 });
32985
32986 /**
32987  * @class Roo.tree.MultiSelectionModel
32988  * @extends Roo.util.Observable
32989  * Multi selection for a TreePanel.
32990  * @param {Object} cfg Configuration
32991  */
32992 Roo.tree.MultiSelectionModel = function(){
32993    this.selNodes = [];
32994    this.selMap = {};
32995    this.addEvents({
32996        /**
32997         * @event selectionchange
32998         * Fires when the selected nodes change
32999         * @param {MultiSelectionModel} this
33000         * @param {Array} nodes Array of the selected nodes
33001         */
33002        "selectionchange" : true
33003    });
33004    Roo.tree.MultiSelectionModel.superclass.constructor.call(this,cfg);
33005    
33006 };
33007
33008 Roo.extend(Roo.tree.MultiSelectionModel, Roo.util.Observable, {
33009     init : function(tree){
33010         this.tree = tree;
33011         tree.getTreeEl().on("keydown", this.onKeyDown, this);
33012         tree.on("click", this.onNodeClick, this);
33013     },
33014     
33015     onNodeClick : function(node, e){
33016         this.select(node, e, e.ctrlKey);
33017     },
33018     
33019     /**
33020      * Select a node.
33021      * @param {TreeNode} node The node to select
33022      * @param {EventObject} e (optional) An event associated with the selection
33023      * @param {Boolean} keepExisting True to retain existing selections
33024      * @return {TreeNode} The selected node
33025      */
33026     select : function(node, e, keepExisting){
33027         if(keepExisting !== true){
33028             this.clearSelections(true);
33029         }
33030         if(this.isSelected(node)){
33031             this.lastSelNode = node;
33032             return node;
33033         }
33034         this.selNodes.push(node);
33035         this.selMap[node.id] = node;
33036         this.lastSelNode = node;
33037         node.ui.onSelectedChange(true);
33038         this.fireEvent("selectionchange", this, this.selNodes);
33039         return node;
33040     },
33041     
33042     /**
33043      * Deselect a node.
33044      * @param {TreeNode} node The node to unselect
33045      */
33046     unselect : function(node){
33047         if(this.selMap[node.id]){
33048             node.ui.onSelectedChange(false);
33049             var sn = this.selNodes;
33050             var index = -1;
33051             if(sn.indexOf){
33052                 index = sn.indexOf(node);
33053             }else{
33054                 for(var i = 0, len = sn.length; i < len; i++){
33055                     if(sn[i] == node){
33056                         index = i;
33057                         break;
33058                     }
33059                 }
33060             }
33061             if(index != -1){
33062                 this.selNodes.splice(index, 1);
33063             }
33064             delete this.selMap[node.id];
33065             this.fireEvent("selectionchange", this, this.selNodes);
33066         }
33067     },
33068     
33069     /**
33070      * Clear all selections
33071      */
33072     clearSelections : function(suppressEvent){
33073         var sn = this.selNodes;
33074         if(sn.length > 0){
33075             for(var i = 0, len = sn.length; i < len; i++){
33076                 sn[i].ui.onSelectedChange(false);
33077             }
33078             this.selNodes = [];
33079             this.selMap = {};
33080             if(suppressEvent !== true){
33081                 this.fireEvent("selectionchange", this, this.selNodes);
33082             }
33083         }
33084     },
33085     
33086     /**
33087      * Returns true if the node is selected
33088      * @param {TreeNode} node The node to check
33089      * @return {Boolean}
33090      */
33091     isSelected : function(node){
33092         return this.selMap[node.id] ? true : false;  
33093     },
33094     
33095     /**
33096      * Returns an array of the selected nodes
33097      * @return {Array}
33098      */
33099     getSelectedNodes : function(){
33100         return this.selNodes;    
33101     },
33102
33103     onKeyDown : Roo.tree.DefaultSelectionModel.prototype.onKeyDown,
33104
33105     selectNext : Roo.tree.DefaultSelectionModel.prototype.selectNext,
33106
33107     selectPrevious : Roo.tree.DefaultSelectionModel.prototype.selectPrevious
33108 });/*
33109  * Based on:
33110  * Ext JS Library 1.1.1
33111  * Copyright(c) 2006-2007, Ext JS, LLC.
33112  *
33113  * Originally Released Under LGPL - original licence link has changed is not relivant.
33114  *
33115  * Fork - LGPL
33116  * <script type="text/javascript">
33117  */
33118  
33119 /**
33120  * @class Roo.tree.TreeNode
33121  * @extends Roo.data.Node
33122  * @cfg {String} text The text for this node
33123  * @cfg {Boolean} expanded true to start the node expanded
33124  * @cfg {Boolean} allowDrag false to make this node undraggable if DD is on (defaults to true)
33125  * @cfg {Boolean} allowDrop false if this node cannot be drop on
33126  * @cfg {Boolean} disabled true to start the node disabled
33127  * @cfg {String} icon The path to an icon for the node. The preferred way to do this
33128  * is to use the cls or iconCls attributes and add the icon via a CSS background image.
33129  * @cfg {String} cls A css class to be added to the node
33130  * @cfg {String} iconCls A css class to be added to the nodes icon element for applying css background images
33131  * @cfg {String} href URL of the link used for the node (defaults to #)
33132  * @cfg {String} hrefTarget target frame for the link
33133  * @cfg {String} qtip An Ext QuickTip for the node
33134  * @cfg {String} qtipCfg An Ext QuickTip config for the node (used instead of qtip)
33135  * @cfg {Boolean} singleClickExpand True for single click expand on this node
33136  * @cfg {Function} uiProvider A UI <b>class</b> to use for this node (defaults to Roo.tree.TreeNodeUI)
33137  * @cfg {Boolean} checked True to render a checked checkbox for this node, false to render an unchecked checkbox
33138  * (defaults to undefined with no checkbox rendered)
33139  * @constructor
33140  * @param {Object/String} attributes The attributes/config for the node or just a string with the text for the node
33141  */
33142 Roo.tree.TreeNode = function(attributes){
33143     attributes = attributes || {};
33144     if(typeof attributes == "string"){
33145         attributes = {text: attributes};
33146     }
33147     this.childrenRendered = false;
33148     this.rendered = false;
33149     Roo.tree.TreeNode.superclass.constructor.call(this, attributes);
33150     this.expanded = attributes.expanded === true;
33151     this.isTarget = attributes.isTarget !== false;
33152     this.draggable = attributes.draggable !== false && attributes.allowDrag !== false;
33153     this.allowChildren = attributes.allowChildren !== false && attributes.allowDrop !== false;
33154
33155     /**
33156      * Read-only. The text for this node. To change it use setText().
33157      * @type String
33158      */
33159     this.text = attributes.text;
33160     /**
33161      * True if this node is disabled.
33162      * @type Boolean
33163      */
33164     this.disabled = attributes.disabled === true;
33165
33166     this.addEvents({
33167         /**
33168         * @event textchange
33169         * Fires when the text for this node is changed
33170         * @param {Node} this This node
33171         * @param {String} text The new text
33172         * @param {String} oldText The old text
33173         */
33174         "textchange" : true,
33175         /**
33176         * @event beforeexpand
33177         * Fires before this node is expanded, return false to cancel.
33178         * @param {Node} this This node
33179         * @param {Boolean} deep
33180         * @param {Boolean} anim
33181         */
33182         "beforeexpand" : true,
33183         /**
33184         * @event beforecollapse
33185         * Fires before this node is collapsed, return false to cancel.
33186         * @param {Node} this This node
33187         * @param {Boolean} deep
33188         * @param {Boolean} anim
33189         */
33190         "beforecollapse" : true,
33191         /**
33192         * @event expand
33193         * Fires when this node is expanded
33194         * @param {Node} this This node
33195         */
33196         "expand" : true,
33197         /**
33198         * @event disabledchange
33199         * Fires when the disabled status of this node changes
33200         * @param {Node} this This node
33201         * @param {Boolean} disabled
33202         */
33203         "disabledchange" : true,
33204         /**
33205         * @event collapse
33206         * Fires when this node is collapsed
33207         * @param {Node} this This node
33208         */
33209         "collapse" : true,
33210         /**
33211         * @event beforeclick
33212         * Fires before click processing. Return false to cancel the default action.
33213         * @param {Node} this This node
33214         * @param {Roo.EventObject} e The event object
33215         */
33216         "beforeclick":true,
33217         /**
33218         * @event checkchange
33219         * Fires when a node with a checkbox's checked property changes
33220         * @param {Node} this This node
33221         * @param {Boolean} checked
33222         */
33223         "checkchange":true,
33224         /**
33225         * @event click
33226         * Fires when this node is clicked
33227         * @param {Node} this This node
33228         * @param {Roo.EventObject} e The event object
33229         */
33230         "click":true,
33231         /**
33232         * @event dblclick
33233         * Fires when this node is double clicked
33234         * @param {Node} this This node
33235         * @param {Roo.EventObject} e The event object
33236         */
33237         "dblclick":true,
33238         /**
33239         * @event contextmenu
33240         * Fires when this node is right clicked
33241         * @param {Node} this This node
33242         * @param {Roo.EventObject} e The event object
33243         */
33244         "contextmenu":true,
33245         /**
33246         * @event beforechildrenrendered
33247         * Fires right before the child nodes for this node are rendered
33248         * @param {Node} this This node
33249         */
33250         "beforechildrenrendered":true
33251     });
33252
33253     var uiClass = this.attributes.uiProvider || Roo.tree.TreeNodeUI;
33254
33255     /**
33256      * Read-only. The UI for this node
33257      * @type TreeNodeUI
33258      */
33259     this.ui = new uiClass(this);
33260     
33261     // finally support items[]
33262     if (typeof(this.attributes.items) == 'undefined' || !this.attributes.items) {
33263         return;
33264     }
33265     
33266     
33267     Roo.each(this.attributes.items, function(c) {
33268         this.appendChild(Roo.factory(c,Roo.Tree));
33269     }, this);
33270     delete this.attributes.items;
33271     
33272     
33273     
33274 };
33275 Roo.extend(Roo.tree.TreeNode, Roo.data.Node, {
33276     preventHScroll: true,
33277     /**
33278      * Returns true if this node is expanded
33279      * @return {Boolean}
33280      */
33281     isExpanded : function(){
33282         return this.expanded;
33283     },
33284
33285     /**
33286      * Returns the UI object for this node
33287      * @return {TreeNodeUI}
33288      */
33289     getUI : function(){
33290         return this.ui;
33291     },
33292
33293     // private override
33294     setFirstChild : function(node){
33295         var of = this.firstChild;
33296         Roo.tree.TreeNode.superclass.setFirstChild.call(this, node);
33297         if(this.childrenRendered && of && node != of){
33298             of.renderIndent(true, true);
33299         }
33300         if(this.rendered){
33301             this.renderIndent(true, true);
33302         }
33303     },
33304
33305     // private override
33306     setLastChild : function(node){
33307         var ol = this.lastChild;
33308         Roo.tree.TreeNode.superclass.setLastChild.call(this, node);
33309         if(this.childrenRendered && ol && node != ol){
33310             ol.renderIndent(true, true);
33311         }
33312         if(this.rendered){
33313             this.renderIndent(true, true);
33314         }
33315     },
33316
33317     // these methods are overridden to provide lazy rendering support
33318     // private override
33319     appendChild : function()
33320     {
33321         var node = Roo.tree.TreeNode.superclass.appendChild.apply(this, arguments);
33322         if(node && this.childrenRendered){
33323             node.render();
33324         }
33325         this.ui.updateExpandIcon();
33326         return node;
33327     },
33328
33329     // private override
33330     removeChild : function(node){
33331         this.ownerTree.getSelectionModel().unselect(node);
33332         Roo.tree.TreeNode.superclass.removeChild.apply(this, arguments);
33333         // if it's been rendered remove dom node
33334         if(this.childrenRendered){
33335             node.ui.remove();
33336         }
33337         if(this.childNodes.length < 1){
33338             this.collapse(false, false);
33339         }else{
33340             this.ui.updateExpandIcon();
33341         }
33342         if(!this.firstChild) {
33343             this.childrenRendered = false;
33344         }
33345         return node;
33346     },
33347
33348     // private override
33349     insertBefore : function(node, refNode){
33350         var newNode = Roo.tree.TreeNode.superclass.insertBefore.apply(this, arguments);
33351         if(newNode && refNode && this.childrenRendered){
33352             node.render();
33353         }
33354         this.ui.updateExpandIcon();
33355         return newNode;
33356     },
33357
33358     /**
33359      * Sets the text for this node
33360      * @param {String} text
33361      */
33362     setText : function(text){
33363         var oldText = this.text;
33364         this.text = text;
33365         this.attributes.text = text;
33366         if(this.rendered){ // event without subscribing
33367             this.ui.onTextChange(this, text, oldText);
33368         }
33369         this.fireEvent("textchange", this, text, oldText);
33370     },
33371
33372     /**
33373      * Triggers selection of this node
33374      */
33375     select : function(){
33376         this.getOwnerTree().getSelectionModel().select(this);
33377     },
33378
33379     /**
33380      * Triggers deselection of this node
33381      */
33382     unselect : function(){
33383         this.getOwnerTree().getSelectionModel().unselect(this);
33384     },
33385
33386     /**
33387      * Returns true if this node is selected
33388      * @return {Boolean}
33389      */
33390     isSelected : function(){
33391         return this.getOwnerTree().getSelectionModel().isSelected(this);
33392     },
33393
33394     /**
33395      * Expand this node.
33396      * @param {Boolean} deep (optional) True to expand all children as well
33397      * @param {Boolean} anim (optional) false to cancel the default animation
33398      * @param {Function} callback (optional) A callback to be called when
33399      * expanding this node completes (does not wait for deep expand to complete).
33400      * Called with 1 parameter, this node.
33401      */
33402     expand : function(deep, anim, callback){
33403         if(!this.expanded){
33404             if(this.fireEvent("beforeexpand", this, deep, anim) === false){
33405                 return;
33406             }
33407             if(!this.childrenRendered){
33408                 this.renderChildren();
33409             }
33410             this.expanded = true;
33411             if(!this.isHiddenRoot() && (this.getOwnerTree().animate && anim !== false) || anim){
33412                 this.ui.animExpand(function(){
33413                     this.fireEvent("expand", this);
33414                     if(typeof callback == "function"){
33415                         callback(this);
33416                     }
33417                     if(deep === true){
33418                         this.expandChildNodes(true);
33419                     }
33420                 }.createDelegate(this));
33421                 return;
33422             }else{
33423                 this.ui.expand();
33424                 this.fireEvent("expand", this);
33425                 if(typeof callback == "function"){
33426                     callback(this);
33427                 }
33428             }
33429         }else{
33430            if(typeof callback == "function"){
33431                callback(this);
33432            }
33433         }
33434         if(deep === true){
33435             this.expandChildNodes(true);
33436         }
33437     },
33438
33439     isHiddenRoot : function(){
33440         return this.isRoot && !this.getOwnerTree().rootVisible;
33441     },
33442
33443     /**
33444      * Collapse this node.
33445      * @param {Boolean} deep (optional) True to collapse all children as well
33446      * @param {Boolean} anim (optional) false to cancel the default animation
33447      */
33448     collapse : function(deep, anim){
33449         if(this.expanded && !this.isHiddenRoot()){
33450             if(this.fireEvent("beforecollapse", this, deep, anim) === false){
33451                 return;
33452             }
33453             this.expanded = false;
33454             if((this.getOwnerTree().animate && anim !== false) || anim){
33455                 this.ui.animCollapse(function(){
33456                     this.fireEvent("collapse", this);
33457                     if(deep === true){
33458                         this.collapseChildNodes(true);
33459                     }
33460                 }.createDelegate(this));
33461                 return;
33462             }else{
33463                 this.ui.collapse();
33464                 this.fireEvent("collapse", this);
33465             }
33466         }
33467         if(deep === true){
33468             var cs = this.childNodes;
33469             for(var i = 0, len = cs.length; i < len; i++) {
33470                 cs[i].collapse(true, false);
33471             }
33472         }
33473     },
33474
33475     // private
33476     delayedExpand : function(delay){
33477         if(!this.expandProcId){
33478             this.expandProcId = this.expand.defer(delay, this);
33479         }
33480     },
33481
33482     // private
33483     cancelExpand : function(){
33484         if(this.expandProcId){
33485             clearTimeout(this.expandProcId);
33486         }
33487         this.expandProcId = false;
33488     },
33489
33490     /**
33491      * Toggles expanded/collapsed state of the node
33492      */
33493     toggle : function(){
33494         if(this.expanded){
33495             this.collapse();
33496         }else{
33497             this.expand();
33498         }
33499     },
33500
33501     /**
33502      * Ensures all parent nodes are expanded
33503      */
33504     ensureVisible : function(callback){
33505         var tree = this.getOwnerTree();
33506         tree.expandPath(this.parentNode.getPath(), false, function(){
33507             tree.getTreeEl().scrollChildIntoView(this.ui.anchor);
33508             Roo.callback(callback);
33509         }.createDelegate(this));
33510     },
33511
33512     /**
33513      * Expand all child nodes
33514      * @param {Boolean} deep (optional) true if the child nodes should also expand their child nodes
33515      */
33516     expandChildNodes : function(deep){
33517         var cs = this.childNodes;
33518         for(var i = 0, len = cs.length; i < len; i++) {
33519                 cs[i].expand(deep);
33520         }
33521     },
33522
33523     /**
33524      * Collapse all child nodes
33525      * @param {Boolean} deep (optional) true if the child nodes should also collapse their child nodes
33526      */
33527     collapseChildNodes : function(deep){
33528         var cs = this.childNodes;
33529         for(var i = 0, len = cs.length; i < len; i++) {
33530                 cs[i].collapse(deep);
33531         }
33532     },
33533
33534     /**
33535      * Disables this node
33536      */
33537     disable : function(){
33538         this.disabled = true;
33539         this.unselect();
33540         if(this.rendered && this.ui.onDisableChange){ // event without subscribing
33541             this.ui.onDisableChange(this, true);
33542         }
33543         this.fireEvent("disabledchange", this, true);
33544     },
33545
33546     /**
33547      * Enables this node
33548      */
33549     enable : function(){
33550         this.disabled = false;
33551         if(this.rendered && this.ui.onDisableChange){ // event without subscribing
33552             this.ui.onDisableChange(this, false);
33553         }
33554         this.fireEvent("disabledchange", this, false);
33555     },
33556
33557     // private
33558     renderChildren : function(suppressEvent){
33559         if(suppressEvent !== false){
33560             this.fireEvent("beforechildrenrendered", this);
33561         }
33562         var cs = this.childNodes;
33563         for(var i = 0, len = cs.length; i < len; i++){
33564             cs[i].render(true);
33565         }
33566         this.childrenRendered = true;
33567     },
33568
33569     // private
33570     sort : function(fn, scope){
33571         Roo.tree.TreeNode.superclass.sort.apply(this, arguments);
33572         if(this.childrenRendered){
33573             var cs = this.childNodes;
33574             for(var i = 0, len = cs.length; i < len; i++){
33575                 cs[i].render(true);
33576             }
33577         }
33578     },
33579
33580     // private
33581     render : function(bulkRender){
33582         this.ui.render(bulkRender);
33583         if(!this.rendered){
33584             this.rendered = true;
33585             if(this.expanded){
33586                 this.expanded = false;
33587                 this.expand(false, false);
33588             }
33589         }
33590     },
33591
33592     // private
33593     renderIndent : function(deep, refresh){
33594         if(refresh){
33595             this.ui.childIndent = null;
33596         }
33597         this.ui.renderIndent();
33598         if(deep === true && this.childrenRendered){
33599             var cs = this.childNodes;
33600             for(var i = 0, len = cs.length; i < len; i++){
33601                 cs[i].renderIndent(true, refresh);
33602             }
33603         }
33604     }
33605 });/*
33606  * Based on:
33607  * Ext JS Library 1.1.1
33608  * Copyright(c) 2006-2007, Ext JS, LLC.
33609  *
33610  * Originally Released Under LGPL - original licence link has changed is not relivant.
33611  *
33612  * Fork - LGPL
33613  * <script type="text/javascript">
33614  */
33615  
33616 /**
33617  * @class Roo.tree.AsyncTreeNode
33618  * @extends Roo.tree.TreeNode
33619  * @cfg {TreeLoader} loader A TreeLoader to be used by this node (defaults to the loader defined on the tree)
33620  * @constructor
33621  * @param {Object/String} attributes The attributes/config for the node or just a string with the text for the node 
33622  */
33623  Roo.tree.AsyncTreeNode = function(config){
33624     this.loaded = false;
33625     this.loading = false;
33626     Roo.tree.AsyncTreeNode.superclass.constructor.apply(this, arguments);
33627     /**
33628     * @event beforeload
33629     * Fires before this node is loaded, return false to cancel
33630     * @param {Node} this This node
33631     */
33632     this.addEvents({'beforeload':true, 'load': true});
33633     /**
33634     * @event load
33635     * Fires when this node is loaded
33636     * @param {Node} this This node
33637     */
33638     /**
33639      * The loader used by this node (defaults to using the tree's defined loader)
33640      * @type TreeLoader
33641      * @property loader
33642      */
33643 };
33644 Roo.extend(Roo.tree.AsyncTreeNode, Roo.tree.TreeNode, {
33645     expand : function(deep, anim, callback){
33646         if(this.loading){ // if an async load is already running, waiting til it's done
33647             var timer;
33648             var f = function(){
33649                 if(!this.loading){ // done loading
33650                     clearInterval(timer);
33651                     this.expand(deep, anim, callback);
33652                 }
33653             }.createDelegate(this);
33654             timer = setInterval(f, 200);
33655             return;
33656         }
33657         if(!this.loaded){
33658             if(this.fireEvent("beforeload", this) === false){
33659                 return;
33660             }
33661             this.loading = true;
33662             this.ui.beforeLoad(this);
33663             var loader = this.loader || this.attributes.loader || this.getOwnerTree().getLoader();
33664             if(loader){
33665                 loader.load(this, this.loadComplete.createDelegate(this, [deep, anim, callback]));
33666                 return;
33667             }
33668         }
33669         Roo.tree.AsyncTreeNode.superclass.expand.call(this, deep, anim, callback);
33670     },
33671     
33672     /**
33673      * Returns true if this node is currently loading
33674      * @return {Boolean}
33675      */
33676     isLoading : function(){
33677         return this.loading;  
33678     },
33679     
33680     loadComplete : function(deep, anim, callback){
33681         this.loading = false;
33682         this.loaded = true;
33683         this.ui.afterLoad(this);
33684         this.fireEvent("load", this);
33685         this.expand(deep, anim, callback);
33686     },
33687     
33688     /**
33689      * Returns true if this node has been loaded
33690      * @return {Boolean}
33691      */
33692     isLoaded : function(){
33693         return this.loaded;
33694     },
33695     
33696     hasChildNodes : function(){
33697         if(!this.isLeaf() && !this.loaded){
33698             return true;
33699         }else{
33700             return Roo.tree.AsyncTreeNode.superclass.hasChildNodes.call(this);
33701         }
33702     },
33703
33704     /**
33705      * Trigger a reload for this node
33706      * @param {Function} callback
33707      */
33708     reload : function(callback){
33709         this.collapse(false, false);
33710         while(this.firstChild){
33711             this.removeChild(this.firstChild);
33712         }
33713         this.childrenRendered = false;
33714         this.loaded = false;
33715         if(this.isHiddenRoot()){
33716             this.expanded = false;
33717         }
33718         this.expand(false, false, callback);
33719     }
33720 });/*
33721  * Based on:
33722  * Ext JS Library 1.1.1
33723  * Copyright(c) 2006-2007, Ext JS, LLC.
33724  *
33725  * Originally Released Under LGPL - original licence link has changed is not relivant.
33726  *
33727  * Fork - LGPL
33728  * <script type="text/javascript">
33729  */
33730  
33731 /**
33732  * @class Roo.tree.TreeNodeUI
33733  * @constructor
33734  * @param {Object} node The node to render
33735  * The TreeNode UI implementation is separate from the
33736  * tree implementation. Unless you are customizing the tree UI,
33737  * you should never have to use this directly.
33738  */
33739 Roo.tree.TreeNodeUI = function(node){
33740     this.node = node;
33741     this.rendered = false;
33742     this.animating = false;
33743     this.emptyIcon = Roo.BLANK_IMAGE_URL;
33744 };
33745
33746 Roo.tree.TreeNodeUI.prototype = {
33747     removeChild : function(node){
33748         if(this.rendered){
33749             this.ctNode.removeChild(node.ui.getEl());
33750         }
33751     },
33752
33753     beforeLoad : function(){
33754          this.addClass("x-tree-node-loading");
33755     },
33756
33757     afterLoad : function(){
33758          this.removeClass("x-tree-node-loading");
33759     },
33760
33761     onTextChange : function(node, text, oldText){
33762         if(this.rendered){
33763             this.textNode.innerHTML = text;
33764         }
33765     },
33766
33767     onDisableChange : function(node, state){
33768         this.disabled = state;
33769         if(state){
33770             this.addClass("x-tree-node-disabled");
33771         }else{
33772             this.removeClass("x-tree-node-disabled");
33773         }
33774     },
33775
33776     onSelectedChange : function(state){
33777         if(state){
33778             this.focus();
33779             this.addClass("x-tree-selected");
33780         }else{
33781             //this.blur();
33782             this.removeClass("x-tree-selected");
33783         }
33784     },
33785
33786     onMove : function(tree, node, oldParent, newParent, index, refNode){
33787         this.childIndent = null;
33788         if(this.rendered){
33789             var targetNode = newParent.ui.getContainer();
33790             if(!targetNode){//target not rendered
33791                 this.holder = document.createElement("div");
33792                 this.holder.appendChild(this.wrap);
33793                 return;
33794             }
33795             var insertBefore = refNode ? refNode.ui.getEl() : null;
33796             if(insertBefore){
33797                 targetNode.insertBefore(this.wrap, insertBefore);
33798             }else{
33799                 targetNode.appendChild(this.wrap);
33800             }
33801             this.node.renderIndent(true);
33802         }
33803     },
33804
33805     addClass : function(cls){
33806         if(this.elNode){
33807             Roo.fly(this.elNode).addClass(cls);
33808         }
33809     },
33810
33811     removeClass : function(cls){
33812         if(this.elNode){
33813             Roo.fly(this.elNode).removeClass(cls);
33814         }
33815     },
33816
33817     remove : function(){
33818         if(this.rendered){
33819             this.holder = document.createElement("div");
33820             this.holder.appendChild(this.wrap);
33821         }
33822     },
33823
33824     fireEvent : function(){
33825         return this.node.fireEvent.apply(this.node, arguments);
33826     },
33827
33828     initEvents : function(){
33829         this.node.on("move", this.onMove, this);
33830         var E = Roo.EventManager;
33831         var a = this.anchor;
33832
33833         var el = Roo.fly(a, '_treeui');
33834
33835         if(Roo.isOpera){ // opera render bug ignores the CSS
33836             el.setStyle("text-decoration", "none");
33837         }
33838
33839         el.on("click", this.onClick, this);
33840         el.on("dblclick", this.onDblClick, this);
33841
33842         if(this.checkbox){
33843             Roo.EventManager.on(this.checkbox,
33844                     Roo.isIE ? 'click' : 'change', this.onCheckChange, this);
33845         }
33846
33847         el.on("contextmenu", this.onContextMenu, this);
33848
33849         var icon = Roo.fly(this.iconNode);
33850         icon.on("click", this.onClick, this);
33851         icon.on("dblclick", this.onDblClick, this);
33852         icon.on("contextmenu", this.onContextMenu, this);
33853         E.on(this.ecNode, "click", this.ecClick, this, true);
33854
33855         if(this.node.disabled){
33856             this.addClass("x-tree-node-disabled");
33857         }
33858         if(this.node.hidden){
33859             this.addClass("x-tree-node-disabled");
33860         }
33861         var ot = this.node.getOwnerTree();
33862         var dd = ot.enableDD || ot.enableDrag || ot.enableDrop;
33863         if(dd && (!this.node.isRoot || ot.rootVisible)){
33864             Roo.dd.Registry.register(this.elNode, {
33865                 node: this.node,
33866                 handles: this.getDDHandles(),
33867                 isHandle: false
33868             });
33869         }
33870     },
33871
33872     getDDHandles : function(){
33873         return [this.iconNode, this.textNode];
33874     },
33875
33876     hide : function(){
33877         if(this.rendered){
33878             this.wrap.style.display = "none";
33879         }
33880     },
33881
33882     show : function(){
33883         if(this.rendered){
33884             this.wrap.style.display = "";
33885         }
33886     },
33887
33888     onContextMenu : function(e){
33889         if (this.node.hasListener("contextmenu") || this.node.getOwnerTree().hasListener("contextmenu")) {
33890             e.preventDefault();
33891             this.focus();
33892             this.fireEvent("contextmenu", this.node, e);
33893         }
33894     },
33895
33896     onClick : function(e){
33897         if(this.dropping){
33898             e.stopEvent();
33899             return;
33900         }
33901         if(this.fireEvent("beforeclick", this.node, e) !== false){
33902             if(!this.disabled && this.node.attributes.href){
33903                 this.fireEvent("click", this.node, e);
33904                 return;
33905             }
33906             e.preventDefault();
33907             if(this.disabled){
33908                 return;
33909             }
33910
33911             if(this.node.attributes.singleClickExpand && !this.animating && this.node.hasChildNodes()){
33912                 this.node.toggle();
33913             }
33914
33915             this.fireEvent("click", this.node, e);
33916         }else{
33917             e.stopEvent();
33918         }
33919     },
33920
33921     onDblClick : function(e){
33922         e.preventDefault();
33923         if(this.disabled){
33924             return;
33925         }
33926         if(this.checkbox){
33927             this.toggleCheck();
33928         }
33929         if(!this.animating && this.node.hasChildNodes()){
33930             this.node.toggle();
33931         }
33932         this.fireEvent("dblclick", this.node, e);
33933     },
33934
33935     onCheckChange : function(){
33936         var checked = this.checkbox.checked;
33937         this.node.attributes.checked = checked;
33938         this.fireEvent('checkchange', this.node, checked);
33939     },
33940
33941     ecClick : function(e){
33942         if(!this.animating && this.node.hasChildNodes()){
33943             this.node.toggle();
33944         }
33945     },
33946
33947     startDrop : function(){
33948         this.dropping = true;
33949     },
33950
33951     // delayed drop so the click event doesn't get fired on a drop
33952     endDrop : function(){
33953        setTimeout(function(){
33954            this.dropping = false;
33955        }.createDelegate(this), 50);
33956     },
33957
33958     expand : function(){
33959         this.updateExpandIcon();
33960         this.ctNode.style.display = "";
33961     },
33962
33963     focus : function(){
33964         if(!this.node.preventHScroll){
33965             try{this.anchor.focus();
33966             }catch(e){}
33967         }else if(!Roo.isIE){
33968             try{
33969                 var noscroll = this.node.getOwnerTree().getTreeEl().dom;
33970                 var l = noscroll.scrollLeft;
33971                 this.anchor.focus();
33972                 noscroll.scrollLeft = l;
33973             }catch(e){}
33974         }
33975     },
33976
33977     toggleCheck : function(value){
33978         var cb = this.checkbox;
33979         if(cb){
33980             cb.checked = (value === undefined ? !cb.checked : value);
33981         }
33982     },
33983
33984     blur : function(){
33985         try{
33986             this.anchor.blur();
33987         }catch(e){}
33988     },
33989
33990     animExpand : function(callback){
33991         var ct = Roo.get(this.ctNode);
33992         ct.stopFx();
33993         if(!this.node.hasChildNodes()){
33994             this.updateExpandIcon();
33995             this.ctNode.style.display = "";
33996             Roo.callback(callback);
33997             return;
33998         }
33999         this.animating = true;
34000         this.updateExpandIcon();
34001
34002         ct.slideIn('t', {
34003            callback : function(){
34004                this.animating = false;
34005                Roo.callback(callback);
34006             },
34007             scope: this,
34008             duration: this.node.ownerTree.duration || .25
34009         });
34010     },
34011
34012     highlight : function(){
34013         var tree = this.node.getOwnerTree();
34014         Roo.fly(this.wrap).highlight(
34015             tree.hlColor || "C3DAF9",
34016             {endColor: tree.hlBaseColor}
34017         );
34018     },
34019
34020     collapse : function(){
34021         this.updateExpandIcon();
34022         this.ctNode.style.display = "none";
34023     },
34024
34025     animCollapse : function(callback){
34026         var ct = Roo.get(this.ctNode);
34027         ct.enableDisplayMode('block');
34028         ct.stopFx();
34029
34030         this.animating = true;
34031         this.updateExpandIcon();
34032
34033         ct.slideOut('t', {
34034             callback : function(){
34035                this.animating = false;
34036                Roo.callback(callback);
34037             },
34038             scope: this,
34039             duration: this.node.ownerTree.duration || .25
34040         });
34041     },
34042
34043     getContainer : function(){
34044         return this.ctNode;
34045     },
34046
34047     getEl : function(){
34048         return this.wrap;
34049     },
34050
34051     appendDDGhost : function(ghostNode){
34052         ghostNode.appendChild(this.elNode.cloneNode(true));
34053     },
34054
34055     getDDRepairXY : function(){
34056         return Roo.lib.Dom.getXY(this.iconNode);
34057     },
34058
34059     onRender : function(){
34060         this.render();
34061     },
34062
34063     render : function(bulkRender){
34064         var n = this.node, a = n.attributes;
34065         var targetNode = n.parentNode ?
34066               n.parentNode.ui.getContainer() : n.ownerTree.innerCt.dom;
34067
34068         if(!this.rendered){
34069             this.rendered = true;
34070
34071             this.renderElements(n, a, targetNode, bulkRender);
34072
34073             if(a.qtip){
34074                if(this.textNode.setAttributeNS){
34075                    this.textNode.setAttributeNS("ext", "qtip", a.qtip);
34076                    if(a.qtipTitle){
34077                        this.textNode.setAttributeNS("ext", "qtitle", a.qtipTitle);
34078                    }
34079                }else{
34080                    this.textNode.setAttribute("ext:qtip", a.qtip);
34081                    if(a.qtipTitle){
34082                        this.textNode.setAttribute("ext:qtitle", a.qtipTitle);
34083                    }
34084                }
34085             }else if(a.qtipCfg){
34086                 a.qtipCfg.target = Roo.id(this.textNode);
34087                 Roo.QuickTips.register(a.qtipCfg);
34088             }
34089             this.initEvents();
34090             if(!this.node.expanded){
34091                 this.updateExpandIcon();
34092             }
34093         }else{
34094             if(bulkRender === true) {
34095                 targetNode.appendChild(this.wrap);
34096             }
34097         }
34098     },
34099
34100     renderElements : function(n, a, targetNode, bulkRender)
34101     {
34102         // add some indent caching, this helps performance when rendering a large tree
34103         this.indentMarkup = n.parentNode ? n.parentNode.ui.getChildIndent() : '';
34104         var t = n.getOwnerTree();
34105         var txt = t.renderer ? t.renderer(n.attributes) : Roo.util.Format.htmlEncode(n.text);
34106         if (typeof(n.attributes.html) != 'undefined') {
34107             txt = n.attributes.html;
34108         }
34109         var tip = t.rendererTip ? t.rendererTip(n.attributes) : txt;
34110         var cb = typeof a.checked == 'boolean';
34111         var href = a.href ? a.href : Roo.isGecko ? "" : "#";
34112         var buf = ['<li class="x-tree-node"><div class="x-tree-node-el ', a.cls,'">',
34113             '<span class="x-tree-node-indent">',this.indentMarkup,"</span>",
34114             '<img src="', this.emptyIcon, '" class="x-tree-ec-icon" />',
34115             '<img src="', a.icon || this.emptyIcon, '" class="x-tree-node-icon',(a.icon ? " x-tree-node-inline-icon" : ""),(a.iconCls ? " "+a.iconCls : ""),'" unselectable="on" />',
34116             cb ? ('<input class="x-tree-node-cb" type="checkbox" ' + (a.checked ? 'checked="checked" />' : ' />')) : '',
34117             '<a hidefocus="on" href="',href,'" tabIndex="1" ',
34118              a.hrefTarget ? ' target="'+a.hrefTarget+'"' : "", 
34119                 '><span unselectable="on" qtip="' , tip ,'">',txt,"</span></a></div>",
34120             '<ul class="x-tree-node-ct" style="display:none;"></ul>',
34121             "</li>"];
34122
34123         if(bulkRender !== true && n.nextSibling && n.nextSibling.ui.getEl()){
34124             this.wrap = Roo.DomHelper.insertHtml("beforeBegin",
34125                                 n.nextSibling.ui.getEl(), buf.join(""));
34126         }else{
34127             this.wrap = Roo.DomHelper.insertHtml("beforeEnd", targetNode, buf.join(""));
34128         }
34129
34130         this.elNode = this.wrap.childNodes[0];
34131         this.ctNode = this.wrap.childNodes[1];
34132         var cs = this.elNode.childNodes;
34133         this.indentNode = cs[0];
34134         this.ecNode = cs[1];
34135         this.iconNode = cs[2];
34136         var index = 3;
34137         if(cb){
34138             this.checkbox = cs[3];
34139             index++;
34140         }
34141         this.anchor = cs[index];
34142         this.textNode = cs[index].firstChild;
34143     },
34144
34145     getAnchor : function(){
34146         return this.anchor;
34147     },
34148
34149     getTextEl : function(){
34150         return this.textNode;
34151     },
34152
34153     getIconEl : function(){
34154         return this.iconNode;
34155     },
34156
34157     isChecked : function(){
34158         return this.checkbox ? this.checkbox.checked : false;
34159     },
34160
34161     updateExpandIcon : function(){
34162         if(this.rendered){
34163             var n = this.node, c1, c2;
34164             var cls = n.isLast() ? "x-tree-elbow-end" : "x-tree-elbow";
34165             var hasChild = n.hasChildNodes();
34166             if(hasChild){
34167                 if(n.expanded){
34168                     cls += "-minus";
34169                     c1 = "x-tree-node-collapsed";
34170                     c2 = "x-tree-node-expanded";
34171                 }else{
34172                     cls += "-plus";
34173                     c1 = "x-tree-node-expanded";
34174                     c2 = "x-tree-node-collapsed";
34175                 }
34176                 if(this.wasLeaf){
34177                     this.removeClass("x-tree-node-leaf");
34178                     this.wasLeaf = false;
34179                 }
34180                 if(this.c1 != c1 || this.c2 != c2){
34181                     Roo.fly(this.elNode).replaceClass(c1, c2);
34182                     this.c1 = c1; this.c2 = c2;
34183                 }
34184             }else{
34185                 // this changes non-leafs into leafs if they have no children.
34186                 // it's not very rational behaviour..
34187                 
34188                 if(!this.wasLeaf && this.node.leaf){
34189                     Roo.fly(this.elNode).replaceClass("x-tree-node-expanded", "x-tree-node-leaf");
34190                     delete this.c1;
34191                     delete this.c2;
34192                     this.wasLeaf = true;
34193                 }
34194             }
34195             var ecc = "x-tree-ec-icon "+cls;
34196             if(this.ecc != ecc){
34197                 this.ecNode.className = ecc;
34198                 this.ecc = ecc;
34199             }
34200         }
34201     },
34202
34203     getChildIndent : function(){
34204         if(!this.childIndent){
34205             var buf = [];
34206             var p = this.node;
34207             while(p){
34208                 if(!p.isRoot || (p.isRoot && p.ownerTree.rootVisible)){
34209                     if(!p.isLast()) {
34210                         buf.unshift('<img src="'+this.emptyIcon+'" class="x-tree-elbow-line" />');
34211                     } else {
34212                         buf.unshift('<img src="'+this.emptyIcon+'" class="x-tree-icon" />');
34213                     }
34214                 }
34215                 p = p.parentNode;
34216             }
34217             this.childIndent = buf.join("");
34218         }
34219         return this.childIndent;
34220     },
34221
34222     renderIndent : function(){
34223         if(this.rendered){
34224             var indent = "";
34225             var p = this.node.parentNode;
34226             if(p){
34227                 indent = p.ui.getChildIndent();
34228             }
34229             if(this.indentMarkup != indent){ // don't rerender if not required
34230                 this.indentNode.innerHTML = indent;
34231                 this.indentMarkup = indent;
34232             }
34233             this.updateExpandIcon();
34234         }
34235     }
34236 };
34237
34238 Roo.tree.RootTreeNodeUI = function(){
34239     Roo.tree.RootTreeNodeUI.superclass.constructor.apply(this, arguments);
34240 };
34241 Roo.extend(Roo.tree.RootTreeNodeUI, Roo.tree.TreeNodeUI, {
34242     render : function(){
34243         if(!this.rendered){
34244             var targetNode = this.node.ownerTree.innerCt.dom;
34245             this.node.expanded = true;
34246             targetNode.innerHTML = '<div class="x-tree-root-node"></div>';
34247             this.wrap = this.ctNode = targetNode.firstChild;
34248         }
34249     },
34250     collapse : function(){
34251     },
34252     expand : function(){
34253     }
34254 });/*
34255  * Based on:
34256  * Ext JS Library 1.1.1
34257  * Copyright(c) 2006-2007, Ext JS, LLC.
34258  *
34259  * Originally Released Under LGPL - original licence link has changed is not relivant.
34260  *
34261  * Fork - LGPL
34262  * <script type="text/javascript">
34263  */
34264 /**
34265  * @class Roo.tree.TreeLoader
34266  * @extends Roo.util.Observable
34267  * A TreeLoader provides for lazy loading of an {@link Roo.tree.TreeNode}'s child
34268  * nodes from a specified URL. The response must be a javascript Array definition
34269  * who's elements are node definition objects. eg:
34270  * <pre><code>
34271 {  success : true,
34272    data :      [
34273    
34274     { 'id': 1, 'text': 'A folder Node', 'leaf': false },
34275     { 'id': 2, 'text': 'A leaf Node', 'leaf': true }
34276     ]
34277 }
34278
34279
34280 </code></pre>
34281  * <br><br>
34282  * The old style respose with just an array is still supported, but not recommended.
34283  * <br><br>
34284  *
34285  * A server request is sent, and child nodes are loaded only when a node is expanded.
34286  * The loading node's id is passed to the server under the parameter name "node" to
34287  * enable the server to produce the correct child nodes.
34288  * <br><br>
34289  * To pass extra parameters, an event handler may be attached to the "beforeload"
34290  * event, and the parameters specified in the TreeLoader's baseParams property:
34291  * <pre><code>
34292     myTreeLoader.on("beforeload", function(treeLoader, node) {
34293         this.baseParams.category = node.attributes.category;
34294     }, this);
34295 </code></pre><
34296  * This would pass an HTTP parameter called "category" to the server containing
34297  * the value of the Node's "category" attribute.
34298  * @constructor
34299  * Creates a new Treeloader.
34300  * @param {Object} config A config object containing config properties.
34301  */
34302 Roo.tree.TreeLoader = function(config){
34303     this.baseParams = {};
34304     this.requestMethod = "POST";
34305     Roo.apply(this, config);
34306
34307     this.addEvents({
34308     
34309         /**
34310          * @event beforeload
34311          * Fires before a network request is made to retrieve the Json text which specifies a node's children.
34312          * @param {Object} This TreeLoader object.
34313          * @param {Object} node The {@link Roo.tree.TreeNode} object being loaded.
34314          * @param {Object} callback The callback function specified in the {@link #load} call.
34315          */
34316         beforeload : true,
34317         /**
34318          * @event load
34319          * Fires when the node has been successfuly loaded.
34320          * @param {Object} This TreeLoader object.
34321          * @param {Object} node The {@link Roo.tree.TreeNode} object being loaded.
34322          * @param {Object} response The response object containing the data from the server.
34323          */
34324         load : true,
34325         /**
34326          * @event loadexception
34327          * Fires if the network request failed.
34328          * @param {Object} This TreeLoader object.
34329          * @param {Object} node The {@link Roo.tree.TreeNode} object being loaded.
34330          * @param {Object} response The response object containing the data from the server.
34331          */
34332         loadexception : true,
34333         /**
34334          * @event create
34335          * Fires before a node is created, enabling you to return custom Node types 
34336          * @param {Object} This TreeLoader object.
34337          * @param {Object} attr - the data returned from the AJAX call (modify it to suit)
34338          */
34339         create : true
34340     });
34341
34342     Roo.tree.TreeLoader.superclass.constructor.call(this);
34343 };
34344
34345 Roo.extend(Roo.tree.TreeLoader, Roo.util.Observable, {
34346     /**
34347     * @cfg {String} dataUrl The URL from which to request a Json string which
34348     * specifies an array of node definition object representing the child nodes
34349     * to be loaded.
34350     */
34351     /**
34352     * @cfg {String} requestMethod either GET or POST
34353     * defaults to POST (due to BC)
34354     * to be loaded.
34355     */
34356     /**
34357     * @cfg {Object} baseParams (optional) An object containing properties which
34358     * specify HTTP parameters to be passed to each request for child nodes.
34359     */
34360     /**
34361     * @cfg {Object} baseAttrs (optional) An object containing attributes to be added to all nodes
34362     * created by this loader. If the attributes sent by the server have an attribute in this object,
34363     * they take priority.
34364     */
34365     /**
34366     * @cfg {Object} uiProviders (optional) An object containing properties which
34367     * 
34368     * DEPRECATED - use 'create' event handler to modify attributes - which affect creation.
34369     * specify custom {@link Roo.tree.TreeNodeUI} implementations. If the optional
34370     * <i>uiProvider</i> attribute of a returned child node is a string rather
34371     * than a reference to a TreeNodeUI implementation, this that string value
34372     * is used as a property name in the uiProviders object. You can define the provider named
34373     * 'default' , and this will be used for all nodes (if no uiProvider is delivered by the node data)
34374     */
34375     uiProviders : {},
34376
34377     /**
34378     * @cfg {Boolean} clearOnLoad (optional) Default to true. Remove previously existing
34379     * child nodes before loading.
34380     */
34381     clearOnLoad : true,
34382
34383     /**
34384     * @cfg {String} root (optional) Default to false. Use this to read data from an object 
34385     * property on loading, rather than expecting an array. (eg. more compatible to a standard
34386     * Grid query { data : [ .....] }
34387     */
34388     
34389     root : false,
34390      /**
34391     * @cfg {String} queryParam (optional) 
34392     * Name of the query as it will be passed on the querystring (defaults to 'node')
34393     * eg. the request will be ?node=[id]
34394     */
34395     
34396     
34397     queryParam: false,
34398     
34399     /**
34400      * Load an {@link Roo.tree.TreeNode} from the URL specified in the constructor.
34401      * This is called automatically when a node is expanded, but may be used to reload
34402      * a node (or append new children if the {@link #clearOnLoad} option is false.)
34403      * @param {Roo.tree.TreeNode} node
34404      * @param {Function} callback
34405      */
34406     load : function(node, callback){
34407         if(this.clearOnLoad){
34408             while(node.firstChild){
34409                 node.removeChild(node.firstChild);
34410             }
34411         }
34412         if(node.attributes.children){ // preloaded json children
34413             var cs = node.attributes.children;
34414             for(var i = 0, len = cs.length; i < len; i++){
34415                 node.appendChild(this.createNode(cs[i]));
34416             }
34417             if(typeof callback == "function"){
34418                 callback();
34419             }
34420         }else if(this.dataUrl){
34421             this.requestData(node, callback);
34422         }
34423     },
34424
34425     getParams: function(node){
34426         var buf = [], bp = this.baseParams;
34427         for(var key in bp){
34428             if(typeof bp[key] != "function"){
34429                 buf.push(encodeURIComponent(key), "=", encodeURIComponent(bp[key]), "&");
34430             }
34431         }
34432         var n = this.queryParam === false ? 'node' : this.queryParam;
34433         buf.push(n + "=", encodeURIComponent(node.id));
34434         return buf.join("");
34435     },
34436
34437     requestData : function(node, callback){
34438         if(this.fireEvent("beforeload", this, node, callback) !== false){
34439             this.transId = Roo.Ajax.request({
34440                 method:this.requestMethod,
34441                 url: this.dataUrl||this.url,
34442                 success: this.handleResponse,
34443                 failure: this.handleFailure,
34444                 scope: this,
34445                 argument: {callback: callback, node: node},
34446                 params: this.getParams(node)
34447             });
34448         }else{
34449             // if the load is cancelled, make sure we notify
34450             // the node that we are done
34451             if(typeof callback == "function"){
34452                 callback();
34453             }
34454         }
34455     },
34456
34457     isLoading : function(){
34458         return this.transId ? true : false;
34459     },
34460
34461     abort : function(){
34462         if(this.isLoading()){
34463             Roo.Ajax.abort(this.transId);
34464         }
34465     },
34466
34467     // private
34468     createNode : function(attr)
34469     {
34470         // apply baseAttrs, nice idea Corey!
34471         if(this.baseAttrs){
34472             Roo.applyIf(attr, this.baseAttrs);
34473         }
34474         if(this.applyLoader !== false){
34475             attr.loader = this;
34476         }
34477         // uiProvider = depreciated..
34478         
34479         if(typeof(attr.uiProvider) == 'string'){
34480            attr.uiProvider = this.uiProviders[attr.uiProvider] || 
34481                 /**  eval:var:attr */ eval(attr.uiProvider);
34482         }
34483         if(typeof(this.uiProviders['default']) != 'undefined') {
34484             attr.uiProvider = this.uiProviders['default'];
34485         }
34486         
34487         this.fireEvent('create', this, attr);
34488         
34489         attr.leaf  = typeof(attr.leaf) == 'string' ? attr.leaf * 1 : attr.leaf;
34490         return(attr.leaf ?
34491                         new Roo.tree.TreeNode(attr) :
34492                         new Roo.tree.AsyncTreeNode(attr));
34493     },
34494
34495     processResponse : function(response, node, callback)
34496     {
34497         var json = response.responseText;
34498         try {
34499             
34500             var o = Roo.decode(json);
34501             
34502             if (this.root === false && typeof(o.success) != undefined) {
34503                 this.root = 'data'; // the default behaviour for list like data..
34504                 }
34505                 
34506             if (this.root !== false &&  !o.success) {
34507                 // it's a failure condition.
34508                 var a = response.argument;
34509                 this.fireEvent("loadexception", this, a.node, response);
34510                 Roo.log("Load failed - should have a handler really");
34511                 return;
34512             }
34513             
34514             
34515             
34516             if (this.root !== false) {
34517                  o = o[this.root];
34518             }
34519             
34520             for(var i = 0, len = o.length; i < len; i++){
34521                 var n = this.createNode(o[i]);
34522                 if(n){
34523                     node.appendChild(n);
34524                 }
34525             }
34526             if(typeof callback == "function"){
34527                 callback(this, node);
34528             }
34529         }catch(e){
34530             this.handleFailure(response);
34531         }
34532     },
34533
34534     handleResponse : function(response){
34535         this.transId = false;
34536         var a = response.argument;
34537         this.processResponse(response, a.node, a.callback);
34538         this.fireEvent("load", this, a.node, response);
34539     },
34540
34541     handleFailure : function(response)
34542     {
34543         // should handle failure better..
34544         this.transId = false;
34545         var a = response.argument;
34546         this.fireEvent("loadexception", this, a.node, response);
34547         if(typeof a.callback == "function"){
34548             a.callback(this, a.node);
34549         }
34550     }
34551 });/*
34552  * Based on:
34553  * Ext JS Library 1.1.1
34554  * Copyright(c) 2006-2007, Ext JS, LLC.
34555  *
34556  * Originally Released Under LGPL - original licence link has changed is not relivant.
34557  *
34558  * Fork - LGPL
34559  * <script type="text/javascript">
34560  */
34561
34562 /**
34563 * @class Roo.tree.TreeFilter
34564 * Note this class is experimental and doesn't update the indent (lines) or expand collapse icons of the nodes
34565 * @param {TreePanel} tree
34566 * @param {Object} config (optional)
34567  */
34568 Roo.tree.TreeFilter = function(tree, config){
34569     this.tree = tree;
34570     this.filtered = {};
34571     Roo.apply(this, config);
34572 };
34573
34574 Roo.tree.TreeFilter.prototype = {
34575     clearBlank:false,
34576     reverse:false,
34577     autoClear:false,
34578     remove:false,
34579
34580      /**
34581      * Filter the data by a specific attribute.
34582      * @param {String/RegExp} value Either string that the attribute value
34583      * should start with or a RegExp to test against the attribute
34584      * @param {String} attr (optional) The attribute passed in your node's attributes collection. Defaults to "text".
34585      * @param {TreeNode} startNode (optional) The node to start the filter at.
34586      */
34587     filter : function(value, attr, startNode){
34588         attr = attr || "text";
34589         var f;
34590         if(typeof value == "string"){
34591             var vlen = value.length;
34592             // auto clear empty filter
34593             if(vlen == 0 && this.clearBlank){
34594                 this.clear();
34595                 return;
34596             }
34597             value = value.toLowerCase();
34598             f = function(n){
34599                 return n.attributes[attr].substr(0, vlen).toLowerCase() == value;
34600             };
34601         }else if(value.exec){ // regex?
34602             f = function(n){
34603                 return value.test(n.attributes[attr]);
34604             };
34605         }else{
34606             throw 'Illegal filter type, must be string or regex';
34607         }
34608         this.filterBy(f, null, startNode);
34609         },
34610
34611     /**
34612      * Filter by a function. The passed function will be called with each
34613      * node in the tree (or from the startNode). If the function returns true, the node is kept
34614      * otherwise it is filtered. If a node is filtered, its children are also filtered.
34615      * @param {Function} fn The filter function
34616      * @param {Object} scope (optional) The scope of the function (defaults to the current node)
34617      */
34618     filterBy : function(fn, scope, startNode){
34619         startNode = startNode || this.tree.root;
34620         if(this.autoClear){
34621             this.clear();
34622         }
34623         var af = this.filtered, rv = this.reverse;
34624         var f = function(n){
34625             if(n == startNode){
34626                 return true;
34627             }
34628             if(af[n.id]){
34629                 return false;
34630             }
34631             var m = fn.call(scope || n, n);
34632             if(!m || rv){
34633                 af[n.id] = n;
34634                 n.ui.hide();
34635                 return false;
34636             }
34637             return true;
34638         };
34639         startNode.cascade(f);
34640         if(this.remove){
34641            for(var id in af){
34642                if(typeof id != "function"){
34643                    var n = af[id];
34644                    if(n && n.parentNode){
34645                        n.parentNode.removeChild(n);
34646                    }
34647                }
34648            }
34649         }
34650     },
34651
34652     /**
34653      * Clears the current filter. Note: with the "remove" option
34654      * set a filter cannot be cleared.
34655      */
34656     clear : function(){
34657         var t = this.tree;
34658         var af = this.filtered;
34659         for(var id in af){
34660             if(typeof id != "function"){
34661                 var n = af[id];
34662                 if(n){
34663                     n.ui.show();
34664                 }
34665             }
34666         }
34667         this.filtered = {};
34668     }
34669 };
34670 /*
34671  * Based on:
34672  * Ext JS Library 1.1.1
34673  * Copyright(c) 2006-2007, Ext JS, LLC.
34674  *
34675  * Originally Released Under LGPL - original licence link has changed is not relivant.
34676  *
34677  * Fork - LGPL
34678  * <script type="text/javascript">
34679  */
34680  
34681
34682 /**
34683  * @class Roo.tree.TreeSorter
34684  * Provides sorting of nodes in a TreePanel
34685  * 
34686  * @cfg {Boolean} folderSort True to sort leaf nodes under non leaf nodes
34687  * @cfg {String} property The named attribute on the node to sort by (defaults to text)
34688  * @cfg {String} dir The direction to sort (asc or desc) (defaults to asc)
34689  * @cfg {String} leafAttr The attribute used to determine leaf nodes in folder sort (defaults to "leaf")
34690  * @cfg {Boolean} caseSensitive true for case sensitive sort (defaults to false)
34691  * @cfg {Function} sortType A custom "casting" function used to convert node values before sorting
34692  * @constructor
34693  * @param {TreePanel} tree
34694  * @param {Object} config
34695  */
34696 Roo.tree.TreeSorter = function(tree, config){
34697     Roo.apply(this, config);
34698     tree.on("beforechildrenrendered", this.doSort, this);
34699     tree.on("append", this.updateSort, this);
34700     tree.on("insert", this.updateSort, this);
34701     
34702     var dsc = this.dir && this.dir.toLowerCase() == "desc";
34703     var p = this.property || "text";
34704     var sortType = this.sortType;
34705     var fs = this.folderSort;
34706     var cs = this.caseSensitive === true;
34707     var leafAttr = this.leafAttr || 'leaf';
34708
34709     this.sortFn = function(n1, n2){
34710         if(fs){
34711             if(n1.attributes[leafAttr] && !n2.attributes[leafAttr]){
34712                 return 1;
34713             }
34714             if(!n1.attributes[leafAttr] && n2.attributes[leafAttr]){
34715                 return -1;
34716             }
34717         }
34718         var v1 = sortType ? sortType(n1) : (cs ? n1.attributes[p] : n1.attributes[p].toUpperCase());
34719         var v2 = sortType ? sortType(n2) : (cs ? n2.attributes[p] : n2.attributes[p].toUpperCase());
34720         if(v1 < v2){
34721                         return dsc ? +1 : -1;
34722                 }else if(v1 > v2){
34723                         return dsc ? -1 : +1;
34724         }else{
34725                 return 0;
34726         }
34727     };
34728 };
34729
34730 Roo.tree.TreeSorter.prototype = {
34731     doSort : function(node){
34732         node.sort(this.sortFn);
34733     },
34734     
34735     compareNodes : function(n1, n2){
34736         return (n1.text.toUpperCase() > n2.text.toUpperCase() ? 1 : -1);
34737     },
34738     
34739     updateSort : function(tree, node){
34740         if(node.childrenRendered){
34741             this.doSort.defer(1, this, [node]);
34742         }
34743     }
34744 };/*
34745  * Based on:
34746  * Ext JS Library 1.1.1
34747  * Copyright(c) 2006-2007, Ext JS, LLC.
34748  *
34749  * Originally Released Under LGPL - original licence link has changed is not relivant.
34750  *
34751  * Fork - LGPL
34752  * <script type="text/javascript">
34753  */
34754
34755 if(Roo.dd.DropZone){
34756     
34757 Roo.tree.TreeDropZone = function(tree, config){
34758     this.allowParentInsert = false;
34759     this.allowContainerDrop = false;
34760     this.appendOnly = false;
34761     Roo.tree.TreeDropZone.superclass.constructor.call(this, tree.innerCt, config);
34762     this.tree = tree;
34763     this.lastInsertClass = "x-tree-no-status";
34764     this.dragOverData = {};
34765 };
34766
34767 Roo.extend(Roo.tree.TreeDropZone, Roo.dd.DropZone, {
34768     ddGroup : "TreeDD",
34769     scroll:  true,
34770     
34771     expandDelay : 1000,
34772     
34773     expandNode : function(node){
34774         if(node.hasChildNodes() && !node.isExpanded()){
34775             node.expand(false, null, this.triggerCacheRefresh.createDelegate(this));
34776         }
34777     },
34778     
34779     queueExpand : function(node){
34780         this.expandProcId = this.expandNode.defer(this.expandDelay, this, [node]);
34781     },
34782     
34783     cancelExpand : function(){
34784         if(this.expandProcId){
34785             clearTimeout(this.expandProcId);
34786             this.expandProcId = false;
34787         }
34788     },
34789     
34790     isValidDropPoint : function(n, pt, dd, e, data){
34791         if(!n || !data){ return false; }
34792         var targetNode = n.node;
34793         var dropNode = data.node;
34794         // default drop rules
34795         if(!(targetNode && targetNode.isTarget && pt)){
34796             return false;
34797         }
34798         if(pt == "append" && targetNode.allowChildren === false){
34799             return false;
34800         }
34801         if((pt == "above" || pt == "below") && (targetNode.parentNode && targetNode.parentNode.allowChildren === false)){
34802             return false;
34803         }
34804         if(dropNode && (targetNode == dropNode || dropNode.contains(targetNode))){
34805             return false;
34806         }
34807         // reuse the object
34808         var overEvent = this.dragOverData;
34809         overEvent.tree = this.tree;
34810         overEvent.target = targetNode;
34811         overEvent.data = data;
34812         overEvent.point = pt;
34813         overEvent.source = dd;
34814         overEvent.rawEvent = e;
34815         overEvent.dropNode = dropNode;
34816         overEvent.cancel = false;  
34817         var result = this.tree.fireEvent("nodedragover", overEvent);
34818         return overEvent.cancel === false && result !== false;
34819     },
34820     
34821     getDropPoint : function(e, n, dd)
34822     {
34823         var tn = n.node;
34824         if(tn.isRoot){
34825             return tn.allowChildren !== false ? "append" : false; // always append for root
34826         }
34827         var dragEl = n.ddel;
34828         var t = Roo.lib.Dom.getY(dragEl), b = t + dragEl.offsetHeight;
34829         var y = Roo.lib.Event.getPageY(e);
34830         //var noAppend = tn.allowChildren === false || tn.isLeaf();
34831         
34832         // we may drop nodes anywhere, as long as allowChildren has not been set to false..
34833         var noAppend = tn.allowChildren === false;
34834         if(this.appendOnly || tn.parentNode.allowChildren === false){
34835             return noAppend ? false : "append";
34836         }
34837         var noBelow = false;
34838         if(!this.allowParentInsert){
34839             noBelow = tn.hasChildNodes() && tn.isExpanded();
34840         }
34841         var q = (b - t) / (noAppend ? 2 : 3);
34842         if(y >= t && y < (t + q)){
34843             return "above";
34844         }else if(!noBelow && (noAppend || y >= b-q && y <= b)){
34845             return "below";
34846         }else{
34847             return "append";
34848         }
34849     },
34850     
34851     onNodeEnter : function(n, dd, e, data)
34852     {
34853         this.cancelExpand();
34854     },
34855     
34856     onNodeOver : function(n, dd, e, data)
34857     {
34858        
34859         var pt = this.getDropPoint(e, n, dd);
34860         var node = n.node;
34861         
34862         // auto node expand check
34863         if(!this.expandProcId && pt == "append" && node.hasChildNodes() && !n.node.isExpanded()){
34864             this.queueExpand(node);
34865         }else if(pt != "append"){
34866             this.cancelExpand();
34867         }
34868         
34869         // set the insert point style on the target node
34870         var returnCls = this.dropNotAllowed;
34871         if(this.isValidDropPoint(n, pt, dd, e, data)){
34872            if(pt){
34873                var el = n.ddel;
34874                var cls;
34875                if(pt == "above"){
34876                    returnCls = n.node.isFirst() ? "x-tree-drop-ok-above" : "x-tree-drop-ok-between";
34877                    cls = "x-tree-drag-insert-above";
34878                }else if(pt == "below"){
34879                    returnCls = n.node.isLast() ? "x-tree-drop-ok-below" : "x-tree-drop-ok-between";
34880                    cls = "x-tree-drag-insert-below";
34881                }else{
34882                    returnCls = "x-tree-drop-ok-append";
34883                    cls = "x-tree-drag-append";
34884                }
34885                if(this.lastInsertClass != cls){
34886                    Roo.fly(el).replaceClass(this.lastInsertClass, cls);
34887                    this.lastInsertClass = cls;
34888                }
34889            }
34890        }
34891        return returnCls;
34892     },
34893     
34894     onNodeOut : function(n, dd, e, data){
34895         
34896         this.cancelExpand();
34897         this.removeDropIndicators(n);
34898     },
34899     
34900     onNodeDrop : function(n, dd, e, data){
34901         var point = this.getDropPoint(e, n, dd);
34902         var targetNode = n.node;
34903         targetNode.ui.startDrop();
34904         if(!this.isValidDropPoint(n, point, dd, e, data)){
34905             targetNode.ui.endDrop();
34906             return false;
34907         }
34908         // first try to find the drop node
34909         var dropNode = data.node || (dd.getTreeNode ? dd.getTreeNode(data, targetNode, point, e) : null);
34910         var dropEvent = {
34911             tree : this.tree,
34912             target: targetNode,
34913             data: data,
34914             point: point,
34915             source: dd,
34916             rawEvent: e,
34917             dropNode: dropNode,
34918             cancel: !dropNode   
34919         };
34920         var retval = this.tree.fireEvent("beforenodedrop", dropEvent);
34921         if(retval === false || dropEvent.cancel === true || !dropEvent.dropNode){
34922             targetNode.ui.endDrop();
34923             return false;
34924         }
34925         // allow target changing
34926         targetNode = dropEvent.target;
34927         if(point == "append" && !targetNode.isExpanded()){
34928             targetNode.expand(false, null, function(){
34929                 this.completeDrop(dropEvent);
34930             }.createDelegate(this));
34931         }else{
34932             this.completeDrop(dropEvent);
34933         }
34934         return true;
34935     },
34936     
34937     completeDrop : function(de){
34938         var ns = de.dropNode, p = de.point, t = de.target;
34939         if(!(ns instanceof Array)){
34940             ns = [ns];
34941         }
34942         var n;
34943         for(var i = 0, len = ns.length; i < len; i++){
34944             n = ns[i];
34945             if(p == "above"){
34946                 t.parentNode.insertBefore(n, t);
34947             }else if(p == "below"){
34948                 t.parentNode.insertBefore(n, t.nextSibling);
34949             }else{
34950                 t.appendChild(n);
34951             }
34952         }
34953         n.ui.focus();
34954         if(this.tree.hlDrop){
34955             n.ui.highlight();
34956         }
34957         t.ui.endDrop();
34958         this.tree.fireEvent("nodedrop", de);
34959     },
34960     
34961     afterNodeMoved : function(dd, data, e, targetNode, dropNode){
34962         if(this.tree.hlDrop){
34963             dropNode.ui.focus();
34964             dropNode.ui.highlight();
34965         }
34966         this.tree.fireEvent("nodedrop", this.tree, targetNode, data, dd, e);
34967     },
34968     
34969     getTree : function(){
34970         return this.tree;
34971     },
34972     
34973     removeDropIndicators : function(n){
34974         if(n && n.ddel){
34975             var el = n.ddel;
34976             Roo.fly(el).removeClass([
34977                     "x-tree-drag-insert-above",
34978                     "x-tree-drag-insert-below",
34979                     "x-tree-drag-append"]);
34980             this.lastInsertClass = "_noclass";
34981         }
34982     },
34983     
34984     beforeDragDrop : function(target, e, id){
34985         this.cancelExpand();
34986         return true;
34987     },
34988     
34989     afterRepair : function(data){
34990         if(data && Roo.enableFx){
34991             data.node.ui.highlight();
34992         }
34993         this.hideProxy();
34994     } 
34995     
34996 });
34997
34998 }
34999 /*
35000  * Based on:
35001  * Ext JS Library 1.1.1
35002  * Copyright(c) 2006-2007, Ext JS, LLC.
35003  *
35004  * Originally Released Under LGPL - original licence link has changed is not relivant.
35005  *
35006  * Fork - LGPL
35007  * <script type="text/javascript">
35008  */
35009  
35010
35011 if(Roo.dd.DragZone){
35012 Roo.tree.TreeDragZone = function(tree, config){
35013     Roo.tree.TreeDragZone.superclass.constructor.call(this, tree.getTreeEl(), config);
35014     this.tree = tree;
35015 };
35016
35017 Roo.extend(Roo.tree.TreeDragZone, Roo.dd.DragZone, {
35018     ddGroup : "TreeDD",
35019    
35020     onBeforeDrag : function(data, e){
35021         var n = data.node;
35022         return n && n.draggable && !n.disabled;
35023     },
35024      
35025     
35026     onInitDrag : function(e){
35027         var data = this.dragData;
35028         this.tree.getSelectionModel().select(data.node);
35029         this.proxy.update("");
35030         data.node.ui.appendDDGhost(this.proxy.ghost.dom);
35031         this.tree.fireEvent("startdrag", this.tree, data.node, e);
35032     },
35033     
35034     getRepairXY : function(e, data){
35035         return data.node.ui.getDDRepairXY();
35036     },
35037     
35038     onEndDrag : function(data, e){
35039         this.tree.fireEvent("enddrag", this.tree, data.node, e);
35040         
35041         
35042     },
35043     
35044     onValidDrop : function(dd, e, id){
35045         this.tree.fireEvent("dragdrop", this.tree, this.dragData.node, dd, e);
35046         this.hideProxy();
35047     },
35048     
35049     beforeInvalidDrop : function(e, id){
35050         // this scrolls the original position back into view
35051         var sm = this.tree.getSelectionModel();
35052         sm.clearSelections();
35053         sm.select(this.dragData.node);
35054     }
35055 });
35056 }/*
35057  * Based on:
35058  * Ext JS Library 1.1.1
35059  * Copyright(c) 2006-2007, Ext JS, LLC.
35060  *
35061  * Originally Released Under LGPL - original licence link has changed is not relivant.
35062  *
35063  * Fork - LGPL
35064  * <script type="text/javascript">
35065  */
35066 /**
35067  * @class Roo.tree.TreeEditor
35068  * @extends Roo.Editor
35069  * Provides editor functionality for inline tree node editing.  Any valid {@link Roo.form.Field} can be used
35070  * as the editor field.
35071  * @constructor
35072  * @param {Object} config (used to be the tree panel.)
35073  * @param {Object} oldconfig DEPRECIATED Either a prebuilt {@link Roo.form.Field} instance or a Field config object
35074  * 
35075  * @cfg {Roo.tree.TreePanel} tree The tree to bind to.
35076  * @cfg {Roo.form.TextField|Object} field The field configuration
35077  *
35078  * 
35079  */
35080 Roo.tree.TreeEditor = function(config, oldconfig) { // was -- (tree, config){
35081     var tree = config;
35082     var field;
35083     if (oldconfig) { // old style..
35084         field = oldconfig.events ? oldconfig : new Roo.form.TextField(oldconfig);
35085     } else {
35086         // new style..
35087         tree = config.tree;
35088         config.field = config.field  || {};
35089         config.field.xtype = 'TextField';
35090         field = Roo.factory(config.field, Roo.form);
35091     }
35092     config = config || {};
35093     
35094     
35095     this.addEvents({
35096         /**
35097          * @event beforenodeedit
35098          * Fires when editing is initiated, but before the value changes.  Editing can be canceled by returning
35099          * false from the handler of this event.
35100          * @param {Editor} this
35101          * @param {Roo.tree.Node} node 
35102          */
35103         "beforenodeedit" : true
35104     });
35105     
35106     //Roo.log(config);
35107     Roo.tree.TreeEditor.superclass.constructor.call(this, field, config);
35108
35109     this.tree = tree;
35110
35111     tree.on('beforeclick', this.beforeNodeClick, this);
35112     tree.getTreeEl().on('mousedown', this.hide, this);
35113     this.on('complete', this.updateNode, this);
35114     this.on('beforestartedit', this.fitToTree, this);
35115     this.on('startedit', this.bindScroll, this, {delay:10});
35116     this.on('specialkey', this.onSpecialKey, this);
35117 };
35118
35119 Roo.extend(Roo.tree.TreeEditor, Roo.Editor, {
35120     /**
35121      * @cfg {String} alignment
35122      * The position to align to (see {@link Roo.Element#alignTo} for more details, defaults to "l-l").
35123      */
35124     alignment: "l-l",
35125     // inherit
35126     autoSize: false,
35127     /**
35128      * @cfg {Boolean} hideEl
35129      * True to hide the bound element while the editor is displayed (defaults to false)
35130      */
35131     hideEl : false,
35132     /**
35133      * @cfg {String} cls
35134      * CSS class to apply to the editor (defaults to "x-small-editor x-tree-editor")
35135      */
35136     cls: "x-small-editor x-tree-editor",
35137     /**
35138      * @cfg {Boolean} shim
35139      * True to shim the editor if selects/iframes could be displayed beneath it (defaults to false)
35140      */
35141     shim:false,
35142     // inherit
35143     shadow:"frame",
35144     /**
35145      * @cfg {Number} maxWidth
35146      * The maximum width in pixels of the editor field (defaults to 250).  Note that if the maxWidth would exceed
35147      * the containing tree element's size, it will be automatically limited for you to the container width, taking
35148      * scroll and client offsets into account prior to each edit.
35149      */
35150     maxWidth: 250,
35151
35152     editDelay : 350,
35153
35154     // private
35155     fitToTree : function(ed, el){
35156         var td = this.tree.getTreeEl().dom, nd = el.dom;
35157         if(td.scrollLeft >  nd.offsetLeft){ // ensure the node left point is visible
35158             td.scrollLeft = nd.offsetLeft;
35159         }
35160         var w = Math.min(
35161                 this.maxWidth,
35162                 (td.clientWidth > 20 ? td.clientWidth : td.offsetWidth) - Math.max(0, nd.offsetLeft-td.scrollLeft) - /*cushion*/5);
35163         this.setSize(w, '');
35164         
35165         return this.fireEvent('beforenodeedit', this, this.editNode);
35166         
35167     },
35168
35169     // private
35170     triggerEdit : function(node){
35171         this.completeEdit();
35172         this.editNode = node;
35173         this.startEdit(node.ui.textNode, node.text);
35174     },
35175
35176     // private
35177     bindScroll : function(){
35178         this.tree.getTreeEl().on('scroll', this.cancelEdit, this);
35179     },
35180
35181     // private
35182     beforeNodeClick : function(node, e){
35183         var sinceLast = (this.lastClick ? this.lastClick.getElapsed() : 0);
35184         this.lastClick = new Date();
35185         if(sinceLast > this.editDelay && this.tree.getSelectionModel().isSelected(node)){
35186             e.stopEvent();
35187             this.triggerEdit(node);
35188             return false;
35189         }
35190         return true;
35191     },
35192
35193     // private
35194     updateNode : function(ed, value){
35195         this.tree.getTreeEl().un('scroll', this.cancelEdit, this);
35196         this.editNode.setText(value);
35197     },
35198
35199     // private
35200     onHide : function(){
35201         Roo.tree.TreeEditor.superclass.onHide.call(this);
35202         if(this.editNode){
35203             this.editNode.ui.focus();
35204         }
35205     },
35206
35207     // private
35208     onSpecialKey : function(field, e){
35209         var k = e.getKey();
35210         if(k == e.ESC){
35211             e.stopEvent();
35212             this.cancelEdit();
35213         }else if(k == e.ENTER && !e.hasModifier()){
35214             e.stopEvent();
35215             this.completeEdit();
35216         }
35217     }
35218 });//<Script type="text/javascript">
35219 /*
35220  * Based on:
35221  * Ext JS Library 1.1.1
35222  * Copyright(c) 2006-2007, Ext JS, LLC.
35223  *
35224  * Originally Released Under LGPL - original licence link has changed is not relivant.
35225  *
35226  * Fork - LGPL
35227  * <script type="text/javascript">
35228  */
35229  
35230 /**
35231  * Not documented??? - probably should be...
35232  */
35233
35234 Roo.tree.ColumnNodeUI = Roo.extend(Roo.tree.TreeNodeUI, {
35235     //focus: Roo.emptyFn, // prevent odd scrolling behavior
35236     
35237     renderElements : function(n, a, targetNode, bulkRender){
35238         //consel.log("renderElements?");
35239         this.indentMarkup = n.parentNode ? n.parentNode.ui.getChildIndent() : '';
35240
35241         var t = n.getOwnerTree();
35242         var tid = Pman.Tab.Document_TypesTree.tree.el.id;
35243         
35244         var cols = t.columns;
35245         var bw = t.borderWidth;
35246         var c = cols[0];
35247         var href = a.href ? a.href : Roo.isGecko ? "" : "#";
35248          var cb = typeof a.checked == "boolean";
35249         var tx = String.format('{0}',n.text || (c.renderer ? c.renderer(a[c.dataIndex], n, a) : a[c.dataIndex]));
35250         var colcls = 'x-t-' + tid + '-c0';
35251         var buf = [
35252             '<li class="x-tree-node">',
35253             
35254                 
35255                 '<div class="x-tree-node-el ', a.cls,'">',
35256                     // extran...
35257                     '<div class="x-tree-col ', colcls, '" style="width:', c.width-bw, 'px;">',
35258                 
35259                 
35260                         '<span class="x-tree-node-indent">',this.indentMarkup,'</span>',
35261                         '<img src="', this.emptyIcon, '" class="x-tree-ec-icon  " />',
35262                         '<img src="', a.icon || this.emptyIcon, '" class="x-tree-node-icon',
35263                            (a.icon ? ' x-tree-node-inline-icon' : ''),
35264                            (a.iconCls ? ' '+a.iconCls : ''),
35265                            '" unselectable="on" />',
35266                         (cb ? ('<input class="x-tree-node-cb" type="checkbox" ' + 
35267                              (a.checked ? 'checked="checked" />' : ' />')) : ''),
35268                              
35269                         '<a class="x-tree-node-anchor" hidefocus="on" href="',href,'" tabIndex="1" ',
35270                             (a.hrefTarget ? ' target="' +a.hrefTarget + '"' : ''), '>',
35271                             '<span unselectable="on" qtip="' + tx + '">',
35272                              tx,
35273                              '</span></a>' ,
35274                     '</div>',
35275                      '<a class="x-tree-node-anchor" hidefocus="on" href="',href,'" tabIndex="1" ',
35276                             (a.hrefTarget ? ' target="' +a.hrefTarget + '"' : ''), '>'
35277                  ];
35278         for(var i = 1, len = cols.length; i < len; i++){
35279             c = cols[i];
35280             colcls = 'x-t-' + tid + '-c' +i;
35281             tx = String.format('{0}', (c.renderer ? c.renderer(a[c.dataIndex], n, a) : a[c.dataIndex]));
35282             buf.push('<div class="x-tree-col ', colcls, ' ' ,(c.cls?c.cls:''),'" style="width:',c.width-bw,'px;">',
35283                         '<div class="x-tree-col-text" qtip="' + tx +'">',tx,"</div>",
35284                       "</div>");
35285          }
35286          
35287          buf.push(
35288             '</a>',
35289             '<div class="x-clear"></div></div>',
35290             '<ul class="x-tree-node-ct" style="display:none;"></ul>',
35291             "</li>");
35292         
35293         if(bulkRender !== true && n.nextSibling && n.nextSibling.ui.getEl()){
35294             this.wrap = Roo.DomHelper.insertHtml("beforeBegin",
35295                                 n.nextSibling.ui.getEl(), buf.join(""));
35296         }else{
35297             this.wrap = Roo.DomHelper.insertHtml("beforeEnd", targetNode, buf.join(""));
35298         }
35299         var el = this.wrap.firstChild;
35300         this.elRow = el;
35301         this.elNode = el.firstChild;
35302         this.ranchor = el.childNodes[1];
35303         this.ctNode = this.wrap.childNodes[1];
35304         var cs = el.firstChild.childNodes;
35305         this.indentNode = cs[0];
35306         this.ecNode = cs[1];
35307         this.iconNode = cs[2];
35308         var index = 3;
35309         if(cb){
35310             this.checkbox = cs[3];
35311             index++;
35312         }
35313         this.anchor = cs[index];
35314         
35315         this.textNode = cs[index].firstChild;
35316         
35317         //el.on("click", this.onClick, this);
35318         //el.on("dblclick", this.onDblClick, this);
35319         
35320         
35321        // console.log(this);
35322     },
35323     initEvents : function(){
35324         Roo.tree.ColumnNodeUI.superclass.initEvents.call(this);
35325         
35326             
35327         var a = this.ranchor;
35328
35329         var el = Roo.get(a);
35330
35331         if(Roo.isOpera){ // opera render bug ignores the CSS
35332             el.setStyle("text-decoration", "none");
35333         }
35334
35335         el.on("click", this.onClick, this);
35336         el.on("dblclick", this.onDblClick, this);
35337         el.on("contextmenu", this.onContextMenu, this);
35338         
35339     },
35340     
35341     /*onSelectedChange : function(state){
35342         if(state){
35343             this.focus();
35344             this.addClass("x-tree-selected");
35345         }else{
35346             //this.blur();
35347             this.removeClass("x-tree-selected");
35348         }
35349     },*/
35350     addClass : function(cls){
35351         if(this.elRow){
35352             Roo.fly(this.elRow).addClass(cls);
35353         }
35354         
35355     },
35356     
35357     
35358     removeClass : function(cls){
35359         if(this.elRow){
35360             Roo.fly(this.elRow).removeClass(cls);
35361         }
35362     }
35363
35364     
35365     
35366 });//<Script type="text/javascript">
35367
35368 /*
35369  * Based on:
35370  * Ext JS Library 1.1.1
35371  * Copyright(c) 2006-2007, Ext JS, LLC.
35372  *
35373  * Originally Released Under LGPL - original licence link has changed is not relivant.
35374  *
35375  * Fork - LGPL
35376  * <script type="text/javascript">
35377  */
35378  
35379
35380 /**
35381  * @class Roo.tree.ColumnTree
35382  * @extends Roo.data.TreePanel
35383  * @cfg {Object} columns  Including width, header, renderer, cls, dataIndex 
35384  * @cfg {int} borderWidth  compined right/left border allowance
35385  * @constructor
35386  * @param {String/HTMLElement/Element} el The container element
35387  * @param {Object} config
35388  */
35389 Roo.tree.ColumnTree =  function(el, config)
35390 {
35391    Roo.tree.ColumnTree.superclass.constructor.call(this, el , config);
35392    this.addEvents({
35393         /**
35394         * @event resize
35395         * Fire this event on a container when it resizes
35396         * @param {int} w Width
35397         * @param {int} h Height
35398         */
35399        "resize" : true
35400     });
35401     this.on('resize', this.onResize, this);
35402 };
35403
35404 Roo.extend(Roo.tree.ColumnTree, Roo.tree.TreePanel, {
35405     //lines:false,
35406     
35407     
35408     borderWidth: Roo.isBorderBox ? 0 : 2, 
35409     headEls : false,
35410     
35411     render : function(){
35412         // add the header.....
35413        
35414         Roo.tree.ColumnTree.superclass.render.apply(this);
35415         
35416         this.el.addClass('x-column-tree');
35417         
35418         this.headers = this.el.createChild(
35419             {cls:'x-tree-headers'},this.innerCt.dom);
35420    
35421         var cols = this.columns, c;
35422         var totalWidth = 0;
35423         this.headEls = [];
35424         var  len = cols.length;
35425         for(var i = 0; i < len; i++){
35426              c = cols[i];
35427              totalWidth += c.width;
35428             this.headEls.push(this.headers.createChild({
35429                  cls:'x-tree-hd ' + (c.cls?c.cls+'-hd':''),
35430                  cn: {
35431                      cls:'x-tree-hd-text',
35432                      html: c.header
35433                  },
35434                  style:'width:'+(c.width-this.borderWidth)+'px;'
35435              }));
35436         }
35437         this.headers.createChild({cls:'x-clear'});
35438         // prevent floats from wrapping when clipped
35439         this.headers.setWidth(totalWidth);
35440         //this.innerCt.setWidth(totalWidth);
35441         this.innerCt.setStyle({ overflow: 'auto' });
35442         this.onResize(this.width, this.height);
35443              
35444         
35445     },
35446     onResize : function(w,h)
35447     {
35448         this.height = h;
35449         this.width = w;
35450         // resize cols..
35451         this.innerCt.setWidth(this.width);
35452         this.innerCt.setHeight(this.height-20);
35453         
35454         // headers...
35455         var cols = this.columns, c;
35456         var totalWidth = 0;
35457         var expEl = false;
35458         var len = cols.length;
35459         for(var i = 0; i < len; i++){
35460             c = cols[i];
35461             if (this.autoExpandColumn !== false && c.dataIndex == this.autoExpandColumn) {
35462                 // it's the expander..
35463                 expEl  = this.headEls[i];
35464                 continue;
35465             }
35466             totalWidth += c.width;
35467             
35468         }
35469         if (expEl) {
35470             expEl.setWidth(  ((w - totalWidth)-this.borderWidth - 20));
35471         }
35472         this.headers.setWidth(w-20);
35473
35474         
35475         
35476         
35477     }
35478 });
35479 /*
35480  * Based on:
35481  * Ext JS Library 1.1.1
35482  * Copyright(c) 2006-2007, Ext JS, LLC.
35483  *
35484  * Originally Released Under LGPL - original licence link has changed is not relivant.
35485  *
35486  * Fork - LGPL
35487  * <script type="text/javascript">
35488  */
35489  
35490 /**
35491  * @class Roo.menu.Menu
35492  * @extends Roo.util.Observable
35493  * A menu object.  This is the container to which you add all other menu items.  Menu can also serve a as a base class
35494  * when you want a specialzed menu based off of another component (like {@link Roo.menu.DateMenu} for example).
35495  * @constructor
35496  * Creates a new Menu
35497  * @param {Object} config Configuration options
35498  */
35499 Roo.menu.Menu = function(config){
35500     Roo.apply(this, config);
35501     this.id = this.id || Roo.id();
35502     this.addEvents({
35503         /**
35504          * @event beforeshow
35505          * Fires before this menu is displayed
35506          * @param {Roo.menu.Menu} this
35507          */
35508         beforeshow : true,
35509         /**
35510          * @event beforehide
35511          * Fires before this menu is hidden
35512          * @param {Roo.menu.Menu} this
35513          */
35514         beforehide : true,
35515         /**
35516          * @event show
35517          * Fires after this menu is displayed
35518          * @param {Roo.menu.Menu} this
35519          */
35520         show : true,
35521         /**
35522          * @event hide
35523          * Fires after this menu is hidden
35524          * @param {Roo.menu.Menu} this
35525          */
35526         hide : true,
35527         /**
35528          * @event click
35529          * Fires when this menu is clicked (or when the enter key is pressed while it is active)
35530          * @param {Roo.menu.Menu} this
35531          * @param {Roo.menu.Item} menuItem The menu item that was clicked
35532          * @param {Roo.EventObject} e
35533          */
35534         click : true,
35535         /**
35536          * @event mouseover
35537          * Fires when the mouse is hovering over this menu
35538          * @param {Roo.menu.Menu} this
35539          * @param {Roo.EventObject} e
35540          * @param {Roo.menu.Item} menuItem The menu item that was clicked
35541          */
35542         mouseover : true,
35543         /**
35544          * @event mouseout
35545          * Fires when the mouse exits this menu
35546          * @param {Roo.menu.Menu} this
35547          * @param {Roo.EventObject} e
35548          * @param {Roo.menu.Item} menuItem The menu item that was clicked
35549          */
35550         mouseout : true,
35551         /**
35552          * @event itemclick
35553          * Fires when a menu item contained in this menu is clicked
35554          * @param {Roo.menu.BaseItem} baseItem The BaseItem that was clicked
35555          * @param {Roo.EventObject} e
35556          */
35557         itemclick: true
35558     });
35559     if (this.registerMenu) {
35560         Roo.menu.MenuMgr.register(this);
35561     }
35562     
35563     var mis = this.items;
35564     this.items = new Roo.util.MixedCollection();
35565     if(mis){
35566         this.add.apply(this, mis);
35567     }
35568 };
35569
35570 Roo.extend(Roo.menu.Menu, Roo.util.Observable, {
35571     /**
35572      * @cfg {Number} minWidth The minimum width of the menu in pixels (defaults to 120)
35573      */
35574     minWidth : 120,
35575     /**
35576      * @cfg {Boolean/String} shadow True or "sides" for the default effect, "frame" for 4-way shadow, and "drop"
35577      * for bottom-right shadow (defaults to "sides")
35578      */
35579     shadow : "sides",
35580     /**
35581      * @cfg {String} subMenuAlign The {@link Roo.Element#alignTo} anchor position value to use for submenus of
35582      * this menu (defaults to "tl-tr?")
35583      */
35584     subMenuAlign : "tl-tr?",
35585     /**
35586      * @cfg {String} defaultAlign The default {@link Roo.Element#alignTo) anchor position value for this menu
35587      * relative to its element of origin (defaults to "tl-bl?")
35588      */
35589     defaultAlign : "tl-bl?",
35590     /**
35591      * @cfg {Boolean} allowOtherMenus True to allow multiple menus to be displayed at the same time (defaults to false)
35592      */
35593     allowOtherMenus : false,
35594     /**
35595      * @cfg {Boolean} registerMenu True (default) - means that clicking on screen etc. hides it.
35596      */
35597     registerMenu : true,
35598
35599     hidden:true,
35600
35601     // private
35602     render : function(){
35603         if(this.el){
35604             return;
35605         }
35606         var el = this.el = new Roo.Layer({
35607             cls: "x-menu",
35608             shadow:this.shadow,
35609             constrain: false,
35610             parentEl: this.parentEl || document.body,
35611             zindex:15000
35612         });
35613
35614         this.keyNav = new Roo.menu.MenuNav(this);
35615
35616         if(this.plain){
35617             el.addClass("x-menu-plain");
35618         }
35619         if(this.cls){
35620             el.addClass(this.cls);
35621         }
35622         // generic focus element
35623         this.focusEl = el.createChild({
35624             tag: "a", cls: "x-menu-focus", href: "#", onclick: "return false;", tabIndex:"-1"
35625         });
35626         var ul = el.createChild({tag: "ul", cls: "x-menu-list"});
35627         ul.on(Roo.isTouch ? 'touchstart' : 'click'   , this.onClick, this);
35628         
35629         ul.on("mouseover", this.onMouseOver, this);
35630         ul.on("mouseout", this.onMouseOut, this);
35631         this.items.each(function(item){
35632             if (item.hidden) {
35633                 return;
35634             }
35635             
35636             var li = document.createElement("li");
35637             li.className = "x-menu-list-item";
35638             ul.dom.appendChild(li);
35639             item.render(li, this);
35640         }, this);
35641         this.ul = ul;
35642         this.autoWidth();
35643     },
35644
35645     // private
35646     autoWidth : function(){
35647         var el = this.el, ul = this.ul;
35648         if(!el){
35649             return;
35650         }
35651         var w = this.width;
35652         if(w){
35653             el.setWidth(w);
35654         }else if(Roo.isIE){
35655             el.setWidth(this.minWidth);
35656             var t = el.dom.offsetWidth; // force recalc
35657             el.setWidth(ul.getWidth()+el.getFrameWidth("lr"));
35658         }
35659     },
35660
35661     // private
35662     delayAutoWidth : function(){
35663         if(this.rendered){
35664             if(!this.awTask){
35665                 this.awTask = new Roo.util.DelayedTask(this.autoWidth, this);
35666             }
35667             this.awTask.delay(20);
35668         }
35669     },
35670
35671     // private
35672     findTargetItem : function(e){
35673         var t = e.getTarget(".x-menu-list-item", this.ul,  true);
35674         if(t && t.menuItemId){
35675             return this.items.get(t.menuItemId);
35676         }
35677     },
35678
35679     // private
35680     onClick : function(e){
35681         Roo.log("menu.onClick");
35682         var t = this.findTargetItem(e);
35683         if(!t){
35684             return;
35685         }
35686         Roo.log(e);
35687         if (Roo.isTouch && e.type == 'touchstart' && t.menu  && !t.disabled) {
35688             if(t == this.activeItem && t.shouldDeactivate(e)){
35689                 this.activeItem.deactivate();
35690                 delete this.activeItem;
35691                 return;
35692             }
35693             if(t.canActivate){
35694                 this.setActiveItem(t, true);
35695             }
35696             return;
35697             
35698             
35699         }
35700         
35701         t.onClick(e);
35702         this.fireEvent("click", this, t, e);
35703     },
35704
35705     // private
35706     setActiveItem : function(item, autoExpand){
35707         if(item != this.activeItem){
35708             if(this.activeItem){
35709                 this.activeItem.deactivate();
35710             }
35711             this.activeItem = item;
35712             item.activate(autoExpand);
35713         }else if(autoExpand){
35714             item.expandMenu();
35715         }
35716     },
35717
35718     // private
35719     tryActivate : function(start, step){
35720         var items = this.items;
35721         for(var i = start, len = items.length; i >= 0 && i < len; i+= step){
35722             var item = items.get(i);
35723             if(!item.disabled && item.canActivate){
35724                 this.setActiveItem(item, false);
35725                 return item;
35726             }
35727         }
35728         return false;
35729     },
35730
35731     // private
35732     onMouseOver : function(e){
35733         var t;
35734         if(t = this.findTargetItem(e)){
35735             if(t.canActivate && !t.disabled){
35736                 this.setActiveItem(t, true);
35737             }
35738         }
35739         this.fireEvent("mouseover", this, e, t);
35740     },
35741
35742     // private
35743     onMouseOut : function(e){
35744         var t;
35745         if(t = this.findTargetItem(e)){
35746             if(t == this.activeItem && t.shouldDeactivate(e)){
35747                 this.activeItem.deactivate();
35748                 delete this.activeItem;
35749             }
35750         }
35751         this.fireEvent("mouseout", this, e, t);
35752     },
35753
35754     /**
35755      * Read-only.  Returns true if the menu is currently displayed, else false.
35756      * @type Boolean
35757      */
35758     isVisible : function(){
35759         return this.el && !this.hidden;
35760     },
35761
35762     /**
35763      * Displays this menu relative to another element
35764      * @param {String/HTMLElement/Roo.Element} element The element to align to
35765      * @param {String} position (optional) The {@link Roo.Element#alignTo} anchor position to use in aligning to
35766      * the element (defaults to this.defaultAlign)
35767      * @param {Roo.menu.Menu} parentMenu (optional) This menu's parent menu, if applicable (defaults to undefined)
35768      */
35769     show : function(el, pos, parentMenu){
35770         this.parentMenu = parentMenu;
35771         if(!this.el){
35772             this.render();
35773         }
35774         this.fireEvent("beforeshow", this);
35775         this.showAt(this.el.getAlignToXY(el, pos || this.defaultAlign), parentMenu, false);
35776     },
35777
35778     /**
35779      * Displays this menu at a specific xy position
35780      * @param {Array} xyPosition Contains X & Y [x, y] values for the position at which to show the menu (coordinates are page-based)
35781      * @param {Roo.menu.Menu} parentMenu (optional) This menu's parent menu, if applicable (defaults to undefined)
35782      */
35783     showAt : function(xy, parentMenu, /* private: */_e){
35784         this.parentMenu = parentMenu;
35785         if(!this.el){
35786             this.render();
35787         }
35788         if(_e !== false){
35789             this.fireEvent("beforeshow", this);
35790             xy = this.el.adjustForConstraints(xy);
35791         }
35792         this.el.setXY(xy);
35793         this.el.show();
35794         this.hidden = false;
35795         this.focus();
35796         this.fireEvent("show", this);
35797     },
35798
35799     focus : function(){
35800         if(!this.hidden){
35801             this.doFocus.defer(50, this);
35802         }
35803     },
35804
35805     doFocus : function(){
35806         if(!this.hidden){
35807             this.focusEl.focus();
35808         }
35809     },
35810
35811     /**
35812      * Hides this menu and optionally all parent menus
35813      * @param {Boolean} deep (optional) True to hide all parent menus recursively, if any (defaults to false)
35814      */
35815     hide : function(deep){
35816         if(this.el && this.isVisible()){
35817             this.fireEvent("beforehide", this);
35818             if(this.activeItem){
35819                 this.activeItem.deactivate();
35820                 this.activeItem = null;
35821             }
35822             this.el.hide();
35823             this.hidden = true;
35824             this.fireEvent("hide", this);
35825         }
35826         if(deep === true && this.parentMenu){
35827             this.parentMenu.hide(true);
35828         }
35829     },
35830
35831     /**
35832      * Addds one or more items of any type supported by the Menu class, or that can be converted into menu items.
35833      * Any of the following are valid:
35834      * <ul>
35835      * <li>Any menu item object based on {@link Roo.menu.Item}</li>
35836      * <li>An HTMLElement object which will be converted to a menu item</li>
35837      * <li>A menu item config object that will be created as a new menu item</li>
35838      * <li>A string, which can either be '-' or 'separator' to add a menu separator, otherwise
35839      * it will be converted into a {@link Roo.menu.TextItem} and added</li>
35840      * </ul>
35841      * Usage:
35842      * <pre><code>
35843 // Create the menu
35844 var menu = new Roo.menu.Menu();
35845
35846 // Create a menu item to add by reference
35847 var menuItem = new Roo.menu.Item({ text: 'New Item!' });
35848
35849 // Add a bunch of items at once using different methods.
35850 // Only the last item added will be returned.
35851 var item = menu.add(
35852     menuItem,                // add existing item by ref
35853     'Dynamic Item',          // new TextItem
35854     '-',                     // new separator
35855     { text: 'Config Item' }  // new item by config
35856 );
35857 </code></pre>
35858      * @param {Mixed} args One or more menu items, menu item configs or other objects that can be converted to menu items
35859      * @return {Roo.menu.Item} The menu item that was added, or the last one if multiple items were added
35860      */
35861     add : function(){
35862         var a = arguments, l = a.length, item;
35863         for(var i = 0; i < l; i++){
35864             var el = a[i];
35865             if ((typeof(el) == "object") && el.xtype && el.xns) {
35866                 el = Roo.factory(el, Roo.menu);
35867             }
35868             
35869             if(el.render){ // some kind of Item
35870                 item = this.addItem(el);
35871             }else if(typeof el == "string"){ // string
35872                 if(el == "separator" || el == "-"){
35873                     item = this.addSeparator();
35874                 }else{
35875                     item = this.addText(el);
35876                 }
35877             }else if(el.tagName || el.el){ // element
35878                 item = this.addElement(el);
35879             }else if(typeof el == "object"){ // must be menu item config?
35880                 item = this.addMenuItem(el);
35881             }
35882         }
35883         return item;
35884     },
35885
35886     /**
35887      * Returns this menu's underlying {@link Roo.Element} object
35888      * @return {Roo.Element} The element
35889      */
35890     getEl : function(){
35891         if(!this.el){
35892             this.render();
35893         }
35894         return this.el;
35895     },
35896
35897     /**
35898      * Adds a separator bar to the menu
35899      * @return {Roo.menu.Item} The menu item that was added
35900      */
35901     addSeparator : function(){
35902         return this.addItem(new Roo.menu.Separator());
35903     },
35904
35905     /**
35906      * Adds an {@link Roo.Element} object to the menu
35907      * @param {String/HTMLElement/Roo.Element} el The element or DOM node to add, or its id
35908      * @return {Roo.menu.Item} The menu item that was added
35909      */
35910     addElement : function(el){
35911         return this.addItem(new Roo.menu.BaseItem(el));
35912     },
35913
35914     /**
35915      * Adds an existing object based on {@link Roo.menu.Item} to the menu
35916      * @param {Roo.menu.Item} item The menu item to add
35917      * @return {Roo.menu.Item} The menu item that was added
35918      */
35919     addItem : function(item){
35920         this.items.add(item);
35921         if(this.ul){
35922             var li = document.createElement("li");
35923             li.className = "x-menu-list-item";
35924             this.ul.dom.appendChild(li);
35925             item.render(li, this);
35926             this.delayAutoWidth();
35927         }
35928         return item;
35929     },
35930
35931     /**
35932      * Creates a new {@link Roo.menu.Item} based an the supplied config object and adds it to the menu
35933      * @param {Object} config A MenuItem config object
35934      * @return {Roo.menu.Item} The menu item that was added
35935      */
35936     addMenuItem : function(config){
35937         if(!(config instanceof Roo.menu.Item)){
35938             if(typeof config.checked == "boolean"){ // must be check menu item config?
35939                 config = new Roo.menu.CheckItem(config);
35940             }else{
35941                 config = new Roo.menu.Item(config);
35942             }
35943         }
35944         return this.addItem(config);
35945     },
35946
35947     /**
35948      * Creates a new {@link Roo.menu.TextItem} with the supplied text and adds it to the menu
35949      * @param {String} text The text to display in the menu item
35950      * @return {Roo.menu.Item} The menu item that was added
35951      */
35952     addText : function(text){
35953         return this.addItem(new Roo.menu.TextItem({ text : text }));
35954     },
35955
35956     /**
35957      * Inserts an existing object based on {@link Roo.menu.Item} to the menu at a specified index
35958      * @param {Number} index The index in the menu's list of current items where the new item should be inserted
35959      * @param {Roo.menu.Item} item The menu item to add
35960      * @return {Roo.menu.Item} The menu item that was added
35961      */
35962     insert : function(index, item){
35963         this.items.insert(index, item);
35964         if(this.ul){
35965             var li = document.createElement("li");
35966             li.className = "x-menu-list-item";
35967             this.ul.dom.insertBefore(li, this.ul.dom.childNodes[index]);
35968             item.render(li, this);
35969             this.delayAutoWidth();
35970         }
35971         return item;
35972     },
35973
35974     /**
35975      * Removes an {@link Roo.menu.Item} from the menu and destroys the object
35976      * @param {Roo.menu.Item} item The menu item to remove
35977      */
35978     remove : function(item){
35979         this.items.removeKey(item.id);
35980         item.destroy();
35981     },
35982
35983     /**
35984      * Removes and destroys all items in the menu
35985      */
35986     removeAll : function(){
35987         var f;
35988         while(f = this.items.first()){
35989             this.remove(f);
35990         }
35991     }
35992 });
35993
35994 // MenuNav is a private utility class used internally by the Menu
35995 Roo.menu.MenuNav = function(menu){
35996     Roo.menu.MenuNav.superclass.constructor.call(this, menu.el);
35997     this.scope = this.menu = menu;
35998 };
35999
36000 Roo.extend(Roo.menu.MenuNav, Roo.KeyNav, {
36001     doRelay : function(e, h){
36002         var k = e.getKey();
36003         if(!this.menu.activeItem && e.isNavKeyPress() && k != e.SPACE && k != e.RETURN){
36004             this.menu.tryActivate(0, 1);
36005             return false;
36006         }
36007         return h.call(this.scope || this, e, this.menu);
36008     },
36009
36010     up : function(e, m){
36011         if(!m.tryActivate(m.items.indexOf(m.activeItem)-1, -1)){
36012             m.tryActivate(m.items.length-1, -1);
36013         }
36014     },
36015
36016     down : function(e, m){
36017         if(!m.tryActivate(m.items.indexOf(m.activeItem)+1, 1)){
36018             m.tryActivate(0, 1);
36019         }
36020     },
36021
36022     right : function(e, m){
36023         if(m.activeItem){
36024             m.activeItem.expandMenu(true);
36025         }
36026     },
36027
36028     left : function(e, m){
36029         m.hide();
36030         if(m.parentMenu && m.parentMenu.activeItem){
36031             m.parentMenu.activeItem.activate();
36032         }
36033     },
36034
36035     enter : function(e, m){
36036         if(m.activeItem){
36037             e.stopPropagation();
36038             m.activeItem.onClick(e);
36039             m.fireEvent("click", this, m.activeItem);
36040             return true;
36041         }
36042     }
36043 });/*
36044  * Based on:
36045  * Ext JS Library 1.1.1
36046  * Copyright(c) 2006-2007, Ext JS, LLC.
36047  *
36048  * Originally Released Under LGPL - original licence link has changed is not relivant.
36049  *
36050  * Fork - LGPL
36051  * <script type="text/javascript">
36052  */
36053  
36054 /**
36055  * @class Roo.menu.MenuMgr
36056  * Provides a common registry of all menu items on a page so that they can be easily accessed by id.
36057  * @singleton
36058  */
36059 Roo.menu.MenuMgr = function(){
36060    var menus, active, groups = {}, attached = false, lastShow = new Date();
36061
36062    // private - called when first menu is created
36063    function init(){
36064        menus = {};
36065        active = new Roo.util.MixedCollection();
36066        Roo.get(document).addKeyListener(27, function(){
36067            if(active.length > 0){
36068                hideAll();
36069            }
36070        });
36071    }
36072
36073    // private
36074    function hideAll(){
36075        if(active && active.length > 0){
36076            var c = active.clone();
36077            c.each(function(m){
36078                m.hide();
36079            });
36080        }
36081    }
36082
36083    // private
36084    function onHide(m){
36085        active.remove(m);
36086        if(active.length < 1){
36087            Roo.get(document).un("mousedown", onMouseDown);
36088            attached = false;
36089        }
36090    }
36091
36092    // private
36093    function onShow(m){
36094        var last = active.last();
36095        lastShow = new Date();
36096        active.add(m);
36097        if(!attached){
36098            Roo.get(document).on("mousedown", onMouseDown);
36099            attached = true;
36100        }
36101        if(m.parentMenu){
36102           m.getEl().setZIndex(parseInt(m.parentMenu.getEl().getStyle("z-index"), 10) + 3);
36103           m.parentMenu.activeChild = m;
36104        }else if(last && last.isVisible()){
36105           m.getEl().setZIndex(parseInt(last.getEl().getStyle("z-index"), 10) + 3);
36106        }
36107    }
36108
36109    // private
36110    function onBeforeHide(m){
36111        if(m.activeChild){
36112            m.activeChild.hide();
36113        }
36114        if(m.autoHideTimer){
36115            clearTimeout(m.autoHideTimer);
36116            delete m.autoHideTimer;
36117        }
36118    }
36119
36120    // private
36121    function onBeforeShow(m){
36122        var pm = m.parentMenu;
36123        if(!pm && !m.allowOtherMenus){
36124            hideAll();
36125        }else if(pm && pm.activeChild && active != m){
36126            pm.activeChild.hide();
36127        }
36128    }
36129
36130    // private
36131    function onMouseDown(e){
36132        if(lastShow.getElapsed() > 50 && active.length > 0 && !e.getTarget(".x-menu")){
36133            hideAll();
36134        }
36135    }
36136
36137    // private
36138    function onBeforeCheck(mi, state){
36139        if(state){
36140            var g = groups[mi.group];
36141            for(var i = 0, l = g.length; i < l; i++){
36142                if(g[i] != mi){
36143                    g[i].setChecked(false);
36144                }
36145            }
36146        }
36147    }
36148
36149    return {
36150
36151        /**
36152         * Hides all menus that are currently visible
36153         */
36154        hideAll : function(){
36155             hideAll();  
36156        },
36157
36158        // private
36159        register : function(menu){
36160            if(!menus){
36161                init();
36162            }
36163            menus[menu.id] = menu;
36164            menu.on("beforehide", onBeforeHide);
36165            menu.on("hide", onHide);
36166            menu.on("beforeshow", onBeforeShow);
36167            menu.on("show", onShow);
36168            var g = menu.group;
36169            if(g && menu.events["checkchange"]){
36170                if(!groups[g]){
36171                    groups[g] = [];
36172                }
36173                groups[g].push(menu);
36174                menu.on("checkchange", onCheck);
36175            }
36176        },
36177
36178         /**
36179          * Returns a {@link Roo.menu.Menu} object
36180          * @param {String/Object} menu The string menu id, an existing menu object reference, or a Menu config that will
36181          * be used to generate and return a new Menu instance.
36182          */
36183        get : function(menu){
36184            if(typeof menu == "string"){ // menu id
36185                return menus[menu];
36186            }else if(menu.events){  // menu instance
36187                return menu;
36188            }else if(typeof menu.length == 'number'){ // array of menu items?
36189                return new Roo.menu.Menu({items:menu});
36190            }else{ // otherwise, must be a config
36191                return new Roo.menu.Menu(menu);
36192            }
36193        },
36194
36195        // private
36196        unregister : function(menu){
36197            delete menus[menu.id];
36198            menu.un("beforehide", onBeforeHide);
36199            menu.un("hide", onHide);
36200            menu.un("beforeshow", onBeforeShow);
36201            menu.un("show", onShow);
36202            var g = menu.group;
36203            if(g && menu.events["checkchange"]){
36204                groups[g].remove(menu);
36205                menu.un("checkchange", onCheck);
36206            }
36207        },
36208
36209        // private
36210        registerCheckable : function(menuItem){
36211            var g = menuItem.group;
36212            if(g){
36213                if(!groups[g]){
36214                    groups[g] = [];
36215                }
36216                groups[g].push(menuItem);
36217                menuItem.on("beforecheckchange", onBeforeCheck);
36218            }
36219        },
36220
36221        // private
36222        unregisterCheckable : function(menuItem){
36223            var g = menuItem.group;
36224            if(g){
36225                groups[g].remove(menuItem);
36226                menuItem.un("beforecheckchange", onBeforeCheck);
36227            }
36228        }
36229    };
36230 }();/*
36231  * Based on:
36232  * Ext JS Library 1.1.1
36233  * Copyright(c) 2006-2007, Ext JS, LLC.
36234  *
36235  * Originally Released Under LGPL - original licence link has changed is not relivant.
36236  *
36237  * Fork - LGPL
36238  * <script type="text/javascript">
36239  */
36240  
36241
36242 /**
36243  * @class Roo.menu.BaseItem
36244  * @extends Roo.Component
36245  * The base class for all items that render into menus.  BaseItem provides default rendering, activated state
36246  * management and base configuration options shared by all menu components.
36247  * @constructor
36248  * Creates a new BaseItem
36249  * @param {Object} config Configuration options
36250  */
36251 Roo.menu.BaseItem = function(config){
36252     Roo.menu.BaseItem.superclass.constructor.call(this, config);
36253
36254     this.addEvents({
36255         /**
36256          * @event click
36257          * Fires when this item is clicked
36258          * @param {Roo.menu.BaseItem} this
36259          * @param {Roo.EventObject} e
36260          */
36261         click: true,
36262         /**
36263          * @event activate
36264          * Fires when this item is activated
36265          * @param {Roo.menu.BaseItem} this
36266          */
36267         activate : true,
36268         /**
36269          * @event deactivate
36270          * Fires when this item is deactivated
36271          * @param {Roo.menu.BaseItem} this
36272          */
36273         deactivate : true
36274     });
36275
36276     if(this.handler){
36277         this.on("click", this.handler, this.scope, true);
36278     }
36279 };
36280
36281 Roo.extend(Roo.menu.BaseItem, Roo.Component, {
36282     /**
36283      * @cfg {Function} handler
36284      * A function that will handle the click event of this menu item (defaults to undefined)
36285      */
36286     /**
36287      * @cfg {Boolean} canActivate True if this item can be visually activated (defaults to false)
36288      */
36289     canActivate : false,
36290     
36291      /**
36292      * @cfg {Boolean} hidden True to prevent creation of this menu item (defaults to false)
36293      */
36294     hidden: false,
36295     
36296     /**
36297      * @cfg {String} activeClass The CSS class to use when the item becomes activated (defaults to "x-menu-item-active")
36298      */
36299     activeClass : "x-menu-item-active",
36300     /**
36301      * @cfg {Boolean} hideOnClick True to hide the containing menu after this item is clicked (defaults to true)
36302      */
36303     hideOnClick : true,
36304     /**
36305      * @cfg {Number} hideDelay Length of time in milliseconds to wait before hiding after a click (defaults to 100)
36306      */
36307     hideDelay : 100,
36308
36309     // private
36310     ctype: "Roo.menu.BaseItem",
36311
36312     // private
36313     actionMode : "container",
36314
36315     // private
36316     render : function(container, parentMenu){
36317         this.parentMenu = parentMenu;
36318         Roo.menu.BaseItem.superclass.render.call(this, container);
36319         this.container.menuItemId = this.id;
36320     },
36321
36322     // private
36323     onRender : function(container, position){
36324         this.el = Roo.get(this.el);
36325         container.dom.appendChild(this.el.dom);
36326     },
36327
36328     // private
36329     onClick : function(e){
36330         if(!this.disabled && this.fireEvent("click", this, e) !== false
36331                 && this.parentMenu.fireEvent("itemclick", this, e) !== false){
36332             this.handleClick(e);
36333         }else{
36334             e.stopEvent();
36335         }
36336     },
36337
36338     // private
36339     activate : function(){
36340         if(this.disabled){
36341             return false;
36342         }
36343         var li = this.container;
36344         li.addClass(this.activeClass);
36345         this.region = li.getRegion().adjust(2, 2, -2, -2);
36346         this.fireEvent("activate", this);
36347         return true;
36348     },
36349
36350     // private
36351     deactivate : function(){
36352         this.container.removeClass(this.activeClass);
36353         this.fireEvent("deactivate", this);
36354     },
36355
36356     // private
36357     shouldDeactivate : function(e){
36358         return !this.region || !this.region.contains(e.getPoint());
36359     },
36360
36361     // private
36362     handleClick : function(e){
36363         if(this.hideOnClick){
36364             this.parentMenu.hide.defer(this.hideDelay, this.parentMenu, [true]);
36365         }
36366     },
36367
36368     // private
36369     expandMenu : function(autoActivate){
36370         // do nothing
36371     },
36372
36373     // private
36374     hideMenu : function(){
36375         // do nothing
36376     }
36377 });/*
36378  * Based on:
36379  * Ext JS Library 1.1.1
36380  * Copyright(c) 2006-2007, Ext JS, LLC.
36381  *
36382  * Originally Released Under LGPL - original licence link has changed is not relivant.
36383  *
36384  * Fork - LGPL
36385  * <script type="text/javascript">
36386  */
36387  
36388 /**
36389  * @class Roo.menu.Adapter
36390  * @extends Roo.menu.BaseItem
36391  * A base utility class that adapts a non-menu component so that it can be wrapped by a menu item and added to a menu.
36392  * It provides basic rendering, activation management and enable/disable logic required to work in menus.
36393  * @constructor
36394  * Creates a new Adapter
36395  * @param {Object} config Configuration options
36396  */
36397 Roo.menu.Adapter = function(component, config){
36398     Roo.menu.Adapter.superclass.constructor.call(this, config);
36399     this.component = component;
36400 };
36401 Roo.extend(Roo.menu.Adapter, Roo.menu.BaseItem, {
36402     // private
36403     canActivate : true,
36404
36405     // private
36406     onRender : function(container, position){
36407         this.component.render(container);
36408         this.el = this.component.getEl();
36409     },
36410
36411     // private
36412     activate : function(){
36413         if(this.disabled){
36414             return false;
36415         }
36416         this.component.focus();
36417         this.fireEvent("activate", this);
36418         return true;
36419     },
36420
36421     // private
36422     deactivate : function(){
36423         this.fireEvent("deactivate", this);
36424     },
36425
36426     // private
36427     disable : function(){
36428         this.component.disable();
36429         Roo.menu.Adapter.superclass.disable.call(this);
36430     },
36431
36432     // private
36433     enable : function(){
36434         this.component.enable();
36435         Roo.menu.Adapter.superclass.enable.call(this);
36436     }
36437 });/*
36438  * Based on:
36439  * Ext JS Library 1.1.1
36440  * Copyright(c) 2006-2007, Ext JS, LLC.
36441  *
36442  * Originally Released Under LGPL - original licence link has changed is not relivant.
36443  *
36444  * Fork - LGPL
36445  * <script type="text/javascript">
36446  */
36447
36448 /**
36449  * @class Roo.menu.TextItem
36450  * @extends Roo.menu.BaseItem
36451  * Adds a static text string to a menu, usually used as either a heading or group separator.
36452  * Note: old style constructor with text is still supported.
36453  * 
36454  * @constructor
36455  * Creates a new TextItem
36456  * @param {Object} cfg Configuration
36457  */
36458 Roo.menu.TextItem = function(cfg){
36459     if (typeof(cfg) == 'string') {
36460         this.text = cfg;
36461     } else {
36462         Roo.apply(this,cfg);
36463     }
36464     
36465     Roo.menu.TextItem.superclass.constructor.call(this);
36466 };
36467
36468 Roo.extend(Roo.menu.TextItem, Roo.menu.BaseItem, {
36469     /**
36470      * @cfg {Boolean} text Text to show on item.
36471      */
36472     text : '',
36473     
36474     /**
36475      * @cfg {Boolean} hideOnClick True to hide the containing menu after this item is clicked (defaults to false)
36476      */
36477     hideOnClick : false,
36478     /**
36479      * @cfg {String} itemCls The default CSS class to use for text items (defaults to "x-menu-text")
36480      */
36481     itemCls : "x-menu-text",
36482
36483     // private
36484     onRender : function(){
36485         var s = document.createElement("span");
36486         s.className = this.itemCls;
36487         s.innerHTML = this.text;
36488         this.el = s;
36489         Roo.menu.TextItem.superclass.onRender.apply(this, arguments);
36490     }
36491 });/*
36492  * Based on:
36493  * Ext JS Library 1.1.1
36494  * Copyright(c) 2006-2007, Ext JS, LLC.
36495  *
36496  * Originally Released Under LGPL - original licence link has changed is not relivant.
36497  *
36498  * Fork - LGPL
36499  * <script type="text/javascript">
36500  */
36501
36502 /**
36503  * @class Roo.menu.Separator
36504  * @extends Roo.menu.BaseItem
36505  * Adds a separator bar to a menu, used to divide logical groups of menu items. Generally you will
36506  * add one of these by using "-" in you call to add() or in your items config rather than creating one directly.
36507  * @constructor
36508  * @param {Object} config Configuration options
36509  */
36510 Roo.menu.Separator = function(config){
36511     Roo.menu.Separator.superclass.constructor.call(this, config);
36512 };
36513
36514 Roo.extend(Roo.menu.Separator, Roo.menu.BaseItem, {
36515     /**
36516      * @cfg {String} itemCls The default CSS class to use for separators (defaults to "x-menu-sep")
36517      */
36518     itemCls : "x-menu-sep",
36519     /**
36520      * @cfg {Boolean} hideOnClick True to hide the containing menu after this item is clicked (defaults to false)
36521      */
36522     hideOnClick : false,
36523
36524     // private
36525     onRender : function(li){
36526         var s = document.createElement("span");
36527         s.className = this.itemCls;
36528         s.innerHTML = "&#160;";
36529         this.el = s;
36530         li.addClass("x-menu-sep-li");
36531         Roo.menu.Separator.superclass.onRender.apply(this, arguments);
36532     }
36533 });/*
36534  * Based on:
36535  * Ext JS Library 1.1.1
36536  * Copyright(c) 2006-2007, Ext JS, LLC.
36537  *
36538  * Originally Released Under LGPL - original licence link has changed is not relivant.
36539  *
36540  * Fork - LGPL
36541  * <script type="text/javascript">
36542  */
36543 /**
36544  * @class Roo.menu.Item
36545  * @extends Roo.menu.BaseItem
36546  * A base class for all menu items that require menu-related functionality (like sub-menus) and are not static
36547  * display items.  Item extends the base functionality of {@link Roo.menu.BaseItem} by adding menu-specific
36548  * activation and click handling.
36549  * @constructor
36550  * Creates a new Item
36551  * @param {Object} config Configuration options
36552  */
36553 Roo.menu.Item = function(config){
36554     Roo.menu.Item.superclass.constructor.call(this, config);
36555     if(this.menu){
36556         this.menu = Roo.menu.MenuMgr.get(this.menu);
36557     }
36558 };
36559 Roo.extend(Roo.menu.Item, Roo.menu.BaseItem, {
36560     
36561     /**
36562      * @cfg {String} text
36563      * The text to show on the menu item.
36564      */
36565     text: '',
36566      /**
36567      * @cfg {String} HTML to render in menu
36568      * The text to show on the menu item (HTML version).
36569      */
36570     html: '',
36571     /**
36572      * @cfg {String} icon
36573      * The path to an icon to display in this menu item (defaults to Roo.BLANK_IMAGE_URL)
36574      */
36575     icon: undefined,
36576     /**
36577      * @cfg {String} itemCls The default CSS class to use for menu items (defaults to "x-menu-item")
36578      */
36579     itemCls : "x-menu-item",
36580     /**
36581      * @cfg {Boolean} canActivate True if this item can be visually activated (defaults to true)
36582      */
36583     canActivate : true,
36584     /**
36585      * @cfg {Number} showDelay Length of time in milliseconds to wait before showing this item (defaults to 200)
36586      */
36587     showDelay: 200,
36588     // doc'd in BaseItem
36589     hideDelay: 200,
36590
36591     // private
36592     ctype: "Roo.menu.Item",
36593     
36594     // private
36595     onRender : function(container, position){
36596         var el = document.createElement("a");
36597         el.hideFocus = true;
36598         el.unselectable = "on";
36599         el.href = this.href || "#";
36600         if(this.hrefTarget){
36601             el.target = this.hrefTarget;
36602         }
36603         el.className = this.itemCls + (this.menu ?  " x-menu-item-arrow" : "") + (this.cls ?  " " + this.cls : "");
36604         
36605         var html = this.html.length ? this.html  : String.format('{0}',this.text);
36606         
36607         el.innerHTML = String.format(
36608                 '<img src="{0}" class="x-menu-item-icon {1}" />' + html,
36609                 this.icon || Roo.BLANK_IMAGE_URL, this.iconCls || '');
36610         this.el = el;
36611         Roo.menu.Item.superclass.onRender.call(this, container, position);
36612     },
36613
36614     /**
36615      * Sets the text to display in this menu item
36616      * @param {String} text The text to display
36617      * @param {Boolean} isHTML true to indicate text is pure html.
36618      */
36619     setText : function(text, isHTML){
36620         if (isHTML) {
36621             this.html = text;
36622         } else {
36623             this.text = text;
36624             this.html = '';
36625         }
36626         if(this.rendered){
36627             var html = this.html.length ? this.html  : String.format('{0}',this.text);
36628      
36629             this.el.update(String.format(
36630                 '<img src="{0}" class="x-menu-item-icon {2}">' + html,
36631                 this.icon || Roo.BLANK_IMAGE_URL, this.text, this.iconCls || ''));
36632             this.parentMenu.autoWidth();
36633         }
36634     },
36635
36636     // private
36637     handleClick : function(e){
36638         if(!this.href){ // if no link defined, stop the event automatically
36639             e.stopEvent();
36640         }
36641         Roo.menu.Item.superclass.handleClick.apply(this, arguments);
36642     },
36643
36644     // private
36645     activate : function(autoExpand){
36646         if(Roo.menu.Item.superclass.activate.apply(this, arguments)){
36647             this.focus();
36648             if(autoExpand){
36649                 this.expandMenu();
36650             }
36651         }
36652         return true;
36653     },
36654
36655     // private
36656     shouldDeactivate : function(e){
36657         if(Roo.menu.Item.superclass.shouldDeactivate.call(this, e)){
36658             if(this.menu && this.menu.isVisible()){
36659                 return !this.menu.getEl().getRegion().contains(e.getPoint());
36660             }
36661             return true;
36662         }
36663         return false;
36664     },
36665
36666     // private
36667     deactivate : function(){
36668         Roo.menu.Item.superclass.deactivate.apply(this, arguments);
36669         this.hideMenu();
36670     },
36671
36672     // private
36673     expandMenu : function(autoActivate){
36674         if(!this.disabled && this.menu){
36675             clearTimeout(this.hideTimer);
36676             delete this.hideTimer;
36677             if(!this.menu.isVisible() && !this.showTimer){
36678                 this.showTimer = this.deferExpand.defer(this.showDelay, this, [autoActivate]);
36679             }else if (this.menu.isVisible() && autoActivate){
36680                 this.menu.tryActivate(0, 1);
36681             }
36682         }
36683     },
36684
36685     // private
36686     deferExpand : function(autoActivate){
36687         delete this.showTimer;
36688         this.menu.show(this.container, this.parentMenu.subMenuAlign || "tl-tr?", this.parentMenu);
36689         if(autoActivate){
36690             this.menu.tryActivate(0, 1);
36691         }
36692     },
36693
36694     // private
36695     hideMenu : function(){
36696         clearTimeout(this.showTimer);
36697         delete this.showTimer;
36698         if(!this.hideTimer && this.menu && this.menu.isVisible()){
36699             this.hideTimer = this.deferHide.defer(this.hideDelay, this);
36700         }
36701     },
36702
36703     // private
36704     deferHide : function(){
36705         delete this.hideTimer;
36706         this.menu.hide();
36707     }
36708 });/*
36709  * Based on:
36710  * Ext JS Library 1.1.1
36711  * Copyright(c) 2006-2007, Ext JS, LLC.
36712  *
36713  * Originally Released Under LGPL - original licence link has changed is not relivant.
36714  *
36715  * Fork - LGPL
36716  * <script type="text/javascript">
36717  */
36718  
36719 /**
36720  * @class Roo.menu.CheckItem
36721  * @extends Roo.menu.Item
36722  * Adds a menu item that contains a checkbox by default, but can also be part of a radio group.
36723  * @constructor
36724  * Creates a new CheckItem
36725  * @param {Object} config Configuration options
36726  */
36727 Roo.menu.CheckItem = function(config){
36728     Roo.menu.CheckItem.superclass.constructor.call(this, config);
36729     this.addEvents({
36730         /**
36731          * @event beforecheckchange
36732          * Fires before the checked value is set, providing an opportunity to cancel if needed
36733          * @param {Roo.menu.CheckItem} this
36734          * @param {Boolean} checked The new checked value that will be set
36735          */
36736         "beforecheckchange" : true,
36737         /**
36738          * @event checkchange
36739          * Fires after the checked value has been set
36740          * @param {Roo.menu.CheckItem} this
36741          * @param {Boolean} checked The checked value that was set
36742          */
36743         "checkchange" : true
36744     });
36745     if(this.checkHandler){
36746         this.on('checkchange', this.checkHandler, this.scope);
36747     }
36748 };
36749 Roo.extend(Roo.menu.CheckItem, Roo.menu.Item, {
36750     /**
36751      * @cfg {String} group
36752      * All check items with the same group name will automatically be grouped into a single-select
36753      * radio button group (defaults to '')
36754      */
36755     /**
36756      * @cfg {String} itemCls The default CSS class to use for check items (defaults to "x-menu-item x-menu-check-item")
36757      */
36758     itemCls : "x-menu-item x-menu-check-item",
36759     /**
36760      * @cfg {String} groupClass The default CSS class to use for radio group check items (defaults to "x-menu-group-item")
36761      */
36762     groupClass : "x-menu-group-item",
36763
36764     /**
36765      * @cfg {Boolean} checked True to initialize this checkbox as checked (defaults to false).  Note that
36766      * if this checkbox is part of a radio group (group = true) only the last item in the group that is
36767      * initialized with checked = true will be rendered as checked.
36768      */
36769     checked: false,
36770
36771     // private
36772     ctype: "Roo.menu.CheckItem",
36773
36774     // private
36775     onRender : function(c){
36776         Roo.menu.CheckItem.superclass.onRender.apply(this, arguments);
36777         if(this.group){
36778             this.el.addClass(this.groupClass);
36779         }
36780         Roo.menu.MenuMgr.registerCheckable(this);
36781         if(this.checked){
36782             this.checked = false;
36783             this.setChecked(true, true);
36784         }
36785     },
36786
36787     // private
36788     destroy : function(){
36789         if(this.rendered){
36790             Roo.menu.MenuMgr.unregisterCheckable(this);
36791         }
36792         Roo.menu.CheckItem.superclass.destroy.apply(this, arguments);
36793     },
36794
36795     /**
36796      * Set the checked state of this item
36797      * @param {Boolean} checked The new checked value
36798      * @param {Boolean} suppressEvent (optional) True to prevent the checkchange event from firing (defaults to false)
36799      */
36800     setChecked : function(state, suppressEvent){
36801         if(this.checked != state && this.fireEvent("beforecheckchange", this, state) !== false){
36802             if(this.container){
36803                 this.container[state ? "addClass" : "removeClass"]("x-menu-item-checked");
36804             }
36805             this.checked = state;
36806             if(suppressEvent !== true){
36807                 this.fireEvent("checkchange", this, state);
36808             }
36809         }
36810     },
36811
36812     // private
36813     handleClick : function(e){
36814        if(!this.disabled && !(this.checked && this.group)){// disable unselect on radio item
36815            this.setChecked(!this.checked);
36816        }
36817        Roo.menu.CheckItem.superclass.handleClick.apply(this, arguments);
36818     }
36819 });/*
36820  * Based on:
36821  * Ext JS Library 1.1.1
36822  * Copyright(c) 2006-2007, Ext JS, LLC.
36823  *
36824  * Originally Released Under LGPL - original licence link has changed is not relivant.
36825  *
36826  * Fork - LGPL
36827  * <script type="text/javascript">
36828  */
36829  
36830 /**
36831  * @class Roo.menu.DateItem
36832  * @extends Roo.menu.Adapter
36833  * A menu item that wraps the {@link Roo.DatPicker} component.
36834  * @constructor
36835  * Creates a new DateItem
36836  * @param {Object} config Configuration options
36837  */
36838 Roo.menu.DateItem = function(config){
36839     Roo.menu.DateItem.superclass.constructor.call(this, new Roo.DatePicker(config), config);
36840     /** The Roo.DatePicker object @type Roo.DatePicker */
36841     this.picker = this.component;
36842     this.addEvents({select: true});
36843     
36844     this.picker.on("render", function(picker){
36845         picker.getEl().swallowEvent("click");
36846         picker.container.addClass("x-menu-date-item");
36847     });
36848
36849     this.picker.on("select", this.onSelect, this);
36850 };
36851
36852 Roo.extend(Roo.menu.DateItem, Roo.menu.Adapter, {
36853     // private
36854     onSelect : function(picker, date){
36855         this.fireEvent("select", this, date, picker);
36856         Roo.menu.DateItem.superclass.handleClick.call(this);
36857     }
36858 });/*
36859  * Based on:
36860  * Ext JS Library 1.1.1
36861  * Copyright(c) 2006-2007, Ext JS, LLC.
36862  *
36863  * Originally Released Under LGPL - original licence link has changed is not relivant.
36864  *
36865  * Fork - LGPL
36866  * <script type="text/javascript">
36867  */
36868  
36869 /**
36870  * @class Roo.menu.ColorItem
36871  * @extends Roo.menu.Adapter
36872  * A menu item that wraps the {@link Roo.ColorPalette} component.
36873  * @constructor
36874  * Creates a new ColorItem
36875  * @param {Object} config Configuration options
36876  */
36877 Roo.menu.ColorItem = function(config){
36878     Roo.menu.ColorItem.superclass.constructor.call(this, new Roo.ColorPalette(config), config);
36879     /** The Roo.ColorPalette object @type Roo.ColorPalette */
36880     this.palette = this.component;
36881     this.relayEvents(this.palette, ["select"]);
36882     if(this.selectHandler){
36883         this.on('select', this.selectHandler, this.scope);
36884     }
36885 };
36886 Roo.extend(Roo.menu.ColorItem, Roo.menu.Adapter);/*
36887  * Based on:
36888  * Ext JS Library 1.1.1
36889  * Copyright(c) 2006-2007, Ext JS, LLC.
36890  *
36891  * Originally Released Under LGPL - original licence link has changed is not relivant.
36892  *
36893  * Fork - LGPL
36894  * <script type="text/javascript">
36895  */
36896  
36897
36898 /**
36899  * @class Roo.menu.DateMenu
36900  * @extends Roo.menu.Menu
36901  * A menu containing a {@link Roo.menu.DateItem} component (which provides a date picker).
36902  * @constructor
36903  * Creates a new DateMenu
36904  * @param {Object} config Configuration options
36905  */
36906 Roo.menu.DateMenu = function(config){
36907     Roo.menu.DateMenu.superclass.constructor.call(this, config);
36908     this.plain = true;
36909     var di = new Roo.menu.DateItem(config);
36910     this.add(di);
36911     /**
36912      * The {@link Roo.DatePicker} instance for this DateMenu
36913      * @type DatePicker
36914      */
36915     this.picker = di.picker;
36916     /**
36917      * @event select
36918      * @param {DatePicker} picker
36919      * @param {Date} date
36920      */
36921     this.relayEvents(di, ["select"]);
36922     this.on('beforeshow', function(){
36923         if(this.picker){
36924             this.picker.hideMonthPicker(false);
36925         }
36926     }, this);
36927 };
36928 Roo.extend(Roo.menu.DateMenu, Roo.menu.Menu, {
36929     cls:'x-date-menu'
36930 });/*
36931  * Based on:
36932  * Ext JS Library 1.1.1
36933  * Copyright(c) 2006-2007, Ext JS, LLC.
36934  *
36935  * Originally Released Under LGPL - original licence link has changed is not relivant.
36936  *
36937  * Fork - LGPL
36938  * <script type="text/javascript">
36939  */
36940  
36941
36942 /**
36943  * @class Roo.menu.ColorMenu
36944  * @extends Roo.menu.Menu
36945  * A menu containing a {@link Roo.menu.ColorItem} component (which provides a basic color picker).
36946  * @constructor
36947  * Creates a new ColorMenu
36948  * @param {Object} config Configuration options
36949  */
36950 Roo.menu.ColorMenu = function(config){
36951     Roo.menu.ColorMenu.superclass.constructor.call(this, config);
36952     this.plain = true;
36953     var ci = new Roo.menu.ColorItem(config);
36954     this.add(ci);
36955     /**
36956      * The {@link Roo.ColorPalette} instance for this ColorMenu
36957      * @type ColorPalette
36958      */
36959     this.palette = ci.palette;
36960     /**
36961      * @event select
36962      * @param {ColorPalette} palette
36963      * @param {String} color
36964      */
36965     this.relayEvents(ci, ["select"]);
36966 };
36967 Roo.extend(Roo.menu.ColorMenu, Roo.menu.Menu);/*
36968  * Based on:
36969  * Ext JS Library 1.1.1
36970  * Copyright(c) 2006-2007, Ext JS, LLC.
36971  *
36972  * Originally Released Under LGPL - original licence link has changed is not relivant.
36973  *
36974  * Fork - LGPL
36975  * <script type="text/javascript">
36976  */
36977  
36978 /**
36979  * @class Roo.form.Field
36980  * @extends Roo.BoxComponent
36981  * Base class for form fields that provides default event handling, sizing, value handling and other functionality.
36982  * @constructor
36983  * Creates a new Field
36984  * @param {Object} config Configuration options
36985  */
36986 Roo.form.Field = function(config){
36987     Roo.form.Field.superclass.constructor.call(this, config);
36988 };
36989
36990 Roo.extend(Roo.form.Field, Roo.BoxComponent,  {
36991     /**
36992      * @cfg {String} fieldLabel Label to use when rendering a form.
36993      */
36994        /**
36995      * @cfg {String} qtip Mouse over tip
36996      */
36997      
36998     /**
36999      * @cfg {String} invalidClass The CSS class to use when marking a field invalid (defaults to "x-form-invalid")
37000      */
37001     invalidClass : "x-form-invalid",
37002     /**
37003      * @cfg {String} invalidText The error text to use when marking a field invalid and no message is provided (defaults to "The value in this field is invalid")
37004      */
37005     invalidText : "The value in this field is invalid",
37006     /**
37007      * @cfg {String} focusClass The CSS class to use when the field receives focus (defaults to "x-form-focus")
37008      */
37009     focusClass : "x-form-focus",
37010     /**
37011      * @cfg {String/Boolean} validationEvent The event that should initiate field validation. Set to false to disable
37012       automatic validation (defaults to "keyup").
37013      */
37014     validationEvent : "keyup",
37015     /**
37016      * @cfg {Boolean} validateOnBlur Whether the field should validate when it loses focus (defaults to true).
37017      */
37018     validateOnBlur : true,
37019     /**
37020      * @cfg {Number} validationDelay The length of time in milliseconds after user input begins until validation is initiated (defaults to 250)
37021      */
37022     validationDelay : 250,
37023     /**
37024      * @cfg {String/Object} autoCreate A DomHelper element spec, or true for a default element spec (defaults to
37025      * {tag: "input", type: "text", size: "20", autocomplete: "off"})
37026      */
37027     defaultAutoCreate : {tag: "input", type: "text", size: "20", autocomplete: "off"},
37028     /**
37029      * @cfg {String} fieldClass The default CSS class for the field (defaults to "x-form-field")
37030      */
37031     fieldClass : "x-form-field",
37032     /**
37033      * @cfg {String} msgTarget The location where error text should display.  Should be one of the following values (defaults to 'qtip'):
37034      *<pre>
37035 Value         Description
37036 -----------   ----------------------------------------------------------------------
37037 qtip          Display a quick tip when the user hovers over the field
37038 title         Display a default browser title attribute popup
37039 under         Add a block div beneath the field containing the error text
37040 side          Add an error icon to the right of the field with a popup on hover
37041 [element id]  Add the error text directly to the innerHTML of the specified element
37042 </pre>
37043      */
37044     msgTarget : 'qtip',
37045     /**
37046      * @cfg {String} msgFx <b>Experimental</b> The effect used when displaying a validation message under the field (defaults to 'normal').
37047      */
37048     msgFx : 'normal',
37049
37050     /**
37051      * @cfg {Boolean} readOnly True to mark the field as readOnly in HTML (defaults to false) -- Note: this only sets the element's readOnly DOM attribute.
37052      */
37053     readOnly : false,
37054
37055     /**
37056      * @cfg {Boolean} disabled True to disable the field (defaults to false).
37057      */
37058     disabled : false,
37059
37060     /**
37061      * @cfg {String} inputType The type attribute for input fields -- e.g. radio, text, password (defaults to "text").
37062      */
37063     inputType : undefined,
37064     
37065     /**
37066      * @cfg {Number} tabIndex The tabIndex for this field. Note this only applies to fields that are rendered, not those which are built via applyTo (defaults to undefined).
37067          */
37068         tabIndex : undefined,
37069         
37070     // private
37071     isFormField : true,
37072
37073     // private
37074     hasFocus : false,
37075     /**
37076      * @property {Roo.Element} fieldEl
37077      * Element Containing the rendered Field (with label etc.)
37078      */
37079     /**
37080      * @cfg {Mixed} value A value to initialize this field with.
37081      */
37082     value : undefined,
37083
37084     /**
37085      * @cfg {String} name The field's HTML name attribute.
37086      */
37087     /**
37088      * @cfg {String} cls A CSS class to apply to the field's underlying element.
37089      */
37090
37091         // private ??
37092         initComponent : function(){
37093         Roo.form.Field.superclass.initComponent.call(this);
37094         this.addEvents({
37095             /**
37096              * @event focus
37097              * Fires when this field receives input focus.
37098              * @param {Roo.form.Field} this
37099              */
37100             focus : true,
37101             /**
37102              * @event blur
37103              * Fires when this field loses input focus.
37104              * @param {Roo.form.Field} this
37105              */
37106             blur : true,
37107             /**
37108              * @event specialkey
37109              * Fires when any key related to navigation (arrows, tab, enter, esc, etc.) is pressed.  You can check
37110              * {@link Roo.EventObject#getKey} to determine which key was pressed.
37111              * @param {Roo.form.Field} this
37112              * @param {Roo.EventObject} e The event object
37113              */
37114             specialkey : true,
37115             /**
37116              * @event change
37117              * Fires just before the field blurs if the field value has changed.
37118              * @param {Roo.form.Field} this
37119              * @param {Mixed} newValue The new value
37120              * @param {Mixed} oldValue The original value
37121              */
37122             change : true,
37123             /**
37124              * @event invalid
37125              * Fires after the field has been marked as invalid.
37126              * @param {Roo.form.Field} this
37127              * @param {String} msg The validation message
37128              */
37129             invalid : true,
37130             /**
37131              * @event valid
37132              * Fires after the field has been validated with no errors.
37133              * @param {Roo.form.Field} this
37134              */
37135             valid : true,
37136              /**
37137              * @event keyup
37138              * Fires after the key up
37139              * @param {Roo.form.Field} this
37140              * @param {Roo.EventObject}  e The event Object
37141              */
37142             keyup : true
37143         });
37144     },
37145
37146     /**
37147      * Returns the name attribute of the field if available
37148      * @return {String} name The field name
37149      */
37150     getName: function(){
37151          return this.rendered && this.el.dom.name ? this.el.dom.name : (this.hiddenName || '');
37152     },
37153
37154     // private
37155     onRender : function(ct, position){
37156         Roo.form.Field.superclass.onRender.call(this, ct, position);
37157         if(!this.el){
37158             var cfg = this.getAutoCreate();
37159             if(!cfg.name){
37160                 cfg.name = typeof(this.name) == 'undefined' ? this.id : this.name;
37161             }
37162             if (!cfg.name.length) {
37163                 delete cfg.name;
37164             }
37165             if(this.inputType){
37166                 cfg.type = this.inputType;
37167             }
37168             this.el = ct.createChild(cfg, position);
37169         }
37170         var type = this.el.dom.type;
37171         if(type){
37172             if(type == 'password'){
37173                 type = 'text';
37174             }
37175             this.el.addClass('x-form-'+type);
37176         }
37177         if(this.readOnly){
37178             this.el.dom.readOnly = true;
37179         }
37180         if(this.tabIndex !== undefined){
37181             this.el.dom.setAttribute('tabIndex', this.tabIndex);
37182         }
37183
37184         this.el.addClass([this.fieldClass, this.cls]);
37185         this.initValue();
37186     },
37187
37188     /**
37189      * Apply the behaviors of this component to an existing element. <b>This is used instead of render().</b>
37190      * @param {String/HTMLElement/Element} el The id of the node, a DOM node or an existing Element
37191      * @return {Roo.form.Field} this
37192      */
37193     applyTo : function(target){
37194         this.allowDomMove = false;
37195         this.el = Roo.get(target);
37196         this.render(this.el.dom.parentNode);
37197         return this;
37198     },
37199
37200     // private
37201     initValue : function(){
37202         if(this.value !== undefined){
37203             this.setValue(this.value);
37204         }else if(this.el.dom.value.length > 0){
37205             this.setValue(this.el.dom.value);
37206         }
37207     },
37208
37209     /**
37210      * Returns true if this field has been changed since it was originally loaded and is not disabled.
37211      */
37212     isDirty : function() {
37213         if(this.disabled) {
37214             return false;
37215         }
37216         return String(this.getValue()) !== String(this.originalValue);
37217     },
37218
37219     // private
37220     afterRender : function(){
37221         Roo.form.Field.superclass.afterRender.call(this);
37222         this.initEvents();
37223     },
37224
37225     // private
37226     fireKey : function(e){
37227         //Roo.log('field ' + e.getKey());
37228         if(e.isNavKeyPress()){
37229             this.fireEvent("specialkey", this, e);
37230         }
37231     },
37232
37233     /**
37234      * Resets the current field value to the originally loaded value and clears any validation messages
37235      */
37236     reset : function(){
37237         this.setValue(this.resetValue);
37238         this.clearInvalid();
37239     },
37240
37241     // private
37242     initEvents : function(){
37243         // safari killled keypress - so keydown is now used..
37244         this.el.on("keydown" , this.fireKey,  this);
37245         this.el.on("focus", this.onFocus,  this);
37246         this.el.on("blur", this.onBlur,  this);
37247         this.el.relayEvent('keyup', this);
37248
37249         // reference to original value for reset
37250         this.originalValue = this.getValue();
37251         this.resetValue =  this.getValue();
37252     },
37253
37254     // private
37255     onFocus : function(){
37256         if(!Roo.isOpera && this.focusClass){ // don't touch in Opera
37257             this.el.addClass(this.focusClass);
37258         }
37259         if(!this.hasFocus){
37260             this.hasFocus = true;
37261             this.startValue = this.getValue();
37262             this.fireEvent("focus", this);
37263         }
37264     },
37265
37266     beforeBlur : Roo.emptyFn,
37267
37268     // private
37269     onBlur : function(){
37270         this.beforeBlur();
37271         if(!Roo.isOpera && this.focusClass){ // don't touch in Opera
37272             this.el.removeClass(this.focusClass);
37273         }
37274         this.hasFocus = false;
37275         if(this.validationEvent !== false && this.validateOnBlur && this.validationEvent != "blur"){
37276             this.validate();
37277         }
37278         var v = this.getValue();
37279         if(String(v) !== String(this.startValue)){
37280             this.fireEvent('change', this, v, this.startValue);
37281         }
37282         this.fireEvent("blur", this);
37283     },
37284
37285     /**
37286      * Returns whether or not the field value is currently valid
37287      * @param {Boolean} preventMark True to disable marking the field invalid
37288      * @return {Boolean} True if the value is valid, else false
37289      */
37290     isValid : function(preventMark){
37291         if(this.disabled){
37292             return true;
37293         }
37294         var restore = this.preventMark;
37295         this.preventMark = preventMark === true;
37296         var v = this.validateValue(this.processValue(this.getRawValue()));
37297         this.preventMark = restore;
37298         return v;
37299     },
37300
37301     /**
37302      * Validates the field value
37303      * @return {Boolean} True if the value is valid, else false
37304      */
37305     validate : function(){
37306         if(this.disabled || this.validateValue(this.processValue(this.getRawValue()))){
37307             this.clearInvalid();
37308             return true;
37309         }
37310         return false;
37311     },
37312
37313     processValue : function(value){
37314         return value;
37315     },
37316
37317     // private
37318     // Subclasses should provide the validation implementation by overriding this
37319     validateValue : function(value){
37320         return true;
37321     },
37322
37323     /**
37324      * Mark this field as invalid
37325      * @param {String} msg The validation message
37326      */
37327     markInvalid : function(msg){
37328         if(!this.rendered || this.preventMark){ // not rendered
37329             return;
37330         }
37331         
37332         var obj = (typeof(this.combo) != 'undefined') ? this.combo : this; // fix the combox array!!
37333         
37334         obj.el.addClass(this.invalidClass);
37335         msg = msg || this.invalidText;
37336         switch(this.msgTarget){
37337             case 'qtip':
37338                 obj.el.dom.qtip = msg;
37339                 obj.el.dom.qclass = 'x-form-invalid-tip';
37340                 if(Roo.QuickTips){ // fix for floating editors interacting with DND
37341                     Roo.QuickTips.enable();
37342                 }
37343                 break;
37344             case 'title':
37345                 this.el.dom.title = msg;
37346                 break;
37347             case 'under':
37348                 if(!this.errorEl){
37349                     var elp = this.el.findParent('.x-form-element', 5, true);
37350                     this.errorEl = elp.createChild({cls:'x-form-invalid-msg'});
37351                     this.errorEl.setWidth(elp.getWidth(true)-20);
37352                 }
37353                 this.errorEl.update(msg);
37354                 Roo.form.Field.msgFx[this.msgFx].show(this.errorEl, this);
37355                 break;
37356             case 'side':
37357                 if(!this.errorIcon){
37358                     var elp = this.el.findParent('.x-form-element', 5, true);
37359                     this.errorIcon = elp.createChild({cls:'x-form-invalid-icon'});
37360                 }
37361                 this.alignErrorIcon();
37362                 this.errorIcon.dom.qtip = msg;
37363                 this.errorIcon.dom.qclass = 'x-form-invalid-tip';
37364                 this.errorIcon.show();
37365                 this.on('resize', this.alignErrorIcon, this);
37366                 break;
37367             default:
37368                 var t = Roo.getDom(this.msgTarget);
37369                 t.innerHTML = msg;
37370                 t.style.display = this.msgDisplay;
37371                 break;
37372         }
37373         this.fireEvent('invalid', this, msg);
37374     },
37375
37376     // private
37377     alignErrorIcon : function(){
37378         this.errorIcon.alignTo(this.el, 'tl-tr', [2, 0]);
37379     },
37380
37381     /**
37382      * Clear any invalid styles/messages for this field
37383      */
37384     clearInvalid : function(){
37385         if(!this.rendered || this.preventMark){ // not rendered
37386             return;
37387         }
37388         var obj = (typeof(this.combo) != 'undefined') ? this.combo : this; // fix the combox array!!
37389         
37390         obj.el.removeClass(this.invalidClass);
37391         switch(this.msgTarget){
37392             case 'qtip':
37393                 obj.el.dom.qtip = '';
37394                 break;
37395             case 'title':
37396                 this.el.dom.title = '';
37397                 break;
37398             case 'under':
37399                 if(this.errorEl){
37400                     Roo.form.Field.msgFx[this.msgFx].hide(this.errorEl, this);
37401                 }
37402                 break;
37403             case 'side':
37404                 if(this.errorIcon){
37405                     this.errorIcon.dom.qtip = '';
37406                     this.errorIcon.hide();
37407                     this.un('resize', this.alignErrorIcon, this);
37408                 }
37409                 break;
37410             default:
37411                 var t = Roo.getDom(this.msgTarget);
37412                 t.innerHTML = '';
37413                 t.style.display = 'none';
37414                 break;
37415         }
37416         this.fireEvent('valid', this);
37417     },
37418
37419     /**
37420      * Returns the raw data value which may or may not be a valid, defined value.  To return a normalized value see {@link #getValue}.
37421      * @return {Mixed} value The field value
37422      */
37423     getRawValue : function(){
37424         var v = this.el.getValue();
37425         
37426         return v;
37427     },
37428
37429     /**
37430      * Returns the normalized data value (undefined or emptyText will be returned as '').  To return the raw value see {@link #getRawValue}.
37431      * @return {Mixed} value The field value
37432      */
37433     getValue : function(){
37434         var v = this.el.getValue();
37435          
37436         return v;
37437     },
37438
37439     /**
37440      * Sets the underlying DOM field's value directly, bypassing validation.  To set the value with validation see {@link #setValue}.
37441      * @param {Mixed} value The value to set
37442      */
37443     setRawValue : function(v){
37444         return this.el.dom.value = (v === null || v === undefined ? '' : v);
37445     },
37446
37447     /**
37448      * Sets a data value into the field and validates it.  To set the value directly without validation see {@link #setRawValue}.
37449      * @param {Mixed} value The value to set
37450      */
37451     setValue : function(v){
37452         this.value = v;
37453         if(this.rendered){
37454             this.el.dom.value = (v === null || v === undefined ? '' : v);
37455              this.validate();
37456         }
37457     },
37458
37459     adjustSize : function(w, h){
37460         var s = Roo.form.Field.superclass.adjustSize.call(this, w, h);
37461         s.width = this.adjustWidth(this.el.dom.tagName, s.width);
37462         return s;
37463     },
37464
37465     adjustWidth : function(tag, w){
37466         tag = tag.toLowerCase();
37467         if(typeof w == 'number' && Roo.isStrict && !Roo.isSafari){
37468             if(Roo.isIE && (tag == 'input' || tag == 'textarea')){
37469                 if(tag == 'input'){
37470                     return w + 2;
37471                 }
37472                 if(tag == 'textarea'){
37473                     return w-2;
37474                 }
37475             }else if(Roo.isOpera){
37476                 if(tag == 'input'){
37477                     return w + 2;
37478                 }
37479                 if(tag == 'textarea'){
37480                     return w-2;
37481                 }
37482             }
37483         }
37484         return w;
37485     }
37486 });
37487
37488
37489 // anything other than normal should be considered experimental
37490 Roo.form.Field.msgFx = {
37491     normal : {
37492         show: function(msgEl, f){
37493             msgEl.setDisplayed('block');
37494         },
37495
37496         hide : function(msgEl, f){
37497             msgEl.setDisplayed(false).update('');
37498         }
37499     },
37500
37501     slide : {
37502         show: function(msgEl, f){
37503             msgEl.slideIn('t', {stopFx:true});
37504         },
37505
37506         hide : function(msgEl, f){
37507             msgEl.slideOut('t', {stopFx:true,useDisplay:true});
37508         }
37509     },
37510
37511     slideRight : {
37512         show: function(msgEl, f){
37513             msgEl.fixDisplay();
37514             msgEl.alignTo(f.el, 'tl-tr');
37515             msgEl.slideIn('l', {stopFx:true});
37516         },
37517
37518         hide : function(msgEl, f){
37519             msgEl.slideOut('l', {stopFx:true,useDisplay:true});
37520         }
37521     }
37522 };/*
37523  * Based on:
37524  * Ext JS Library 1.1.1
37525  * Copyright(c) 2006-2007, Ext JS, LLC.
37526  *
37527  * Originally Released Under LGPL - original licence link has changed is not relivant.
37528  *
37529  * Fork - LGPL
37530  * <script type="text/javascript">
37531  */
37532  
37533
37534 /**
37535  * @class Roo.form.TextField
37536  * @extends Roo.form.Field
37537  * Basic text field.  Can be used as a direct replacement for traditional text inputs, or as the base
37538  * class for more sophisticated input controls (like {@link Roo.form.TextArea} and {@link Roo.form.ComboBox}).
37539  * @constructor
37540  * Creates a new TextField
37541  * @param {Object} config Configuration options
37542  */
37543 Roo.form.TextField = function(config){
37544     Roo.form.TextField.superclass.constructor.call(this, config);
37545     this.addEvents({
37546         /**
37547          * @event autosize
37548          * Fires when the autosize function is triggered.  The field may or may not have actually changed size
37549          * according to the default logic, but this event provides a hook for the developer to apply additional
37550          * logic at runtime to resize the field if needed.
37551              * @param {Roo.form.Field} this This text field
37552              * @param {Number} width The new field width
37553              */
37554         autosize : true
37555     });
37556 };
37557
37558 Roo.extend(Roo.form.TextField, Roo.form.Field,  {
37559     /**
37560      * @cfg {Boolean} grow True if this field should automatically grow and shrink to its content
37561      */
37562     grow : false,
37563     /**
37564      * @cfg {Number} growMin The minimum width to allow when grow = true (defaults to 30)
37565      */
37566     growMin : 30,
37567     /**
37568      * @cfg {Number} growMax The maximum width to allow when grow = true (defaults to 800)
37569      */
37570     growMax : 800,
37571     /**
37572      * @cfg {String} vtype A validation type name as defined in {@link Roo.form.VTypes} (defaults to null)
37573      */
37574     vtype : null,
37575     /**
37576      * @cfg {String} maskRe An input mask regular expression that will be used to filter keystrokes that don't match (defaults to null)
37577      */
37578     maskRe : null,
37579     /**
37580      * @cfg {Boolean} disableKeyFilter True to disable input keystroke filtering (defaults to false)
37581      */
37582     disableKeyFilter : false,
37583     /**
37584      * @cfg {Boolean} allowBlank False to validate that the value length > 0 (defaults to true)
37585      */
37586     allowBlank : true,
37587     /**
37588      * @cfg {Number} minLength Minimum input field length required (defaults to 0)
37589      */
37590     minLength : 0,
37591     /**
37592      * @cfg {Number} maxLength Maximum input field length allowed (defaults to Number.MAX_VALUE)
37593      */
37594     maxLength : Number.MAX_VALUE,
37595     /**
37596      * @cfg {String} minLengthText Error text to display if the minimum length validation fails (defaults to "The minimum length for this field is {minLength}")
37597      */
37598     minLengthText : "The minimum length for this field is {0}",
37599     /**
37600      * @cfg {String} maxLengthText Error text to display if the maximum length validation fails (defaults to "The maximum length for this field is {maxLength}")
37601      */
37602     maxLengthText : "The maximum length for this field is {0}",
37603     /**
37604      * @cfg {Boolean} selectOnFocus True to automatically select any existing field text when the field receives input focus (defaults to false)
37605      */
37606     selectOnFocus : false,
37607     /**
37608      * @cfg {String} blankText Error text to display if the allow blank validation fails (defaults to "This field is required")
37609      */
37610     blankText : "This field is required",
37611     /**
37612      * @cfg {Function} validator A custom validation function to be called during field validation (defaults to null).
37613      * If available, this function will be called only after the basic validators all return true, and will be passed the
37614      * current field value and expected to return boolean true if the value is valid or a string error message if invalid.
37615      */
37616     validator : null,
37617     /**
37618      * @cfg {RegExp} regex A JavaScript RegExp object to be tested against the field value during validation (defaults to null).
37619      * If available, this regex will be evaluated only after the basic validators all return true, and will be passed the
37620      * current field value.  If the test fails, the field will be marked invalid using {@link #regexText}.
37621      */
37622     regex : null,
37623     /**
37624      * @cfg {String} regexText The error text to display if {@link #regex} is used and the test fails during validation (defaults to "")
37625      */
37626     regexText : "",
37627     /**
37628      * @cfg {String} emptyText The default text to display in an empty field - placeholder... (defaults to null).
37629      */
37630     emptyText : null,
37631    
37632
37633     // private
37634     initEvents : function()
37635     {
37636         if (this.emptyText) {
37637             this.el.attr('placeholder', this.emptyText);
37638         }
37639         
37640         Roo.form.TextField.superclass.initEvents.call(this);
37641         if(this.validationEvent == 'keyup'){
37642             this.validationTask = new Roo.util.DelayedTask(this.validate, this);
37643             this.el.on('keyup', this.filterValidation, this);
37644         }
37645         else if(this.validationEvent !== false){
37646             this.el.on(this.validationEvent, this.validate, this, {buffer: this.validationDelay});
37647         }
37648         
37649         if(this.selectOnFocus){
37650             this.on("focus", this.preFocus, this);
37651             
37652         }
37653         if(this.maskRe || (this.vtype && this.disableKeyFilter !== true && (this.maskRe = Roo.form.VTypes[this.vtype+'Mask']))){
37654             this.el.on("keypress", this.filterKeys, this);
37655         }
37656         if(this.grow){
37657             this.el.on("keyup", this.onKeyUp,  this, {buffer:50});
37658             this.el.on("click", this.autoSize,  this);
37659         }
37660         if(this.el.is('input[type=password]') && Roo.isSafari){
37661             this.el.on('keydown', this.SafariOnKeyDown, this);
37662         }
37663     },
37664
37665     processValue : function(value){
37666         if(this.stripCharsRe){
37667             var newValue = value.replace(this.stripCharsRe, '');
37668             if(newValue !== value){
37669                 this.setRawValue(newValue);
37670                 return newValue;
37671             }
37672         }
37673         return value;
37674     },
37675
37676     filterValidation : function(e){
37677         if(!e.isNavKeyPress()){
37678             this.validationTask.delay(this.validationDelay);
37679         }
37680     },
37681
37682     // private
37683     onKeyUp : function(e){
37684         if(!e.isNavKeyPress()){
37685             this.autoSize();
37686         }
37687     },
37688
37689     /**
37690      * Resets the current field value to the originally-loaded value and clears any validation messages.
37691      *  
37692      */
37693     reset : function(){
37694         Roo.form.TextField.superclass.reset.call(this);
37695        
37696     },
37697
37698     
37699     // private
37700     preFocus : function(){
37701         
37702         if(this.selectOnFocus){
37703             this.el.dom.select();
37704         }
37705     },
37706
37707     
37708     // private
37709     filterKeys : function(e){
37710         var k = e.getKey();
37711         if(!Roo.isIE && (e.isNavKeyPress() || k == e.BACKSPACE || (k == e.DELETE && e.button == -1))){
37712             return;
37713         }
37714         var c = e.getCharCode(), cc = String.fromCharCode(c);
37715         if(Roo.isIE && (e.isSpecialKey() || !cc)){
37716             return;
37717         }
37718         if(!this.maskRe.test(cc)){
37719             e.stopEvent();
37720         }
37721     },
37722
37723     setValue : function(v){
37724         
37725         Roo.form.TextField.superclass.setValue.apply(this, arguments);
37726         
37727         this.autoSize();
37728     },
37729
37730     /**
37731      * Validates a value according to the field's validation rules and marks the field as invalid
37732      * if the validation fails
37733      * @param {Mixed} value The value to validate
37734      * @return {Boolean} True if the value is valid, else false
37735      */
37736     validateValue : function(value){
37737         if(value.length < 1)  { // if it's blank
37738              if(this.allowBlank){
37739                 this.clearInvalid();
37740                 return true;
37741              }else{
37742                 this.markInvalid(this.blankText);
37743                 return false;
37744              }
37745         }
37746         if(value.length < this.minLength){
37747             this.markInvalid(String.format(this.minLengthText, this.minLength));
37748             return false;
37749         }
37750         if(value.length > this.maxLength){
37751             this.markInvalid(String.format(this.maxLengthText, this.maxLength));
37752             return false;
37753         }
37754         if(this.vtype){
37755             var vt = Roo.form.VTypes;
37756             if(!vt[this.vtype](value, this)){
37757                 this.markInvalid(this.vtypeText || vt[this.vtype +'Text']);
37758                 return false;
37759             }
37760         }
37761         if(typeof this.validator == "function"){
37762             var msg = this.validator(value);
37763             if(msg !== true){
37764                 this.markInvalid(msg);
37765                 return false;
37766             }
37767         }
37768         if(this.regex && !this.regex.test(value)){
37769             this.markInvalid(this.regexText);
37770             return false;
37771         }
37772         return true;
37773     },
37774
37775     /**
37776      * Selects text in this field
37777      * @param {Number} start (optional) The index where the selection should start (defaults to 0)
37778      * @param {Number} end (optional) The index where the selection should end (defaults to the text length)
37779      */
37780     selectText : function(start, end){
37781         var v = this.getRawValue();
37782         if(v.length > 0){
37783             start = start === undefined ? 0 : start;
37784             end = end === undefined ? v.length : end;
37785             var d = this.el.dom;
37786             if(d.setSelectionRange){
37787                 d.setSelectionRange(start, end);
37788             }else if(d.createTextRange){
37789                 var range = d.createTextRange();
37790                 range.moveStart("character", start);
37791                 range.moveEnd("character", v.length-end);
37792                 range.select();
37793             }
37794         }
37795     },
37796
37797     /**
37798      * Automatically grows the field to accomodate the width of the text up to the maximum field width allowed.
37799      * This only takes effect if grow = true, and fires the autosize event.
37800      */
37801     autoSize : function(){
37802         if(!this.grow || !this.rendered){
37803             return;
37804         }
37805         if(!this.metrics){
37806             this.metrics = Roo.util.TextMetrics.createInstance(this.el);
37807         }
37808         var el = this.el;
37809         var v = el.dom.value;
37810         var d = document.createElement('div');
37811         d.appendChild(document.createTextNode(v));
37812         v = d.innerHTML;
37813         d = null;
37814         v += "&#160;";
37815         var w = Math.min(this.growMax, Math.max(this.metrics.getWidth(v) + /* add extra padding */ 10, this.growMin));
37816         this.el.setWidth(w);
37817         this.fireEvent("autosize", this, w);
37818     },
37819     
37820     // private
37821     SafariOnKeyDown : function(event)
37822     {
37823         // this is a workaround for a password hang bug on chrome/ webkit.
37824         
37825         var isSelectAll = false;
37826         
37827         if(this.el.dom.selectionEnd > 0){
37828             isSelectAll = (this.el.dom.selectionEnd - this.el.dom.selectionStart - this.getValue().length == 0) ? true : false;
37829         }
37830         if(((event.getKey() == 8 || event.getKey() == 46) && this.getValue().length ==1)){ // backspace and delete key
37831             event.preventDefault();
37832             this.setValue('');
37833             return;
37834         }
37835         
37836         if(isSelectAll){ // backspace and delete key
37837             
37838             event.preventDefault();
37839             // this is very hacky as keydown always get's upper case.
37840             //
37841             var cc = String.fromCharCode(event.getCharCode());
37842             this.setValue( event.shiftKey ?  cc : cc.toLowerCase());
37843             
37844         }
37845         
37846         
37847     }
37848 });/*
37849  * Based on:
37850  * Ext JS Library 1.1.1
37851  * Copyright(c) 2006-2007, Ext JS, LLC.
37852  *
37853  * Originally Released Under LGPL - original licence link has changed is not relivant.
37854  *
37855  * Fork - LGPL
37856  * <script type="text/javascript">
37857  */
37858  
37859 /**
37860  * @class Roo.form.Hidden
37861  * @extends Roo.form.TextField
37862  * Simple Hidden element used on forms 
37863  * 
37864  * usage: form.add(new Roo.form.HiddenField({ 'name' : 'test1' }));
37865  * 
37866  * @constructor
37867  * Creates a new Hidden form element.
37868  * @param {Object} config Configuration options
37869  */
37870
37871
37872
37873 // easy hidden field...
37874 Roo.form.Hidden = function(config){
37875     Roo.form.Hidden.superclass.constructor.call(this, config);
37876 };
37877   
37878 Roo.extend(Roo.form.Hidden, Roo.form.TextField, {
37879     fieldLabel:      '',
37880     inputType:      'hidden',
37881     width:          50,
37882     allowBlank:     true,
37883     labelSeparator: '',
37884     hidden:         true,
37885     itemCls :       'x-form-item-display-none'
37886
37887
37888 });
37889
37890
37891 /*
37892  * Based on:
37893  * Ext JS Library 1.1.1
37894  * Copyright(c) 2006-2007, Ext JS, LLC.
37895  *
37896  * Originally Released Under LGPL - original licence link has changed is not relivant.
37897  *
37898  * Fork - LGPL
37899  * <script type="text/javascript">
37900  */
37901  
37902 /**
37903  * @class Roo.form.TriggerField
37904  * @extends Roo.form.TextField
37905  * Provides a convenient wrapper for TextFields that adds a clickable trigger button (looks like a combobox by default).
37906  * The trigger has no default action, so you must assign a function to implement the trigger click handler by
37907  * overriding {@link #onTriggerClick}. You can create a TriggerField directly, as it renders exactly like a combobox
37908  * for which you can provide a custom implementation.  For example:
37909  * <pre><code>
37910 var trigger = new Roo.form.TriggerField();
37911 trigger.onTriggerClick = myTriggerFn;
37912 trigger.applyTo('my-field');
37913 </code></pre>
37914  *
37915  * However, in general you will most likely want to use TriggerField as the base class for a reusable component.
37916  * {@link Roo.form.DateField} and {@link Roo.form.ComboBox} are perfect examples of this.
37917  * @cfg {String} triggerClass An additional CSS class used to style the trigger button.  The trigger will always get the
37918  * class 'x-form-trigger' by default and triggerClass will be <b>appended</b> if specified.
37919  * @constructor
37920  * Create a new TriggerField.
37921  * @param {Object} config Configuration options (valid {@Roo.form.TextField} config options will also be applied
37922  * to the base TextField)
37923  */
37924 Roo.form.TriggerField = function(config){
37925     this.mimicing = false;
37926     Roo.form.TriggerField.superclass.constructor.call(this, config);
37927 };
37928
37929 Roo.extend(Roo.form.TriggerField, Roo.form.TextField,  {
37930     /**
37931      * @cfg {String} triggerClass A CSS class to apply to the trigger
37932      */
37933     /**
37934      * @cfg {String/Object} autoCreate A DomHelper element spec, or true for a default element spec (defaults to
37935      * {tag: "input", type: "text", size: "16", autocomplete: "off"})
37936      */
37937     defaultAutoCreate : {tag: "input", type: "text", size: "16", autocomplete: "off"},
37938     /**
37939      * @cfg {Boolean} hideTrigger True to hide the trigger element and display only the base text field (defaults to false)
37940      */
37941     hideTrigger:false,
37942
37943     /** @cfg {Boolean} grow @hide */
37944     /** @cfg {Number} growMin @hide */
37945     /** @cfg {Number} growMax @hide */
37946
37947     /**
37948      * @hide 
37949      * @method
37950      */
37951     autoSize: Roo.emptyFn,
37952     // private
37953     monitorTab : true,
37954     // private
37955     deferHeight : true,
37956
37957     
37958     actionMode : 'wrap',
37959     // private
37960     onResize : function(w, h){
37961         Roo.form.TriggerField.superclass.onResize.apply(this, arguments);
37962         if(typeof w == 'number'){
37963             var x = w - this.trigger.getWidth();
37964             this.el.setWidth(this.adjustWidth('input', x));
37965             this.trigger.setStyle('left', x+'px');
37966         }
37967     },
37968
37969     // private
37970     adjustSize : Roo.BoxComponent.prototype.adjustSize,
37971
37972     // private
37973     getResizeEl : function(){
37974         return this.wrap;
37975     },
37976
37977     // private
37978     getPositionEl : function(){
37979         return this.wrap;
37980     },
37981
37982     // private
37983     alignErrorIcon : function(){
37984         this.errorIcon.alignTo(this.wrap, 'tl-tr', [2, 0]);
37985     },
37986
37987     // private
37988     onRender : function(ct, position){
37989         Roo.form.TriggerField.superclass.onRender.call(this, ct, position);
37990         this.wrap = this.el.wrap({cls: "x-form-field-wrap"});
37991         this.trigger = this.wrap.createChild(this.triggerConfig ||
37992                 {tag: "img", src: Roo.BLANK_IMAGE_URL, cls: "x-form-trigger " + this.triggerClass});
37993         if(this.hideTrigger){
37994             this.trigger.setDisplayed(false);
37995         }
37996         this.initTrigger();
37997         if(!this.width){
37998             this.wrap.setWidth(this.el.getWidth()+this.trigger.getWidth());
37999         }
38000     },
38001
38002     // private
38003     initTrigger : function(){
38004         this.trigger.on("click", this.onTriggerClick, this, {preventDefault:true});
38005         this.trigger.addClassOnOver('x-form-trigger-over');
38006         this.trigger.addClassOnClick('x-form-trigger-click');
38007     },
38008
38009     // private
38010     onDestroy : function(){
38011         if(this.trigger){
38012             this.trigger.removeAllListeners();
38013             this.trigger.remove();
38014         }
38015         if(this.wrap){
38016             this.wrap.remove();
38017         }
38018         Roo.form.TriggerField.superclass.onDestroy.call(this);
38019     },
38020
38021     // private
38022     onFocus : function(){
38023         Roo.form.TriggerField.superclass.onFocus.call(this);
38024         if(!this.mimicing){
38025             this.wrap.addClass('x-trigger-wrap-focus');
38026             this.mimicing = true;
38027             Roo.get(Roo.isIE ? document.body : document).on("mousedown", this.mimicBlur, this);
38028             if(this.monitorTab){
38029                 this.el.on("keydown", this.checkTab, this);
38030             }
38031         }
38032     },
38033
38034     // private
38035     checkTab : function(e){
38036         if(e.getKey() == e.TAB){
38037             this.triggerBlur();
38038         }
38039     },
38040
38041     // private
38042     onBlur : function(){
38043         // do nothing
38044     },
38045
38046     // private
38047     mimicBlur : function(e, t){
38048         if(!this.wrap.contains(t) && this.validateBlur()){
38049             this.triggerBlur();
38050         }
38051     },
38052
38053     // private
38054     triggerBlur : function(){
38055         this.mimicing = false;
38056         Roo.get(Roo.isIE ? document.body : document).un("mousedown", this.mimicBlur);
38057         if(this.monitorTab){
38058             this.el.un("keydown", this.checkTab, this);
38059         }
38060         this.wrap.removeClass('x-trigger-wrap-focus');
38061         Roo.form.TriggerField.superclass.onBlur.call(this);
38062     },
38063
38064     // private
38065     // This should be overriden by any subclass that needs to check whether or not the field can be blurred.
38066     validateBlur : function(e, t){
38067         return true;
38068     },
38069
38070     // private
38071     onDisable : function(){
38072         Roo.form.TriggerField.superclass.onDisable.call(this);
38073         if(this.wrap){
38074             this.wrap.addClass('x-item-disabled');
38075         }
38076     },
38077
38078     // private
38079     onEnable : function(){
38080         Roo.form.TriggerField.superclass.onEnable.call(this);
38081         if(this.wrap){
38082             this.wrap.removeClass('x-item-disabled');
38083         }
38084     },
38085
38086     // private
38087     onShow : function(){
38088         var ae = this.getActionEl();
38089         
38090         if(ae){
38091             ae.dom.style.display = '';
38092             ae.dom.style.visibility = 'visible';
38093         }
38094     },
38095
38096     // private
38097     
38098     onHide : function(){
38099         var ae = this.getActionEl();
38100         ae.dom.style.display = 'none';
38101     },
38102
38103     /**
38104      * The function that should handle the trigger's click event.  This method does nothing by default until overridden
38105      * by an implementing function.
38106      * @method
38107      * @param {EventObject} e
38108      */
38109     onTriggerClick : Roo.emptyFn
38110 });
38111
38112 // TwinTriggerField is not a public class to be used directly.  It is meant as an abstract base class
38113 // to be extended by an implementing class.  For an example of implementing this class, see the custom
38114 // SearchField implementation here: http://extjs.com/deploy/ext/examples/form/custom.html
38115 Roo.form.TwinTriggerField = Roo.extend(Roo.form.TriggerField, {
38116     initComponent : function(){
38117         Roo.form.TwinTriggerField.superclass.initComponent.call(this);
38118
38119         this.triggerConfig = {
38120             tag:'span', cls:'x-form-twin-triggers', cn:[
38121             {tag: "img", src: Roo.BLANK_IMAGE_URL, cls: "x-form-trigger " + this.trigger1Class},
38122             {tag: "img", src: Roo.BLANK_IMAGE_URL, cls: "x-form-trigger " + this.trigger2Class}
38123         ]};
38124     },
38125
38126     getTrigger : function(index){
38127         return this.triggers[index];
38128     },
38129
38130     initTrigger : function(){
38131         var ts = this.trigger.select('.x-form-trigger', true);
38132         this.wrap.setStyle('overflow', 'hidden');
38133         var triggerField = this;
38134         ts.each(function(t, all, index){
38135             t.hide = function(){
38136                 var w = triggerField.wrap.getWidth();
38137                 this.dom.style.display = 'none';
38138                 triggerField.el.setWidth(w-triggerField.trigger.getWidth());
38139             };
38140             t.show = function(){
38141                 var w = triggerField.wrap.getWidth();
38142                 this.dom.style.display = '';
38143                 triggerField.el.setWidth(w-triggerField.trigger.getWidth());
38144             };
38145             var triggerIndex = 'Trigger'+(index+1);
38146
38147             if(this['hide'+triggerIndex]){
38148                 t.dom.style.display = 'none';
38149             }
38150             t.on("click", this['on'+triggerIndex+'Click'], this, {preventDefault:true});
38151             t.addClassOnOver('x-form-trigger-over');
38152             t.addClassOnClick('x-form-trigger-click');
38153         }, this);
38154         this.triggers = ts.elements;
38155     },
38156
38157     onTrigger1Click : Roo.emptyFn,
38158     onTrigger2Click : Roo.emptyFn
38159 });/*
38160  * Based on:
38161  * Ext JS Library 1.1.1
38162  * Copyright(c) 2006-2007, Ext JS, LLC.
38163  *
38164  * Originally Released Under LGPL - original licence link has changed is not relivant.
38165  *
38166  * Fork - LGPL
38167  * <script type="text/javascript">
38168  */
38169  
38170 /**
38171  * @class Roo.form.TextArea
38172  * @extends Roo.form.TextField
38173  * Multiline text field.  Can be used as a direct replacement for traditional textarea fields, plus adds
38174  * support for auto-sizing.
38175  * @constructor
38176  * Creates a new TextArea
38177  * @param {Object} config Configuration options
38178  */
38179 Roo.form.TextArea = function(config){
38180     Roo.form.TextArea.superclass.constructor.call(this, config);
38181     // these are provided exchanges for backwards compat
38182     // minHeight/maxHeight were replaced by growMin/growMax to be
38183     // compatible with TextField growing config values
38184     if(this.minHeight !== undefined){
38185         this.growMin = this.minHeight;
38186     }
38187     if(this.maxHeight !== undefined){
38188         this.growMax = this.maxHeight;
38189     }
38190 };
38191
38192 Roo.extend(Roo.form.TextArea, Roo.form.TextField,  {
38193     /**
38194      * @cfg {Number} growMin The minimum height to allow when grow = true (defaults to 60)
38195      */
38196     growMin : 60,
38197     /**
38198      * @cfg {Number} growMax The maximum height to allow when grow = true (defaults to 1000)
38199      */
38200     growMax: 1000,
38201     /**
38202      * @cfg {Boolean} preventScrollbars True to prevent scrollbars from appearing regardless of how much text is
38203      * in the field (equivalent to setting overflow: hidden, defaults to false)
38204      */
38205     preventScrollbars: false,
38206     /**
38207      * @cfg {String/Object} autoCreate A DomHelper element spec, or true for a default element spec (defaults to
38208      * {tag: "textarea", style: "width:300px;height:60px;", autocomplete: "off"})
38209      */
38210
38211     // private
38212     onRender : function(ct, position){
38213         if(!this.el){
38214             this.defaultAutoCreate = {
38215                 tag: "textarea",
38216                 style:"width:300px;height:60px;",
38217                 autocomplete: "off"
38218             };
38219         }
38220         Roo.form.TextArea.superclass.onRender.call(this, ct, position);
38221         if(this.grow){
38222             this.textSizeEl = Roo.DomHelper.append(document.body, {
38223                 tag: "pre", cls: "x-form-grow-sizer"
38224             });
38225             if(this.preventScrollbars){
38226                 this.el.setStyle("overflow", "hidden");
38227             }
38228             this.el.setHeight(this.growMin);
38229         }
38230     },
38231
38232     onDestroy : function(){
38233         if(this.textSizeEl){
38234             this.textSizeEl.parentNode.removeChild(this.textSizeEl);
38235         }
38236         Roo.form.TextArea.superclass.onDestroy.call(this);
38237     },
38238
38239     // private
38240     onKeyUp : function(e){
38241         if(!e.isNavKeyPress() || e.getKey() == e.ENTER){
38242             this.autoSize();
38243         }
38244     },
38245
38246     /**
38247      * Automatically grows the field to accomodate the height of the text up to the maximum field height allowed.
38248      * This only takes effect if grow = true, and fires the autosize event if the height changes.
38249      */
38250     autoSize : function(){
38251         if(!this.grow || !this.textSizeEl){
38252             return;
38253         }
38254         var el = this.el;
38255         var v = el.dom.value;
38256         var ts = this.textSizeEl;
38257
38258         ts.innerHTML = '';
38259         ts.appendChild(document.createTextNode(v));
38260         v = ts.innerHTML;
38261
38262         Roo.fly(ts).setWidth(this.el.getWidth());
38263         if(v.length < 1){
38264             v = "&#160;&#160;";
38265         }else{
38266             if(Roo.isIE){
38267                 v = v.replace(/\n/g, '<p>&#160;</p>');
38268             }
38269             v += "&#160;\n&#160;";
38270         }
38271         ts.innerHTML = v;
38272         var h = Math.min(this.growMax, Math.max(ts.offsetHeight, this.growMin));
38273         if(h != this.lastHeight){
38274             this.lastHeight = h;
38275             this.el.setHeight(h);
38276             this.fireEvent("autosize", this, h);
38277         }
38278     }
38279 });/*
38280  * Based on:
38281  * Ext JS Library 1.1.1
38282  * Copyright(c) 2006-2007, Ext JS, LLC.
38283  *
38284  * Originally Released Under LGPL - original licence link has changed is not relivant.
38285  *
38286  * Fork - LGPL
38287  * <script type="text/javascript">
38288  */
38289  
38290
38291 /**
38292  * @class Roo.form.NumberField
38293  * @extends Roo.form.TextField
38294  * Numeric text field that provides automatic keystroke filtering and numeric validation.
38295  * @constructor
38296  * Creates a new NumberField
38297  * @param {Object} config Configuration options
38298  */
38299 Roo.form.NumberField = function(config){
38300     Roo.form.NumberField.superclass.constructor.call(this, config);
38301 };
38302
38303 Roo.extend(Roo.form.NumberField, Roo.form.TextField,  {
38304     /**
38305      * @cfg {String} fieldClass The default CSS class for the field (defaults to "x-form-field x-form-num-field")
38306      */
38307     fieldClass: "x-form-field x-form-num-field",
38308     /**
38309      * @cfg {Boolean} allowDecimals False to disallow decimal values (defaults to true)
38310      */
38311     allowDecimals : true,
38312     /**
38313      * @cfg {String} decimalSeparator Character(s) to allow as the decimal separator (defaults to '.')
38314      */
38315     decimalSeparator : ".",
38316     /**
38317      * @cfg {Number} decimalPrecision The maximum precision to display after the decimal separator (defaults to 2)
38318      */
38319     decimalPrecision : 2,
38320     /**
38321      * @cfg {Boolean} allowNegative False to prevent entering a negative sign (defaults to true)
38322      */
38323     allowNegative : true,
38324     /**
38325      * @cfg {Number} minValue The minimum allowed value (defaults to Number.NEGATIVE_INFINITY)
38326      */
38327     minValue : Number.NEGATIVE_INFINITY,
38328     /**
38329      * @cfg {Number} maxValue The maximum allowed value (defaults to Number.MAX_VALUE)
38330      */
38331     maxValue : Number.MAX_VALUE,
38332     /**
38333      * @cfg {String} minText Error text to display if the minimum value validation fails (defaults to "The minimum value for this field is {minValue}")
38334      */
38335     minText : "The minimum value for this field is {0}",
38336     /**
38337      * @cfg {String} maxText Error text to display if the maximum value validation fails (defaults to "The maximum value for this field is {maxValue}")
38338      */
38339     maxText : "The maximum value for this field is {0}",
38340     /**
38341      * @cfg {String} nanText Error text to display if the value is not a valid number.  For example, this can happen
38342      * if a valid character like '.' or '-' is left in the field with no number (defaults to "{value} is not a valid number")
38343      */
38344     nanText : "{0} is not a valid number",
38345
38346     // private
38347     initEvents : function(){
38348         Roo.form.NumberField.superclass.initEvents.call(this);
38349         var allowed = "0123456789";
38350         if(this.allowDecimals){
38351             allowed += this.decimalSeparator;
38352         }
38353         if(this.allowNegative){
38354             allowed += "-";
38355         }
38356         this.stripCharsRe = new RegExp('[^'+allowed+']', 'gi');
38357         var keyPress = function(e){
38358             var k = e.getKey();
38359             if(!Roo.isIE && (e.isSpecialKey() || k == e.BACKSPACE || k == e.DELETE)){
38360                 return;
38361             }
38362             var c = e.getCharCode();
38363             if(allowed.indexOf(String.fromCharCode(c)) === -1){
38364                 e.stopEvent();
38365             }
38366         };
38367         this.el.on("keypress", keyPress, this);
38368     },
38369
38370     // private
38371     validateValue : function(value){
38372         if(!Roo.form.NumberField.superclass.validateValue.call(this, value)){
38373             return false;
38374         }
38375         if(value.length < 1){ // if it's blank and textfield didn't flag it then it's valid
38376              return true;
38377         }
38378         var num = this.parseValue(value);
38379         if(isNaN(num)){
38380             this.markInvalid(String.format(this.nanText, value));
38381             return false;
38382         }
38383         if(num < this.minValue){
38384             this.markInvalid(String.format(this.minText, this.minValue));
38385             return false;
38386         }
38387         if(num > this.maxValue){
38388             this.markInvalid(String.format(this.maxText, this.maxValue));
38389             return false;
38390         }
38391         return true;
38392     },
38393
38394     getValue : function(){
38395         return this.fixPrecision(this.parseValue(Roo.form.NumberField.superclass.getValue.call(this)));
38396     },
38397
38398     // private
38399     parseValue : function(value){
38400         value = parseFloat(String(value).replace(this.decimalSeparator, "."));
38401         return isNaN(value) ? '' : value;
38402     },
38403
38404     // private
38405     fixPrecision : function(value){
38406         var nan = isNaN(value);
38407         if(!this.allowDecimals || this.decimalPrecision == -1 || nan || !value){
38408             return nan ? '' : value;
38409         }
38410         return parseFloat(value).toFixed(this.decimalPrecision);
38411     },
38412
38413     setValue : function(v){
38414         v = this.fixPrecision(v);
38415         Roo.form.NumberField.superclass.setValue.call(this, String(v).replace(".", this.decimalSeparator));
38416     },
38417
38418     // private
38419     decimalPrecisionFcn : function(v){
38420         return Math.floor(v);
38421     },
38422
38423     beforeBlur : function(){
38424         var v = this.parseValue(this.getRawValue());
38425         if(v){
38426             this.setValue(v);
38427         }
38428     }
38429 });/*
38430  * Based on:
38431  * Ext JS Library 1.1.1
38432  * Copyright(c) 2006-2007, Ext JS, LLC.
38433  *
38434  * Originally Released Under LGPL - original licence link has changed is not relivant.
38435  *
38436  * Fork - LGPL
38437  * <script type="text/javascript">
38438  */
38439  
38440 /**
38441  * @class Roo.form.DateField
38442  * @extends Roo.form.TriggerField
38443  * Provides a date input field with a {@link Roo.DatePicker} dropdown and automatic date validation.
38444 * @constructor
38445 * Create a new DateField
38446 * @param {Object} config
38447  */
38448 Roo.form.DateField = function(config){
38449     Roo.form.DateField.superclass.constructor.call(this, config);
38450     
38451       this.addEvents({
38452          
38453         /**
38454          * @event select
38455          * Fires when a date is selected
38456              * @param {Roo.form.DateField} combo This combo box
38457              * @param {Date} date The date selected
38458              */
38459         'select' : true
38460          
38461     });
38462     
38463     
38464     if(typeof this.minValue == "string") this.minValue = this.parseDate(this.minValue);
38465     if(typeof this.maxValue == "string") this.maxValue = this.parseDate(this.maxValue);
38466     this.ddMatch = null;
38467     if(this.disabledDates){
38468         var dd = this.disabledDates;
38469         var re = "(?:";
38470         for(var i = 0; i < dd.length; i++){
38471             re += dd[i];
38472             if(i != dd.length-1) re += "|";
38473         }
38474         this.ddMatch = new RegExp(re + ")");
38475     }
38476 };
38477
38478 Roo.extend(Roo.form.DateField, Roo.form.TriggerField,  {
38479     /**
38480      * @cfg {String} format
38481      * The default date format string which can be overriden for localization support.  The format must be
38482      * valid according to {@link Date#parseDate} (defaults to 'm/d/y').
38483      */
38484     format : "m/d/y",
38485     /**
38486      * @cfg {String} altFormats
38487      * Multiple date formats separated by "|" to try when parsing a user input value and it doesn't match the defined
38488      * format (defaults to 'm/d/Y|m-d-y|m-d-Y|m/d|m-d|d').
38489      */
38490     altFormats : "m/d/Y|m-d-y|m-d-Y|m/d|m-d|md|mdy|mdY|d",
38491     /**
38492      * @cfg {Array} disabledDays
38493      * An array of days to disable, 0 based. For example, [0, 6] disables Sunday and Saturday (defaults to null).
38494      */
38495     disabledDays : null,
38496     /**
38497      * @cfg {String} disabledDaysText
38498      * The tooltip to display when the date falls on a disabled day (defaults to 'Disabled')
38499      */
38500     disabledDaysText : "Disabled",
38501     /**
38502      * @cfg {Array} disabledDates
38503      * An array of "dates" to disable, as strings. These strings will be used to build a dynamic regular
38504      * expression so they are very powerful. Some examples:
38505      * <ul>
38506      * <li>["03/08/2003", "09/16/2003"] would disable those exact dates</li>
38507      * <li>["03/08", "09/16"] would disable those days for every year</li>
38508      * <li>["^03/08"] would only match the beginning (useful if you are using short years)</li>
38509      * <li>["03/../2006"] would disable every day in March 2006</li>
38510      * <li>["^03"] would disable every day in every March</li>
38511      * </ul>
38512      * In order to support regular expressions, if you are using a date format that has "." in it, you will have to
38513      * escape the dot when restricting dates. For example: ["03\\.08\\.03"].
38514      */
38515     disabledDates : null,
38516     /**
38517      * @cfg {String} disabledDatesText
38518      * The tooltip text to display when the date falls on a disabled date (defaults to 'Disabled')
38519      */
38520     disabledDatesText : "Disabled",
38521     /**
38522      * @cfg {Date/String} minValue
38523      * The minimum allowed date. Can be either a Javascript date object or a string date in a
38524      * valid format (defaults to null).
38525      */
38526     minValue : null,
38527     /**
38528      * @cfg {Date/String} maxValue
38529      * The maximum allowed date. Can be either a Javascript date object or a string date in a
38530      * valid format (defaults to null).
38531      */
38532     maxValue : null,
38533     /**
38534      * @cfg {String} minText
38535      * The error text to display when the date in the cell is before minValue (defaults to
38536      * 'The date in this field must be after {minValue}').
38537      */
38538     minText : "The date in this field must be equal to or after {0}",
38539     /**
38540      * @cfg {String} maxText
38541      * The error text to display when the date in the cell is after maxValue (defaults to
38542      * 'The date in this field must be before {maxValue}').
38543      */
38544     maxText : "The date in this field must be equal to or before {0}",
38545     /**
38546      * @cfg {String} invalidText
38547      * The error text to display when the date in the field is invalid (defaults to
38548      * '{value} is not a valid date - it must be in the format {format}').
38549      */
38550     invalidText : "{0} is not a valid date - it must be in the format {1}",
38551     /**
38552      * @cfg {String} triggerClass
38553      * An additional CSS class used to style the trigger button.  The trigger will always get the
38554      * class 'x-form-trigger' and triggerClass will be <b>appended</b> if specified (defaults to 'x-form-date-trigger'
38555      * which displays a calendar icon).
38556      */
38557     triggerClass : 'x-form-date-trigger',
38558     
38559
38560     /**
38561      * @cfg {Boolean} useIso
38562      * if enabled, then the date field will use a hidden field to store the 
38563      * real value as iso formated date. default (false)
38564      */ 
38565     useIso : false,
38566     /**
38567      * @cfg {String/Object} autoCreate
38568      * A DomHelper element spec, or true for a default element spec (defaults to
38569      * {tag: "input", type: "text", size: "10", autocomplete: "off"})
38570      */ 
38571     // private
38572     defaultAutoCreate : {tag: "input", type: "text", size: "10", autocomplete: "off"},
38573     
38574     // private
38575     hiddenField: false,
38576     
38577     onRender : function(ct, position)
38578     {
38579         Roo.form.DateField.superclass.onRender.call(this, ct, position);
38580         if (this.useIso) {
38581             //this.el.dom.removeAttribute('name'); 
38582             Roo.log("Changing name?");
38583             this.el.dom.setAttribute('name', this.name + '____hidden___' ); 
38584             this.hiddenField = this.el.insertSibling({ tag:'input', type:'hidden', name: this.name },
38585                     'before', true);
38586             this.hiddenField.value = this.value ? this.formatDate(this.value, 'Y-m-d') : '';
38587             // prevent input submission
38588             this.hiddenName = this.name;
38589         }
38590             
38591             
38592     },
38593     
38594     // private
38595     validateValue : function(value)
38596     {
38597         value = this.formatDate(value);
38598         if(!Roo.form.DateField.superclass.validateValue.call(this, value)){
38599             Roo.log('super failed');
38600             return false;
38601         }
38602         if(value.length < 1){ // if it's blank and textfield didn't flag it then it's valid
38603              return true;
38604         }
38605         var svalue = value;
38606         value = this.parseDate(value);
38607         if(!value){
38608             Roo.log('parse date failed' + svalue);
38609             this.markInvalid(String.format(this.invalidText, svalue, this.format));
38610             return false;
38611         }
38612         var time = value.getTime();
38613         if(this.minValue && time < this.minValue.getTime()){
38614             this.markInvalid(String.format(this.minText, this.formatDate(this.minValue)));
38615             return false;
38616         }
38617         if(this.maxValue && time > this.maxValue.getTime()){
38618             this.markInvalid(String.format(this.maxText, this.formatDate(this.maxValue)));
38619             return false;
38620         }
38621         if(this.disabledDays){
38622             var day = value.getDay();
38623             for(var i = 0; i < this.disabledDays.length; i++) {
38624                 if(day === this.disabledDays[i]){
38625                     this.markInvalid(this.disabledDaysText);
38626                     return false;
38627                 }
38628             }
38629         }
38630         var fvalue = this.formatDate(value);
38631         if(this.ddMatch && this.ddMatch.test(fvalue)){
38632             this.markInvalid(String.format(this.disabledDatesText, fvalue));
38633             return false;
38634         }
38635         return true;
38636     },
38637
38638     // private
38639     // Provides logic to override the default TriggerField.validateBlur which just returns true
38640     validateBlur : function(){
38641         return !this.menu || !this.menu.isVisible();
38642     },
38643     
38644     getName: function()
38645     {
38646         // returns hidden if it's set..
38647         if (!this.rendered) {return ''};
38648         return !this.hiddenName && this.el.dom.name  ? this.el.dom.name : (this.hiddenName || '');
38649         
38650     },
38651
38652     /**
38653      * Returns the current date value of the date field.
38654      * @return {Date} The date value
38655      */
38656     getValue : function(){
38657         
38658         return  this.hiddenField ?
38659                 this.hiddenField.value :
38660                 this.parseDate(Roo.form.DateField.superclass.getValue.call(this)) || "";
38661     },
38662
38663     /**
38664      * Sets the value of the date field.  You can pass a date object or any string that can be parsed into a valid
38665      * date, using DateField.format as the date format, according to the same rules as {@link Date#parseDate}
38666      * (the default format used is "m/d/y").
38667      * <br />Usage:
38668      * <pre><code>
38669 //All of these calls set the same date value (May 4, 2006)
38670
38671 //Pass a date object:
38672 var dt = new Date('5/4/06');
38673 dateField.setValue(dt);
38674
38675 //Pass a date string (default format):
38676 dateField.setValue('5/4/06');
38677
38678 //Pass a date string (custom format):
38679 dateField.format = 'Y-m-d';
38680 dateField.setValue('2006-5-4');
38681 </code></pre>
38682      * @param {String/Date} date The date or valid date string
38683      */
38684     setValue : function(date){
38685         if (this.hiddenField) {
38686             this.hiddenField.value = this.formatDate(this.parseDate(date), 'Y-m-d');
38687         }
38688         Roo.form.DateField.superclass.setValue.call(this, this.formatDate(this.parseDate(date)));
38689         // make sure the value field is always stored as a date..
38690         this.value = this.parseDate(date);
38691         
38692         
38693     },
38694
38695     // private
38696     parseDate : function(value){
38697         if(!value || value instanceof Date){
38698             return value;
38699         }
38700         var v = Date.parseDate(value, this.format);
38701          if (!v && this.useIso) {
38702             v = Date.parseDate(value, 'Y-m-d');
38703         }
38704         if(!v && this.altFormats){
38705             if(!this.altFormatsArray){
38706                 this.altFormatsArray = this.altFormats.split("|");
38707             }
38708             for(var i = 0, len = this.altFormatsArray.length; i < len && !v; i++){
38709                 v = Date.parseDate(value, this.altFormatsArray[i]);
38710             }
38711         }
38712         return v;
38713     },
38714
38715     // private
38716     formatDate : function(date, fmt){
38717         return (!date || !(date instanceof Date)) ?
38718                date : date.dateFormat(fmt || this.format);
38719     },
38720
38721     // private
38722     menuListeners : {
38723         select: function(m, d){
38724             
38725             this.setValue(d);
38726             this.fireEvent('select', this, d);
38727         },
38728         show : function(){ // retain focus styling
38729             this.onFocus();
38730         },
38731         hide : function(){
38732             this.focus.defer(10, this);
38733             var ml = this.menuListeners;
38734             this.menu.un("select", ml.select,  this);
38735             this.menu.un("show", ml.show,  this);
38736             this.menu.un("hide", ml.hide,  this);
38737         }
38738     },
38739
38740     // private
38741     // Implements the default empty TriggerField.onTriggerClick function to display the DatePicker
38742     onTriggerClick : function(){
38743         if(this.disabled){
38744             return;
38745         }
38746         if(this.menu == null){
38747             this.menu = new Roo.menu.DateMenu();
38748         }
38749         Roo.apply(this.menu.picker,  {
38750             showClear: this.allowBlank,
38751             minDate : this.minValue,
38752             maxDate : this.maxValue,
38753             disabledDatesRE : this.ddMatch,
38754             disabledDatesText : this.disabledDatesText,
38755             disabledDays : this.disabledDays,
38756             disabledDaysText : this.disabledDaysText,
38757             format : this.useIso ? 'Y-m-d' : this.format,
38758             minText : String.format(this.minText, this.formatDate(this.minValue)),
38759             maxText : String.format(this.maxText, this.formatDate(this.maxValue))
38760         });
38761         this.menu.on(Roo.apply({}, this.menuListeners, {
38762             scope:this
38763         }));
38764         this.menu.picker.setValue(this.getValue() || new Date());
38765         this.menu.show(this.el, "tl-bl?");
38766     },
38767
38768     beforeBlur : function(){
38769         var v = this.parseDate(this.getRawValue());
38770         if(v){
38771             this.setValue(v);
38772         }
38773     },
38774
38775     /*@
38776      * overide
38777      * 
38778      */
38779     isDirty : function() {
38780         if(this.disabled) {
38781             return false;
38782         }
38783         
38784         if(typeof(this.startValue) === 'undefined'){
38785             return false;
38786         }
38787         
38788         return String(this.getValue()) !== String(this.startValue);
38789         
38790     }
38791 });/*
38792  * Based on:
38793  * Ext JS Library 1.1.1
38794  * Copyright(c) 2006-2007, Ext JS, LLC.
38795  *
38796  * Originally Released Under LGPL - original licence link has changed is not relivant.
38797  *
38798  * Fork - LGPL
38799  * <script type="text/javascript">
38800  */
38801  
38802 /**
38803  * @class Roo.form.MonthField
38804  * @extends Roo.form.TriggerField
38805  * Provides a date input field with a {@link Roo.DatePicker} dropdown and automatic date validation.
38806 * @constructor
38807 * Create a new MonthField
38808 * @param {Object} config
38809  */
38810 Roo.form.MonthField = function(config){
38811     
38812     Roo.form.MonthField.superclass.constructor.call(this, config);
38813     
38814       this.addEvents({
38815          
38816         /**
38817          * @event select
38818          * Fires when a date is selected
38819              * @param {Roo.form.MonthFieeld} combo This combo box
38820              * @param {Date} date The date selected
38821              */
38822         'select' : true
38823          
38824     });
38825     
38826     
38827     if(typeof this.minValue == "string") this.minValue = this.parseDate(this.minValue);
38828     if(typeof this.maxValue == "string") this.maxValue = this.parseDate(this.maxValue);
38829     this.ddMatch = null;
38830     if(this.disabledDates){
38831         var dd = this.disabledDates;
38832         var re = "(?:";
38833         for(var i = 0; i < dd.length; i++){
38834             re += dd[i];
38835             if(i != dd.length-1) re += "|";
38836         }
38837         this.ddMatch = new RegExp(re + ")");
38838     }
38839 };
38840
38841 Roo.extend(Roo.form.MonthField, Roo.form.TriggerField,  {
38842     /**
38843      * @cfg {String} format
38844      * The default date format string which can be overriden for localization support.  The format must be
38845      * valid according to {@link Date#parseDate} (defaults to 'm/d/y').
38846      */
38847     format : "M Y",
38848     /**
38849      * @cfg {String} altFormats
38850      * Multiple date formats separated by "|" to try when parsing a user input value and it doesn't match the defined
38851      * format (defaults to 'm/d/Y|m-d-y|m-d-Y|m/d|m-d|d').
38852      */
38853     altFormats : "M Y|m/Y|m-y|m-Y|my|mY",
38854     /**
38855      * @cfg {Array} disabledDays
38856      * An array of days to disable, 0 based. For example, [0, 6] disables Sunday and Saturday (defaults to null).
38857      */
38858     disabledDays : [0,1,2,3,4,5,6],
38859     /**
38860      * @cfg {String} disabledDaysText
38861      * The tooltip to display when the date falls on a disabled day (defaults to 'Disabled')
38862      */
38863     disabledDaysText : "Disabled",
38864     /**
38865      * @cfg {Array} disabledDates
38866      * An array of "dates" to disable, as strings. These strings will be used to build a dynamic regular
38867      * expression so they are very powerful. Some examples:
38868      * <ul>
38869      * <li>["03/08/2003", "09/16/2003"] would disable those exact dates</li>
38870      * <li>["03/08", "09/16"] would disable those days for every year</li>
38871      * <li>["^03/08"] would only match the beginning (useful if you are using short years)</li>
38872      * <li>["03/../2006"] would disable every day in March 2006</li>
38873      * <li>["^03"] would disable every day in every March</li>
38874      * </ul>
38875      * In order to support regular expressions, if you are using a date format that has "." in it, you will have to
38876      * escape the dot when restricting dates. For example: ["03\\.08\\.03"].
38877      */
38878     disabledDates : null,
38879     /**
38880      * @cfg {String} disabledDatesText
38881      * The tooltip text to display when the date falls on a disabled date (defaults to 'Disabled')
38882      */
38883     disabledDatesText : "Disabled",
38884     /**
38885      * @cfg {Date/String} minValue
38886      * The minimum allowed date. Can be either a Javascript date object or a string date in a
38887      * valid format (defaults to null).
38888      */
38889     minValue : null,
38890     /**
38891      * @cfg {Date/String} maxValue
38892      * The maximum allowed date. Can be either a Javascript date object or a string date in a
38893      * valid format (defaults to null).
38894      */
38895     maxValue : null,
38896     /**
38897      * @cfg {String} minText
38898      * The error text to display when the date in the cell is before minValue (defaults to
38899      * 'The date in this field must be after {minValue}').
38900      */
38901     minText : "The date in this field must be equal to or after {0}",
38902     /**
38903      * @cfg {String} maxTextf
38904      * The error text to display when the date in the cell is after maxValue (defaults to
38905      * 'The date in this field must be before {maxValue}').
38906      */
38907     maxText : "The date in this field must be equal to or before {0}",
38908     /**
38909      * @cfg {String} invalidText
38910      * The error text to display when the date in the field is invalid (defaults to
38911      * '{value} is not a valid date - it must be in the format {format}').
38912      */
38913     invalidText : "{0} is not a valid date - it must be in the format {1}",
38914     /**
38915      * @cfg {String} triggerClass
38916      * An additional CSS class used to style the trigger button.  The trigger will always get the
38917      * class 'x-form-trigger' and triggerClass will be <b>appended</b> if specified (defaults to 'x-form-date-trigger'
38918      * which displays a calendar icon).
38919      */
38920     triggerClass : 'x-form-date-trigger',
38921     
38922
38923     /**
38924      * @cfg {Boolean} useIso
38925      * if enabled, then the date field will use a hidden field to store the 
38926      * real value as iso formated date. default (true)
38927      */ 
38928     useIso : true,
38929     /**
38930      * @cfg {String/Object} autoCreate
38931      * A DomHelper element spec, or true for a default element spec (defaults to
38932      * {tag: "input", type: "text", size: "10", autocomplete: "off"})
38933      */ 
38934     // private
38935     defaultAutoCreate : {tag: "input", type: "text", size: "10", autocomplete: "off"},
38936     
38937     // private
38938     hiddenField: false,
38939     
38940     hideMonthPicker : false,
38941     
38942     onRender : function(ct, position)
38943     {
38944         Roo.form.MonthField.superclass.onRender.call(this, ct, position);
38945         if (this.useIso) {
38946             this.el.dom.removeAttribute('name'); 
38947             this.hiddenField = this.el.insertSibling({ tag:'input', type:'hidden', name: this.name },
38948                     'before', true);
38949             this.hiddenField.value = this.value ? this.formatDate(this.value, 'Y-m-d') : '';
38950             // prevent input submission
38951             this.hiddenName = this.name;
38952         }
38953             
38954             
38955     },
38956     
38957     // private
38958     validateValue : function(value)
38959     {
38960         value = this.formatDate(value);
38961         if(!Roo.form.MonthField.superclass.validateValue.call(this, value)){
38962             return false;
38963         }
38964         if(value.length < 1){ // if it's blank and textfield didn't flag it then it's valid
38965              return true;
38966         }
38967         var svalue = value;
38968         value = this.parseDate(value);
38969         if(!value){
38970             this.markInvalid(String.format(this.invalidText, svalue, this.format));
38971             return false;
38972         }
38973         var time = value.getTime();
38974         if(this.minValue && time < this.minValue.getTime()){
38975             this.markInvalid(String.format(this.minText, this.formatDate(this.minValue)));
38976             return false;
38977         }
38978         if(this.maxValue && time > this.maxValue.getTime()){
38979             this.markInvalid(String.format(this.maxText, this.formatDate(this.maxValue)));
38980             return false;
38981         }
38982         /*if(this.disabledDays){
38983             var day = value.getDay();
38984             for(var i = 0; i < this.disabledDays.length; i++) {
38985                 if(day === this.disabledDays[i]){
38986                     this.markInvalid(this.disabledDaysText);
38987                     return false;
38988                 }
38989             }
38990         }
38991         */
38992         var fvalue = this.formatDate(value);
38993         /*if(this.ddMatch && this.ddMatch.test(fvalue)){
38994             this.markInvalid(String.format(this.disabledDatesText, fvalue));
38995             return false;
38996         }
38997         */
38998         return true;
38999     },
39000
39001     // private
39002     // Provides logic to override the default TriggerField.validateBlur which just returns true
39003     validateBlur : function(){
39004         return !this.menu || !this.menu.isVisible();
39005     },
39006
39007     /**
39008      * Returns the current date value of the date field.
39009      * @return {Date} The date value
39010      */
39011     getValue : function(){
39012         
39013         
39014         
39015         return  this.hiddenField ?
39016                 this.hiddenField.value :
39017                 this.parseDate(Roo.form.MonthField.superclass.getValue.call(this)) || "";
39018     },
39019
39020     /**
39021      * Sets the value of the date field.  You can pass a date object or any string that can be parsed into a valid
39022      * date, using MonthField.format as the date format, according to the same rules as {@link Date#parseDate}
39023      * (the default format used is "m/d/y").
39024      * <br />Usage:
39025      * <pre><code>
39026 //All of these calls set the same date value (May 4, 2006)
39027
39028 //Pass a date object:
39029 var dt = new Date('5/4/06');
39030 monthField.setValue(dt);
39031
39032 //Pass a date string (default format):
39033 monthField.setValue('5/4/06');
39034
39035 //Pass a date string (custom format):
39036 monthField.format = 'Y-m-d';
39037 monthField.setValue('2006-5-4');
39038 </code></pre>
39039      * @param {String/Date} date The date or valid date string
39040      */
39041     setValue : function(date){
39042         Roo.log('month setValue' + date);
39043         // can only be first of month..
39044         
39045         var val = this.parseDate(date);
39046         
39047         if (this.hiddenField) {
39048             this.hiddenField.value = this.formatDate(this.parseDate(date), 'Y-m-d');
39049         }
39050         Roo.form.MonthField.superclass.setValue.call(this, this.formatDate(this.parseDate(date)));
39051         this.value = this.parseDate(date);
39052     },
39053
39054     // private
39055     parseDate : function(value){
39056         if(!value || value instanceof Date){
39057             value = value ? Date.parseDate(value.format('Y-m') + '-01', 'Y-m-d') : null;
39058             return value;
39059         }
39060         var v = Date.parseDate(value, this.format);
39061         if (!v && this.useIso) {
39062             v = Date.parseDate(value, 'Y-m-d');
39063         }
39064         if (v) {
39065             // 
39066             v = Date.parseDate(v.format('Y-m') +'-01', 'Y-m-d');
39067         }
39068         
39069         
39070         if(!v && this.altFormats){
39071             if(!this.altFormatsArray){
39072                 this.altFormatsArray = this.altFormats.split("|");
39073             }
39074             for(var i = 0, len = this.altFormatsArray.length; i < len && !v; i++){
39075                 v = Date.parseDate(value, this.altFormatsArray[i]);
39076             }
39077         }
39078         return v;
39079     },
39080
39081     // private
39082     formatDate : function(date, fmt){
39083         return (!date || !(date instanceof Date)) ?
39084                date : date.dateFormat(fmt || this.format);
39085     },
39086
39087     // private
39088     menuListeners : {
39089         select: function(m, d){
39090             this.setValue(d);
39091             this.fireEvent('select', this, d);
39092         },
39093         show : function(){ // retain focus styling
39094             this.onFocus();
39095         },
39096         hide : function(){
39097             this.focus.defer(10, this);
39098             var ml = this.menuListeners;
39099             this.menu.un("select", ml.select,  this);
39100             this.menu.un("show", ml.show,  this);
39101             this.menu.un("hide", ml.hide,  this);
39102         }
39103     },
39104     // private
39105     // Implements the default empty TriggerField.onTriggerClick function to display the DatePicker
39106     onTriggerClick : function(){
39107         if(this.disabled){
39108             return;
39109         }
39110         if(this.menu == null){
39111             this.menu = new Roo.menu.DateMenu();
39112            
39113         }
39114         
39115         Roo.apply(this.menu.picker,  {
39116             
39117             showClear: this.allowBlank,
39118             minDate : this.minValue,
39119             maxDate : this.maxValue,
39120             disabledDatesRE : this.ddMatch,
39121             disabledDatesText : this.disabledDatesText,
39122             
39123             format : this.useIso ? 'Y-m-d' : this.format,
39124             minText : String.format(this.minText, this.formatDate(this.minValue)),
39125             maxText : String.format(this.maxText, this.formatDate(this.maxValue))
39126             
39127         });
39128          this.menu.on(Roo.apply({}, this.menuListeners, {
39129             scope:this
39130         }));
39131        
39132         
39133         var m = this.menu;
39134         var p = m.picker;
39135         
39136         // hide month picker get's called when we called by 'before hide';
39137         
39138         var ignorehide = true;
39139         p.hideMonthPicker  = function(disableAnim){
39140             if (ignorehide) {
39141                 return;
39142             }
39143              if(this.monthPicker){
39144                 Roo.log("hideMonthPicker called");
39145                 if(disableAnim === true){
39146                     this.monthPicker.hide();
39147                 }else{
39148                     this.monthPicker.slideOut('t', {duration:.2});
39149                     p.setValue(new Date(m.picker.mpSelYear, m.picker.mpSelMonth, 1));
39150                     p.fireEvent("select", this, this.value);
39151                     m.hide();
39152                 }
39153             }
39154         }
39155         
39156         Roo.log('picker set value');
39157         Roo.log(this.getValue());
39158         p.setValue(this.getValue() ? this.parseDate(this.getValue()) : new Date());
39159         m.show(this.el, 'tl-bl?');
39160         ignorehide  = false;
39161         // this will trigger hideMonthPicker..
39162         
39163         
39164         // hidden the day picker
39165         Roo.select('.x-date-picker table', true).first().dom.style.visibility = "hidden";
39166         
39167         
39168         
39169       
39170         
39171         p.showMonthPicker.defer(100, p);
39172     
39173         
39174        
39175     },
39176
39177     beforeBlur : function(){
39178         var v = this.parseDate(this.getRawValue());
39179         if(v){
39180             this.setValue(v);
39181         }
39182     }
39183
39184     /** @cfg {Boolean} grow @hide */
39185     /** @cfg {Number} growMin @hide */
39186     /** @cfg {Number} growMax @hide */
39187     /**
39188      * @hide
39189      * @method autoSize
39190      */
39191 });/*
39192  * Based on:
39193  * Ext JS Library 1.1.1
39194  * Copyright(c) 2006-2007, Ext JS, LLC.
39195  *
39196  * Originally Released Under LGPL - original licence link has changed is not relivant.
39197  *
39198  * Fork - LGPL
39199  * <script type="text/javascript">
39200  */
39201  
39202
39203 /**
39204  * @class Roo.form.ComboBox
39205  * @extends Roo.form.TriggerField
39206  * A combobox control with support for autocomplete, remote-loading, paging and many other features.
39207  * @constructor
39208  * Create a new ComboBox.
39209  * @param {Object} config Configuration options
39210  */
39211 Roo.form.ComboBox = function(config){
39212     Roo.form.ComboBox.superclass.constructor.call(this, config);
39213     this.addEvents({
39214         /**
39215          * @event expand
39216          * Fires when the dropdown list is expanded
39217              * @param {Roo.form.ComboBox} combo This combo box
39218              */
39219         'expand' : true,
39220         /**
39221          * @event collapse
39222          * Fires when the dropdown list is collapsed
39223              * @param {Roo.form.ComboBox} combo This combo box
39224              */
39225         'collapse' : true,
39226         /**
39227          * @event beforeselect
39228          * Fires before a list item is selected. Return false to cancel the selection.
39229              * @param {Roo.form.ComboBox} combo This combo box
39230              * @param {Roo.data.Record} record The data record returned from the underlying store
39231              * @param {Number} index The index of the selected item in the dropdown list
39232              */
39233         'beforeselect' : true,
39234         /**
39235          * @event select
39236          * Fires when a list item is selected
39237              * @param {Roo.form.ComboBox} combo This combo box
39238              * @param {Roo.data.Record} record The data record returned from the underlying store (or false on clear)
39239              * @param {Number} index The index of the selected item in the dropdown list
39240              */
39241         'select' : true,
39242         /**
39243          * @event beforequery
39244          * Fires before all queries are processed. Return false to cancel the query or set cancel to true.
39245          * The event object passed has these properties:
39246              * @param {Roo.form.ComboBox} combo This combo box
39247              * @param {String} query The query
39248              * @param {Boolean} forceAll true to force "all" query
39249              * @param {Boolean} cancel true to cancel the query
39250              * @param {Object} e The query event object
39251              */
39252         'beforequery': true,
39253          /**
39254          * @event add
39255          * Fires when the 'add' icon is pressed (add a listener to enable add button)
39256              * @param {Roo.form.ComboBox} combo This combo box
39257              */
39258         'add' : true,
39259         /**
39260          * @event edit
39261          * Fires when the 'edit' icon is pressed (add a listener to enable add button)
39262              * @param {Roo.form.ComboBox} combo This combo box
39263              * @param {Roo.data.Record|false} record The data record returned from the underlying store (or false on nothing selected)
39264              */
39265         'edit' : true
39266         
39267         
39268     });
39269     if(this.transform){
39270         this.allowDomMove = false;
39271         var s = Roo.getDom(this.transform);
39272         if(!this.hiddenName){
39273             this.hiddenName = s.name;
39274         }
39275         if(!this.store){
39276             this.mode = 'local';
39277             var d = [], opts = s.options;
39278             for(var i = 0, len = opts.length;i < len; i++){
39279                 var o = opts[i];
39280                 var value = (Roo.isIE ? o.getAttributeNode('value').specified : o.hasAttribute('value')) ? o.value : o.text;
39281                 if(o.selected) {
39282                     this.value = value;
39283                 }
39284                 d.push([value, o.text]);
39285             }
39286             this.store = new Roo.data.SimpleStore({
39287                 'id': 0,
39288                 fields: ['value', 'text'],
39289                 data : d
39290             });
39291             this.valueField = 'value';
39292             this.displayField = 'text';
39293         }
39294         s.name = Roo.id(); // wipe out the name in case somewhere else they have a reference
39295         if(!this.lazyRender){
39296             this.target = true;
39297             this.el = Roo.DomHelper.insertBefore(s, this.autoCreate || this.defaultAutoCreate);
39298             s.parentNode.removeChild(s); // remove it
39299             this.render(this.el.parentNode);
39300         }else{
39301             s.parentNode.removeChild(s); // remove it
39302         }
39303
39304     }
39305     if (this.store) {
39306         this.store = Roo.factory(this.store, Roo.data);
39307     }
39308     
39309     this.selectedIndex = -1;
39310     if(this.mode == 'local'){
39311         if(config.queryDelay === undefined){
39312             this.queryDelay = 10;
39313         }
39314         if(config.minChars === undefined){
39315             this.minChars = 0;
39316         }
39317     }
39318 };
39319
39320 Roo.extend(Roo.form.ComboBox, Roo.form.TriggerField, {
39321     /**
39322      * @cfg {String/HTMLElement/Element} transform The id, DOM node or element of an existing select to convert to a ComboBox
39323      */
39324     /**
39325      * @cfg {Boolean} lazyRender True to prevent the ComboBox from rendering until requested (should always be used when
39326      * rendering into an Roo.Editor, defaults to false)
39327      */
39328     /**
39329      * @cfg {Boolean/Object} autoCreate A DomHelper element spec, or true for a default element spec (defaults to:
39330      * {tag: "input", type: "text", size: "24", autocomplete: "off"})
39331      */
39332     /**
39333      * @cfg {Roo.data.Store} store The data store to which this combo is bound (defaults to undefined)
39334      */
39335     /**
39336      * @cfg {String} title If supplied, a header element is created containing this text and added into the top of
39337      * the dropdown list (defaults to undefined, with no header element)
39338      */
39339
39340      /**
39341      * @cfg {String/Roo.Template} tpl The template to use to render the output
39342      */
39343      
39344     // private
39345     defaultAutoCreate : {tag: "input", type: "text", size: "24", autocomplete: "off"},
39346     /**
39347      * @cfg {Number} listWidth The width in pixels of the dropdown list (defaults to the width of the ComboBox field)
39348      */
39349     listWidth: undefined,
39350     /**
39351      * @cfg {String} displayField The underlying data field name to bind to this CombBox (defaults to undefined if
39352      * mode = 'remote' or 'text' if mode = 'local')
39353      */
39354     displayField: undefined,
39355     /**
39356      * @cfg {String} valueField The underlying data value name to bind to this CombBox (defaults to undefined if
39357      * mode = 'remote' or 'value' if mode = 'local'). 
39358      * Note: use of a valueField requires the user make a selection
39359      * in order for a value to be mapped.
39360      */
39361     valueField: undefined,
39362     
39363     
39364     /**
39365      * @cfg {String} hiddenName If specified, a hidden form field with this name is dynamically generated to store the
39366      * field's data value (defaults to the underlying DOM element's name)
39367      */
39368     hiddenName: undefined,
39369     /**
39370      * @cfg {String} listClass CSS class to apply to the dropdown list element (defaults to '')
39371      */
39372     listClass: '',
39373     /**
39374      * @cfg {String} selectedClass CSS class to apply to the selected item in the dropdown list (defaults to 'x-combo-selected')
39375      */
39376     selectedClass: 'x-combo-selected',
39377     /**
39378      * @cfg {String} triggerClass An additional CSS class used to style the trigger button.  The trigger will always get the
39379      * class 'x-form-trigger' and triggerClass will be <b>appended</b> if specified (defaults to 'x-form-arrow-trigger'
39380      * which displays a downward arrow icon).
39381      */
39382     triggerClass : 'x-form-arrow-trigger',
39383     /**
39384      * @cfg {Boolean/String} shadow True or "sides" for the default effect, "frame" for 4-way shadow, and "drop" for bottom-right
39385      */
39386     shadow:'sides',
39387     /**
39388      * @cfg {String} listAlign A valid anchor position value. See {@link Roo.Element#alignTo} for details on supported
39389      * anchor positions (defaults to 'tl-bl')
39390      */
39391     listAlign: 'tl-bl?',
39392     /**
39393      * @cfg {Number} maxHeight The maximum height in pixels of the dropdown list before scrollbars are shown (defaults to 300)
39394      */
39395     maxHeight: 300,
39396     /**
39397      * @cfg {String} triggerAction The action to execute when the trigger field is activated.  Use 'all' to run the
39398      * query specified by the allQuery config option (defaults to 'query')
39399      */
39400     triggerAction: 'query',
39401     /**
39402      * @cfg {Number} minChars The minimum number of characters the user must type before autocomplete and typeahead activate
39403      * (defaults to 4, does not apply if editable = false)
39404      */
39405     minChars : 4,
39406     /**
39407      * @cfg {Boolean} typeAhead True to populate and autoselect the remainder of the text being typed after a configurable
39408      * delay (typeAheadDelay) if it matches a known value (defaults to false)
39409      */
39410     typeAhead: false,
39411     /**
39412      * @cfg {Number} queryDelay The length of time in milliseconds to delay between the start of typing and sending the
39413      * query to filter the dropdown list (defaults to 500 if mode = 'remote' or 10 if mode = 'local')
39414      */
39415     queryDelay: 500,
39416     /**
39417      * @cfg {Number} pageSize If greater than 0, a paging toolbar is displayed in the footer of the dropdown list and the
39418      * filter queries will execute with page start and limit parameters.  Only applies when mode = 'remote' (defaults to 0)
39419      */
39420     pageSize: 0,
39421     /**
39422      * @cfg {Boolean} selectOnFocus True to select any existing text in the field immediately on focus.  Only applies
39423      * when editable = true (defaults to false)
39424      */
39425     selectOnFocus:false,
39426     /**
39427      * @cfg {String} queryParam Name of the query as it will be passed on the querystring (defaults to 'query')
39428      */
39429     queryParam: 'query',
39430     /**
39431      * @cfg {String} loadingText The text to display in the dropdown list while data is loading.  Only applies
39432      * when mode = 'remote' (defaults to 'Loading...')
39433      */
39434     loadingText: 'Loading...',
39435     /**
39436      * @cfg {Boolean} resizable True to add a resize handle to the bottom of the dropdown list (defaults to false)
39437      */
39438     resizable: false,
39439     /**
39440      * @cfg {Number} handleHeight The height in pixels of the dropdown list resize handle if resizable = true (defaults to 8)
39441      */
39442     handleHeight : 8,
39443     /**
39444      * @cfg {Boolean} editable False to prevent the user from typing text directly into the field, just like a
39445      * traditional select (defaults to true)
39446      */
39447     editable: true,
39448     /**
39449      * @cfg {String} allQuery The text query to send to the server to return all records for the list with no filtering (defaults to '')
39450      */
39451     allQuery: '',
39452     /**
39453      * @cfg {String} mode Set to 'local' if the ComboBox loads local data (defaults to 'remote' which loads from the server)
39454      */
39455     mode: 'remote',
39456     /**
39457      * @cfg {Number} minListWidth The minimum width of the dropdown list in pixels (defaults to 70, will be ignored if
39458      * listWidth has a higher value)
39459      */
39460     minListWidth : 70,
39461     /**
39462      * @cfg {Boolean} forceSelection True to restrict the selected value to one of the values in the list, false to
39463      * allow the user to set arbitrary text into the field (defaults to false)
39464      */
39465     forceSelection:false,
39466     /**
39467      * @cfg {Number} typeAheadDelay The length of time in milliseconds to wait until the typeahead text is displayed
39468      * if typeAhead = true (defaults to 250)
39469      */
39470     typeAheadDelay : 250,
39471     /**
39472      * @cfg {String} valueNotFoundText When using a name/value combo, if the value passed to setValue is not found in
39473      * the store, valueNotFoundText will be displayed as the field text if defined (defaults to undefined)
39474      */
39475     valueNotFoundText : undefined,
39476     /**
39477      * @cfg {Boolean} blockFocus Prevents all focus calls, so it can work with things like HTML edtor bar
39478      */
39479     blockFocus : false,
39480     
39481     /**
39482      * @cfg {Boolean} disableClear Disable showing of clear button.
39483      */
39484     disableClear : false,
39485     /**
39486      * @cfg {Boolean} alwaysQuery  Disable caching of results, and always send query
39487      */
39488     alwaysQuery : false,
39489     
39490     //private
39491     addicon : false,
39492     editicon: false,
39493     
39494     // element that contains real text value.. (when hidden is used..)
39495      
39496     // private
39497     onRender : function(ct, position){
39498         Roo.form.ComboBox.superclass.onRender.call(this, ct, position);
39499         if(this.hiddenName){
39500             this.hiddenField = this.el.insertSibling({tag:'input', type:'hidden', name: this.hiddenName, id:  (this.hiddenId||this.hiddenName)},
39501                     'before', true);
39502             this.hiddenField.value =
39503                 this.hiddenValue !== undefined ? this.hiddenValue :
39504                 this.value !== undefined ? this.value : '';
39505
39506             // prevent input submission
39507             this.el.dom.removeAttribute('name');
39508              
39509              
39510         }
39511         if(Roo.isGecko){
39512             this.el.dom.setAttribute('autocomplete', 'off');
39513         }
39514
39515         var cls = 'x-combo-list';
39516
39517         this.list = new Roo.Layer({
39518             shadow: this.shadow, cls: [cls, this.listClass].join(' '), constrain:false
39519         });
39520
39521         var lw = this.listWidth || Math.max(this.wrap.getWidth(), this.minListWidth);
39522         this.list.setWidth(lw);
39523         this.list.swallowEvent('mousewheel');
39524         this.assetHeight = 0;
39525
39526         if(this.title){
39527             this.header = this.list.createChild({cls:cls+'-hd', html: this.title});
39528             this.assetHeight += this.header.getHeight();
39529         }
39530
39531         this.innerList = this.list.createChild({cls:cls+'-inner'});
39532         this.innerList.on('mouseover', this.onViewOver, this);
39533         this.innerList.on('mousemove', this.onViewMove, this);
39534         this.innerList.setWidth(lw - this.list.getFrameWidth('lr'));
39535         
39536         if(this.allowBlank && !this.pageSize && !this.disableClear){
39537             this.footer = this.list.createChild({cls:cls+'-ft'});
39538             this.pageTb = new Roo.Toolbar(this.footer);
39539            
39540         }
39541         if(this.pageSize){
39542             this.footer = this.list.createChild({cls:cls+'-ft'});
39543             this.pageTb = new Roo.PagingToolbar(this.footer, this.store,
39544                     {pageSize: this.pageSize});
39545             
39546         }
39547         
39548         if (this.pageTb && this.allowBlank && !this.disableClear) {
39549             var _this = this;
39550             this.pageTb.add(new Roo.Toolbar.Fill(), {
39551                 cls: 'x-btn-icon x-btn-clear',
39552                 text: '&#160;',
39553                 handler: function()
39554                 {
39555                     _this.collapse();
39556                     _this.clearValue();
39557                     _this.onSelect(false, -1);
39558                 }
39559             });
39560         }
39561         if (this.footer) {
39562             this.assetHeight += this.footer.getHeight();
39563         }
39564         
39565
39566         if(!this.tpl){
39567             this.tpl = '<div class="'+cls+'-item">{' + this.displayField + '}</div>';
39568         }
39569
39570         this.view = new Roo.View(this.innerList, this.tpl, {
39571             singleSelect:true, store: this.store, selectedClass: this.selectedClass
39572         });
39573
39574         this.view.on('click', this.onViewClick, this);
39575
39576         this.store.on('beforeload', this.onBeforeLoad, this);
39577         this.store.on('load', this.onLoad, this);
39578         this.store.on('loadexception', this.onLoadException, this);
39579
39580         if(this.resizable){
39581             this.resizer = new Roo.Resizable(this.list,  {
39582                pinned:true, handles:'se'
39583             });
39584             this.resizer.on('resize', function(r, w, h){
39585                 this.maxHeight = h-this.handleHeight-this.list.getFrameWidth('tb')-this.assetHeight;
39586                 this.listWidth = w;
39587                 this.innerList.setWidth(w - this.list.getFrameWidth('lr'));
39588                 this.restrictHeight();
39589             }, this);
39590             this[this.pageSize?'footer':'innerList'].setStyle('margin-bottom', this.handleHeight+'px');
39591         }
39592         if(!this.editable){
39593             this.editable = true;
39594             this.setEditable(false);
39595         }  
39596         
39597         
39598         if (typeof(this.events.add.listeners) != 'undefined') {
39599             
39600             this.addicon = this.wrap.createChild(
39601                 {tag: 'img', src: Roo.BLANK_IMAGE_URL, cls: 'x-form-combo-add' });  
39602        
39603             this.addicon.on('click', function(e) {
39604                 this.fireEvent('add', this);
39605             }, this);
39606         }
39607         if (typeof(this.events.edit.listeners) != 'undefined') {
39608             
39609             this.editicon = this.wrap.createChild(
39610                 {tag: 'img', src: Roo.BLANK_IMAGE_URL, cls: 'x-form-combo-edit' });  
39611             if (this.addicon) {
39612                 this.editicon.setStyle('margin-left', '40px');
39613             }
39614             this.editicon.on('click', function(e) {
39615                 
39616                 // we fire even  if inothing is selected..
39617                 this.fireEvent('edit', this, this.lastData );
39618                 
39619             }, this);
39620         }
39621         
39622         
39623         
39624     },
39625
39626     // private
39627     initEvents : function(){
39628         Roo.form.ComboBox.superclass.initEvents.call(this);
39629
39630         this.keyNav = new Roo.KeyNav(this.el, {
39631             "up" : function(e){
39632                 this.inKeyMode = true;
39633                 this.selectPrev();
39634             },
39635
39636             "down" : function(e){
39637                 if(!this.isExpanded()){
39638                     this.onTriggerClick();
39639                 }else{
39640                     this.inKeyMode = true;
39641                     this.selectNext();
39642                 }
39643             },
39644
39645             "enter" : function(e){
39646                 this.onViewClick();
39647                 //return true;
39648             },
39649
39650             "esc" : function(e){
39651                 this.collapse();
39652             },
39653
39654             "tab" : function(e){
39655                 this.onViewClick(false);
39656                 this.fireEvent("specialkey", this, e);
39657                 return true;
39658             },
39659
39660             scope : this,
39661
39662             doRelay : function(foo, bar, hname){
39663                 if(hname == 'down' || this.scope.isExpanded()){
39664                    return Roo.KeyNav.prototype.doRelay.apply(this, arguments);
39665                 }
39666                 return true;
39667             },
39668
39669             forceKeyDown: true
39670         });
39671         this.queryDelay = Math.max(this.queryDelay || 10,
39672                 this.mode == 'local' ? 10 : 250);
39673         this.dqTask = new Roo.util.DelayedTask(this.initQuery, this);
39674         if(this.typeAhead){
39675             this.taTask = new Roo.util.DelayedTask(this.onTypeAhead, this);
39676         }
39677         if(this.editable !== false){
39678             this.el.on("keyup", this.onKeyUp, this);
39679         }
39680         if(this.forceSelection){
39681             this.on('blur', this.doForce, this);
39682         }
39683     },
39684
39685     onDestroy : function(){
39686         if(this.view){
39687             this.view.setStore(null);
39688             this.view.el.removeAllListeners();
39689             this.view.el.remove();
39690             this.view.purgeListeners();
39691         }
39692         if(this.list){
39693             this.list.destroy();
39694         }
39695         if(this.store){
39696             this.store.un('beforeload', this.onBeforeLoad, this);
39697             this.store.un('load', this.onLoad, this);
39698             this.store.un('loadexception', this.onLoadException, this);
39699         }
39700         Roo.form.ComboBox.superclass.onDestroy.call(this);
39701     },
39702
39703     // private
39704     fireKey : function(e){
39705         if(e.isNavKeyPress() && !this.list.isVisible()){
39706             this.fireEvent("specialkey", this, e);
39707         }
39708     },
39709
39710     // private
39711     onResize: function(w, h){
39712         Roo.form.ComboBox.superclass.onResize.apply(this, arguments);
39713         
39714         if(typeof w != 'number'){
39715             // we do not handle it!?!?
39716             return;
39717         }
39718         var tw = this.trigger.getWidth();
39719         tw += this.addicon ? this.addicon.getWidth() : 0;
39720         tw += this.editicon ? this.editicon.getWidth() : 0;
39721         var x = w - tw;
39722         this.el.setWidth( this.adjustWidth('input', x));
39723             
39724         this.trigger.setStyle('left', x+'px');
39725         
39726         if(this.list && this.listWidth === undefined){
39727             var lw = Math.max(x + this.trigger.getWidth(), this.minListWidth);
39728             this.list.setWidth(lw);
39729             this.innerList.setWidth(lw - this.list.getFrameWidth('lr'));
39730         }
39731         
39732     
39733         
39734     },
39735
39736     /**
39737      * Allow or prevent the user from directly editing the field text.  If false is passed,
39738      * the user will only be able to select from the items defined in the dropdown list.  This method
39739      * is the runtime equivalent of setting the 'editable' config option at config time.
39740      * @param {Boolean} value True to allow the user to directly edit the field text
39741      */
39742     setEditable : function(value){
39743         if(value == this.editable){
39744             return;
39745         }
39746         this.editable = value;
39747         if(!value){
39748             this.el.dom.setAttribute('readOnly', true);
39749             this.el.on('mousedown', this.onTriggerClick,  this);
39750             this.el.addClass('x-combo-noedit');
39751         }else{
39752             this.el.dom.setAttribute('readOnly', false);
39753             this.el.un('mousedown', this.onTriggerClick,  this);
39754             this.el.removeClass('x-combo-noedit');
39755         }
39756     },
39757
39758     // private
39759     onBeforeLoad : function(){
39760         if(!this.hasFocus){
39761             return;
39762         }
39763         this.innerList.update(this.loadingText ?
39764                '<div class="loading-indicator">'+this.loadingText+'</div>' : '');
39765         this.restrictHeight();
39766         this.selectedIndex = -1;
39767     },
39768
39769     // private
39770     onLoad : function(){
39771         if(!this.hasFocus){
39772             return;
39773         }
39774         if(this.store.getCount() > 0){
39775             this.expand();
39776             this.restrictHeight();
39777             if(this.lastQuery == this.allQuery){
39778                 if(this.editable){
39779                     this.el.dom.select();
39780                 }
39781                 if(!this.selectByValue(this.value, true)){
39782                     this.select(0, true);
39783                 }
39784             }else{
39785                 this.selectNext();
39786                 if(this.typeAhead && this.lastKey != Roo.EventObject.BACKSPACE && this.lastKey != Roo.EventObject.DELETE){
39787                     this.taTask.delay(this.typeAheadDelay);
39788                 }
39789             }
39790         }else{
39791             this.onEmptyResults();
39792         }
39793         //this.el.focus();
39794     },
39795     // private
39796     onLoadException : function()
39797     {
39798         this.collapse();
39799         Roo.log(this.store.reader.jsonData);
39800         if (this.store && typeof(this.store.reader.jsonData.errorMsg) != 'undefined') {
39801             Roo.MessageBox.alert("Error loading",this.store.reader.jsonData.errorMsg);
39802         }
39803         
39804         
39805     },
39806     // private
39807     onTypeAhead : function(){
39808         if(this.store.getCount() > 0){
39809             var r = this.store.getAt(0);
39810             var newValue = r.data[this.displayField];
39811             var len = newValue.length;
39812             var selStart = this.getRawValue().length;
39813             if(selStart != len){
39814                 this.setRawValue(newValue);
39815                 this.selectText(selStart, newValue.length);
39816             }
39817         }
39818     },
39819
39820     // private
39821     onSelect : function(record, index){
39822         if(this.fireEvent('beforeselect', this, record, index) !== false){
39823             this.setFromData(index > -1 ? record.data : false);
39824             this.collapse();
39825             this.fireEvent('select', this, record, index);
39826         }
39827     },
39828
39829     /**
39830      * Returns the currently selected field value or empty string if no value is set.
39831      * @return {String} value The selected value
39832      */
39833     getValue : function(){
39834         if(this.valueField){
39835             return typeof this.value != 'undefined' ? this.value : '';
39836         }
39837         return Roo.form.ComboBox.superclass.getValue.call(this);
39838     },
39839
39840     /**
39841      * Clears any text/value currently set in the field
39842      */
39843     clearValue : function(){
39844         if(this.hiddenField){
39845             this.hiddenField.value = '';
39846         }
39847         this.value = '';
39848         this.setRawValue('');
39849         this.lastSelectionText = '';
39850         
39851     },
39852
39853     /**
39854      * Sets the specified value into the field.  If the value finds a match, the corresponding record text
39855      * will be displayed in the field.  If the value does not match the data value of an existing item,
39856      * and the valueNotFoundText config option is defined, it will be displayed as the default field text.
39857      * Otherwise the field will be blank (although the value will still be set).
39858      * @param {String} value The value to match
39859      */
39860     setValue : function(v){
39861         var text = v;
39862         if(this.valueField){
39863             var r = this.findRecord(this.valueField, v);
39864             if(r){
39865                 text = r.data[this.displayField];
39866             }else if(this.valueNotFoundText !== undefined){
39867                 text = this.valueNotFoundText;
39868             }
39869         }
39870         this.lastSelectionText = text;
39871         if(this.hiddenField){
39872             this.hiddenField.value = v;
39873         }
39874         Roo.form.ComboBox.superclass.setValue.call(this, text);
39875         this.value = v;
39876     },
39877     /**
39878      * @property {Object} the last set data for the element
39879      */
39880     
39881     lastData : false,
39882     /**
39883      * Sets the value of the field based on a object which is related to the record format for the store.
39884      * @param {Object} value the value to set as. or false on reset?
39885      */
39886     setFromData : function(o){
39887         var dv = ''; // display value
39888         var vv = ''; // value value..
39889         this.lastData = o;
39890         if (this.displayField) {
39891             dv = !o || typeof(o[this.displayField]) == 'undefined' ? '' : o[this.displayField];
39892         } else {
39893             // this is an error condition!!!
39894             Roo.log('no  displayField value set for '+ (this.name ? this.name : this.id));
39895         }
39896         
39897         if(this.valueField){
39898             vv = !o || typeof(o[this.valueField]) == 'undefined' ? dv : o[this.valueField];
39899         }
39900         if(this.hiddenField){
39901             this.hiddenField.value = vv;
39902             
39903             this.lastSelectionText = dv;
39904             Roo.form.ComboBox.superclass.setValue.call(this, dv);
39905             this.value = vv;
39906             return;
39907         }
39908         // no hidden field.. - we store the value in 'value', but still display
39909         // display field!!!!
39910         this.lastSelectionText = dv;
39911         Roo.form.ComboBox.superclass.setValue.call(this, dv);
39912         this.value = vv;
39913         
39914         
39915     },
39916     // private
39917     reset : function(){
39918         // overridden so that last data is reset..
39919         this.setValue(this.resetValue);
39920         this.clearInvalid();
39921         this.lastData = false;
39922         if (this.view) {
39923             this.view.clearSelections();
39924         }
39925     },
39926     // private
39927     findRecord : function(prop, value){
39928         var record;
39929         if(this.store.getCount() > 0){
39930             this.store.each(function(r){
39931                 if(r.data[prop] == value){
39932                     record = r;
39933                     return false;
39934                 }
39935                 return true;
39936             });
39937         }
39938         return record;
39939     },
39940     
39941     getName: function()
39942     {
39943         // returns hidden if it's set..
39944         if (!this.rendered) {return ''};
39945         return !this.hiddenName && this.el.dom.name  ? this.el.dom.name : (this.hiddenName || '');
39946         
39947     },
39948     // private
39949     onViewMove : function(e, t){
39950         this.inKeyMode = false;
39951     },
39952
39953     // private
39954     onViewOver : function(e, t){
39955         if(this.inKeyMode){ // prevent key nav and mouse over conflicts
39956             return;
39957         }
39958         var item = this.view.findItemFromChild(t);
39959         if(item){
39960             var index = this.view.indexOf(item);
39961             this.select(index, false);
39962         }
39963     },
39964
39965     // private
39966     onViewClick : function(doFocus)
39967     {
39968         var index = this.view.getSelectedIndexes()[0];
39969         var r = this.store.getAt(index);
39970         if(r){
39971             this.onSelect(r, index);
39972         }
39973         if(doFocus !== false && !this.blockFocus){
39974             this.el.focus();
39975         }
39976     },
39977
39978     // private
39979     restrictHeight : function(){
39980         this.innerList.dom.style.height = '';
39981         var inner = this.innerList.dom;
39982         var h = Math.max(inner.clientHeight, inner.offsetHeight, inner.scrollHeight);
39983         this.innerList.setHeight(h < this.maxHeight ? 'auto' : this.maxHeight);
39984         this.list.beginUpdate();
39985         this.list.setHeight(this.innerList.getHeight()+this.list.getFrameWidth('tb')+(this.resizable?this.handleHeight:0)+this.assetHeight);
39986         this.list.alignTo(this.el, this.listAlign);
39987         this.list.endUpdate();
39988     },
39989
39990     // private
39991     onEmptyResults : function(){
39992         this.collapse();
39993     },
39994
39995     /**
39996      * Returns true if the dropdown list is expanded, else false.
39997      */
39998     isExpanded : function(){
39999         return this.list.isVisible();
40000     },
40001
40002     /**
40003      * Select an item in the dropdown list by its data value. This function does NOT cause the select event to fire.
40004      * The store must be loaded and the list expanded for this function to work, otherwise use setValue.
40005      * @param {String} value The data value of the item to select
40006      * @param {Boolean} scrollIntoView False to prevent the dropdown list from autoscrolling to display the
40007      * selected item if it is not currently in view (defaults to true)
40008      * @return {Boolean} True if the value matched an item in the list, else false
40009      */
40010     selectByValue : function(v, scrollIntoView){
40011         if(v !== undefined && v !== null){
40012             var r = this.findRecord(this.valueField || this.displayField, v);
40013             if(r){
40014                 this.select(this.store.indexOf(r), scrollIntoView);
40015                 return true;
40016             }
40017         }
40018         return false;
40019     },
40020
40021     /**
40022      * Select an item in the dropdown list by its numeric index in the list. This function does NOT cause the select event to fire.
40023      * The store must be loaded and the list expanded for this function to work, otherwise use setValue.
40024      * @param {Number} index The zero-based index of the list item to select
40025      * @param {Boolean} scrollIntoView False to prevent the dropdown list from autoscrolling to display the
40026      * selected item if it is not currently in view (defaults to true)
40027      */
40028     select : function(index, scrollIntoView){
40029         this.selectedIndex = index;
40030         this.view.select(index);
40031         if(scrollIntoView !== false){
40032             var el = this.view.getNode(index);
40033             if(el){
40034                 this.innerList.scrollChildIntoView(el, false);
40035             }
40036         }
40037     },
40038
40039     // private
40040     selectNext : function(){
40041         var ct = this.store.getCount();
40042         if(ct > 0){
40043             if(this.selectedIndex == -1){
40044                 this.select(0);
40045             }else if(this.selectedIndex < ct-1){
40046                 this.select(this.selectedIndex+1);
40047             }
40048         }
40049     },
40050
40051     // private
40052     selectPrev : function(){
40053         var ct = this.store.getCount();
40054         if(ct > 0){
40055             if(this.selectedIndex == -1){
40056                 this.select(0);
40057             }else if(this.selectedIndex != 0){
40058                 this.select(this.selectedIndex-1);
40059             }
40060         }
40061     },
40062
40063     // private
40064     onKeyUp : function(e){
40065         if(this.editable !== false && !e.isSpecialKey()){
40066             this.lastKey = e.getKey();
40067             this.dqTask.delay(this.queryDelay);
40068         }
40069     },
40070
40071     // private
40072     validateBlur : function(){
40073         return !this.list || !this.list.isVisible();   
40074     },
40075
40076     // private
40077     initQuery : function(){
40078         this.doQuery(this.getRawValue());
40079     },
40080
40081     // private
40082     doForce : function(){
40083         if(this.el.dom.value.length > 0){
40084             this.el.dom.value =
40085                 this.lastSelectionText === undefined ? '' : this.lastSelectionText;
40086              
40087         }
40088     },
40089
40090     /**
40091      * Execute a query to filter the dropdown list.  Fires the beforequery event prior to performing the
40092      * query allowing the query action to be canceled if needed.
40093      * @param {String} query The SQL query to execute
40094      * @param {Boolean} forceAll True to force the query to execute even if there are currently fewer characters
40095      * in the field than the minimum specified by the minChars config option.  It also clears any filter previously
40096      * saved in the current store (defaults to false)
40097      */
40098     doQuery : function(q, forceAll){
40099         if(q === undefined || q === null){
40100             q = '';
40101         }
40102         var qe = {
40103             query: q,
40104             forceAll: forceAll,
40105             combo: this,
40106             cancel:false
40107         };
40108         if(this.fireEvent('beforequery', qe)===false || qe.cancel){
40109             return false;
40110         }
40111         q = qe.query;
40112         forceAll = qe.forceAll;
40113         if(forceAll === true || (q.length >= this.minChars)){
40114             if(this.lastQuery != q || this.alwaysQuery){
40115                 this.lastQuery = q;
40116                 if(this.mode == 'local'){
40117                     this.selectedIndex = -1;
40118                     if(forceAll){
40119                         this.store.clearFilter();
40120                     }else{
40121                         this.store.filter(this.displayField, q);
40122                     }
40123                     this.onLoad();
40124                 }else{
40125                     this.store.baseParams[this.queryParam] = q;
40126                     this.store.load({
40127                         params: this.getParams(q)
40128                     });
40129                     this.expand();
40130                 }
40131             }else{
40132                 this.selectedIndex = -1;
40133                 this.onLoad();   
40134             }
40135         }
40136     },
40137
40138     // private
40139     getParams : function(q){
40140         var p = {};
40141         //p[this.queryParam] = q;
40142         if(this.pageSize){
40143             p.start = 0;
40144             p.limit = this.pageSize;
40145         }
40146         return p;
40147     },
40148
40149     /**
40150      * Hides the dropdown list if it is currently expanded. Fires the 'collapse' event on completion.
40151      */
40152     collapse : function(){
40153         if(!this.isExpanded()){
40154             return;
40155         }
40156         this.list.hide();
40157         Roo.get(document).un('mousedown', this.collapseIf, this);
40158         Roo.get(document).un('mousewheel', this.collapseIf, this);
40159         if (!this.editable) {
40160             Roo.get(document).un('keydown', this.listKeyPress, this);
40161         }
40162         this.fireEvent('collapse', this);
40163     },
40164
40165     // private
40166     collapseIf : function(e){
40167         if(!e.within(this.wrap) && !e.within(this.list)){
40168             this.collapse();
40169         }
40170     },
40171
40172     /**
40173      * Expands the dropdown list if it is currently hidden. Fires the 'expand' event on completion.
40174      */
40175     expand : function(){
40176         if(this.isExpanded() || !this.hasFocus){
40177             return;
40178         }
40179         this.list.alignTo(this.el, this.listAlign);
40180         this.list.show();
40181         Roo.get(document).on('mousedown', this.collapseIf, this);
40182         Roo.get(document).on('mousewheel', this.collapseIf, this);
40183         if (!this.editable) {
40184             Roo.get(document).on('keydown', this.listKeyPress, this);
40185         }
40186         
40187         this.fireEvent('expand', this);
40188     },
40189
40190     // private
40191     // Implements the default empty TriggerField.onTriggerClick function
40192     onTriggerClick : function(){
40193         if(this.disabled){
40194             return;
40195         }
40196         if(this.isExpanded()){
40197             this.collapse();
40198             if (!this.blockFocus) {
40199                 this.el.focus();
40200             }
40201             
40202         }else {
40203             this.hasFocus = true;
40204             if(this.triggerAction == 'all') {
40205                 this.doQuery(this.allQuery, true);
40206             } else {
40207                 this.doQuery(this.getRawValue());
40208             }
40209             if (!this.blockFocus) {
40210                 this.el.focus();
40211             }
40212         }
40213     },
40214     listKeyPress : function(e)
40215     {
40216         //Roo.log('listkeypress');
40217         // scroll to first matching element based on key pres..
40218         if (e.isSpecialKey()) {
40219             return false;
40220         }
40221         var k = String.fromCharCode(e.getKey()).toUpperCase();
40222         //Roo.log(k);
40223         var match  = false;
40224         var csel = this.view.getSelectedNodes();
40225         var cselitem = false;
40226         if (csel.length) {
40227             var ix = this.view.indexOf(csel[0]);
40228             cselitem  = this.store.getAt(ix);
40229             if (!cselitem.get(this.displayField) || cselitem.get(this.displayField).substring(0,1).toUpperCase() != k) {
40230                 cselitem = false;
40231             }
40232             
40233         }
40234         
40235         this.store.each(function(v) { 
40236             if (cselitem) {
40237                 // start at existing selection.
40238                 if (cselitem.id == v.id) {
40239                     cselitem = false;
40240                 }
40241                 return;
40242             }
40243                 
40244             if (v.get(this.displayField) && v.get(this.displayField).substring(0,1).toUpperCase() == k) {
40245                 match = this.store.indexOf(v);
40246                 return false;
40247             }
40248         }, this);
40249         
40250         if (match === false) {
40251             return true; // no more action?
40252         }
40253         // scroll to?
40254         this.view.select(match);
40255         var sn = Roo.get(this.view.getSelectedNodes()[0])
40256         sn.scrollIntoView(sn.dom.parentNode, false);
40257     }
40258
40259     /** 
40260     * @cfg {Boolean} grow 
40261     * @hide 
40262     */
40263     /** 
40264     * @cfg {Number} growMin 
40265     * @hide 
40266     */
40267     /** 
40268     * @cfg {Number} growMax 
40269     * @hide 
40270     */
40271     /**
40272      * @hide
40273      * @method autoSize
40274      */
40275 });/*
40276  * Copyright(c) 2010-2012, Roo J Solutions Limited
40277  *
40278  * Licence LGPL
40279  *
40280  */
40281
40282 /**
40283  * @class Roo.form.ComboBoxArray
40284  * @extends Roo.form.TextField
40285  * A facebook style adder... for lists of email / people / countries  etc...
40286  * pick multiple items from a combo box, and shows each one.
40287  *
40288  *  Fred [x]  Brian [x]  [Pick another |v]
40289  *
40290  *
40291  *  For this to work: it needs various extra information
40292  *    - normal combo problay has
40293  *      name, hiddenName
40294  *    + displayField, valueField
40295  *
40296  *    For our purpose...
40297  *
40298  *
40299  *   If we change from 'extends' to wrapping...
40300  *   
40301  *  
40302  *
40303  
40304  
40305  * @constructor
40306  * Create a new ComboBoxArray.
40307  * @param {Object} config Configuration options
40308  */
40309  
40310
40311 Roo.form.ComboBoxArray = function(config)
40312 {
40313     this.addEvents({
40314         /**
40315          * @event remove
40316          * Fires when remove the value from the list
40317              * @param {Roo.form.ComboBoxArray} _self This combo box array
40318              * @param {Roo.form.ComboBoxArray.Item} item removed item
40319              */
40320         'remove' : true
40321         
40322         
40323     });
40324     
40325     Roo.form.ComboBoxArray.superclass.constructor.call(this, config);
40326     
40327     this.items = new Roo.util.MixedCollection(false);
40328     
40329     // construct the child combo...
40330     
40331     
40332     
40333     
40334    
40335     
40336 }
40337
40338  
40339 Roo.extend(Roo.form.ComboBoxArray, Roo.form.TextField,
40340
40341     /**
40342      * @cfg {Roo.form.Combo} combo The combo box that is wrapped
40343      */
40344     
40345     lastData : false,
40346     
40347     // behavies liek a hiddne field
40348     inputType:      'hidden',
40349     /**
40350      * @cfg {Number} width The width of the box that displays the selected element
40351      */ 
40352     width:          300,
40353
40354     
40355     
40356     /**
40357      * @cfg {String} name    The name of the visable items on this form (eg. titles not ids)
40358      */
40359     name : false,
40360     /**
40361      * @cfg {String} hiddenName    The hidden name of the field, often contains an comma seperated list of names
40362      */
40363     hiddenName : false,
40364     
40365     
40366     // private the array of items that are displayed..
40367     items  : false,
40368     // private - the hidden field el.
40369     hiddenEl : false,
40370     // private - the filed el..
40371     el : false,
40372     
40373     //validateValue : function() { return true; }, // all values are ok!
40374     //onAddClick: function() { },
40375     
40376     onRender : function(ct, position) 
40377     {
40378         
40379         // create the standard hidden element
40380         //Roo.form.ComboBoxArray.superclass.onRender.call(this, ct, position);
40381         
40382         
40383         // give fake names to child combo;
40384         this.combo.hiddenName = this.hiddenName ? (this.hiddenName+'-subcombo') : this.hiddenName;
40385         this.combo.name = this.name? (this.name+'-subcombo') : this.name;
40386         
40387         this.combo = Roo.factory(this.combo, Roo.form);
40388         this.combo.onRender(ct, position);
40389         if (typeof(this.combo.width) != 'undefined') {
40390             this.combo.onResize(this.combo.width,0);
40391         }
40392         
40393         this.combo.initEvents();
40394         
40395         // assigned so form know we need to do this..
40396         this.store          = this.combo.store;
40397         this.valueField     = this.combo.valueField;
40398         this.displayField   = this.combo.displayField ;
40399         
40400         
40401         this.combo.wrap.addClass('x-cbarray-grp');
40402         
40403         var cbwrap = this.combo.wrap.createChild(
40404             {tag: 'div', cls: 'x-cbarray-cb'},
40405             this.combo.el.dom
40406         );
40407         
40408              
40409         this.hiddenEl = this.combo.wrap.createChild({
40410             tag: 'input',  type:'hidden' , name: this.hiddenName, value : ''
40411         });
40412         this.el = this.combo.wrap.createChild({
40413             tag: 'input',  type:'hidden' , name: this.name, value : ''
40414         });
40415          //   this.el.dom.removeAttribute("name");
40416         
40417         
40418         this.outerWrap = this.combo.wrap;
40419         this.wrap = cbwrap;
40420         
40421         this.outerWrap.setWidth(this.width);
40422         this.outerWrap.dom.removeChild(this.el.dom);
40423         
40424         this.wrap.dom.appendChild(this.el.dom);
40425         this.outerWrap.dom.removeChild(this.combo.trigger.dom);
40426         this.combo.wrap.dom.appendChild(this.combo.trigger.dom);
40427         
40428         this.combo.trigger.setStyle('position','relative');
40429         this.combo.trigger.setStyle('left', '0px');
40430         this.combo.trigger.setStyle('top', '2px');
40431         
40432         this.combo.el.setStyle('vertical-align', 'text-bottom');
40433         
40434         //this.trigger.setStyle('vertical-align', 'top');
40435         
40436         // this should use the code from combo really... on('add' ....)
40437         if (this.adder) {
40438             
40439         
40440             this.adder = this.outerWrap.createChild(
40441                 {tag: 'img', src: Roo.BLANK_IMAGE_URL, cls: 'x-form-adder', style: 'margin-left:2px'});  
40442             var _t = this;
40443             this.adder.on('click', function(e) {
40444                 _t.fireEvent('adderclick', this, e);
40445             }, _t);
40446         }
40447         //var _t = this;
40448         //this.adder.on('click', this.onAddClick, _t);
40449         
40450         
40451         this.combo.on('select', function(cb, rec, ix) {
40452             this.addItem(rec.data);
40453             
40454             cb.setValue('');
40455             cb.el.dom.value = '';
40456             //cb.lastData = rec.data;
40457             // add to list
40458             
40459         }, this);
40460         
40461         
40462     },
40463     
40464     
40465     getName: function()
40466     {
40467         // returns hidden if it's set..
40468         if (!this.rendered) {return ''};
40469         return  this.hiddenName ? this.hiddenName : this.name;
40470         
40471     },
40472     
40473     
40474     onResize: function(w, h){
40475         
40476         return;
40477         // not sure if this is needed..
40478         //this.combo.onResize(w,h);
40479         
40480         if(typeof w != 'number'){
40481             // we do not handle it!?!?
40482             return;
40483         }
40484         var tw = this.combo.trigger.getWidth();
40485         tw += this.addicon ? this.addicon.getWidth() : 0;
40486         tw += this.editicon ? this.editicon.getWidth() : 0;
40487         var x = w - tw;
40488         this.combo.el.setWidth( this.combo.adjustWidth('input', x));
40489             
40490         this.combo.trigger.setStyle('left', '0px');
40491         
40492         if(this.list && this.listWidth === undefined){
40493             var lw = Math.max(x + this.combo.trigger.getWidth(), this.combo.minListWidth);
40494             this.list.setWidth(lw);
40495             this.innerList.setWidth(lw - this.list.getFrameWidth('lr'));
40496         }
40497         
40498     
40499         
40500     },
40501     
40502     addItem: function(rec)
40503     {
40504         var valueField = this.combo.valueField;
40505         var displayField = this.combo.displayField;
40506         if (this.items.indexOfKey(rec[valueField]) > -1) {
40507             //console.log("GOT " + rec.data.id);
40508             return;
40509         }
40510         
40511         var x = new Roo.form.ComboBoxArray.Item({
40512             //id : rec[this.idField],
40513             data : rec,
40514             displayField : displayField ,
40515             tipField : displayField ,
40516             cb : this
40517         });
40518         // use the 
40519         this.items.add(rec[valueField],x);
40520         // add it before the element..
40521         this.updateHiddenEl();
40522         x.render(this.outerWrap, this.wrap.dom);
40523         // add the image handler..
40524     },
40525     
40526     updateHiddenEl : function()
40527     {
40528         this.validate();
40529         if (!this.hiddenEl) {
40530             return;
40531         }
40532         var ar = [];
40533         var idField = this.combo.valueField;
40534         
40535         this.items.each(function(f) {
40536             ar.push(f.data[idField]);
40537            
40538         });
40539         this.hiddenEl.dom.value = ar.join(',');
40540         this.validate();
40541     },
40542     
40543     reset : function()
40544     {
40545         //Roo.form.ComboBoxArray.superclass.reset.call(this); 
40546         this.items.each(function(f) {
40547            f.remove(); 
40548         });
40549         this.el.dom.value = '';
40550         if (this.hiddenEl) {
40551             this.hiddenEl.dom.value = '';
40552         }
40553         
40554     },
40555     getValue: function()
40556     {
40557         return this.hiddenEl ? this.hiddenEl.dom.value : '';
40558     },
40559     setValue: function(v) // not a valid action - must use addItems..
40560     {
40561          
40562         this.reset();
40563         
40564         
40565         
40566         if (this.store.isLocal && (typeof(v) == 'string')) {
40567             // then we can use the store to find the values..
40568             // comma seperated at present.. this needs to allow JSON based encoding..
40569             this.hiddenEl.value  = v;
40570             var v_ar = [];
40571             Roo.each(v.split(','), function(k) {
40572                 Roo.log("CHECK " + this.valueField + ',' + k);
40573                 var li = this.store.query(this.valueField, k);
40574                 if (!li.length) {
40575                     return;
40576                 }
40577                 var add = {};
40578                 add[this.valueField] = k;
40579                 add[this.displayField] = li.item(0).data[this.displayField];
40580                 
40581                 this.addItem(add);
40582             }, this) 
40583              
40584         }
40585         if (typeof(v) == 'object' ) {
40586             // then let's assume it's an array of objects..
40587             Roo.each(v, function(l) {
40588                 this.addItem(l);
40589             }, this);
40590              
40591         }
40592         
40593         
40594     },
40595     setFromData: function(v)
40596     {
40597         // this recieves an object, if setValues is called.
40598         this.reset();
40599         this.el.dom.value = v[this.displayField];
40600         this.hiddenEl.dom.value = v[this.valueField];
40601         if (typeof(v[this.valueField]) != 'string' || !v[this.valueField].length) {
40602             return;
40603         }
40604         var kv = v[this.valueField];
40605         var dv = v[this.displayField];
40606         kv = typeof(kv) != 'string' ? '' : kv;
40607         dv = typeof(dv) != 'string' ? '' : dv;
40608         
40609         
40610         var keys = kv.split(',');
40611         var display = dv.split(',');
40612         for (var i = 0 ; i < keys.length; i++) {
40613             
40614             add = {};
40615             add[this.valueField] = keys[i];
40616             add[this.displayField] = display[i];
40617             this.addItem(add);
40618         }
40619       
40620         
40621     },
40622     
40623     /**
40624      * Validates the combox array value
40625      * @return {Boolean} True if the value is valid, else false
40626      */
40627     validate : function(){
40628         if(this.disabled || this.validateValue(this.processValue(this.getValue()))){
40629             this.clearInvalid();
40630             return true;
40631         }
40632         return false;
40633     },
40634     
40635     validateValue : function(value){
40636         return Roo.form.ComboBoxArray.superclass.validateValue.call(this, this.getValue());
40637         
40638     },
40639     
40640     /*@
40641      * overide
40642      * 
40643      */
40644     isDirty : function() {
40645         if(this.disabled) {
40646             return false;
40647         }
40648         
40649         try {
40650             var d = Roo.decode(String(this.originalValue));
40651         } catch (e) {
40652             return String(this.getValue()) !== String(this.originalValue);
40653         }
40654         
40655         var originalValue = [];
40656         
40657         for (var i = 0; i < d.length; i++){
40658             originalValue.push(d[i][this.valueField]);
40659         }
40660         
40661         return String(this.getValue()) !== String(originalValue.join(','));
40662         
40663     }
40664     
40665 });
40666
40667
40668
40669 /**
40670  * @class Roo.form.ComboBoxArray.Item
40671  * @extends Roo.BoxComponent
40672  * A selected item in the list
40673  *  Fred [x]  Brian [x]  [Pick another |v]
40674  * 
40675  * @constructor
40676  * Create a new item.
40677  * @param {Object} config Configuration options
40678  */
40679  
40680 Roo.form.ComboBoxArray.Item = function(config) {
40681     config.id = Roo.id();
40682     Roo.form.ComboBoxArray.Item.superclass.constructor.call(this, config);
40683 }
40684
40685 Roo.extend(Roo.form.ComboBoxArray.Item, Roo.BoxComponent, {
40686     data : {},
40687     cb: false,
40688     displayField : false,
40689     tipField : false,
40690     
40691     
40692     defaultAutoCreate : {
40693         tag: 'div',
40694         cls: 'x-cbarray-item',
40695         cn : [ 
40696             { tag: 'div' },
40697             {
40698                 tag: 'img',
40699                 width:16,
40700                 height : 16,
40701                 src : Roo.BLANK_IMAGE_URL ,
40702                 align: 'center'
40703             }
40704         ]
40705         
40706     },
40707     
40708  
40709     onRender : function(ct, position)
40710     {
40711         Roo.form.Field.superclass.onRender.call(this, ct, position);
40712         
40713         if(!this.el){
40714             var cfg = this.getAutoCreate();
40715             this.el = ct.createChild(cfg, position);
40716         }
40717         
40718         this.el.child('img').dom.setAttribute('src', Roo.BLANK_IMAGE_URL);
40719         
40720         this.el.child('div').dom.innerHTML = this.cb.renderer ? 
40721             this.cb.renderer(this.data) :
40722             String.format('{0}',this.data[this.displayField]);
40723         
40724             
40725         this.el.child('div').dom.setAttribute('qtip',
40726                         String.format('{0}',this.data[this.tipField])
40727         );
40728         
40729         this.el.child('img').on('click', this.remove, this);
40730         
40731     },
40732    
40733     remove : function()
40734     {
40735         if(this.cb.disabled){
40736             return;
40737         }
40738         this.cb.items.remove(this);
40739         this.el.child('img').un('click', this.remove, this);
40740         this.el.remove();
40741         this.cb.updateHiddenEl();
40742         
40743         this.cb.fireEvent('remove', this.cb, this);
40744     }
40745 });/*
40746  * Based on:
40747  * Ext JS Library 1.1.1
40748  * Copyright(c) 2006-2007, Ext JS, LLC.
40749  *
40750  * Originally Released Under LGPL - original licence link has changed is not relivant.
40751  *
40752  * Fork - LGPL
40753  * <script type="text/javascript">
40754  */
40755 /**
40756  * @class Roo.form.Checkbox
40757  * @extends Roo.form.Field
40758  * Single checkbox field.  Can be used as a direct replacement for traditional checkbox fields.
40759  * @constructor
40760  * Creates a new Checkbox
40761  * @param {Object} config Configuration options
40762  */
40763 Roo.form.Checkbox = function(config){
40764     Roo.form.Checkbox.superclass.constructor.call(this, config);
40765     this.addEvents({
40766         /**
40767          * @event check
40768          * Fires when the checkbox is checked or unchecked.
40769              * @param {Roo.form.Checkbox} this This checkbox
40770              * @param {Boolean} checked The new checked value
40771              */
40772         check : true
40773     });
40774 };
40775
40776 Roo.extend(Roo.form.Checkbox, Roo.form.Field,  {
40777     /**
40778      * @cfg {String} focusClass The CSS class to use when the checkbox receives focus (defaults to undefined)
40779      */
40780     focusClass : undefined,
40781     /**
40782      * @cfg {String} fieldClass The default CSS class for the checkbox (defaults to "x-form-field")
40783      */
40784     fieldClass: "x-form-field",
40785     /**
40786      * @cfg {Boolean} checked True if the the checkbox should render already checked (defaults to false)
40787      */
40788     checked: false,
40789     /**
40790      * @cfg {String/Object} autoCreate A DomHelper element spec, or true for a default element spec (defaults to
40791      * {tag: "input", type: "checkbox", autocomplete: "off"})
40792      */
40793     defaultAutoCreate : { tag: "input", type: 'hidden', autocomplete: "off"},
40794     /**
40795      * @cfg {String} boxLabel The text that appears beside the checkbox
40796      */
40797     boxLabel : "",
40798     /**
40799      * @cfg {String} inputValue The value that should go into the generated input element's value attribute
40800      */  
40801     inputValue : '1',
40802     /**
40803      * @cfg {String} valueOff The value that should go into the generated input element's value when unchecked.
40804      */
40805      valueOff: '0', // value when not checked..
40806
40807     actionMode : 'viewEl', 
40808     //
40809     // private
40810     itemCls : 'x-menu-check-item x-form-item',
40811     groupClass : 'x-menu-group-item',
40812     inputType : 'hidden',
40813     
40814     
40815     inSetChecked: false, // check that we are not calling self...
40816     
40817     inputElement: false, // real input element?
40818     basedOn: false, // ????
40819     
40820     isFormField: true, // not sure where this is needed!!!!
40821
40822     onResize : function(){
40823         Roo.form.Checkbox.superclass.onResize.apply(this, arguments);
40824         if(!this.boxLabel){
40825             this.el.alignTo(this.wrap, 'c-c');
40826         }
40827     },
40828
40829     initEvents : function(){
40830         Roo.form.Checkbox.superclass.initEvents.call(this);
40831         this.el.on("click", this.onClick,  this);
40832         this.el.on("change", this.onClick,  this);
40833     },
40834
40835
40836     getResizeEl : function(){
40837         return this.wrap;
40838     },
40839
40840     getPositionEl : function(){
40841         return this.wrap;
40842     },
40843
40844     // private
40845     onRender : function(ct, position){
40846         Roo.form.Checkbox.superclass.onRender.call(this, ct, position);
40847         /*
40848         if(this.inputValue !== undefined){
40849             this.el.dom.value = this.inputValue;
40850         }
40851         */
40852         //this.wrap = this.el.wrap({cls: "x-form-check-wrap"});
40853         this.wrap = this.el.wrap({cls: 'x-menu-check-item '});
40854         var viewEl = this.wrap.createChild({ 
40855             tag: 'img', cls: 'x-menu-item-icon', style: 'margin: 0px;' ,src : Roo.BLANK_IMAGE_URL });
40856         this.viewEl = viewEl;   
40857         this.wrap.on('click', this.onClick,  this); 
40858         
40859         this.el.on('DOMAttrModified', this.setFromHidden,  this); //ff
40860         this.el.on('propertychange', this.setFromHidden,  this);  //ie
40861         
40862         
40863         
40864         if(this.boxLabel){
40865             this.wrap.createChild({tag: 'label', htmlFor: this.el.id, cls: 'x-form-cb-label', html: this.boxLabel});
40866         //    viewEl.on('click', this.onClick,  this); 
40867         }
40868         //if(this.checked){
40869             this.setChecked(this.checked);
40870         //}else{
40871             //this.checked = this.el.dom;
40872         //}
40873
40874     },
40875
40876     // private
40877     initValue : Roo.emptyFn,
40878
40879     /**
40880      * Returns the checked state of the checkbox.
40881      * @return {Boolean} True if checked, else false
40882      */
40883     getValue : function(){
40884         if(this.el){
40885             return String(this.el.dom.value) == String(this.inputValue ) ? this.inputValue : this.valueOff;
40886         }
40887         return this.valueOff;
40888         
40889     },
40890
40891         // private
40892     onClick : function(){ 
40893         if (this.disabled) {
40894             return;
40895         }
40896         this.setChecked(!this.checked);
40897
40898         //if(this.el.dom.checked != this.checked){
40899         //    this.setValue(this.el.dom.checked);
40900        // }
40901     },
40902
40903     /**
40904      * Sets the checked state of the checkbox.
40905      * On is always based on a string comparison between inputValue and the param.
40906      * @param {Boolean/String} value - the value to set 
40907      * @param {Boolean/String} suppressEvent - whether to suppress the checkchange event.
40908      */
40909     setValue : function(v,suppressEvent){
40910         
40911         
40912         //this.checked = (v === true || v === 'true' || v == '1' || String(v).toLowerCase() == 'on');
40913         //if(this.el && this.el.dom){
40914         //    this.el.dom.checked = this.checked;
40915         //    this.el.dom.defaultChecked = this.checked;
40916         //}
40917         this.setChecked(String(v) === String(this.inputValue), suppressEvent);
40918         //this.fireEvent("check", this, this.checked);
40919     },
40920     // private..
40921     setChecked : function(state,suppressEvent)
40922     {
40923         if (this.inSetChecked) {
40924             this.checked = state;
40925             return;
40926         }
40927         
40928     
40929         if(this.wrap){
40930             this.wrap[state ? 'addClass' : 'removeClass']('x-menu-item-checked');
40931         }
40932         this.checked = state;
40933         if(suppressEvent !== true){
40934             this.fireEvent('check', this, state);
40935         }
40936         this.inSetChecked = true;
40937         this.el.dom.value = state ? this.inputValue : this.valueOff;
40938         this.inSetChecked = false;
40939         
40940     },
40941     // handle setting of hidden value by some other method!!?!?
40942     setFromHidden: function()
40943     {
40944         if(!this.el){
40945             return;
40946         }
40947         //console.log("SET FROM HIDDEN");
40948         //alert('setFrom hidden');
40949         this.setValue(this.el.dom.value);
40950     },
40951     
40952     onDestroy : function()
40953     {
40954         if(this.viewEl){
40955             Roo.get(this.viewEl).remove();
40956         }
40957          
40958         Roo.form.Checkbox.superclass.onDestroy.call(this);
40959     }
40960
40961 });/*
40962  * Based on:
40963  * Ext JS Library 1.1.1
40964  * Copyright(c) 2006-2007, Ext JS, LLC.
40965  *
40966  * Originally Released Under LGPL - original licence link has changed is not relivant.
40967  *
40968  * Fork - LGPL
40969  * <script type="text/javascript">
40970  */
40971  
40972 /**
40973  * @class Roo.form.Radio
40974  * @extends Roo.form.Checkbox
40975  * Single radio field.  Same as Checkbox, but provided as a convenience for automatically setting the input type.
40976  * Radio grouping is handled automatically by the browser if you give each radio in a group the same name.
40977  * @constructor
40978  * Creates a new Radio
40979  * @param {Object} config Configuration options
40980  */
40981 Roo.form.Radio = function(){
40982     Roo.form.Radio.superclass.constructor.apply(this, arguments);
40983 };
40984 Roo.extend(Roo.form.Radio, Roo.form.Checkbox, {
40985     inputType: 'radio',
40986
40987     /**
40988      * If this radio is part of a group, it will return the selected value
40989      * @return {String}
40990      */
40991     getGroupValue : function(){
40992         return this.el.up('form').child('input[name='+this.el.dom.name+']:checked', true).value;
40993     },
40994     
40995     
40996     onRender : function(ct, position){
40997         Roo.form.Checkbox.superclass.onRender.call(this, ct, position);
40998         
40999         if(this.inputValue !== undefined){
41000             this.el.dom.value = this.inputValue;
41001         }
41002          
41003         this.wrap = this.el.wrap({cls: "x-form-check-wrap"});
41004         //this.wrap = this.el.wrap({cls: 'x-menu-check-item '});
41005         //var viewEl = this.wrap.createChild({ 
41006         //    tag: 'img', cls: 'x-menu-item-icon', style: 'margin: 0px;' ,src : Roo.BLANK_IMAGE_URL });
41007         //this.viewEl = viewEl;   
41008         //this.wrap.on('click', this.onClick,  this); 
41009         
41010         //this.el.on('DOMAttrModified', this.setFromHidden,  this); //ff
41011         //this.el.on('propertychange', this.setFromHidden,  this);  //ie
41012         
41013         
41014         
41015         if(this.boxLabel){
41016             this.wrap.createChild({tag: 'label', htmlFor: this.el.id, cls: 'x-form-cb-label', html: this.boxLabel});
41017         //    viewEl.on('click', this.onClick,  this); 
41018         }
41019          if(this.checked){
41020             this.el.dom.checked =   'checked' ;
41021         }
41022          
41023     } 
41024     
41025     
41026 });//<script type="text/javascript">
41027
41028 /*
41029  * Based  Ext JS Library 1.1.1
41030  * Copyright(c) 2006-2007, Ext JS, LLC.
41031  * LGPL
41032  *
41033  */
41034  
41035 /**
41036  * @class Roo.HtmlEditorCore
41037  * @extends Roo.Component
41038  * Provides a the editing component for the HTML editors in Roo. (bootstrap and Roo.form)
41039  *
41040  * any element that has display set to 'none' can cause problems in Safari and Firefox.<br/><br/>
41041  */
41042
41043 Roo.HtmlEditorCore = function(config){
41044     
41045     
41046     Roo.HtmlEditorCore.superclass.constructor.call(this, config);
41047     
41048     
41049     this.addEvents({
41050         /**
41051          * @event initialize
41052          * Fires when the editor is fully initialized (including the iframe)
41053          * @param {Roo.HtmlEditorCore} this
41054          */
41055         initialize: true,
41056         /**
41057          * @event activate
41058          * Fires when the editor is first receives the focus. Any insertion must wait
41059          * until after this event.
41060          * @param {Roo.HtmlEditorCore} this
41061          */
41062         activate: true,
41063          /**
41064          * @event beforesync
41065          * Fires before the textarea is updated with content from the editor iframe. Return false
41066          * to cancel the sync.
41067          * @param {Roo.HtmlEditorCore} this
41068          * @param {String} html
41069          */
41070         beforesync: true,
41071          /**
41072          * @event beforepush
41073          * Fires before the iframe editor is updated with content from the textarea. Return false
41074          * to cancel the push.
41075          * @param {Roo.HtmlEditorCore} this
41076          * @param {String} html
41077          */
41078         beforepush: true,
41079          /**
41080          * @event sync
41081          * Fires when the textarea is updated with content from the editor iframe.
41082          * @param {Roo.HtmlEditorCore} this
41083          * @param {String} html
41084          */
41085         sync: true,
41086          /**
41087          * @event push
41088          * Fires when the iframe editor is updated with content from the textarea.
41089          * @param {Roo.HtmlEditorCore} this
41090          * @param {String} html
41091          */
41092         push: true,
41093         
41094         /**
41095          * @event editorevent
41096          * Fires when on any editor (mouse up/down cursor movement etc.) - used for toolbar hooks.
41097          * @param {Roo.HtmlEditorCore} this
41098          */
41099         editorevent: true
41100     });
41101     
41102     // at this point this.owner is set, so we can start working out the whitelisted / blacklisted elements
41103     
41104     // defaults : white / black...
41105     this.applyBlacklists();
41106     
41107     
41108     
41109 };
41110
41111
41112 Roo.extend(Roo.HtmlEditorCore, Roo.Component,  {
41113
41114
41115      /**
41116      * @cfg {Roo.form.HtmlEditor|Roo.bootstrap.HtmlEditor} the owner field 
41117      */
41118     
41119     owner : false,
41120     
41121      /**
41122      * @cfg {String} resizable  's' or 'se' or 'e' - wrapps the element in a
41123      *                        Roo.resizable.
41124      */
41125     resizable : false,
41126      /**
41127      * @cfg {Number} height (in pixels)
41128      */   
41129     height: 300,
41130    /**
41131      * @cfg {Number} width (in pixels)
41132      */   
41133     width: 500,
41134     
41135     /**
41136      * @cfg {Array} stylesheets url of stylesheets. set to [] to disable stylesheets.
41137      * 
41138      */
41139     stylesheets: false,
41140     
41141     // id of frame..
41142     frameId: false,
41143     
41144     // private properties
41145     validationEvent : false,
41146     deferHeight: true,
41147     initialized : false,
41148     activated : false,
41149     sourceEditMode : false,
41150     onFocus : Roo.emptyFn,
41151     iframePad:3,
41152     hideMode:'offsets',
41153     
41154     clearUp: true,
41155     
41156     // blacklist + whitelisted elements..
41157     black: false,
41158     white: false,
41159      
41160     
41161
41162     /**
41163      * Protected method that will not generally be called directly. It
41164      * is called when the editor initializes the iframe with HTML contents. Override this method if you
41165      * want to change the initialization markup of the iframe (e.g. to add stylesheets).
41166      */
41167     getDocMarkup : function(){
41168         // body styles..
41169         var st = '';
41170         Roo.log(this.stylesheets);
41171         
41172         // inherit styels from page...?? 
41173         if (this.stylesheets === false) {
41174             
41175             Roo.get(document.head).select('style').each(function(node) {
41176                 st += node.dom.outerHTML || new XMLSerializer().serializeToString(node.dom);
41177             });
41178             
41179             Roo.get(document.head).select('link').each(function(node) { 
41180                 st += node.dom.outerHTML || new XMLSerializer().serializeToString(node.dom);
41181             });
41182             
41183         } else if (!this.stylesheets.length) {
41184                 // simple..
41185                 st = '<style type="text/css">' +
41186                     'body{border:0;margin:0;padding:3px;height:98%;cursor:text;}' +
41187                    '</style>';
41188         } else {
41189             Roo.each(this.stylesheets, function(s) {
41190                 st += '<link rel="stylesheet" type="text/css" href="' + s +'" />'
41191             });
41192             
41193         }
41194         
41195         st +=  '<style type="text/css">' +
41196             'IMG { cursor: pointer } ' +
41197         '</style>';
41198
41199         
41200         return '<html><head>' + st  +
41201             //<style type="text/css">' +
41202             //'body{border:0;margin:0;padding:3px;height:98%;cursor:text;}' +
41203             //'</style>' +
41204             ' </head><body class="roo-htmleditor-body"></body></html>';
41205     },
41206
41207     // private
41208     onRender : function(ct, position)
41209     {
41210         var _t = this;
41211         //Roo.HtmlEditorCore.superclass.onRender.call(this, ct, position);
41212         this.el = this.owner.inputEl ? this.owner.inputEl() : this.owner.el;
41213         
41214         
41215         this.el.dom.style.border = '0 none';
41216         this.el.dom.setAttribute('tabIndex', -1);
41217         this.el.addClass('x-hidden hide');
41218         
41219         
41220         
41221         if(Roo.isIE){ // fix IE 1px bogus margin
41222             this.el.applyStyles('margin-top:-1px;margin-bottom:-1px;')
41223         }
41224        
41225         
41226         this.frameId = Roo.id();
41227         
41228          
41229         
41230         var iframe = this.owner.wrap.createChild({
41231             tag: 'iframe',
41232             cls: 'form-control', // bootstrap..
41233             id: this.frameId,
41234             name: this.frameId,
41235             frameBorder : 'no',
41236             'src' : Roo.SSL_SECURE_URL ? Roo.SSL_SECURE_URL  :  "javascript:false"
41237         }, this.el
41238         );
41239         
41240         
41241         this.iframe = iframe.dom;
41242
41243          this.assignDocWin();
41244         
41245         this.doc.designMode = 'on';
41246        
41247         this.doc.open();
41248         this.doc.write(this.getDocMarkup());
41249         this.doc.close();
41250
41251         
41252         var task = { // must defer to wait for browser to be ready
41253             run : function(){
41254                 //console.log("run task?" + this.doc.readyState);
41255                 this.assignDocWin();
41256                 if(this.doc.body || this.doc.readyState == 'complete'){
41257                     try {
41258                         this.doc.designMode="on";
41259                     } catch (e) {
41260                         return;
41261                     }
41262                     Roo.TaskMgr.stop(task);
41263                     this.initEditor.defer(10, this);
41264                 }
41265             },
41266             interval : 10,
41267             duration: 10000,
41268             scope: this
41269         };
41270         Roo.TaskMgr.start(task);
41271
41272         
41273          
41274     },
41275
41276     // private
41277     onResize : function(w, h)
41278     {
41279          Roo.log('resize: ' +w + ',' + h );
41280         //Roo.HtmlEditorCore.superclass.onResize.apply(this, arguments);
41281         if(!this.iframe){
41282             return;
41283         }
41284         if(typeof w == 'number'){
41285             
41286             this.iframe.style.width = w + 'px';
41287         }
41288         if(typeof h == 'number'){
41289             
41290             this.iframe.style.height = h + 'px';
41291             if(this.doc){
41292                 (this.doc.body || this.doc.documentElement).style.height = (h - (this.iframePad*2)) + 'px';
41293             }
41294         }
41295         
41296     },
41297
41298     /**
41299      * Toggles the editor between standard and source edit mode.
41300      * @param {Boolean} sourceEdit (optional) True for source edit, false for standard
41301      */
41302     toggleSourceEdit : function(sourceEditMode){
41303         
41304         this.sourceEditMode = sourceEditMode === true;
41305         
41306         if(this.sourceEditMode){
41307  
41308             Roo.get(this.iframe).addClass(['x-hidden','hide']);     //FIXME - what's the BS styles for these
41309             
41310         }else{
41311             Roo.get(this.iframe).removeClass(['x-hidden','hide']);
41312             //this.iframe.className = '';
41313             this.deferFocus();
41314         }
41315         //this.setSize(this.owner.wrap.getSize());
41316         //this.fireEvent('editmodechange', this, this.sourceEditMode);
41317     },
41318
41319     
41320   
41321
41322     /**
41323      * Protected method that will not generally be called directly. If you need/want
41324      * custom HTML cleanup, this is the method you should override.
41325      * @param {String} html The HTML to be cleaned
41326      * return {String} The cleaned HTML
41327      */
41328     cleanHtml : function(html){
41329         html = String(html);
41330         if(html.length > 5){
41331             if(Roo.isSafari){ // strip safari nonsense
41332                 html = html.replace(/\sclass="(?:Apple-style-span|khtml-block-placeholder)"/gi, '');
41333             }
41334         }
41335         if(html == '&nbsp;'){
41336             html = '';
41337         }
41338         return html;
41339     },
41340
41341     /**
41342      * HTML Editor -> Textarea
41343      * Protected method that will not generally be called directly. Syncs the contents
41344      * of the editor iframe with the textarea.
41345      */
41346     syncValue : function(){
41347         if(this.initialized){
41348             var bd = (this.doc.body || this.doc.documentElement);
41349             //this.cleanUpPaste(); -- this is done else where and causes havoc..
41350             var html = bd.innerHTML;
41351             if(Roo.isSafari){
41352                 var bs = bd.getAttribute('style'); // Safari puts text-align styles on the body element!
41353                 var m = bs ? bs.match(/text-align:(.*?);/i) : false;
41354                 if(m && m[1]){
41355                     html = '<div style="'+m[0]+'">' + html + '</div>';
41356                 }
41357             }
41358             html = this.cleanHtml(html);
41359             // fix up the special chars.. normaly like back quotes in word...
41360             // however we do not want to do this with chinese..
41361             html = html.replace(/([\x80-\uffff])/g, function (a, b) {
41362                 var cc = b.charCodeAt();
41363                 if (
41364                     (cc >= 0x4E00 && cc < 0xA000 ) ||
41365                     (cc >= 0x3400 && cc < 0x4E00 ) ||
41366                     (cc >= 0xf900 && cc < 0xfb00 )
41367                 ) {
41368                         return b;
41369                 }
41370                 return "&#"+cc+";" 
41371             });
41372             if(this.owner.fireEvent('beforesync', this, html) !== false){
41373                 this.el.dom.value = html;
41374                 this.owner.fireEvent('sync', this, html);
41375             }
41376         }
41377     },
41378
41379     /**
41380      * Protected method that will not generally be called directly. Pushes the value of the textarea
41381      * into the iframe editor.
41382      */
41383     pushValue : function(){
41384         if(this.initialized){
41385             var v = this.el.dom.value.trim();
41386             
41387 //            if(v.length < 1){
41388 //                v = '&#160;';
41389 //            }
41390             
41391             if(this.owner.fireEvent('beforepush', this, v) !== false){
41392                 var d = (this.doc.body || this.doc.documentElement);
41393                 d.innerHTML = v;
41394                 this.cleanUpPaste();
41395                 this.el.dom.value = d.innerHTML;
41396                 this.owner.fireEvent('push', this, v);
41397             }
41398         }
41399     },
41400
41401     // private
41402     deferFocus : function(){
41403         this.focus.defer(10, this);
41404     },
41405
41406     // doc'ed in Field
41407     focus : function(){
41408         if(this.win && !this.sourceEditMode){
41409             this.win.focus();
41410         }else{
41411             this.el.focus();
41412         }
41413     },
41414     
41415     assignDocWin: function()
41416     {
41417         var iframe = this.iframe;
41418         
41419          if(Roo.isIE){
41420             this.doc = iframe.contentWindow.document;
41421             this.win = iframe.contentWindow;
41422         } else {
41423 //            if (!Roo.get(this.frameId)) {
41424 //                return;
41425 //            }
41426 //            this.doc = (iframe.contentDocument || Roo.get(this.frameId).dom.document);
41427 //            this.win = Roo.get(this.frameId).dom.contentWindow;
41428             
41429             if (!Roo.get(this.frameId) && !iframe.contentDocument) {
41430                 return;
41431             }
41432             
41433             this.doc = (iframe.contentDocument || Roo.get(this.frameId).dom.document);
41434             this.win = (iframe.contentWindow || Roo.get(this.frameId).dom.contentWindow);
41435         }
41436     },
41437     
41438     // private
41439     initEditor : function(){
41440         //console.log("INIT EDITOR");
41441         this.assignDocWin();
41442         
41443         
41444         
41445         this.doc.designMode="on";
41446         this.doc.open();
41447         this.doc.write(this.getDocMarkup());
41448         this.doc.close();
41449         
41450         var dbody = (this.doc.body || this.doc.documentElement);
41451         //var ss = this.el.getStyles('font-size', 'font-family', 'background-image', 'background-repeat');
41452         // this copies styles from the containing element into thsi one..
41453         // not sure why we need all of this..
41454         //var ss = this.el.getStyles('font-size', 'background-image', 'background-repeat');
41455         
41456         //var ss = this.el.getStyles( 'background-image', 'background-repeat');
41457         //ss['background-attachment'] = 'fixed'; // w3c
41458         dbody.bgProperties = 'fixed'; // ie
41459         //Roo.DomHelper.applyStyles(dbody, ss);
41460         Roo.EventManager.on(this.doc, {
41461             //'mousedown': this.onEditorEvent,
41462             'mouseup': this.onEditorEvent,
41463             'dblclick': this.onEditorEvent,
41464             'click': this.onEditorEvent,
41465             'keyup': this.onEditorEvent,
41466             buffer:100,
41467             scope: this
41468         });
41469         if(Roo.isGecko){
41470             Roo.EventManager.on(this.doc, 'keypress', this.mozKeyPress, this);
41471         }
41472         if(Roo.isIE || Roo.isSafari || Roo.isOpera){
41473             Roo.EventManager.on(this.doc, 'keydown', this.fixKeys, this);
41474         }
41475         this.initialized = true;
41476
41477         this.owner.fireEvent('initialize', this);
41478         this.pushValue();
41479     },
41480
41481     // private
41482     onDestroy : function(){
41483         
41484         
41485         
41486         if(this.rendered){
41487             
41488             //for (var i =0; i < this.toolbars.length;i++) {
41489             //    // fixme - ask toolbars for heights?
41490             //    this.toolbars[i].onDestroy();
41491            // }
41492             
41493             //this.wrap.dom.innerHTML = '';
41494             //this.wrap.remove();
41495         }
41496     },
41497
41498     // private
41499     onFirstFocus : function(){
41500         
41501         this.assignDocWin();
41502         
41503         
41504         this.activated = true;
41505          
41506     
41507         if(Roo.isGecko){ // prevent silly gecko errors
41508             this.win.focus();
41509             var s = this.win.getSelection();
41510             if(!s.focusNode || s.focusNode.nodeType != 3){
41511                 var r = s.getRangeAt(0);
41512                 r.selectNodeContents((this.doc.body || this.doc.documentElement));
41513                 r.collapse(true);
41514                 this.deferFocus();
41515             }
41516             try{
41517                 this.execCmd('useCSS', true);
41518                 this.execCmd('styleWithCSS', false);
41519             }catch(e){}
41520         }
41521         this.owner.fireEvent('activate', this);
41522     },
41523
41524     // private
41525     adjustFont: function(btn){
41526         var adjust = btn.cmd == 'increasefontsize' ? 1 : -1;
41527         //if(Roo.isSafari){ // safari
41528         //    adjust *= 2;
41529        // }
41530         var v = parseInt(this.doc.queryCommandValue('FontSize')|| 3, 10);
41531         if(Roo.isSafari){ // safari
41532             var sm = { 10 : 1, 13: 2, 16:3, 18:4, 24: 5, 32:6, 48: 7 };
41533             v =  (v < 10) ? 10 : v;
41534             v =  (v > 48) ? 48 : v;
41535             v = typeof(sm[v]) == 'undefined' ? 1 : sm[v];
41536             
41537         }
41538         
41539         
41540         v = Math.max(1, v+adjust);
41541         
41542         this.execCmd('FontSize', v  );
41543     },
41544
41545     onEditorEvent : function(e){
41546         this.owner.fireEvent('editorevent', this, e);
41547       //  this.updateToolbar();
41548         this.syncValue(); //we can not sync so often.. sync cleans, so this breaks stuff
41549     },
41550
41551     insertTag : function(tg)
41552     {
41553         // could be a bit smarter... -> wrap the current selected tRoo..
41554         if (tg.toLowerCase() == 'span' || tg.toLowerCase() == 'code') {
41555             
41556             range = this.createRange(this.getSelection());
41557             var wrappingNode = this.doc.createElement(tg.toLowerCase());
41558             wrappingNode.appendChild(range.extractContents());
41559             range.insertNode(wrappingNode);
41560
41561             return;
41562             
41563             
41564             
41565         }
41566         this.execCmd("formatblock",   tg);
41567         
41568     },
41569     
41570     insertText : function(txt)
41571     {
41572         
41573         
41574         var range = this.createRange();
41575         range.deleteContents();
41576                //alert(Sender.getAttribute('label'));
41577                
41578         range.insertNode(this.doc.createTextNode(txt));
41579     } ,
41580     
41581      
41582
41583     /**
41584      * Executes a Midas editor command on the editor document and performs necessary focus and
41585      * toolbar updates. <b>This should only be called after the editor is initialized.</b>
41586      * @param {String} cmd The Midas command
41587      * @param {String/Boolean} value (optional) The value to pass to the command (defaults to null)
41588      */
41589     relayCmd : function(cmd, value){
41590         this.win.focus();
41591         this.execCmd(cmd, value);
41592         this.owner.fireEvent('editorevent', this);
41593         //this.updateToolbar();
41594         this.owner.deferFocus();
41595     },
41596
41597     /**
41598      * Executes a Midas editor command directly on the editor document.
41599      * For visual commands, you should use {@link #relayCmd} instead.
41600      * <b>This should only be called after the editor is initialized.</b>
41601      * @param {String} cmd The Midas command
41602      * @param {String/Boolean} value (optional) The value to pass to the command (defaults to null)
41603      */
41604     execCmd : function(cmd, value){
41605         this.doc.execCommand(cmd, false, value === undefined ? null : value);
41606         this.syncValue();
41607     },
41608  
41609  
41610    
41611     /**
41612      * Inserts the passed text at the current cursor position. Note: the editor must be initialized and activated
41613      * to insert tRoo.
41614      * @param {String} text | dom node.. 
41615      */
41616     insertAtCursor : function(text)
41617     {
41618         
41619         
41620         
41621         if(!this.activated){
41622             return;
41623         }
41624         /*
41625         if(Roo.isIE){
41626             this.win.focus();
41627             var r = this.doc.selection.createRange();
41628             if(r){
41629                 r.collapse(true);
41630                 r.pasteHTML(text);
41631                 this.syncValue();
41632                 this.deferFocus();
41633             
41634             }
41635             return;
41636         }
41637         */
41638         if(Roo.isGecko || Roo.isOpera || Roo.isSafari){
41639             this.win.focus();
41640             
41641             
41642             // from jquery ui (MIT licenced)
41643             var range, node;
41644             var win = this.win;
41645             
41646             if (win.getSelection && win.getSelection().getRangeAt) {
41647                 range = win.getSelection().getRangeAt(0);
41648                 node = typeof(text) == 'string' ? range.createContextualFragment(text) : text;
41649                 range.insertNode(node);
41650             } else if (win.document.selection && win.document.selection.createRange) {
41651                 // no firefox support
41652                 var txt = typeof(text) == 'string' ? text : text.outerHTML;
41653                 win.document.selection.createRange().pasteHTML(txt);
41654             } else {
41655                 // no firefox support
41656                 var txt = typeof(text) == 'string' ? text : text.outerHTML;
41657                 this.execCmd('InsertHTML', txt);
41658             } 
41659             
41660             this.syncValue();
41661             
41662             this.deferFocus();
41663         }
41664     },
41665  // private
41666     mozKeyPress : function(e){
41667         if(e.ctrlKey){
41668             var c = e.getCharCode(), cmd;
41669           
41670             if(c > 0){
41671                 c = String.fromCharCode(c).toLowerCase();
41672                 switch(c){
41673                     case 'b':
41674                         cmd = 'bold';
41675                         break;
41676                     case 'i':
41677                         cmd = 'italic';
41678                         break;
41679                     
41680                     case 'u':
41681                         cmd = 'underline';
41682                         break;
41683                     
41684                     case 'v':
41685                         this.cleanUpPaste.defer(100, this);
41686                         return;
41687                         
41688                 }
41689                 if(cmd){
41690                     this.win.focus();
41691                     this.execCmd(cmd);
41692                     this.deferFocus();
41693                     e.preventDefault();
41694                 }
41695                 
41696             }
41697         }
41698     },
41699
41700     // private
41701     fixKeys : function(){ // load time branching for fastest keydown performance
41702         if(Roo.isIE){
41703             return function(e){
41704                 var k = e.getKey(), r;
41705                 if(k == e.TAB){
41706                     e.stopEvent();
41707                     r = this.doc.selection.createRange();
41708                     if(r){
41709                         r.collapse(true);
41710                         r.pasteHTML('&#160;&#160;&#160;&#160;');
41711                         this.deferFocus();
41712                     }
41713                     return;
41714                 }
41715                 
41716                 if(k == e.ENTER){
41717                     r = this.doc.selection.createRange();
41718                     if(r){
41719                         var target = r.parentElement();
41720                         if(!target || target.tagName.toLowerCase() != 'li'){
41721                             e.stopEvent();
41722                             r.pasteHTML('<br />');
41723                             r.collapse(false);
41724                             r.select();
41725                         }
41726                     }
41727                 }
41728                 if (String.fromCharCode(k).toLowerCase() == 'v') { // paste
41729                     this.cleanUpPaste.defer(100, this);
41730                     return;
41731                 }
41732                 
41733                 
41734             };
41735         }else if(Roo.isOpera){
41736             return function(e){
41737                 var k = e.getKey();
41738                 if(k == e.TAB){
41739                     e.stopEvent();
41740                     this.win.focus();
41741                     this.execCmd('InsertHTML','&#160;&#160;&#160;&#160;');
41742                     this.deferFocus();
41743                 }
41744                 if (String.fromCharCode(k).toLowerCase() == 'v') { // paste
41745                     this.cleanUpPaste.defer(100, this);
41746                     return;
41747                 }
41748                 
41749             };
41750         }else if(Roo.isSafari){
41751             return function(e){
41752                 var k = e.getKey();
41753                 
41754                 if(k == e.TAB){
41755                     e.stopEvent();
41756                     this.execCmd('InsertText','\t');
41757                     this.deferFocus();
41758                     return;
41759                 }
41760                if (String.fromCharCode(k).toLowerCase() == 'v') { // paste
41761                     this.cleanUpPaste.defer(100, this);
41762                     return;
41763                 }
41764                 
41765              };
41766         }
41767     }(),
41768     
41769     getAllAncestors: function()
41770     {
41771         var p = this.getSelectedNode();
41772         var a = [];
41773         if (!p) {
41774             a.push(p); // push blank onto stack..
41775             p = this.getParentElement();
41776         }
41777         
41778         
41779         while (p && (p.nodeType == 1) && (p.tagName.toLowerCase() != 'body')) {
41780             a.push(p);
41781             p = p.parentNode;
41782         }
41783         a.push(this.doc.body);
41784         return a;
41785     },
41786     lastSel : false,
41787     lastSelNode : false,
41788     
41789     
41790     getSelection : function() 
41791     {
41792         this.assignDocWin();
41793         return Roo.isIE ? this.doc.selection : this.win.getSelection();
41794     },
41795     
41796     getSelectedNode: function() 
41797     {
41798         // this may only work on Gecko!!!
41799         
41800         // should we cache this!!!!
41801         
41802         
41803         
41804          
41805         var range = this.createRange(this.getSelection()).cloneRange();
41806         
41807         if (Roo.isIE) {
41808             var parent = range.parentElement();
41809             while (true) {
41810                 var testRange = range.duplicate();
41811                 testRange.moveToElementText(parent);
41812                 if (testRange.inRange(range)) {
41813                     break;
41814                 }
41815                 if ((parent.nodeType != 1) || (parent.tagName.toLowerCase() == 'body')) {
41816                     break;
41817                 }
41818                 parent = parent.parentElement;
41819             }
41820             return parent;
41821         }
41822         
41823         // is ancestor a text element.
41824         var ac =  range.commonAncestorContainer;
41825         if (ac.nodeType == 3) {
41826             ac = ac.parentNode;
41827         }
41828         
41829         var ar = ac.childNodes;
41830          
41831         var nodes = [];
41832         var other_nodes = [];
41833         var has_other_nodes = false;
41834         for (var i=0;i<ar.length;i++) {
41835             if ((ar[i].nodeType == 3) && (!ar[i].data.length)) { // empty text ? 
41836                 continue;
41837             }
41838             // fullly contained node.
41839             
41840             if (this.rangeIntersectsNode(range,ar[i]) && this.rangeCompareNode(range,ar[i]) == 3) {
41841                 nodes.push(ar[i]);
41842                 continue;
41843             }
41844             
41845             // probably selected..
41846             if ((ar[i].nodeType == 1) && this.rangeIntersectsNode(range,ar[i]) && (this.rangeCompareNode(range,ar[i]) > 0)) {
41847                 other_nodes.push(ar[i]);
41848                 continue;
41849             }
41850             // outer..
41851             if (!this.rangeIntersectsNode(range,ar[i])|| (this.rangeCompareNode(range,ar[i]) == 0))  {
41852                 continue;
41853             }
41854             
41855             
41856             has_other_nodes = true;
41857         }
41858         if (!nodes.length && other_nodes.length) {
41859             nodes= other_nodes;
41860         }
41861         if (has_other_nodes || !nodes.length || (nodes.length > 1)) {
41862             return false;
41863         }
41864         
41865         return nodes[0];
41866     },
41867     createRange: function(sel)
41868     {
41869         // this has strange effects when using with 
41870         // top toolbar - not sure if it's a great idea.
41871         //this.editor.contentWindow.focus();
41872         if (typeof sel != "undefined") {
41873             try {
41874                 return sel.getRangeAt ? sel.getRangeAt(0) : sel.createRange();
41875             } catch(e) {
41876                 return this.doc.createRange();
41877             }
41878         } else {
41879             return this.doc.createRange();
41880         }
41881     },
41882     getParentElement: function()
41883     {
41884         
41885         this.assignDocWin();
41886         var sel = Roo.isIE ? this.doc.selection : this.win.getSelection();
41887         
41888         var range = this.createRange(sel);
41889          
41890         try {
41891             var p = range.commonAncestorContainer;
41892             while (p.nodeType == 3) { // text node
41893                 p = p.parentNode;
41894             }
41895             return p;
41896         } catch (e) {
41897             return null;
41898         }
41899     
41900     },
41901     /***
41902      *
41903      * Range intersection.. the hard stuff...
41904      *  '-1' = before
41905      *  '0' = hits..
41906      *  '1' = after.
41907      *         [ -- selected range --- ]
41908      *   [fail]                        [fail]
41909      *
41910      *    basically..
41911      *      if end is before start or  hits it. fail.
41912      *      if start is after end or hits it fail.
41913      *
41914      *   if either hits (but other is outside. - then it's not 
41915      *   
41916      *    
41917      **/
41918     
41919     
41920     // @see http://www.thismuchiknow.co.uk/?p=64.
41921     rangeIntersectsNode : function(range, node)
41922     {
41923         var nodeRange = node.ownerDocument.createRange();
41924         try {
41925             nodeRange.selectNode(node);
41926         } catch (e) {
41927             nodeRange.selectNodeContents(node);
41928         }
41929     
41930         var rangeStartRange = range.cloneRange();
41931         rangeStartRange.collapse(true);
41932     
41933         var rangeEndRange = range.cloneRange();
41934         rangeEndRange.collapse(false);
41935     
41936         var nodeStartRange = nodeRange.cloneRange();
41937         nodeStartRange.collapse(true);
41938     
41939         var nodeEndRange = nodeRange.cloneRange();
41940         nodeEndRange.collapse(false);
41941     
41942         return rangeStartRange.compareBoundaryPoints(
41943                  Range.START_TO_START, nodeEndRange) == -1 &&
41944                rangeEndRange.compareBoundaryPoints(
41945                  Range.START_TO_START, nodeStartRange) == 1;
41946         
41947          
41948     },
41949     rangeCompareNode : function(range, node)
41950     {
41951         var nodeRange = node.ownerDocument.createRange();
41952         try {
41953             nodeRange.selectNode(node);
41954         } catch (e) {
41955             nodeRange.selectNodeContents(node);
41956         }
41957         
41958         
41959         range.collapse(true);
41960     
41961         nodeRange.collapse(true);
41962      
41963         var ss = range.compareBoundaryPoints( Range.START_TO_START, nodeRange);
41964         var ee = range.compareBoundaryPoints(  Range.END_TO_END, nodeRange);
41965          
41966         //Roo.log(node.tagName + ': ss='+ss +', ee='+ee)
41967         
41968         var nodeIsBefore   =  ss == 1;
41969         var nodeIsAfter    = ee == -1;
41970         
41971         if (nodeIsBefore && nodeIsAfter)
41972             return 0; // outer
41973         if (!nodeIsBefore && nodeIsAfter)
41974             return 1; //right trailed.
41975         
41976         if (nodeIsBefore && !nodeIsAfter)
41977             return 2;  // left trailed.
41978         // fully contined.
41979         return 3;
41980     },
41981
41982     // private? - in a new class?
41983     cleanUpPaste :  function()
41984     {
41985         // cleans up the whole document..
41986         Roo.log('cleanuppaste');
41987         
41988         this.cleanUpChildren(this.doc.body);
41989         var clean = this.cleanWordChars(this.doc.body.innerHTML);
41990         if (clean != this.doc.body.innerHTML) {
41991             this.doc.body.innerHTML = clean;
41992         }
41993         
41994     },
41995     
41996     cleanWordChars : function(input) {// change the chars to hex code
41997         var he = Roo.HtmlEditorCore;
41998         
41999         var output = input;
42000         Roo.each(he.swapCodes, function(sw) { 
42001             var swapper = new RegExp("\\u" + sw[0].toString(16), "g"); // hex codes
42002             
42003             output = output.replace(swapper, sw[1]);
42004         });
42005         
42006         return output;
42007     },
42008     
42009     
42010     cleanUpChildren : function (n)
42011     {
42012         if (!n.childNodes.length) {
42013             return;
42014         }
42015         for (var i = n.childNodes.length-1; i > -1 ; i--) {
42016            this.cleanUpChild(n.childNodes[i]);
42017         }
42018     },
42019     
42020     
42021         
42022     
42023     cleanUpChild : function (node)
42024     {
42025         var ed = this;
42026         //console.log(node);
42027         if (node.nodeName == "#text") {
42028             // clean up silly Windows -- stuff?
42029             return; 
42030         }
42031         if (node.nodeName == "#comment") {
42032             node.parentNode.removeChild(node);
42033             // clean up silly Windows -- stuff?
42034             return; 
42035         }
42036         var lcname = node.tagName.toLowerCase();
42037         // we ignore whitelists... ?? = not really the way to go, but we probably have not got a full
42038         // whitelist of tags..
42039         
42040         if (this.black.indexOf(lcname) > -1 && this.clearUp ) {
42041             // remove node.
42042             node.parentNode.removeChild(node);
42043             return;
42044             
42045         }
42046         
42047         var remove_keep_children= Roo.HtmlEditorCore.remove.indexOf(node.tagName.toLowerCase()) > -1;
42048         
42049         // remove <a name=....> as rendering on yahoo mailer is borked with this.
42050         // this will have to be flaged elsewhere - perhaps ablack=name... on the mailer..
42051         
42052         //if (node.tagName.toLowerCase() == 'a' && !node.hasAttribute('href')) {
42053         //    remove_keep_children = true;
42054         //}
42055         
42056         if (remove_keep_children) {
42057             this.cleanUpChildren(node);
42058             // inserts everything just before this node...
42059             while (node.childNodes.length) {
42060                 var cn = node.childNodes[0];
42061                 node.removeChild(cn);
42062                 node.parentNode.insertBefore(cn, node);
42063             }
42064             node.parentNode.removeChild(node);
42065             return;
42066         }
42067         
42068         if (!node.attributes || !node.attributes.length) {
42069             this.cleanUpChildren(node);
42070             return;
42071         }
42072         
42073         function cleanAttr(n,v)
42074         {
42075             
42076             if (v.match(/^\./) || v.match(/^\//)) {
42077                 return;
42078             }
42079             if (v.match(/^(http|https):\/\//) || v.match(/^mailto:/)) {
42080                 return;
42081             }
42082             if (v.match(/^#/)) {
42083                 return;
42084             }
42085 //            Roo.log("(REMOVE TAG)"+ node.tagName +'.' + n + '=' + v);
42086             node.removeAttribute(n);
42087             
42088         }
42089         
42090         var cwhite = this.cwhite;
42091         var cblack = this.cblack;
42092             
42093         function cleanStyle(n,v)
42094         {
42095             if (v.match(/expression/)) { //XSS?? should we even bother..
42096                 node.removeAttribute(n);
42097                 return;
42098             }
42099             
42100             var parts = v.split(/;/);
42101             var clean = [];
42102             
42103             Roo.each(parts, function(p) {
42104                 p = p.replace(/^\s+/g,'').replace(/\s+$/g,'');
42105                 if (!p.length) {
42106                     return true;
42107                 }
42108                 var l = p.split(':').shift().replace(/\s+/g,'');
42109                 l = l.replace(/^\s+/g,'').replace(/\s+$/g,'');
42110                 
42111                 if ( cwhite.length && cblack.indexOf(l) > -1) {
42112 //                    Roo.log('(REMOVE CSS)' + node.tagName +'.' + n + ':'+l + '=' + v);
42113                     //node.removeAttribute(n);
42114                     return true;
42115                 }
42116                 //Roo.log()
42117                 // only allow 'c whitelisted system attributes'
42118                 if ( cwhite.length &&  cwhite.indexOf(l) < 0) {
42119 //                    Roo.log('(REMOVE CSS)' + node.tagName +'.' + n + ':'+l + '=' + v);
42120                     //node.removeAttribute(n);
42121                     return true;
42122                 }
42123                 
42124                 
42125                  
42126                 
42127                 clean.push(p);
42128                 return true;
42129             });
42130             if (clean.length) { 
42131                 node.setAttribute(n, clean.join(';'));
42132             } else {
42133                 node.removeAttribute(n);
42134             }
42135             
42136         }
42137         
42138         
42139         for (var i = node.attributes.length-1; i > -1 ; i--) {
42140             var a = node.attributes[i];
42141             //console.log(a);
42142             
42143             if (a.name.toLowerCase().substr(0,2)=='on')  {
42144                 node.removeAttribute(a.name);
42145                 continue;
42146             }
42147             if (Roo.HtmlEditorCore.ablack.indexOf(a.name.toLowerCase()) > -1) {
42148                 node.removeAttribute(a.name);
42149                 continue;
42150             }
42151             if (Roo.HtmlEditorCore.aclean.indexOf(a.name.toLowerCase()) > -1) {
42152                 cleanAttr(a.name,a.value); // fixme..
42153                 continue;
42154             }
42155             if (a.name == 'style') {
42156                 cleanStyle(a.name,a.value);
42157                 continue;
42158             }
42159             /// clean up MS crap..
42160             // tecnically this should be a list of valid class'es..
42161             
42162             
42163             if (a.name == 'class') {
42164                 if (a.value.match(/^Mso/)) {
42165                     node.className = '';
42166                 }
42167                 
42168                 if (a.value.match(/body/)) {
42169                     node.className = '';
42170                 }
42171                 continue;
42172             }
42173             
42174             // style cleanup!?
42175             // class cleanup?
42176             
42177         }
42178         
42179         
42180         this.cleanUpChildren(node);
42181         
42182         
42183     },
42184     /**
42185      * Clean up MS wordisms...
42186      */
42187     cleanWord : function(node)
42188     {
42189         var _t = this;
42190         var cleanWordChildren = function()
42191         {
42192             if (!node.childNodes.length) {
42193                 return;
42194             }
42195             for (var i = node.childNodes.length-1; i > -1 ; i--) {
42196                _t.cleanWord(node.childNodes[i]);
42197             }
42198         }
42199         
42200         
42201         if (!node) {
42202             this.cleanWord(this.doc.body);
42203             return;
42204         }
42205         if (node.nodeName == "#text") {
42206             // clean up silly Windows -- stuff?
42207             return; 
42208         }
42209         if (node.nodeName == "#comment") {
42210             node.parentNode.removeChild(node);
42211             // clean up silly Windows -- stuff?
42212             return; 
42213         }
42214         
42215         if (node.tagName.toLowerCase().match(/^(style|script|applet|embed|noframes|noscript)$/)) {
42216             node.parentNode.removeChild(node);
42217             return;
42218         }
42219         
42220         // remove - but keep children..
42221         if (node.tagName.toLowerCase().match(/^(meta|link|\\?xml:|st1:|o:|font)/)) {
42222             while (node.childNodes.length) {
42223                 var cn = node.childNodes[0];
42224                 node.removeChild(cn);
42225                 node.parentNode.insertBefore(cn, node);
42226             }
42227             node.parentNode.removeChild(node);
42228             cleanWordChildren();
42229             return;
42230         }
42231         // clean styles
42232         if (node.className.length) {
42233             
42234             var cn = node.className.split(/\W+/);
42235             var cna = [];
42236             Roo.each(cn, function(cls) {
42237                 if (cls.match(/Mso[a-zA-Z]+/)) {
42238                     return;
42239                 }
42240                 cna.push(cls);
42241             });
42242             node.className = cna.length ? cna.join(' ') : '';
42243             if (!cna.length) {
42244                 node.removeAttribute("class");
42245             }
42246         }
42247         
42248         if (node.hasAttribute("lang")) {
42249             node.removeAttribute("lang");
42250         }
42251         
42252         if (node.hasAttribute("style")) {
42253             
42254             var styles = node.getAttribute("style").split(";");
42255             var nstyle = [];
42256             Roo.each(styles, function(s) {
42257                 if (!s.match(/:/)) {
42258                     return;
42259                 }
42260                 var kv = s.split(":");
42261                 if (kv[0].match(/^(mso-|line|font|background|margin|padding|color)/)) {
42262                     return;
42263                 }
42264                 // what ever is left... we allow.
42265                 nstyle.push(s);
42266             });
42267             node.setAttribute("style", nstyle.length ? nstyle.join(';') : '');
42268             if (!nstyle.length) {
42269                 node.removeAttribute('style');
42270             }
42271         }
42272         
42273         cleanWordChildren();
42274         
42275         
42276     },
42277     domToHTML : function(currentElement, depth, nopadtext) {
42278         
42279         depth = depth || 0;
42280         nopadtext = nopadtext || false;
42281     
42282         if (!currentElement) {
42283             return this.domToHTML(this.doc.body);
42284         }
42285         
42286         //Roo.log(currentElement);
42287         var j;
42288         var allText = false;
42289         var nodeName = currentElement.nodeName;
42290         var tagName = Roo.util.Format.htmlEncode(currentElement.tagName);
42291         
42292         if  (nodeName == '#text') {
42293             return currentElement.nodeValue;
42294         }
42295         
42296         
42297         var ret = '';
42298         if (nodeName != 'BODY') {
42299              
42300             var i = 0;
42301             // Prints the node tagName, such as <A>, <IMG>, etc
42302             if (tagName) {
42303                 var attr = [];
42304                 for(i = 0; i < currentElement.attributes.length;i++) {
42305                     // quoting?
42306                     var aname = currentElement.attributes.item(i).name;
42307                     if (!currentElement.attributes.item(i).value.length) {
42308                         continue;
42309                     }
42310                     attr.push(aname + '="' + Roo.util.Format.htmlEncode(currentElement.attributes.item(i).value) + '"' );
42311                 }
42312                 
42313                 ret = "<"+currentElement.tagName+ ( attr.length ? (' ' + attr.join(' ') ) : '') + ">";
42314             } 
42315             else {
42316                 
42317                 // eack
42318             }
42319         } else {
42320             tagName = false;
42321         }
42322         if (['IMG', 'BR', 'HR', 'INPUT'].indexOf(tagName) > -1) {
42323             return ret;
42324         }
42325         if (['PRE', 'TEXTAREA', 'TD', 'A', 'SPAN'].indexOf(tagName) > -1) { // or code?
42326             nopadtext = true;
42327         }
42328         
42329         
42330         // Traverse the tree
42331         i = 0;
42332         var currentElementChild = currentElement.childNodes.item(i);
42333         var allText = true;
42334         var innerHTML  = '';
42335         lastnode = '';
42336         while (currentElementChild) {
42337             // Formatting code (indent the tree so it looks nice on the screen)
42338             var nopad = nopadtext;
42339             if (lastnode == 'SPAN') {
42340                 nopad  = true;
42341             }
42342             // text
42343             if  (currentElementChild.nodeName == '#text') {
42344                 var toadd = Roo.util.Format.htmlEncode(currentElementChild.nodeValue);
42345                 if (!nopad && toadd.length > 80) {
42346                     innerHTML  += "\n" + (new Array( depth + 1 )).join( "  "  );
42347                 }
42348                 innerHTML  += toadd;
42349                 
42350                 i++;
42351                 currentElementChild = currentElement.childNodes.item(i);
42352                 lastNode = '';
42353                 continue;
42354             }
42355             allText = false;
42356             
42357             innerHTML  += nopad ? '' : "\n" + (new Array( depth + 1 )).join( "  "  );
42358                 
42359             // Recursively traverse the tree structure of the child node
42360             innerHTML   += this.domToHTML(currentElementChild, depth+1, nopadtext);
42361             lastnode = currentElementChild.nodeName;
42362             i++;
42363             currentElementChild=currentElement.childNodes.item(i);
42364         }
42365         
42366         ret += innerHTML;
42367         
42368         if (!allText) {
42369                 // The remaining code is mostly for formatting the tree
42370             ret+= nopadtext ? '' : "\n" + (new Array( depth  )).join( "  "  );
42371         }
42372         
42373         
42374         if (tagName) {
42375             ret+= "</"+tagName+">";
42376         }
42377         return ret;
42378         
42379     },
42380         
42381     applyBlacklists : function()
42382     {
42383         var w = typeof(this.owner.white) != 'undefined' && this.owner.white ? this.owner.white  : [];
42384         var b = typeof(this.owner.black) != 'undefined' && this.owner.black ? this.owner.black :  [];
42385         
42386         this.white = [];
42387         this.black = [];
42388         Roo.each(Roo.HtmlEditorCore.white, function(tag) {
42389             if (b.indexOf(tag) > -1) {
42390                 return;
42391             }
42392             this.white.push(tag);
42393             
42394         }, this);
42395         
42396         Roo.each(w, function(tag) {
42397             if (b.indexOf(tag) > -1) {
42398                 return;
42399             }
42400             if (this.white.indexOf(tag) > -1) {
42401                 return;
42402             }
42403             this.white.push(tag);
42404             
42405         }, this);
42406         
42407         
42408         Roo.each(Roo.HtmlEditorCore.black, function(tag) {
42409             if (w.indexOf(tag) > -1) {
42410                 return;
42411             }
42412             this.black.push(tag);
42413             
42414         }, this);
42415         
42416         Roo.each(b, function(tag) {
42417             if (w.indexOf(tag) > -1) {
42418                 return;
42419             }
42420             if (this.black.indexOf(tag) > -1) {
42421                 return;
42422             }
42423             this.black.push(tag);
42424             
42425         }, this);
42426         
42427         
42428         w = typeof(this.owner.cwhite) != 'undefined' && this.owner.cwhite ? this.owner.cwhite  : [];
42429         b = typeof(this.owner.cblack) != 'undefined' && this.owner.cblack ? this.owner.cblack :  [];
42430         
42431         this.cwhite = [];
42432         this.cblack = [];
42433         Roo.each(Roo.HtmlEditorCore.cwhite, function(tag) {
42434             if (b.indexOf(tag) > -1) {
42435                 return;
42436             }
42437             this.cwhite.push(tag);
42438             
42439         }, this);
42440         
42441         Roo.each(w, function(tag) {
42442             if (b.indexOf(tag) > -1) {
42443                 return;
42444             }
42445             if (this.cwhite.indexOf(tag) > -1) {
42446                 return;
42447             }
42448             this.cwhite.push(tag);
42449             
42450         }, this);
42451         
42452         
42453         Roo.each(Roo.HtmlEditorCore.cblack, function(tag) {
42454             if (w.indexOf(tag) > -1) {
42455                 return;
42456             }
42457             this.cblack.push(tag);
42458             
42459         }, this);
42460         
42461         Roo.each(b, function(tag) {
42462             if (w.indexOf(tag) > -1) {
42463                 return;
42464             }
42465             if (this.cblack.indexOf(tag) > -1) {
42466                 return;
42467             }
42468             this.cblack.push(tag);
42469             
42470         }, this);
42471     }
42472     
42473     // hide stuff that is not compatible
42474     /**
42475      * @event blur
42476      * @hide
42477      */
42478     /**
42479      * @event change
42480      * @hide
42481      */
42482     /**
42483      * @event focus
42484      * @hide
42485      */
42486     /**
42487      * @event specialkey
42488      * @hide
42489      */
42490     /**
42491      * @cfg {String} fieldClass @hide
42492      */
42493     /**
42494      * @cfg {String} focusClass @hide
42495      */
42496     /**
42497      * @cfg {String} autoCreate @hide
42498      */
42499     /**
42500      * @cfg {String} inputType @hide
42501      */
42502     /**
42503      * @cfg {String} invalidClass @hide
42504      */
42505     /**
42506      * @cfg {String} invalidText @hide
42507      */
42508     /**
42509      * @cfg {String} msgFx @hide
42510      */
42511     /**
42512      * @cfg {String} validateOnBlur @hide
42513      */
42514 });
42515
42516 Roo.HtmlEditorCore.white = [
42517         'area', 'br', 'img', 'input', 'hr', 'wbr',
42518         
42519        'address', 'blockquote', 'center', 'dd',      'dir',       'div', 
42520        'dl',      'dt',         'h1',     'h2',      'h3',        'h4', 
42521        'h5',      'h6',         'hr',     'isindex', 'listing',   'marquee', 
42522        'menu',    'multicol',   'ol',     'p',       'plaintext', 'pre', 
42523        'table',   'ul',         'xmp', 
42524        
42525        'caption', 'col', 'colgroup', 'tbody', 'td', 'tfoot', 'th', 
42526       'thead',   'tr', 
42527      
42528       'dir', 'menu', 'ol', 'ul', 'dl',
42529        
42530       'embed',  'object'
42531 ];
42532
42533
42534 Roo.HtmlEditorCore.black = [
42535     //    'embed',  'object', // enable - backend responsiblity to clean thiese
42536         'applet', // 
42537         'base',   'basefont', 'bgsound', 'blink',  'body', 
42538         'frame',  'frameset', 'head',    'html',   'ilayer', 
42539         'iframe', 'layer',  'link',     'meta',    'object',   
42540         'script', 'style' ,'title',  'xml' // clean later..
42541 ];
42542 Roo.HtmlEditorCore.clean = [
42543     'script', 'style', 'title', 'xml'
42544 ];
42545 Roo.HtmlEditorCore.remove = [
42546     'font'
42547 ];
42548 // attributes..
42549
42550 Roo.HtmlEditorCore.ablack = [
42551     'on'
42552 ];
42553     
42554 Roo.HtmlEditorCore.aclean = [ 
42555     'action', 'background', 'codebase', 'dynsrc', 'href', 'lowsrc' 
42556 ];
42557
42558 // protocols..
42559 Roo.HtmlEditorCore.pwhite= [
42560         'http',  'https',  'mailto'
42561 ];
42562
42563 // white listed style attributes.
42564 Roo.HtmlEditorCore.cwhite= [
42565       //  'text-align', /// default is to allow most things..
42566       
42567          
42568 //        'font-size'//??
42569 ];
42570
42571 // black listed style attributes.
42572 Roo.HtmlEditorCore.cblack= [
42573       //  'font-size' -- this can be set by the project 
42574 ];
42575
42576
42577 Roo.HtmlEditorCore.swapCodes   =[ 
42578     [    8211, "--" ], 
42579     [    8212, "--" ], 
42580     [    8216,  "'" ],  
42581     [    8217, "'" ],  
42582     [    8220, '"' ],  
42583     [    8221, '"' ],  
42584     [    8226, "*" ],  
42585     [    8230, "..." ]
42586 ]; 
42587
42588     //<script type="text/javascript">
42589
42590 /*
42591  * Ext JS Library 1.1.1
42592  * Copyright(c) 2006-2007, Ext JS, LLC.
42593  * Licence LGPL
42594  * 
42595  */
42596  
42597  
42598 Roo.form.HtmlEditor = function(config){
42599     
42600     
42601     
42602     Roo.form.HtmlEditor.superclass.constructor.call(this, config);
42603     
42604     if (!this.toolbars) {
42605         this.toolbars = [];
42606     }
42607     this.editorcore = new Roo.HtmlEditorCore(Roo.apply({ owner : this} , config));
42608     
42609     
42610 };
42611
42612 /**
42613  * @class Roo.form.HtmlEditor
42614  * @extends Roo.form.Field
42615  * Provides a lightweight HTML Editor component.
42616  *
42617  * This has been tested on Fireforx / Chrome.. IE may not be so great..
42618  * 
42619  * <br><br><b>Note: The focus/blur and validation marking functionality inherited from Ext.form.Field is NOT
42620  * supported by this editor.</b><br/><br/>
42621  * An Editor is a sensitive component that can't be used in all spots standard fields can be used. Putting an Editor within
42622  * any element that has display set to 'none' can cause problems in Safari and Firefox.<br/><br/>
42623  */
42624 Roo.extend(Roo.form.HtmlEditor, Roo.form.Field, {
42625     /**
42626      * @cfg {Boolean} clearUp
42627      */
42628     clearUp : true,
42629       /**
42630      * @cfg {Array} toolbars Array of toolbars. - defaults to just the Standard one
42631      */
42632     toolbars : false,
42633    
42634      /**
42635      * @cfg {String} resizable  's' or 'se' or 'e' - wrapps the element in a
42636      *                        Roo.resizable.
42637      */
42638     resizable : false,
42639      /**
42640      * @cfg {Number} height (in pixels)
42641      */   
42642     height: 300,
42643    /**
42644      * @cfg {Number} width (in pixels)
42645      */   
42646     width: 500,
42647     
42648     /**
42649      * @cfg {Array} stylesheets url of stylesheets. set to [] to disable stylesheets.
42650      * 
42651      */
42652     stylesheets: false,
42653     
42654     
42655      /**
42656      * @cfg {Array} blacklist of css styles style attributes (blacklist overrides whitelist)
42657      * 
42658      */
42659     cblack: false,
42660     /**
42661      * @cfg {Array} whitelist of css styles style attributes (blacklist overrides whitelist)
42662      * 
42663      */
42664     cwhite: false,
42665     
42666      /**
42667      * @cfg {Array} blacklist of html tags - in addition to standard blacklist.
42668      * 
42669      */
42670     black: false,
42671     /**
42672      * @cfg {Array} whitelist of html tags - in addition to statndard whitelist
42673      * 
42674      */
42675     white: false,
42676     
42677     // id of frame..
42678     frameId: false,
42679     
42680     // private properties
42681     validationEvent : false,
42682     deferHeight: true,
42683     initialized : false,
42684     activated : false,
42685     
42686     onFocus : Roo.emptyFn,
42687     iframePad:3,
42688     hideMode:'offsets',
42689     
42690     actionMode : 'container', // defaults to hiding it...
42691     
42692     defaultAutoCreate : { // modified by initCompnoent..
42693         tag: "textarea",
42694         style:"width:500px;height:300px;",
42695         autocomplete: "off"
42696     },
42697
42698     // private
42699     initComponent : function(){
42700         this.addEvents({
42701             /**
42702              * @event initialize
42703              * Fires when the editor is fully initialized (including the iframe)
42704              * @param {HtmlEditor} this
42705              */
42706             initialize: true,
42707             /**
42708              * @event activate
42709              * Fires when the editor is first receives the focus. Any insertion must wait
42710              * until after this event.
42711              * @param {HtmlEditor} this
42712              */
42713             activate: true,
42714              /**
42715              * @event beforesync
42716              * Fires before the textarea is updated with content from the editor iframe. Return false
42717              * to cancel the sync.
42718              * @param {HtmlEditor} this
42719              * @param {String} html
42720              */
42721             beforesync: true,
42722              /**
42723              * @event beforepush
42724              * Fires before the iframe editor is updated with content from the textarea. Return false
42725              * to cancel the push.
42726              * @param {HtmlEditor} this
42727              * @param {String} html
42728              */
42729             beforepush: true,
42730              /**
42731              * @event sync
42732              * Fires when the textarea is updated with content from the editor iframe.
42733              * @param {HtmlEditor} this
42734              * @param {String} html
42735              */
42736             sync: true,
42737              /**
42738              * @event push
42739              * Fires when the iframe editor is updated with content from the textarea.
42740              * @param {HtmlEditor} this
42741              * @param {String} html
42742              */
42743             push: true,
42744              /**
42745              * @event editmodechange
42746              * Fires when the editor switches edit modes
42747              * @param {HtmlEditor} this
42748              * @param {Boolean} sourceEdit True if source edit, false if standard editing.
42749              */
42750             editmodechange: true,
42751             /**
42752              * @event editorevent
42753              * Fires when on any editor (mouse up/down cursor movement etc.) - used for toolbar hooks.
42754              * @param {HtmlEditor} this
42755              */
42756             editorevent: true,
42757             /**
42758              * @event firstfocus
42759              * Fires when on first focus - needed by toolbars..
42760              * @param {HtmlEditor} this
42761              */
42762             firstfocus: true,
42763             /**
42764              * @event autosave
42765              * Auto save the htmlEditor value as a file into Events
42766              * @param {HtmlEditor} this
42767              */
42768             autosave: true,
42769             /**
42770              * @event savedpreview
42771              * preview the saved version of htmlEditor
42772              * @param {HtmlEditor} this
42773              */
42774             savedpreview: true
42775         });
42776         this.defaultAutoCreate =  {
42777             tag: "textarea",
42778             style:'width: ' + this.width + 'px;height: ' + this.height + 'px;',
42779             autocomplete: "off"
42780         };
42781     },
42782
42783     /**
42784      * Protected method that will not generally be called directly. It
42785      * is called when the editor creates its toolbar. Override this method if you need to
42786      * add custom toolbar buttons.
42787      * @param {HtmlEditor} editor
42788      */
42789     createToolbar : function(editor){
42790         Roo.log("create toolbars");
42791         if (!editor.toolbars || !editor.toolbars.length) {
42792             editor.toolbars = [ new Roo.form.HtmlEditor.ToolbarStandard() ]; // can be empty?
42793         }
42794         
42795         for (var i =0 ; i < editor.toolbars.length;i++) {
42796             editor.toolbars[i] = Roo.factory(
42797                     typeof(editor.toolbars[i]) == 'string' ?
42798                         { xtype: editor.toolbars[i]} : editor.toolbars[i],
42799                 Roo.form.HtmlEditor);
42800             editor.toolbars[i].init(editor);
42801         }
42802          
42803         
42804     },
42805
42806      
42807     // private
42808     onRender : function(ct, position)
42809     {
42810         var _t = this;
42811         Roo.form.HtmlEditor.superclass.onRender.call(this, ct, position);
42812         
42813         this.wrap = this.el.wrap({
42814             cls:'x-html-editor-wrap', cn:{cls:'x-html-editor-tb'}
42815         });
42816         
42817         this.editorcore.onRender(ct, position);
42818          
42819         if (this.resizable) {
42820             this.resizeEl = new Roo.Resizable(this.wrap, {
42821                 pinned : true,
42822                 wrap: true,
42823                 dynamic : true,
42824                 minHeight : this.height,
42825                 height: this.height,
42826                 handles : this.resizable,
42827                 width: this.width,
42828                 listeners : {
42829                     resize : function(r, w, h) {
42830                         _t.onResize(w,h); // -something
42831                     }
42832                 }
42833             });
42834             
42835         }
42836         this.createToolbar(this);
42837        
42838         
42839         if(!this.width){
42840             this.setSize(this.wrap.getSize());
42841         }
42842         if (this.resizeEl) {
42843             this.resizeEl.resizeTo.defer(100, this.resizeEl,[ this.width,this.height ] );
42844             // should trigger onReize..
42845         }
42846         
42847 //        if(this.autosave && this.w){
42848 //            this.autoSaveFn = setInterval(this.autosave, 1000);
42849 //        }
42850     },
42851
42852     // private
42853     onResize : function(w, h)
42854     {
42855         //Roo.log('resize: ' +w + ',' + h );
42856         Roo.form.HtmlEditor.superclass.onResize.apply(this, arguments);
42857         var ew = false;
42858         var eh = false;
42859         
42860         if(this.el ){
42861             if(typeof w == 'number'){
42862                 var aw = w - this.wrap.getFrameWidth('lr');
42863                 this.el.setWidth(this.adjustWidth('textarea', aw));
42864                 ew = aw;
42865             }
42866             if(typeof h == 'number'){
42867                 var tbh = 0;
42868                 for (var i =0; i < this.toolbars.length;i++) {
42869                     // fixme - ask toolbars for heights?
42870                     tbh += this.toolbars[i].tb.el.getHeight();
42871                     if (this.toolbars[i].footer) {
42872                         tbh += this.toolbars[i].footer.el.getHeight();
42873                     }
42874                 }
42875                 
42876                 
42877                 
42878                 
42879                 var ah = h - this.wrap.getFrameWidth('tb') - tbh;// this.tb.el.getHeight();
42880                 ah -= 5; // knock a few pixes off for look..
42881                 this.el.setHeight(this.adjustWidth('textarea', ah));
42882                 var eh = ah;
42883             }
42884         }
42885         Roo.log('onResize:' + [w,h,ew,eh].join(',') );
42886         this.editorcore.onResize(ew,eh);
42887         
42888     },
42889
42890     /**
42891      * Toggles the editor between standard and source edit mode.
42892      * @param {Boolean} sourceEdit (optional) True for source edit, false for standard
42893      */
42894     toggleSourceEdit : function(sourceEditMode)
42895     {
42896         this.editorcore.toggleSourceEdit(sourceEditMode);
42897         
42898         if(this.editorcore.sourceEditMode){
42899             Roo.log('editor - showing textarea');
42900             
42901 //            Roo.log('in');
42902 //            Roo.log(this.syncValue());
42903             this.editorcore.syncValue();
42904             this.el.removeClass('x-hidden');
42905             this.el.dom.removeAttribute('tabIndex');
42906             this.el.focus();
42907         }else{
42908             Roo.log('editor - hiding textarea');
42909 //            Roo.log('out')
42910 //            Roo.log(this.pushValue()); 
42911             this.editorcore.pushValue();
42912             
42913             this.el.addClass('x-hidden');
42914             this.el.dom.setAttribute('tabIndex', -1);
42915             //this.deferFocus();
42916         }
42917          
42918         this.setSize(this.wrap.getSize());
42919         this.fireEvent('editmodechange', this, this.editorcore.sourceEditMode);
42920     },
42921  
42922     // private (for BoxComponent)
42923     adjustSize : Roo.BoxComponent.prototype.adjustSize,
42924
42925     // private (for BoxComponent)
42926     getResizeEl : function(){
42927         return this.wrap;
42928     },
42929
42930     // private (for BoxComponent)
42931     getPositionEl : function(){
42932         return this.wrap;
42933     },
42934
42935     // private
42936     initEvents : function(){
42937         this.originalValue = this.getValue();
42938     },
42939
42940     /**
42941      * Overridden and disabled. The editor element does not support standard valid/invalid marking. @hide
42942      * @method
42943      */
42944     markInvalid : Roo.emptyFn,
42945     /**
42946      * Overridden and disabled. The editor element does not support standard valid/invalid marking. @hide
42947      * @method
42948      */
42949     clearInvalid : Roo.emptyFn,
42950
42951     setValue : function(v){
42952         Roo.form.HtmlEditor.superclass.setValue.call(this, v);
42953         this.editorcore.pushValue();
42954     },
42955
42956      
42957     // private
42958     deferFocus : function(){
42959         this.focus.defer(10, this);
42960     },
42961
42962     // doc'ed in Field
42963     focus : function(){
42964         this.editorcore.focus();
42965         
42966     },
42967       
42968
42969     // private
42970     onDestroy : function(){
42971         
42972         
42973         
42974         if(this.rendered){
42975             
42976             for (var i =0; i < this.toolbars.length;i++) {
42977                 // fixme - ask toolbars for heights?
42978                 this.toolbars[i].onDestroy();
42979             }
42980             
42981             this.wrap.dom.innerHTML = '';
42982             this.wrap.remove();
42983         }
42984     },
42985
42986     // private
42987     onFirstFocus : function(){
42988         //Roo.log("onFirstFocus");
42989         this.editorcore.onFirstFocus();
42990          for (var i =0; i < this.toolbars.length;i++) {
42991             this.toolbars[i].onFirstFocus();
42992         }
42993         
42994     },
42995     
42996     // private
42997     syncValue : function()
42998     {
42999         this.editorcore.syncValue();
43000     },
43001     
43002     pushValue : function()
43003     {
43004         this.editorcore.pushValue();
43005     }
43006      
43007     
43008     // hide stuff that is not compatible
43009     /**
43010      * @event blur
43011      * @hide
43012      */
43013     /**
43014      * @event change
43015      * @hide
43016      */
43017     /**
43018      * @event focus
43019      * @hide
43020      */
43021     /**
43022      * @event specialkey
43023      * @hide
43024      */
43025     /**
43026      * @cfg {String} fieldClass @hide
43027      */
43028     /**
43029      * @cfg {String} focusClass @hide
43030      */
43031     /**
43032      * @cfg {String} autoCreate @hide
43033      */
43034     /**
43035      * @cfg {String} inputType @hide
43036      */
43037     /**
43038      * @cfg {String} invalidClass @hide
43039      */
43040     /**
43041      * @cfg {String} invalidText @hide
43042      */
43043     /**
43044      * @cfg {String} msgFx @hide
43045      */
43046     /**
43047      * @cfg {String} validateOnBlur @hide
43048      */
43049 });
43050  
43051     // <script type="text/javascript">
43052 /*
43053  * Based on
43054  * Ext JS Library 1.1.1
43055  * Copyright(c) 2006-2007, Ext JS, LLC.
43056  *  
43057  
43058  */
43059
43060 /**
43061  * @class Roo.form.HtmlEditorToolbar1
43062  * Basic Toolbar
43063  * 
43064  * Usage:
43065  *
43066  new Roo.form.HtmlEditor({
43067     ....
43068     toolbars : [
43069         new Roo.form.HtmlEditorToolbar1({
43070             disable : { fonts: 1 , format: 1, ..., ... , ...],
43071             btns : [ .... ]
43072         })
43073     }
43074      
43075  * 
43076  * @cfg {Object} disable List of elements to disable..
43077  * @cfg {Array} btns List of additional buttons.
43078  * 
43079  * 
43080  * NEEDS Extra CSS? 
43081  * .x-html-editor-tb .x-edit-none .x-btn-text { background: none; }
43082  */
43083  
43084 Roo.form.HtmlEditor.ToolbarStandard = function(config)
43085 {
43086     
43087     Roo.apply(this, config);
43088     
43089     // default disabled, based on 'good practice'..
43090     this.disable = this.disable || {};
43091     Roo.applyIf(this.disable, {
43092         fontSize : true,
43093         colors : true,
43094         specialElements : true
43095     });
43096     
43097     
43098     //Roo.form.HtmlEditorToolbar1.superclass.constructor.call(this, editor.wrap.dom.firstChild, [], config);
43099     // dont call parent... till later.
43100 }
43101
43102 Roo.apply(Roo.form.HtmlEditor.ToolbarStandard.prototype,  {
43103     
43104     tb: false,
43105     
43106     rendered: false,
43107     
43108     editor : false,
43109     editorcore : false,
43110     /**
43111      * @cfg {Object} disable  List of toolbar elements to disable
43112          
43113      */
43114     disable : false,
43115     
43116     
43117      /**
43118      * @cfg {String} createLinkText The default text for the create link prompt
43119      */
43120     createLinkText : 'Please enter the URL for the link:',
43121     /**
43122      * @cfg {String} defaultLinkValue The default value for the create link prompt (defaults to http:/ /)
43123      */
43124     defaultLinkValue : 'http:/'+'/',
43125    
43126     
43127       /**
43128      * @cfg {Array} fontFamilies An array of available font families
43129      */
43130     fontFamilies : [
43131         'Arial',
43132         'Courier New',
43133         'Tahoma',
43134         'Times New Roman',
43135         'Verdana'
43136     ],
43137     
43138     specialChars : [
43139            "&#169;",
43140           "&#174;",     
43141           "&#8482;",    
43142           "&#163;" ,    
43143          // "&#8212;",    
43144           "&#8230;",    
43145           "&#247;" ,    
43146         //  "&#225;" ,     ?? a acute?
43147            "&#8364;"    , //Euro
43148        //   "&#8220;"    ,
43149         //  "&#8221;"    ,
43150         //  "&#8226;"    ,
43151           "&#176;"  //   , // degrees
43152
43153          // "&#233;"     , // e ecute
43154          // "&#250;"     , // u ecute?
43155     ],
43156     
43157     specialElements : [
43158         {
43159             text: "Insert Table",
43160             xtype: 'MenuItem',
43161             xns : Roo.Menu,
43162             ihtml :  '<table><tr><td>Cell</td></tr></table>' 
43163                 
43164         },
43165         {    
43166             text: "Insert Image",
43167             xtype: 'MenuItem',
43168             xns : Roo.Menu,
43169             ihtml : '<img src="about:blank"/>'
43170             
43171         }
43172         
43173          
43174     ],
43175     
43176     
43177     inputElements : [ 
43178             "form", "input:text", "input:hidden", "input:checkbox", "input:radio", "input:password", 
43179             "input:submit", "input:button", "select", "textarea", "label" ],
43180     formats : [
43181         ["p"] ,  
43182         ["h1"],["h2"],["h3"],["h4"],["h5"],["h6"], 
43183         ["pre"],[ "code"], 
43184         ["abbr"],[ "acronym"],[ "address"],[ "cite"],[ "samp"],[ "var"],
43185         ['div'],['span']
43186     ],
43187     
43188     cleanStyles : [
43189         "font-size"
43190     ],
43191      /**
43192      * @cfg {String} defaultFont default font to use.
43193      */
43194     defaultFont: 'tahoma',
43195    
43196     fontSelect : false,
43197     
43198     
43199     formatCombo : false,
43200     
43201     init : function(editor)
43202     {
43203         this.editor = editor;
43204         this.editorcore = editor.editorcore ? editor.editorcore : editor;
43205         var editorcore = this.editorcore;
43206         
43207         var _t = this;
43208         
43209         var fid = editorcore.frameId;
43210         var etb = this;
43211         function btn(id, toggle, handler){
43212             var xid = fid + '-'+ id ;
43213             return {
43214                 id : xid,
43215                 cmd : id,
43216                 cls : 'x-btn-icon x-edit-'+id,
43217                 enableToggle:toggle !== false,
43218                 scope: _t, // was editor...
43219                 handler:handler||_t.relayBtnCmd,
43220                 clickEvent:'mousedown',
43221                 tooltip: etb.buttonTips[id] || undefined, ///tips ???
43222                 tabIndex:-1
43223             };
43224         }
43225         
43226         
43227         
43228         var tb = new Roo.Toolbar(editor.wrap.dom.firstChild);
43229         this.tb = tb;
43230          // stop form submits
43231         tb.el.on('click', function(e){
43232             e.preventDefault(); // what does this do?
43233         });
43234
43235         if(!this.disable.font) { // && !Roo.isSafari){
43236             /* why no safari for fonts 
43237             editor.fontSelect = tb.el.createChild({
43238                 tag:'select',
43239                 tabIndex: -1,
43240                 cls:'x-font-select',
43241                 html: this.createFontOptions()
43242             });
43243             
43244             editor.fontSelect.on('change', function(){
43245                 var font = editor.fontSelect.dom.value;
43246                 editor.relayCmd('fontname', font);
43247                 editor.deferFocus();
43248             }, editor);
43249             
43250             tb.add(
43251                 editor.fontSelect.dom,
43252                 '-'
43253             );
43254             */
43255             
43256         };
43257         if(!this.disable.formats){
43258             this.formatCombo = new Roo.form.ComboBox({
43259                 store: new Roo.data.SimpleStore({
43260                     id : 'tag',
43261                     fields: ['tag'],
43262                     data : this.formats // from states.js
43263                 }),
43264                 blockFocus : true,
43265                 name : '',
43266                 //autoCreate : {tag: "div",  size: "20"},
43267                 displayField:'tag',
43268                 typeAhead: false,
43269                 mode: 'local',
43270                 editable : false,
43271                 triggerAction: 'all',
43272                 emptyText:'Add tag',
43273                 selectOnFocus:true,
43274                 width:135,
43275                 listeners : {
43276                     'select': function(c, r, i) {
43277                         editorcore.insertTag(r.get('tag'));
43278                         editor.focus();
43279                     }
43280                 }
43281
43282             });
43283             tb.addField(this.formatCombo);
43284             
43285         }
43286         
43287         if(!this.disable.format){
43288             tb.add(
43289                 btn('bold'),
43290                 btn('italic'),
43291                 btn('underline')
43292             );
43293         };
43294         if(!this.disable.fontSize){
43295             tb.add(
43296                 '-',
43297                 
43298                 
43299                 btn('increasefontsize', false, editorcore.adjustFont),
43300                 btn('decreasefontsize', false, editorcore.adjustFont)
43301             );
43302         };
43303         
43304         
43305         if(!this.disable.colors){
43306             tb.add(
43307                 '-', {
43308                     id:editorcore.frameId +'-forecolor',
43309                     cls:'x-btn-icon x-edit-forecolor',
43310                     clickEvent:'mousedown',
43311                     tooltip: this.buttonTips['forecolor'] || undefined,
43312                     tabIndex:-1,
43313                     menu : new Roo.menu.ColorMenu({
43314                         allowReselect: true,
43315                         focus: Roo.emptyFn,
43316                         value:'000000',
43317                         plain:true,
43318                         selectHandler: function(cp, color){
43319                             editorcore.execCmd('forecolor', Roo.isSafari || Roo.isIE ? '#'+color : color);
43320                             editor.deferFocus();
43321                         },
43322                         scope: editorcore,
43323                         clickEvent:'mousedown'
43324                     })
43325                 }, {
43326                     id:editorcore.frameId +'backcolor',
43327                     cls:'x-btn-icon x-edit-backcolor',
43328                     clickEvent:'mousedown',
43329                     tooltip: this.buttonTips['backcolor'] || undefined,
43330                     tabIndex:-1,
43331                     menu : new Roo.menu.ColorMenu({
43332                         focus: Roo.emptyFn,
43333                         value:'FFFFFF',
43334                         plain:true,
43335                         allowReselect: true,
43336                         selectHandler: function(cp, color){
43337                             if(Roo.isGecko){
43338                                 editorcore.execCmd('useCSS', false);
43339                                 editorcore.execCmd('hilitecolor', color);
43340                                 editorcore.execCmd('useCSS', true);
43341                                 editor.deferFocus();
43342                             }else{
43343                                 editorcore.execCmd(Roo.isOpera ? 'hilitecolor' : 'backcolor', 
43344                                     Roo.isSafari || Roo.isIE ? '#'+color : color);
43345                                 editor.deferFocus();
43346                             }
43347                         },
43348                         scope:editorcore,
43349                         clickEvent:'mousedown'
43350                     })
43351                 }
43352             );
43353         };
43354         // now add all the items...
43355         
43356
43357         if(!this.disable.alignments){
43358             tb.add(
43359                 '-',
43360                 btn('justifyleft'),
43361                 btn('justifycenter'),
43362                 btn('justifyright')
43363             );
43364         };
43365
43366         //if(!Roo.isSafari){
43367             if(!this.disable.links){
43368                 tb.add(
43369                     '-',
43370                     btn('createlink', false, this.createLink)    /// MOVE TO HERE?!!?!?!?!
43371                 );
43372             };
43373
43374             if(!this.disable.lists){
43375                 tb.add(
43376                     '-',
43377                     btn('insertorderedlist'),
43378                     btn('insertunorderedlist')
43379                 );
43380             }
43381             if(!this.disable.sourceEdit){
43382                 tb.add(
43383                     '-',
43384                     btn('sourceedit', true, function(btn){
43385                         Roo.log(this);
43386                         this.toggleSourceEdit(btn.pressed);
43387                     })
43388                 );
43389             }
43390         //}
43391         
43392         var smenu = { };
43393         // special menu.. - needs to be tidied up..
43394         if (!this.disable.special) {
43395             smenu = {
43396                 text: "&#169;",
43397                 cls: 'x-edit-none',
43398                 
43399                 menu : {
43400                     items : []
43401                 }
43402             };
43403             for (var i =0; i < this.specialChars.length; i++) {
43404                 smenu.menu.items.push({
43405                     
43406                     html: this.specialChars[i],
43407                     handler: function(a,b) {
43408                         editorcore.insertAtCursor(String.fromCharCode(a.html.replace('&#','').replace(';', '')));
43409                         //editor.insertAtCursor(a.html);
43410                         
43411                     },
43412                     tabIndex:-1
43413                 });
43414             }
43415             
43416             
43417             tb.add(smenu);
43418             
43419             
43420         }
43421         
43422         var cmenu = { };
43423         if (!this.disable.cleanStyles) {
43424             cmenu = {
43425                 cls: 'x-btn-icon x-btn-clear',
43426                 
43427                 menu : {
43428                     items : []
43429                 }
43430             };
43431             for (var i =0; i < this.cleanStyles.length; i++) {
43432                 cmenu.menu.items.push({
43433                     actiontype : this.cleanStyles[i],
43434                     html: 'Remove ' + this.cleanStyles[i],
43435                     handler: function(a,b) {
43436                         Roo.log(a);
43437                         Roo.log(b);
43438                         var c = Roo.get(editorcore.doc.body);
43439                         c.select('[style]').each(function(s) {
43440                             s.dom.style.removeProperty(a.actiontype);
43441                         });
43442                         editorcore.syncValue();
43443                     },
43444                     tabIndex:-1
43445                 });
43446             }
43447             cmenu.menu.items.push({
43448                 actiontype : 'word',
43449                 html: 'Remove MS Word Formating',
43450                 handler: function(a,b) {
43451                     editorcore.cleanWord();
43452                     editorcore.syncValue();
43453                 },
43454                 tabIndex:-1
43455             });
43456             
43457             cmenu.menu.items.push({
43458                 actiontype : 'all',
43459                 html: 'Remove All Styles',
43460                 handler: function(a,b) {
43461                     
43462                     var c = Roo.get(editorcore.doc.body);
43463                     c.select('[style]').each(function(s) {
43464                         s.dom.removeAttribute('style');
43465                     });
43466                     editorcore.syncValue();
43467                 },
43468                 tabIndex:-1
43469             });
43470              cmenu.menu.items.push({
43471                 actiontype : 'word',
43472                 html: 'Tidy HTML Source',
43473                 handler: function(a,b) {
43474                     editorcore.doc.body.innerHTML = editorcore.domToHTML();
43475                     editorcore.syncValue();
43476                 },
43477                 tabIndex:-1
43478             });
43479             
43480             
43481             tb.add(cmenu);
43482         }
43483          
43484         if (!this.disable.specialElements) {
43485             var semenu = {
43486                 text: "Other;",
43487                 cls: 'x-edit-none',
43488                 menu : {
43489                     items : []
43490                 }
43491             };
43492             for (var i =0; i < this.specialElements.length; i++) {
43493                 semenu.menu.items.push(
43494                     Roo.apply({ 
43495                         handler: function(a,b) {
43496                             editor.insertAtCursor(this.ihtml);
43497                         }
43498                     }, this.specialElements[i])
43499                 );
43500                     
43501             }
43502             
43503             tb.add(semenu);
43504             
43505             
43506         }
43507          
43508         
43509         if (this.btns) {
43510             for(var i =0; i< this.btns.length;i++) {
43511                 var b = Roo.factory(this.btns[i],Roo.form);
43512                 b.cls =  'x-edit-none';
43513                 b.scope = editorcore;
43514                 tb.add(b);
43515             }
43516         
43517         }
43518         
43519         
43520         
43521         // disable everything...
43522         
43523         this.tb.items.each(function(item){
43524            if(item.id != editorcore.frameId+ '-sourceedit'){
43525                 item.disable();
43526             }
43527         });
43528         this.rendered = true;
43529         
43530         // the all the btns;
43531         editor.on('editorevent', this.updateToolbar, this);
43532         // other toolbars need to implement this..
43533         //editor.on('editmodechange', this.updateToolbar, this);
43534     },
43535     
43536     
43537     relayBtnCmd : function(btn) {
43538         this.editorcore.relayCmd(btn.cmd);
43539     },
43540     // private used internally
43541     createLink : function(){
43542         Roo.log("create link?");
43543         var url = prompt(this.createLinkText, this.defaultLinkValue);
43544         if(url && url != 'http:/'+'/'){
43545             this.editorcore.relayCmd('createlink', url);
43546         }
43547     },
43548
43549     
43550     /**
43551      * Protected method that will not generally be called directly. It triggers
43552      * a toolbar update by reading the markup state of the current selection in the editor.
43553      */
43554     updateToolbar: function(){
43555
43556         if(!this.editorcore.activated){
43557             this.editor.onFirstFocus();
43558             return;
43559         }
43560
43561         var btns = this.tb.items.map, 
43562             doc = this.editorcore.doc,
43563             frameId = this.editorcore.frameId;
43564
43565         if(!this.disable.font && !Roo.isSafari){
43566             /*
43567             var name = (doc.queryCommandValue('FontName')||this.editor.defaultFont).toLowerCase();
43568             if(name != this.fontSelect.dom.value){
43569                 this.fontSelect.dom.value = name;
43570             }
43571             */
43572         }
43573         if(!this.disable.format){
43574             btns[frameId + '-bold'].toggle(doc.queryCommandState('bold'));
43575             btns[frameId + '-italic'].toggle(doc.queryCommandState('italic'));
43576             btns[frameId + '-underline'].toggle(doc.queryCommandState('underline'));
43577         }
43578         if(!this.disable.alignments){
43579             btns[frameId + '-justifyleft'].toggle(doc.queryCommandState('justifyleft'));
43580             btns[frameId + '-justifycenter'].toggle(doc.queryCommandState('justifycenter'));
43581             btns[frameId + '-justifyright'].toggle(doc.queryCommandState('justifyright'));
43582         }
43583         if(!Roo.isSafari && !this.disable.lists){
43584             btns[frameId + '-insertorderedlist'].toggle(doc.queryCommandState('insertorderedlist'));
43585             btns[frameId + '-insertunorderedlist'].toggle(doc.queryCommandState('insertunorderedlist'));
43586         }
43587         
43588         var ans = this.editorcore.getAllAncestors();
43589         if (this.formatCombo) {
43590             
43591             
43592             var store = this.formatCombo.store;
43593             this.formatCombo.setValue("");
43594             for (var i =0; i < ans.length;i++) {
43595                 if (ans[i] && store.query('tag',ans[i].tagName.toLowerCase(), false).length) {
43596                     // select it..
43597                     this.formatCombo.setValue(ans[i].tagName.toLowerCase());
43598                     break;
43599                 }
43600             }
43601         }
43602         
43603         
43604         
43605         // hides menus... - so this cant be on a menu...
43606         Roo.menu.MenuMgr.hideAll();
43607
43608         //this.editorsyncValue();
43609     },
43610    
43611     
43612     createFontOptions : function(){
43613         var buf = [], fs = this.fontFamilies, ff, lc;
43614         
43615         
43616         
43617         for(var i = 0, len = fs.length; i< len; i++){
43618             ff = fs[i];
43619             lc = ff.toLowerCase();
43620             buf.push(
43621                 '<option value="',lc,'" style="font-family:',ff,';"',
43622                     (this.defaultFont == lc ? ' selected="true">' : '>'),
43623                     ff,
43624                 '</option>'
43625             );
43626         }
43627         return buf.join('');
43628     },
43629     
43630     toggleSourceEdit : function(sourceEditMode){
43631         
43632         Roo.log("toolbar toogle");
43633         if(sourceEditMode === undefined){
43634             sourceEditMode = !this.sourceEditMode;
43635         }
43636         this.sourceEditMode = sourceEditMode === true;
43637         var btn = this.tb.items.get(this.editorcore.frameId +'-sourceedit');
43638         // just toggle the button?
43639         if(btn.pressed !== this.sourceEditMode){
43640             btn.toggle(this.sourceEditMode);
43641             return;
43642         }
43643         
43644         if(sourceEditMode){
43645             Roo.log("disabling buttons");
43646             this.tb.items.each(function(item){
43647                 if(item.cmd != 'sourceedit'){
43648                     item.disable();
43649                 }
43650             });
43651           
43652         }else{
43653             Roo.log("enabling buttons");
43654             if(this.editorcore.initialized){
43655                 this.tb.items.each(function(item){
43656                     item.enable();
43657                 });
43658             }
43659             
43660         }
43661         Roo.log("calling toggole on editor");
43662         // tell the editor that it's been pressed..
43663         this.editor.toggleSourceEdit(sourceEditMode);
43664        
43665     },
43666      /**
43667      * Object collection of toolbar tooltips for the buttons in the editor. The key
43668      * is the command id associated with that button and the value is a valid QuickTips object.
43669      * For example:
43670 <pre><code>
43671 {
43672     bold : {
43673         title: 'Bold (Ctrl+B)',
43674         text: 'Make the selected text bold.',
43675         cls: 'x-html-editor-tip'
43676     },
43677     italic : {
43678         title: 'Italic (Ctrl+I)',
43679         text: 'Make the selected text italic.',
43680         cls: 'x-html-editor-tip'
43681     },
43682     ...
43683 </code></pre>
43684     * @type Object
43685      */
43686     buttonTips : {
43687         bold : {
43688             title: 'Bold (Ctrl+B)',
43689             text: 'Make the selected text bold.',
43690             cls: 'x-html-editor-tip'
43691         },
43692         italic : {
43693             title: 'Italic (Ctrl+I)',
43694             text: 'Make the selected text italic.',
43695             cls: 'x-html-editor-tip'
43696         },
43697         underline : {
43698             title: 'Underline (Ctrl+U)',
43699             text: 'Underline the selected text.',
43700             cls: 'x-html-editor-tip'
43701         },
43702         increasefontsize : {
43703             title: 'Grow Text',
43704             text: 'Increase the font size.',
43705             cls: 'x-html-editor-tip'
43706         },
43707         decreasefontsize : {
43708             title: 'Shrink Text',
43709             text: 'Decrease the font size.',
43710             cls: 'x-html-editor-tip'
43711         },
43712         backcolor : {
43713             title: 'Text Highlight Color',
43714             text: 'Change the background color of the selected text.',
43715             cls: 'x-html-editor-tip'
43716         },
43717         forecolor : {
43718             title: 'Font Color',
43719             text: 'Change the color of the selected text.',
43720             cls: 'x-html-editor-tip'
43721         },
43722         justifyleft : {
43723             title: 'Align Text Left',
43724             text: 'Align text to the left.',
43725             cls: 'x-html-editor-tip'
43726         },
43727         justifycenter : {
43728             title: 'Center Text',
43729             text: 'Center text in the editor.',
43730             cls: 'x-html-editor-tip'
43731         },
43732         justifyright : {
43733             title: 'Align Text Right',
43734             text: 'Align text to the right.',
43735             cls: 'x-html-editor-tip'
43736         },
43737         insertunorderedlist : {
43738             title: 'Bullet List',
43739             text: 'Start a bulleted list.',
43740             cls: 'x-html-editor-tip'
43741         },
43742         insertorderedlist : {
43743             title: 'Numbered List',
43744             text: 'Start a numbered list.',
43745             cls: 'x-html-editor-tip'
43746         },
43747         createlink : {
43748             title: 'Hyperlink',
43749             text: 'Make the selected text a hyperlink.',
43750             cls: 'x-html-editor-tip'
43751         },
43752         sourceedit : {
43753             title: 'Source Edit',
43754             text: 'Switch to source editing mode.',
43755             cls: 'x-html-editor-tip'
43756         }
43757     },
43758     // private
43759     onDestroy : function(){
43760         if(this.rendered){
43761             
43762             this.tb.items.each(function(item){
43763                 if(item.menu){
43764                     item.menu.removeAll();
43765                     if(item.menu.el){
43766                         item.menu.el.destroy();
43767                     }
43768                 }
43769                 item.destroy();
43770             });
43771              
43772         }
43773     },
43774     onFirstFocus: function() {
43775         this.tb.items.each(function(item){
43776            item.enable();
43777         });
43778     }
43779 });
43780
43781
43782
43783
43784 // <script type="text/javascript">
43785 /*
43786  * Based on
43787  * Ext JS Library 1.1.1
43788  * Copyright(c) 2006-2007, Ext JS, LLC.
43789  *  
43790  
43791  */
43792
43793  
43794 /**
43795  * @class Roo.form.HtmlEditor.ToolbarContext
43796  * Context Toolbar
43797  * 
43798  * Usage:
43799  *
43800  new Roo.form.HtmlEditor({
43801     ....
43802     toolbars : [
43803         { xtype: 'ToolbarStandard', styles : {} }
43804         { xtype: 'ToolbarContext', disable : {} }
43805     ]
43806 })
43807
43808      
43809  * 
43810  * @config : {Object} disable List of elements to disable.. (not done yet.)
43811  * @config : {Object} styles  Map of styles available.
43812  * 
43813  */
43814
43815 Roo.form.HtmlEditor.ToolbarContext = function(config)
43816 {
43817     
43818     Roo.apply(this, config);
43819     //Roo.form.HtmlEditorToolbar1.superclass.constructor.call(this, editor.wrap.dom.firstChild, [], config);
43820     // dont call parent... till later.
43821     this.styles = this.styles || {};
43822 }
43823
43824  
43825
43826 Roo.form.HtmlEditor.ToolbarContext.types = {
43827     'IMG' : {
43828         width : {
43829             title: "Width",
43830             width: 40
43831         },
43832         height:  {
43833             title: "Height",
43834             width: 40
43835         },
43836         align: {
43837             title: "Align",
43838             opts : [ [""],[ "left"],[ "right"],[ "center"],[ "top"]],
43839             width : 80
43840             
43841         },
43842         border: {
43843             title: "Border",
43844             width: 40
43845         },
43846         alt: {
43847             title: "Alt",
43848             width: 120
43849         },
43850         src : {
43851             title: "Src",
43852             width: 220
43853         }
43854         
43855     },
43856     'A' : {
43857         name : {
43858             title: "Name",
43859             width: 50
43860         },
43861         target:  {
43862             title: "Target",
43863             width: 120
43864         },
43865         href:  {
43866             title: "Href",
43867             width: 220
43868         } // border?
43869         
43870     },
43871     'TABLE' : {
43872         rows : {
43873             title: "Rows",
43874             width: 20
43875         },
43876         cols : {
43877             title: "Cols",
43878             width: 20
43879         },
43880         width : {
43881             title: "Width",
43882             width: 40
43883         },
43884         height : {
43885             title: "Height",
43886             width: 40
43887         },
43888         border : {
43889             title: "Border",
43890             width: 20
43891         }
43892     },
43893     'TD' : {
43894         width : {
43895             title: "Width",
43896             width: 40
43897         },
43898         height : {
43899             title: "Height",
43900             width: 40
43901         },   
43902         align: {
43903             title: "Align",
43904             opts : [[""],[ "left"],[ "center"],[ "right"],[ "justify"],[ "char"]],
43905             width: 80
43906         },
43907         valign: {
43908             title: "Valign",
43909             opts : [[""],[ "top"],[ "middle"],[ "bottom"],[ "baseline"]],
43910             width: 80
43911         },
43912         colspan: {
43913             title: "Colspan",
43914             width: 20
43915             
43916         },
43917          'font-family'  : {
43918             title : "Font",
43919             style : 'fontFamily',
43920             displayField: 'display',
43921             optname : 'font-family',
43922             width: 140
43923         }
43924     },
43925     'INPUT' : {
43926         name : {
43927             title: "name",
43928             width: 120
43929         },
43930         value : {
43931             title: "Value",
43932             width: 120
43933         },
43934         width : {
43935             title: "Width",
43936             width: 40
43937         }
43938     },
43939     'LABEL' : {
43940         'for' : {
43941             title: "For",
43942             width: 120
43943         }
43944     },
43945     'TEXTAREA' : {
43946           name : {
43947             title: "name",
43948             width: 120
43949         },
43950         rows : {
43951             title: "Rows",
43952             width: 20
43953         },
43954         cols : {
43955             title: "Cols",
43956             width: 20
43957         }
43958     },
43959     'SELECT' : {
43960         name : {
43961             title: "name",
43962             width: 120
43963         },
43964         selectoptions : {
43965             title: "Options",
43966             width: 200
43967         }
43968     },
43969     
43970     // should we really allow this??
43971     // should this just be 
43972     'BODY' : {
43973         title : {
43974             title: "Title",
43975             width: 200,
43976             disabled : true
43977         }
43978     },
43979     'SPAN' : {
43980         'font-family'  : {
43981             title : "Font",
43982             style : 'fontFamily',
43983             displayField: 'display',
43984             optname : 'font-family',
43985             width: 140
43986         }
43987     },
43988     'DIV' : {
43989         'font-family'  : {
43990             title : "Font",
43991             style : 'fontFamily',
43992             displayField: 'display',
43993             optname : 'font-family',
43994             width: 140
43995         }
43996     },
43997      'P' : {
43998         'font-family'  : {
43999             title : "Font",
44000             style : 'fontFamily',
44001             displayField: 'display',
44002             optname : 'font-family',
44003             width: 140
44004         }
44005     },
44006     
44007     '*' : {
44008         // empty..
44009     }
44010
44011 };
44012
44013 // this should be configurable.. - you can either set it up using stores, or modify options somehwere..
44014 Roo.form.HtmlEditor.ToolbarContext.stores = false;
44015
44016 Roo.form.HtmlEditor.ToolbarContext.options = {
44017         'font-family'  : [ 
44018                 [ 'Helvetica,Arial,sans-serif', 'Helvetica'],
44019                 [ 'Courier New', 'Courier New'],
44020                 [ 'Tahoma', 'Tahoma'],
44021                 [ 'Times New Roman,serif', 'Times'],
44022                 [ 'Verdana','Verdana' ]
44023         ]
44024 };
44025
44026 // fixme - these need to be configurable..
44027  
44028
44029 Roo.form.HtmlEditor.ToolbarContext.types
44030
44031
44032 Roo.apply(Roo.form.HtmlEditor.ToolbarContext.prototype,  {
44033     
44034     tb: false,
44035     
44036     rendered: false,
44037     
44038     editor : false,
44039     editorcore : false,
44040     /**
44041      * @cfg {Object} disable  List of toolbar elements to disable
44042          
44043      */
44044     disable : false,
44045     /**
44046      * @cfg {Object} styles List of styles 
44047      *    eg. { '*' : [ 'headline' ] , 'TD' : [ 'underline', 'double-underline' ] } 
44048      *
44049      * These must be defined in the page, so they get rendered correctly..
44050      * .headline { }
44051      * TD.underline { }
44052      * 
44053      */
44054     styles : false,
44055     
44056     options: false,
44057     
44058     toolbars : false,
44059     
44060     init : function(editor)
44061     {
44062         this.editor = editor;
44063         this.editorcore = editor.editorcore ? editor.editorcore : editor;
44064         var editorcore = this.editorcore;
44065         
44066         var fid = editorcore.frameId;
44067         var etb = this;
44068         function btn(id, toggle, handler){
44069             var xid = fid + '-'+ id ;
44070             return {
44071                 id : xid,
44072                 cmd : id,
44073                 cls : 'x-btn-icon x-edit-'+id,
44074                 enableToggle:toggle !== false,
44075                 scope: editorcore, // was editor...
44076                 handler:handler||editorcore.relayBtnCmd,
44077                 clickEvent:'mousedown',
44078                 tooltip: etb.buttonTips[id] || undefined, ///tips ???
44079                 tabIndex:-1
44080             };
44081         }
44082         // create a new element.
44083         var wdiv = editor.wrap.createChild({
44084                 tag: 'div'
44085             }, editor.wrap.dom.firstChild.nextSibling, true);
44086         
44087         // can we do this more than once??
44088         
44089          // stop form submits
44090       
44091  
44092         // disable everything...
44093         var ty= Roo.form.HtmlEditor.ToolbarContext.types;
44094         this.toolbars = {};
44095            
44096         for (var i in  ty) {
44097           
44098             this.toolbars[i] = this.buildToolbar(ty[i],i);
44099         }
44100         this.tb = this.toolbars.BODY;
44101         this.tb.el.show();
44102         this.buildFooter();
44103         this.footer.show();
44104         editor.on('hide', function( ) { this.footer.hide() }, this);
44105         editor.on('show', function( ) { this.footer.show() }, this);
44106         
44107          
44108         this.rendered = true;
44109         
44110         // the all the btns;
44111         editor.on('editorevent', this.updateToolbar, this);
44112         // other toolbars need to implement this..
44113         //editor.on('editmodechange', this.updateToolbar, this);
44114     },
44115     
44116     
44117     
44118     /**
44119      * Protected method that will not generally be called directly. It triggers
44120      * a toolbar update by reading the markup state of the current selection in the editor.
44121      */
44122     updateToolbar: function(editor,ev,sel){
44123
44124         //Roo.log(ev);
44125         // capture mouse up - this is handy for selecting images..
44126         // perhaps should go somewhere else...
44127         if(!this.editorcore.activated){
44128              this.editor.onFirstFocus();
44129             return;
44130         }
44131         
44132         // http://developer.yahoo.com/yui/docs/simple-editor.js.html
44133         // selectNode - might want to handle IE?
44134         if (ev &&
44135             (ev.type == 'mouseup' || ev.type == 'click' ) &&
44136             ev.target && ev.target.tagName == 'IMG') {
44137             // they have click on an image...
44138             // let's see if we can change the selection...
44139             sel = ev.target;
44140          
44141               var nodeRange = sel.ownerDocument.createRange();
44142             try {
44143                 nodeRange.selectNode(sel);
44144             } catch (e) {
44145                 nodeRange.selectNodeContents(sel);
44146             }
44147             //nodeRange.collapse(true);
44148             var s = this.editorcore.win.getSelection();
44149             s.removeAllRanges();
44150             s.addRange(nodeRange);
44151         }  
44152         
44153       
44154         var updateFooter = sel ? false : true;
44155         
44156         
44157         var ans = this.editorcore.getAllAncestors();
44158         
44159         // pick
44160         var ty= Roo.form.HtmlEditor.ToolbarContext.types;
44161         
44162         if (!sel) { 
44163             sel = ans.length ? (ans[0] ?  ans[0]  : ans[1]) : this.editorcore.doc.body;
44164             sel = sel ? sel : this.editorcore.doc.body;
44165             sel = sel.tagName.length ? sel : this.editorcore.doc.body;
44166             
44167         }
44168         // pick a menu that exists..
44169         var tn = sel.tagName.toUpperCase();
44170         //sel = typeof(ty[tn]) != 'undefined' ? sel : this.editor.doc.body;
44171         
44172         tn = sel.tagName.toUpperCase();
44173         
44174         var lastSel = this.tb.selectedNode
44175         
44176         this.tb.selectedNode = sel;
44177         
44178         // if current menu does not match..
44179         if ((this.tb.name != tn) || (lastSel != this.tb.selectedNode)) {
44180                 
44181             this.tb.el.hide();
44182             ///console.log("show: " + tn);
44183             this.tb =  typeof(ty[tn]) != 'undefined' ? this.toolbars[tn] : this.toolbars['*'];
44184             this.tb.el.show();
44185             // update name
44186             this.tb.items.first().el.innerHTML = tn + ':&nbsp;';
44187             
44188             
44189             // update attributes
44190             if (this.tb.fields) {
44191                 this.tb.fields.each(function(e) {
44192                     if (e.stylename) {
44193                         e.setValue(sel.style[e.stylename]);
44194                         return;
44195                     } 
44196                    e.setValue(sel.getAttribute(e.attrname));
44197                 });
44198             }
44199             
44200             var hasStyles = false;
44201             for(var i in this.styles) {
44202                 hasStyles = true;
44203                 break;
44204             }
44205             
44206             // update styles
44207             if (hasStyles) { 
44208                 var st = this.tb.fields.item(0);
44209                 
44210                 st.store.removeAll();
44211                
44212                 
44213                 var cn = sel.className.split(/\s+/);
44214                 
44215                 var avs = [];
44216                 if (this.styles['*']) {
44217                     
44218                     Roo.each(this.styles['*'], function(v) {
44219                         avs.push( [ v , cn.indexOf(v) > -1 ? 1 : 0 ] );         
44220                     });
44221                 }
44222                 if (this.styles[tn]) { 
44223                     Roo.each(this.styles[tn], function(v) {
44224                         avs.push( [ v , cn.indexOf(v) > -1 ? 1 : 0 ] );         
44225                     });
44226                 }
44227                 
44228                 st.store.loadData(avs);
44229                 st.collapse();
44230                 st.setValue(cn);
44231             }
44232             // flag our selected Node.
44233             this.tb.selectedNode = sel;
44234            
44235            
44236             Roo.menu.MenuMgr.hideAll();
44237
44238         }
44239         
44240         if (!updateFooter) {
44241             //this.footDisp.dom.innerHTML = ''; 
44242             return;
44243         }
44244         // update the footer
44245         //
44246         var html = '';
44247         
44248         this.footerEls = ans.reverse();
44249         Roo.each(this.footerEls, function(a,i) {
44250             if (!a) { return; }
44251             html += html.length ? ' &gt; '  :  '';
44252             
44253             html += '<span class="x-ed-loc-' + i + '">' + a.tagName + '</span>';
44254             
44255         });
44256        
44257         // 
44258         var sz = this.footDisp.up('td').getSize();
44259         this.footDisp.dom.style.width = (sz.width -10) + 'px';
44260         this.footDisp.dom.style.marginLeft = '5px';
44261         
44262         this.footDisp.dom.style.overflow = 'hidden';
44263         
44264         this.footDisp.dom.innerHTML = html;
44265             
44266         //this.editorsyncValue();
44267     },
44268      
44269     
44270    
44271        
44272     // private
44273     onDestroy : function(){
44274         if(this.rendered){
44275             
44276             this.tb.items.each(function(item){
44277                 if(item.menu){
44278                     item.menu.removeAll();
44279                     if(item.menu.el){
44280                         item.menu.el.destroy();
44281                     }
44282                 }
44283                 item.destroy();
44284             });
44285              
44286         }
44287     },
44288     onFirstFocus: function() {
44289         // need to do this for all the toolbars..
44290         this.tb.items.each(function(item){
44291            item.enable();
44292         });
44293     },
44294     buildToolbar: function(tlist, nm)
44295     {
44296         var editor = this.editor;
44297         var editorcore = this.editorcore;
44298          // create a new element.
44299         var wdiv = editor.wrap.createChild({
44300                 tag: 'div'
44301             }, editor.wrap.dom.firstChild.nextSibling, true);
44302         
44303        
44304         var tb = new Roo.Toolbar(wdiv);
44305         // add the name..
44306         
44307         tb.add(nm+ ":&nbsp;");
44308         
44309         var styles = [];
44310         for(var i in this.styles) {
44311             styles.push(i);
44312         }
44313         
44314         // styles...
44315         if (styles && styles.length) {
44316             
44317             // this needs a multi-select checkbox...
44318             tb.addField( new Roo.form.ComboBox({
44319                 store: new Roo.data.SimpleStore({
44320                     id : 'val',
44321                     fields: ['val', 'selected'],
44322                     data : [] 
44323                 }),
44324                 name : '-roo-edit-className',
44325                 attrname : 'className',
44326                 displayField: 'val',
44327                 typeAhead: false,
44328                 mode: 'local',
44329                 editable : false,
44330                 triggerAction: 'all',
44331                 emptyText:'Select Style',
44332                 selectOnFocus:true,
44333                 width: 130,
44334                 listeners : {
44335                     'select': function(c, r, i) {
44336                         // initial support only for on class per el..
44337                         tb.selectedNode.className =  r ? r.get('val') : '';
44338                         editorcore.syncValue();
44339                     }
44340                 }
44341     
44342             }));
44343         }
44344         
44345         var tbc = Roo.form.HtmlEditor.ToolbarContext;
44346         var tbops = tbc.options;
44347         
44348         for (var i in tlist) {
44349             
44350             var item = tlist[i];
44351             tb.add(item.title + ":&nbsp;");
44352             
44353             
44354             //optname == used so you can configure the options available..
44355             var opts = item.opts ? item.opts : false;
44356             if (item.optname) {
44357                 opts = tbops[item.optname];
44358            
44359             }
44360             
44361             if (opts) {
44362                 // opts == pulldown..
44363                 tb.addField( new Roo.form.ComboBox({
44364                     store:   typeof(tbc.stores[i]) != 'undefined' ?  Roo.factory(tbc.stores[i],Roo.data) : new Roo.data.SimpleStore({
44365                         id : 'val',
44366                         fields: ['val', 'display'],
44367                         data : opts  
44368                     }),
44369                     name : '-roo-edit-' + i,
44370                     attrname : i,
44371                     stylename : item.style ? item.style : false,
44372                     displayField: item.displayField ? item.displayField : 'val',
44373                     valueField :  'val',
44374                     typeAhead: false,
44375                     mode: typeof(tbc.stores[i]) != 'undefined'  ? 'remote' : 'local',
44376                     editable : false,
44377                     triggerAction: 'all',
44378                     emptyText:'Select',
44379                     selectOnFocus:true,
44380                     width: item.width ? item.width  : 130,
44381                     listeners : {
44382                         'select': function(c, r, i) {
44383                             if (c.stylename) {
44384                                 tb.selectedNode.style[c.stylename] =  r.get('val');
44385                                 return;
44386                             }
44387                             tb.selectedNode.setAttribute(c.attrname, r.get('val'));
44388                         }
44389                     }
44390
44391                 }));
44392                 continue;
44393                     
44394                  
44395                 
44396                 tb.addField( new Roo.form.TextField({
44397                     name: i,
44398                     width: 100,
44399                     //allowBlank:false,
44400                     value: ''
44401                 }));
44402                 continue;
44403             }
44404             tb.addField( new Roo.form.TextField({
44405                 name: '-roo-edit-' + i,
44406                 attrname : i,
44407                 
44408                 width: item.width,
44409                 //allowBlank:true,
44410                 value: '',
44411                 listeners: {
44412                     'change' : function(f, nv, ov) {
44413                         tb.selectedNode.setAttribute(f.attrname, nv);
44414                     }
44415                 }
44416             }));
44417              
44418         }
44419         tb.addFill();
44420         var _this = this;
44421         tb.addButton( {
44422             text: 'Remove Tag',
44423     
44424             listeners : {
44425                 click : function ()
44426                 {
44427                     // remove
44428                     // undo does not work.
44429                      
44430                     var sn = tb.selectedNode;
44431                     
44432                     var pn = sn.parentNode;
44433                     
44434                     var stn =  sn.childNodes[0];
44435                     var en = sn.childNodes[sn.childNodes.length - 1 ];
44436                     while (sn.childNodes.length) {
44437                         var node = sn.childNodes[0];
44438                         sn.removeChild(node);
44439                         //Roo.log(node);
44440                         pn.insertBefore(node, sn);
44441                         
44442                     }
44443                     pn.removeChild(sn);
44444                     var range = editorcore.createRange();
44445         
44446                     range.setStart(stn,0);
44447                     range.setEnd(en,0); //????
44448                     //range.selectNode(sel);
44449                     
44450                     
44451                     var selection = editorcore.getSelection();
44452                     selection.removeAllRanges();
44453                     selection.addRange(range);
44454                     
44455                     
44456                     
44457                     //_this.updateToolbar(null, null, pn);
44458                     _this.updateToolbar(null, null, null);
44459                     _this.footDisp.dom.innerHTML = ''; 
44460                 }
44461             }
44462             
44463                     
44464                 
44465             
44466         });
44467         
44468         
44469         tb.el.on('click', function(e){
44470             e.preventDefault(); // what does this do?
44471         });
44472         tb.el.setVisibilityMode( Roo.Element.DISPLAY);
44473         tb.el.hide();
44474         tb.name = nm;
44475         // dont need to disable them... as they will get hidden
44476         return tb;
44477          
44478         
44479     },
44480     buildFooter : function()
44481     {
44482         
44483         var fel = this.editor.wrap.createChild();
44484         this.footer = new Roo.Toolbar(fel);
44485         // toolbar has scrolly on left / right?
44486         var footDisp= new Roo.Toolbar.Fill();
44487         var _t = this;
44488         this.footer.add(
44489             {
44490                 text : '&lt;',
44491                 xtype: 'Button',
44492                 handler : function() {
44493                     _t.footDisp.scrollTo('left',0,true)
44494                 }
44495             }
44496         );
44497         this.footer.add( footDisp );
44498         this.footer.add( 
44499             {
44500                 text : '&gt;',
44501                 xtype: 'Button',
44502                 handler : function() {
44503                     // no animation..
44504                     _t.footDisp.select('span').last().scrollIntoView(_t.footDisp,true);
44505                 }
44506             }
44507         );
44508         var fel = Roo.get(footDisp.el);
44509         fel.addClass('x-editor-context');
44510         this.footDispWrap = fel; 
44511         this.footDispWrap.overflow  = 'hidden';
44512         
44513         this.footDisp = fel.createChild();
44514         this.footDispWrap.on('click', this.onContextClick, this)
44515         
44516         
44517     },
44518     onContextClick : function (ev,dom)
44519     {
44520         ev.preventDefault();
44521         var  cn = dom.className;
44522         //Roo.log(cn);
44523         if (!cn.match(/x-ed-loc-/)) {
44524             return;
44525         }
44526         var n = cn.split('-').pop();
44527         var ans = this.footerEls;
44528         var sel = ans[n];
44529         
44530          // pick
44531         var range = this.editorcore.createRange();
44532         
44533         range.selectNodeContents(sel);
44534         //range.selectNode(sel);
44535         
44536         
44537         var selection = this.editorcore.getSelection();
44538         selection.removeAllRanges();
44539         selection.addRange(range);
44540         
44541         
44542         
44543         this.updateToolbar(null, null, sel);
44544         
44545         
44546     }
44547     
44548     
44549     
44550     
44551     
44552 });
44553
44554
44555
44556
44557
44558 /*
44559  * Based on:
44560  * Ext JS Library 1.1.1
44561  * Copyright(c) 2006-2007, Ext JS, LLC.
44562  *
44563  * Originally Released Under LGPL - original licence link has changed is not relivant.
44564  *
44565  * Fork - LGPL
44566  * <script type="text/javascript">
44567  */
44568  
44569 /**
44570  * @class Roo.form.BasicForm
44571  * @extends Roo.util.Observable
44572  * Supplies the functionality to do "actions" on forms and initialize Roo.form.Field types on existing markup.
44573  * @constructor
44574  * @param {String/HTMLElement/Roo.Element} el The form element or its id
44575  * @param {Object} config Configuration options
44576  */
44577 Roo.form.BasicForm = function(el, config){
44578     this.allItems = [];
44579     this.childForms = [];
44580     Roo.apply(this, config);
44581     /*
44582      * The Roo.form.Field items in this form.
44583      * @type MixedCollection
44584      */
44585      
44586      
44587     this.items = new Roo.util.MixedCollection(false, function(o){
44588         return o.id || (o.id = Roo.id());
44589     });
44590     this.addEvents({
44591         /**
44592          * @event beforeaction
44593          * Fires before any action is performed. Return false to cancel the action.
44594          * @param {Form} this
44595          * @param {Action} action The action to be performed
44596          */
44597         beforeaction: true,
44598         /**
44599          * @event actionfailed
44600          * Fires when an action fails.
44601          * @param {Form} this
44602          * @param {Action} action The action that failed
44603          */
44604         actionfailed : true,
44605         /**
44606          * @event actioncomplete
44607          * Fires when an action is completed.
44608          * @param {Form} this
44609          * @param {Action} action The action that completed
44610          */
44611         actioncomplete : true
44612     });
44613     if(el){
44614         this.initEl(el);
44615     }
44616     Roo.form.BasicForm.superclass.constructor.call(this);
44617 };
44618
44619 Roo.extend(Roo.form.BasicForm, Roo.util.Observable, {
44620     /**
44621      * @cfg {String} method
44622      * The request method to use (GET or POST) for form actions if one isn't supplied in the action options.
44623      */
44624     /**
44625      * @cfg {DataReader} reader
44626      * An Roo.data.DataReader (e.g. {@link Roo.data.XmlReader}) to be used to read data when executing "load" actions.
44627      * This is optional as there is built-in support for processing JSON.
44628      */
44629     /**
44630      * @cfg {DataReader} errorReader
44631      * An Roo.data.DataReader (e.g. {@link Roo.data.XmlReader}) to be used to read data when reading validation errors on "submit" actions.
44632      * This is completely optional as there is built-in support for processing JSON.
44633      */
44634     /**
44635      * @cfg {String} url
44636      * The URL to use for form actions if one isn't supplied in the action options.
44637      */
44638     /**
44639      * @cfg {Boolean} fileUpload
44640      * Set to true if this form is a file upload.
44641      */
44642      
44643     /**
44644      * @cfg {Object} baseParams
44645      * Parameters to pass with all requests. e.g. baseParams: {id: '123', foo: 'bar'}.
44646      */
44647      /**
44648      
44649     /**
44650      * @cfg {Number} timeout Timeout for form actions in seconds (default is 30 seconds).
44651      */
44652     timeout: 30,
44653
44654     // private
44655     activeAction : null,
44656
44657     /**
44658      * @cfg {Boolean} trackResetOnLoad If set to true, form.reset() resets to the last loaded
44659      * or setValues() data instead of when the form was first created.
44660      */
44661     trackResetOnLoad : false,
44662     
44663     
44664     /**
44665      * childForms - used for multi-tab forms
44666      * @type {Array}
44667      */
44668     childForms : false,
44669     
44670     /**
44671      * allItems - full list of fields.
44672      * @type {Array}
44673      */
44674     allItems : false,
44675     
44676     /**
44677      * By default wait messages are displayed with Roo.MessageBox.wait. You can target a specific
44678      * element by passing it or its id or mask the form itself by passing in true.
44679      * @type Mixed
44680      */
44681     waitMsgTarget : false,
44682
44683     // private
44684     initEl : function(el){
44685         this.el = Roo.get(el);
44686         this.id = this.el.id || Roo.id();
44687         this.el.on('submit', this.onSubmit, this);
44688         this.el.addClass('x-form');
44689     },
44690
44691     // private
44692     onSubmit : function(e){
44693         e.stopEvent();
44694     },
44695
44696     /**
44697      * Returns true if client-side validation on the form is successful.
44698      * @return Boolean
44699      */
44700     isValid : function(){
44701         var valid = true;
44702         this.items.each(function(f){
44703            if(!f.validate()){
44704                valid = false;
44705            }
44706         });
44707         return valid;
44708     },
44709
44710     /**
44711      * Returns true if any fields in this form have changed since their original load.
44712      * @return Boolean
44713      */
44714     isDirty : function(){
44715         var dirty = false;
44716         this.items.each(function(f){
44717            if(f.isDirty()){
44718                dirty = true;
44719                return false;
44720            }
44721         });
44722         return dirty;
44723     },
44724
44725     /**
44726      * Performs a predefined action (submit or load) or custom actions you define on this form.
44727      * @param {String} actionName The name of the action type
44728      * @param {Object} options (optional) The options to pass to the action.  All of the config options listed
44729      * below are supported by both the submit and load actions unless otherwise noted (custom actions could also
44730      * accept other config options):
44731      * <pre>
44732 Property          Type             Description
44733 ----------------  ---------------  ----------------------------------------------------------------------------------
44734 url               String           The url for the action (defaults to the form's url)
44735 method            String           The form method to use (defaults to the form's method, or POST if not defined)
44736 params            String/Object    The params to pass (defaults to the form's baseParams, or none if not defined)
44737 clientValidation  Boolean          Applies to submit only.  Pass true to call form.isValid() prior to posting to
44738                                    validate the form on the client (defaults to false)
44739      * </pre>
44740      * @return {BasicForm} this
44741      */
44742     doAction : function(action, options){
44743         if(typeof action == 'string'){
44744             action = new Roo.form.Action.ACTION_TYPES[action](this, options);
44745         }
44746         if(this.fireEvent('beforeaction', this, action) !== false){
44747             this.beforeAction(action);
44748             action.run.defer(100, action);
44749         }
44750         return this;
44751     },
44752
44753     /**
44754      * Shortcut to do a submit action.
44755      * @param {Object} options The options to pass to the action (see {@link #doAction} for details)
44756      * @return {BasicForm} this
44757      */
44758     submit : function(options){
44759         this.doAction('submit', options);
44760         return this;
44761     },
44762
44763     /**
44764      * Shortcut to do a load action.
44765      * @param {Object} options The options to pass to the action (see {@link #doAction} for details)
44766      * @return {BasicForm} this
44767      */
44768     load : function(options){
44769         this.doAction('load', options);
44770         return this;
44771     },
44772
44773     /**
44774      * Persists the values in this form into the passed Roo.data.Record object in a beginEdit/endEdit block.
44775      * @param {Record} record The record to edit
44776      * @return {BasicForm} this
44777      */
44778     updateRecord : function(record){
44779         record.beginEdit();
44780         var fs = record.fields;
44781         fs.each(function(f){
44782             var field = this.findField(f.name);
44783             if(field){
44784                 record.set(f.name, field.getValue());
44785             }
44786         }, this);
44787         record.endEdit();
44788         return this;
44789     },
44790
44791     /**
44792      * Loads an Roo.data.Record into this form.
44793      * @param {Record} record The record to load
44794      * @return {BasicForm} this
44795      */
44796     loadRecord : function(record){
44797         this.setValues(record.data);
44798         return this;
44799     },
44800
44801     // private
44802     beforeAction : function(action){
44803         var o = action.options;
44804         
44805        
44806         if(this.waitMsgTarget === true){
44807             this.el.mask(o.waitMsg || "Sending", 'x-mask-loading');
44808         }else if(this.waitMsgTarget){
44809             this.waitMsgTarget = Roo.get(this.waitMsgTarget);
44810             this.waitMsgTarget.mask(o.waitMsg || "Sending", 'x-mask-loading');
44811         }else {
44812             Roo.MessageBox.wait(o.waitMsg || "Sending", o.waitTitle || this.waitTitle || 'Please Wait...');
44813         }
44814          
44815     },
44816
44817     // private
44818     afterAction : function(action, success){
44819         this.activeAction = null;
44820         var o = action.options;
44821         
44822         if(this.waitMsgTarget === true){
44823             this.el.unmask();
44824         }else if(this.waitMsgTarget){
44825             this.waitMsgTarget.unmask();
44826         }else{
44827             Roo.MessageBox.updateProgress(1);
44828             Roo.MessageBox.hide();
44829         }
44830          
44831         if(success){
44832             if(o.reset){
44833                 this.reset();
44834             }
44835             Roo.callback(o.success, o.scope, [this, action]);
44836             this.fireEvent('actioncomplete', this, action);
44837             
44838         }else{
44839             
44840             // failure condition..
44841             // we have a scenario where updates need confirming.
44842             // eg. if a locking scenario exists..
44843             // we look for { errors : { needs_confirm : true }} in the response.
44844             if (
44845                 (typeof(action.result) != 'undefined')  &&
44846                 (typeof(action.result.errors) != 'undefined')  &&
44847                 (typeof(action.result.errors.needs_confirm) != 'undefined')
44848            ){
44849                 var _t = this;
44850                 Roo.MessageBox.confirm(
44851                     "Change requires confirmation",
44852                     action.result.errorMsg,
44853                     function(r) {
44854                         if (r != 'yes') {
44855                             return;
44856                         }
44857                         _t.doAction('submit', { params :  { _submit_confirmed : 1 } }  );
44858                     }
44859                     
44860                 );
44861                 
44862                 
44863                 
44864                 return;
44865             }
44866             
44867             Roo.callback(o.failure, o.scope, [this, action]);
44868             // show an error message if no failed handler is set..
44869             if (!this.hasListener('actionfailed')) {
44870                 Roo.MessageBox.alert("Error",
44871                     (typeof(action.result) != 'undefined' && typeof(action.result.errorMsg) != 'undefined') ?
44872                         action.result.errorMsg :
44873                         "Saving Failed, please check your entries or try again"
44874                 );
44875             }
44876             
44877             this.fireEvent('actionfailed', this, action);
44878         }
44879         
44880     },
44881
44882     /**
44883      * Find a Roo.form.Field in this form by id, dataIndex, name or hiddenName
44884      * @param {String} id The value to search for
44885      * @return Field
44886      */
44887     findField : function(id){
44888         var field = this.items.get(id);
44889         if(!field){
44890             this.items.each(function(f){
44891                 if(f.isFormField && (f.dataIndex == id || f.id == id || f.getName() == id)){
44892                     field = f;
44893                     return false;
44894                 }
44895             });
44896         }
44897         return field || null;
44898     },
44899
44900     /**
44901      * Add a secondary form to this one, 
44902      * Used to provide tabbed forms. One form is primary, with hidden values 
44903      * which mirror the elements from the other forms.
44904      * 
44905      * @param {Roo.form.Form} form to add.
44906      * 
44907      */
44908     addForm : function(form)
44909     {
44910        
44911         if (this.childForms.indexOf(form) > -1) {
44912             // already added..
44913             return;
44914         }
44915         this.childForms.push(form);
44916         var n = '';
44917         Roo.each(form.allItems, function (fe) {
44918             
44919             n = typeof(fe.getName) == 'undefined' ? fe.name : fe.getName();
44920             if (this.findField(n)) { // already added..
44921                 return;
44922             }
44923             var add = new Roo.form.Hidden({
44924                 name : n
44925             });
44926             add.render(this.el);
44927             
44928             this.add( add );
44929         }, this);
44930         
44931     },
44932     /**
44933      * Mark fields in this form invalid in bulk.
44934      * @param {Array/Object} errors Either an array in the form [{id:'fieldId', msg:'The message'},...] or an object hash of {id: msg, id2: msg2}
44935      * @return {BasicForm} this
44936      */
44937     markInvalid : function(errors){
44938         if(errors instanceof Array){
44939             for(var i = 0, len = errors.length; i < len; i++){
44940                 var fieldError = errors[i];
44941                 var f = this.findField(fieldError.id);
44942                 if(f){
44943                     f.markInvalid(fieldError.msg);
44944                 }
44945             }
44946         }else{
44947             var field, id;
44948             for(id in errors){
44949                 if(typeof errors[id] != 'function' && (field = this.findField(id))){
44950                     field.markInvalid(errors[id]);
44951                 }
44952             }
44953         }
44954         Roo.each(this.childForms || [], function (f) {
44955             f.markInvalid(errors);
44956         });
44957         
44958         return this;
44959     },
44960
44961     /**
44962      * Set values for fields in this form in bulk.
44963      * @param {Array/Object} values Either an array in the form [{id:'fieldId', value:'foo'},...] or an object hash of {id: value, id2: value2}
44964      * @return {BasicForm} this
44965      */
44966     setValues : function(values){
44967         if(values instanceof Array){ // array of objects
44968             for(var i = 0, len = values.length; i < len; i++){
44969                 var v = values[i];
44970                 var f = this.findField(v.id);
44971                 if(f){
44972                     f.setValue(v.value);
44973                     if(this.trackResetOnLoad){
44974                         f.originalValue = f.getValue();
44975                     }
44976                 }
44977             }
44978         }else{ // object hash
44979             var field, id;
44980             for(id in values){
44981                 if(typeof values[id] != 'function' && (field = this.findField(id))){
44982                     
44983                     if (field.setFromData && 
44984                         field.valueField && 
44985                         field.displayField &&
44986                         // combos' with local stores can 
44987                         // be queried via setValue()
44988                         // to set their value..
44989                         (field.store && !field.store.isLocal)
44990                         ) {
44991                         // it's a combo
44992                         var sd = { };
44993                         sd[field.valueField] = typeof(values[field.hiddenName]) == 'undefined' ? '' : values[field.hiddenName];
44994                         sd[field.displayField] = typeof(values[field.name]) == 'undefined' ? '' : values[field.name];
44995                         field.setFromData(sd);
44996                         
44997                     } else {
44998                         field.setValue(values[id]);
44999                     }
45000                     
45001                     
45002                     if(this.trackResetOnLoad){
45003                         field.originalValue = field.getValue();
45004                     }
45005                 }
45006             }
45007         }
45008          
45009         Roo.each(this.childForms || [], function (f) {
45010             f.setValues(values);
45011         });
45012                 
45013         return this;
45014     },
45015
45016     /**
45017      * Returns the fields in this form as an object with key/value pairs. If multiple fields exist with the same name
45018      * they are returned as an array.
45019      * @param {Boolean} asString
45020      * @return {Object}
45021      */
45022     getValues : function(asString){
45023         if (this.childForms) {
45024             // copy values from the child forms
45025             Roo.each(this.childForms, function (f) {
45026                 this.setValues(f.getValues());
45027             }, this);
45028         }
45029         
45030         
45031         
45032         var fs = Roo.lib.Ajax.serializeForm(this.el.dom);
45033         if(asString === true){
45034             return fs;
45035         }
45036         return Roo.urlDecode(fs);
45037     },
45038     
45039     /**
45040      * Returns the fields in this form as an object with key/value pairs. 
45041      * This differs from getValues as it calls getValue on each child item, rather than using dom data.
45042      * @return {Object}
45043      */
45044     getFieldValues : function(with_hidden)
45045     {
45046         if (this.childForms) {
45047             // copy values from the child forms
45048             // should this call getFieldValues - probably not as we do not currently copy
45049             // hidden fields when we generate..
45050             Roo.each(this.childForms, function (f) {
45051                 this.setValues(f.getValues());
45052             }, this);
45053         }
45054         
45055         var ret = {};
45056         this.items.each(function(f){
45057             if (!f.getName()) {
45058                 return;
45059             }
45060             var v = f.getValue();
45061             if (f.inputType =='radio') {
45062                 if (typeof(ret[f.getName()]) == 'undefined') {
45063                     ret[f.getName()] = ''; // empty..
45064                 }
45065                 
45066                 if (!f.el.dom.checked) {
45067                     return;
45068                     
45069                 }
45070                 v = f.el.dom.value;
45071                 
45072             }
45073             
45074             // not sure if this supported any more..
45075             if ((typeof(v) == 'object') && f.getRawValue) {
45076                 v = f.getRawValue() ; // dates..
45077             }
45078             // combo boxes where name != hiddenName...
45079             if (f.name != f.getName()) {
45080                 ret[f.name] = f.getRawValue();
45081             }
45082             ret[f.getName()] = v;
45083         });
45084         
45085         return ret;
45086     },
45087
45088     /**
45089      * Clears all invalid messages in this form.
45090      * @return {BasicForm} this
45091      */
45092     clearInvalid : function(){
45093         this.items.each(function(f){
45094            f.clearInvalid();
45095         });
45096         
45097         Roo.each(this.childForms || [], function (f) {
45098             f.clearInvalid();
45099         });
45100         
45101         
45102         return this;
45103     },
45104
45105     /**
45106      * Resets this form.
45107      * @return {BasicForm} this
45108      */
45109     reset : function(){
45110         this.items.each(function(f){
45111             f.reset();
45112         });
45113         
45114         Roo.each(this.childForms || [], function (f) {
45115             f.reset();
45116         });
45117        
45118         
45119         return this;
45120     },
45121
45122     /**
45123      * Add Roo.form components to this form.
45124      * @param {Field} field1
45125      * @param {Field} field2 (optional)
45126      * @param {Field} etc (optional)
45127      * @return {BasicForm} this
45128      */
45129     add : function(){
45130         this.items.addAll(Array.prototype.slice.call(arguments, 0));
45131         return this;
45132     },
45133
45134
45135     /**
45136      * Removes a field from the items collection (does NOT remove its markup).
45137      * @param {Field} field
45138      * @return {BasicForm} this
45139      */
45140     remove : function(field){
45141         this.items.remove(field);
45142         return this;
45143     },
45144
45145     /**
45146      * Looks at the fields in this form, checks them for an id attribute,
45147      * and calls applyTo on the existing dom element with that id.
45148      * @return {BasicForm} this
45149      */
45150     render : function(){
45151         this.items.each(function(f){
45152             if(f.isFormField && !f.rendered && document.getElementById(f.id)){ // if the element exists
45153                 f.applyTo(f.id);
45154             }
45155         });
45156         return this;
45157     },
45158
45159     /**
45160      * Calls {@link Ext#apply} for all fields in this form with the passed object.
45161      * @param {Object} values
45162      * @return {BasicForm} this
45163      */
45164     applyToFields : function(o){
45165         this.items.each(function(f){
45166            Roo.apply(f, o);
45167         });
45168         return this;
45169     },
45170
45171     /**
45172      * Calls {@link Ext#applyIf} for all field in this form with the passed object.
45173      * @param {Object} values
45174      * @return {BasicForm} this
45175      */
45176     applyIfToFields : function(o){
45177         this.items.each(function(f){
45178            Roo.applyIf(f, o);
45179         });
45180         return this;
45181     }
45182 });
45183
45184 // back compat
45185 Roo.BasicForm = Roo.form.BasicForm;/*
45186  * Based on:
45187  * Ext JS Library 1.1.1
45188  * Copyright(c) 2006-2007, Ext JS, LLC.
45189  *
45190  * Originally Released Under LGPL - original licence link has changed is not relivant.
45191  *
45192  * Fork - LGPL
45193  * <script type="text/javascript">
45194  */
45195
45196 /**
45197  * @class Roo.form.Form
45198  * @extends Roo.form.BasicForm
45199  * Adds the ability to dynamically render forms with JavaScript to {@link Roo.form.BasicForm}.
45200  * @constructor
45201  * @param {Object} config Configuration options
45202  */
45203 Roo.form.Form = function(config){
45204     var xitems =  [];
45205     if (config.items) {
45206         xitems = config.items;
45207         delete config.items;
45208     }
45209    
45210     
45211     Roo.form.Form.superclass.constructor.call(this, null, config);
45212     this.url = this.url || this.action;
45213     if(!this.root){
45214         this.root = new Roo.form.Layout(Roo.applyIf({
45215             id: Roo.id()
45216         }, config));
45217     }
45218     this.active = this.root;
45219     /**
45220      * Array of all the buttons that have been added to this form via {@link addButton}
45221      * @type Array
45222      */
45223     this.buttons = [];
45224     this.allItems = [];
45225     this.addEvents({
45226         /**
45227          * @event clientvalidation
45228          * If the monitorValid config option is true, this event fires repetitively to notify of valid state
45229          * @param {Form} this
45230          * @param {Boolean} valid true if the form has passed client-side validation
45231          */
45232         clientvalidation: true,
45233         /**
45234          * @event rendered
45235          * Fires when the form is rendered
45236          * @param {Roo.form.Form} form
45237          */
45238         rendered : true
45239     });
45240     
45241     if (this.progressUrl) {
45242             // push a hidden field onto the list of fields..
45243             this.addxtype( {
45244                     xns: Roo.form, 
45245                     xtype : 'Hidden', 
45246                     name : 'UPLOAD_IDENTIFIER' 
45247             });
45248         }
45249         
45250     
45251     Roo.each(xitems, this.addxtype, this);
45252     
45253     
45254     
45255 };
45256
45257 Roo.extend(Roo.form.Form, Roo.form.BasicForm, {
45258     /**
45259      * @cfg {Number} labelWidth The width of labels. This property cascades to child containers.
45260      */
45261     /**
45262      * @cfg {String} itemCls A css class to apply to the x-form-item of fields. This property cascades to child containers.
45263      */
45264     /**
45265      * @cfg {String} buttonAlign Valid values are "left," "center" and "right" (defaults to "center")
45266      */
45267     buttonAlign:'center',
45268
45269     /**
45270      * @cfg {Number} minButtonWidth Minimum width of all buttons in pixels (defaults to 75)
45271      */
45272     minButtonWidth:75,
45273
45274     /**
45275      * @cfg {String} labelAlign Valid values are "left," "top" and "right" (defaults to "left").
45276      * This property cascades to child containers if not set.
45277      */
45278     labelAlign:'left',
45279
45280     /**
45281      * @cfg {Boolean} monitorValid If true the form monitors its valid state <b>client-side</b> and
45282      * fires a looping event with that state. This is required to bind buttons to the valid
45283      * state using the config value formBind:true on the button.
45284      */
45285     monitorValid : false,
45286
45287     /**
45288      * @cfg {Number} monitorPoll The milliseconds to poll valid state, ignored if monitorValid is not true (defaults to 200)
45289      */
45290     monitorPoll : 200,
45291     
45292     /**
45293      * @cfg {String} progressUrl - Url to return progress data 
45294      */
45295     
45296     progressUrl : false,
45297   
45298     /**
45299      * Opens a new {@link Roo.form.Column} container in the layout stack. If fields are passed after the config, the
45300      * fields are added and the column is closed. If no fields are passed the column remains open
45301      * until end() is called.
45302      * @param {Object} config The config to pass to the column
45303      * @param {Field} field1 (optional)
45304      * @param {Field} field2 (optional)
45305      * @param {Field} etc (optional)
45306      * @return Column The column container object
45307      */
45308     column : function(c){
45309         var col = new Roo.form.Column(c);
45310         this.start(col);
45311         if(arguments.length > 1){ // duplicate code required because of Opera
45312             this.add.apply(this, Array.prototype.slice.call(arguments, 1));
45313             this.end();
45314         }
45315         return col;
45316     },
45317
45318     /**
45319      * Opens a new {@link Roo.form.FieldSet} container in the layout stack. If fields are passed after the config, the
45320      * fields are added and the fieldset is closed. If no fields are passed the fieldset remains open
45321      * until end() is called.
45322      * @param {Object} config The config to pass to the fieldset
45323      * @param {Field} field1 (optional)
45324      * @param {Field} field2 (optional)
45325      * @param {Field} etc (optional)
45326      * @return FieldSet The fieldset container object
45327      */
45328     fieldset : function(c){
45329         var fs = new Roo.form.FieldSet(c);
45330         this.start(fs);
45331         if(arguments.length > 1){ // duplicate code required because of Opera
45332             this.add.apply(this, Array.prototype.slice.call(arguments, 1));
45333             this.end();
45334         }
45335         return fs;
45336     },
45337
45338     /**
45339      * Opens a new {@link Roo.form.Layout} container in the layout stack. If fields are passed after the config, the
45340      * fields are added and the container is closed. If no fields are passed the container remains open
45341      * until end() is called.
45342      * @param {Object} config The config to pass to the Layout
45343      * @param {Field} field1 (optional)
45344      * @param {Field} field2 (optional)
45345      * @param {Field} etc (optional)
45346      * @return Layout The container object
45347      */
45348     container : function(c){
45349         var l = new Roo.form.Layout(c);
45350         this.start(l);
45351         if(arguments.length > 1){ // duplicate code required because of Opera
45352             this.add.apply(this, Array.prototype.slice.call(arguments, 1));
45353             this.end();
45354         }
45355         return l;
45356     },
45357
45358     /**
45359      * Opens the passed container in the layout stack. The container can be any {@link Roo.form.Layout} or subclass.
45360      * @param {Object} container A Roo.form.Layout or subclass of Layout
45361      * @return {Form} this
45362      */
45363     start : function(c){
45364         // cascade label info
45365         Roo.applyIf(c, {'labelAlign': this.active.labelAlign, 'labelWidth': this.active.labelWidth, 'itemCls': this.active.itemCls});
45366         this.active.stack.push(c);
45367         c.ownerCt = this.active;
45368         this.active = c;
45369         return this;
45370     },
45371
45372     /**
45373      * Closes the current open container
45374      * @return {Form} this
45375      */
45376     end : function(){
45377         if(this.active == this.root){
45378             return this;
45379         }
45380         this.active = this.active.ownerCt;
45381         return this;
45382     },
45383
45384     /**
45385      * Add Roo.form components to the current open container (e.g. column, fieldset, etc.).  Fields added via this method
45386      * can also be passed with an additional property of fieldLabel, which if supplied, will provide the text to display
45387      * as the label of the field.
45388      * @param {Field} field1
45389      * @param {Field} field2 (optional)
45390      * @param {Field} etc. (optional)
45391      * @return {Form} this
45392      */
45393     add : function(){
45394         this.active.stack.push.apply(this.active.stack, arguments);
45395         this.allItems.push.apply(this.allItems,arguments);
45396         var r = [];
45397         for(var i = 0, a = arguments, len = a.length; i < len; i++) {
45398             if(a[i].isFormField){
45399                 r.push(a[i]);
45400             }
45401         }
45402         if(r.length > 0){
45403             Roo.form.Form.superclass.add.apply(this, r);
45404         }
45405         return this;
45406     },
45407     
45408
45409     
45410     
45411     
45412      /**
45413      * Find any element that has been added to a form, using it's ID or name
45414      * This can include framesets, columns etc. along with regular fields..
45415      * @param {String} id - id or name to find.
45416      
45417      * @return {Element} e - or false if nothing found.
45418      */
45419     findbyId : function(id)
45420     {
45421         var ret = false;
45422         if (!id) {
45423             return ret;
45424         }
45425         Roo.each(this.allItems, function(f){
45426             if (f.id == id || f.name == id ){
45427                 ret = f;
45428                 return false;
45429             }
45430         });
45431         return ret;
45432     },
45433
45434     
45435     
45436     /**
45437      * Render this form into the passed container. This should only be called once!
45438      * @param {String/HTMLElement/Element} container The element this component should be rendered into
45439      * @return {Form} this
45440      */
45441     render : function(ct)
45442     {
45443         
45444         
45445         
45446         ct = Roo.get(ct);
45447         var o = this.autoCreate || {
45448             tag: 'form',
45449             method : this.method || 'POST',
45450             id : this.id || Roo.id()
45451         };
45452         this.initEl(ct.createChild(o));
45453
45454         this.root.render(this.el);
45455         
45456        
45457              
45458         this.items.each(function(f){
45459             f.render('x-form-el-'+f.id);
45460         });
45461
45462         if(this.buttons.length > 0){
45463             // tables are required to maintain order and for correct IE layout
45464             var tb = this.el.createChild({cls:'x-form-btns-ct', cn: {
45465                 cls:"x-form-btns x-form-btns-"+this.buttonAlign,
45466                 html:'<table cellspacing="0"><tbody><tr></tr></tbody></table><div class="x-clear"></div>'
45467             }}, null, true);
45468             var tr = tb.getElementsByTagName('tr')[0];
45469             for(var i = 0, len = this.buttons.length; i < len; i++) {
45470                 var b = this.buttons[i];
45471                 var td = document.createElement('td');
45472                 td.className = 'x-form-btn-td';
45473                 b.render(tr.appendChild(td));
45474             }
45475         }
45476         if(this.monitorValid){ // initialize after render
45477             this.startMonitoring();
45478         }
45479         this.fireEvent('rendered', this);
45480         return this;
45481     },
45482
45483     /**
45484      * Adds a button to the footer of the form - this <b>must</b> be called before the form is rendered.
45485      * @param {String/Object} config A string becomes the button text, an object can either be a Button config
45486      * object or a valid Roo.DomHelper element config
45487      * @param {Function} handler The function called when the button is clicked
45488      * @param {Object} scope (optional) The scope of the handler function
45489      * @return {Roo.Button}
45490      */
45491     addButton : function(config, handler, scope){
45492         var bc = {
45493             handler: handler,
45494             scope: scope,
45495             minWidth: this.minButtonWidth,
45496             hideParent:true
45497         };
45498         if(typeof config == "string"){
45499             bc.text = config;
45500         }else{
45501             Roo.apply(bc, config);
45502         }
45503         var btn = new Roo.Button(null, bc);
45504         this.buttons.push(btn);
45505         return btn;
45506     },
45507
45508      /**
45509      * Adds a series of form elements (using the xtype property as the factory method.
45510      * Valid xtypes are:  TextField, TextArea .... Button, Layout, FieldSet, Column, (and 'end' to close a block)
45511      * @param {Object} config 
45512      */
45513     
45514     addxtype : function()
45515     {
45516         var ar = Array.prototype.slice.call(arguments, 0);
45517         var ret = false;
45518         for(var i = 0; i < ar.length; i++) {
45519             if (!ar[i]) {
45520                 continue; // skip -- if this happends something invalid got sent, we 
45521                 // should ignore it, as basically that interface element will not show up
45522                 // and that should be pretty obvious!!
45523             }
45524             
45525             if (Roo.form[ar[i].xtype]) {
45526                 ar[i].form = this;
45527                 var fe = Roo.factory(ar[i], Roo.form);
45528                 if (!ret) {
45529                     ret = fe;
45530                 }
45531                 fe.form = this;
45532                 if (fe.store) {
45533                     fe.store.form = this;
45534                 }
45535                 if (fe.isLayout) {  
45536                          
45537                     this.start(fe);
45538                     this.allItems.push(fe);
45539                     if (fe.items && fe.addxtype) {
45540                         fe.addxtype.apply(fe, fe.items);
45541                         delete fe.items;
45542                     }
45543                      this.end();
45544                     continue;
45545                 }
45546                 
45547                 
45548                  
45549                 this.add(fe);
45550               //  console.log('adding ' + ar[i].xtype);
45551             }
45552             if (ar[i].xtype == 'Button') {  
45553                 //console.log('adding button');
45554                 //console.log(ar[i]);
45555                 this.addButton(ar[i]);
45556                 this.allItems.push(fe);
45557                 continue;
45558             }
45559             
45560             if (ar[i].xtype == 'end') { // so we can add fieldsets... / layout etc.
45561                 alert('end is not supported on xtype any more, use items');
45562             //    this.end();
45563             //    //console.log('adding end');
45564             }
45565             
45566         }
45567         return ret;
45568     },
45569     
45570     /**
45571      * Starts monitoring of the valid state of this form. Usually this is done by passing the config
45572      * option "monitorValid"
45573      */
45574     startMonitoring : function(){
45575         if(!this.bound){
45576             this.bound = true;
45577             Roo.TaskMgr.start({
45578                 run : this.bindHandler,
45579                 interval : this.monitorPoll || 200,
45580                 scope: this
45581             });
45582         }
45583     },
45584
45585     /**
45586      * Stops monitoring of the valid state of this form
45587      */
45588     stopMonitoring : function(){
45589         this.bound = false;
45590     },
45591
45592     // private
45593     bindHandler : function(){
45594         if(!this.bound){
45595             return false; // stops binding
45596         }
45597         var valid = true;
45598         this.items.each(function(f){
45599             if(!f.isValid(true)){
45600                 valid = false;
45601                 return false;
45602             }
45603         });
45604         for(var i = 0, len = this.buttons.length; i < len; i++){
45605             var btn = this.buttons[i];
45606             if(btn.formBind === true && btn.disabled === valid){
45607                 btn.setDisabled(!valid);
45608             }
45609         }
45610         this.fireEvent('clientvalidation', this, valid);
45611     }
45612     
45613     
45614     
45615     
45616     
45617     
45618     
45619     
45620 });
45621
45622
45623 // back compat
45624 Roo.Form = Roo.form.Form;
45625 /*
45626  * Based on:
45627  * Ext JS Library 1.1.1
45628  * Copyright(c) 2006-2007, Ext JS, LLC.
45629  *
45630  * Originally Released Under LGPL - original licence link has changed is not relivant.
45631  *
45632  * Fork - LGPL
45633  * <script type="text/javascript">
45634  */
45635
45636 // as we use this in bootstrap.
45637 Roo.namespace('Roo.form');
45638  /**
45639  * @class Roo.form.Action
45640  * Internal Class used to handle form actions
45641  * @constructor
45642  * @param {Roo.form.BasicForm} el The form element or its id
45643  * @param {Object} config Configuration options
45644  */
45645
45646  
45647  
45648 // define the action interface
45649 Roo.form.Action = function(form, options){
45650     this.form = form;
45651     this.options = options || {};
45652 };
45653 /**
45654  * Client Validation Failed
45655  * @const 
45656  */
45657 Roo.form.Action.CLIENT_INVALID = 'client';
45658 /**
45659  * Server Validation Failed
45660  * @const 
45661  */
45662 Roo.form.Action.SERVER_INVALID = 'server';
45663  /**
45664  * Connect to Server Failed
45665  * @const 
45666  */
45667 Roo.form.Action.CONNECT_FAILURE = 'connect';
45668 /**
45669  * Reading Data from Server Failed
45670  * @const 
45671  */
45672 Roo.form.Action.LOAD_FAILURE = 'load';
45673
45674 Roo.form.Action.prototype = {
45675     type : 'default',
45676     failureType : undefined,
45677     response : undefined,
45678     result : undefined,
45679
45680     // interface method
45681     run : function(options){
45682
45683     },
45684
45685     // interface method
45686     success : function(response){
45687
45688     },
45689
45690     // interface method
45691     handleResponse : function(response){
45692
45693     },
45694
45695     // default connection failure
45696     failure : function(response){
45697         
45698         this.response = response;
45699         this.failureType = Roo.form.Action.CONNECT_FAILURE;
45700         this.form.afterAction(this, false);
45701     },
45702
45703     processResponse : function(response){
45704         this.response = response;
45705         if(!response.responseText){
45706             return true;
45707         }
45708         this.result = this.handleResponse(response);
45709         return this.result;
45710     },
45711
45712     // utility functions used internally
45713     getUrl : function(appendParams){
45714         var url = this.options.url || this.form.url || this.form.el.dom.action;
45715         if(appendParams){
45716             var p = this.getParams();
45717             if(p){
45718                 url += (url.indexOf('?') != -1 ? '&' : '?') + p;
45719             }
45720         }
45721         return url;
45722     },
45723
45724     getMethod : function(){
45725         return (this.options.method || this.form.method || this.form.el.dom.method || 'POST').toUpperCase();
45726     },
45727
45728     getParams : function(){
45729         var bp = this.form.baseParams;
45730         var p = this.options.params;
45731         if(p){
45732             if(typeof p == "object"){
45733                 p = Roo.urlEncode(Roo.applyIf(p, bp));
45734             }else if(typeof p == 'string' && bp){
45735                 p += '&' + Roo.urlEncode(bp);
45736             }
45737         }else if(bp){
45738             p = Roo.urlEncode(bp);
45739         }
45740         return p;
45741     },
45742
45743     createCallback : function(){
45744         return {
45745             success: this.success,
45746             failure: this.failure,
45747             scope: this,
45748             timeout: (this.form.timeout*1000),
45749             upload: this.form.fileUpload ? this.success : undefined
45750         };
45751     }
45752 };
45753
45754 Roo.form.Action.Submit = function(form, options){
45755     Roo.form.Action.Submit.superclass.constructor.call(this, form, options);
45756 };
45757
45758 Roo.extend(Roo.form.Action.Submit, Roo.form.Action, {
45759     type : 'submit',
45760
45761     haveProgress : false,
45762     uploadComplete : false,
45763     
45764     // uploadProgress indicator.
45765     uploadProgress : function()
45766     {
45767         if (!this.form.progressUrl) {
45768             return;
45769         }
45770         
45771         if (!this.haveProgress) {
45772             Roo.MessageBox.progress("Uploading", "Uploading");
45773         }
45774         if (this.uploadComplete) {
45775            Roo.MessageBox.hide();
45776            return;
45777         }
45778         
45779         this.haveProgress = true;
45780    
45781         var uid = this.form.findField('UPLOAD_IDENTIFIER').getValue();
45782         
45783         var c = new Roo.data.Connection();
45784         c.request({
45785             url : this.form.progressUrl,
45786             params: {
45787                 id : uid
45788             },
45789             method: 'GET',
45790             success : function(req){
45791                //console.log(data);
45792                 var rdata = false;
45793                 var edata;
45794                 try  {
45795                    rdata = Roo.decode(req.responseText)
45796                 } catch (e) {
45797                     Roo.log("Invalid data from server..");
45798                     Roo.log(edata);
45799                     return;
45800                 }
45801                 if (!rdata || !rdata.success) {
45802                     Roo.log(rdata);
45803                     Roo.MessageBox.alert(Roo.encode(rdata));
45804                     return;
45805                 }
45806                 var data = rdata.data;
45807                 
45808                 if (this.uploadComplete) {
45809                    Roo.MessageBox.hide();
45810                    return;
45811                 }
45812                    
45813                 if (data){
45814                     Roo.MessageBox.updateProgress(data.bytes_uploaded/data.bytes_total,
45815                        Math.floor((data.bytes_total - data.bytes_uploaded)/1000) + 'k remaining'
45816                     );
45817                 }
45818                 this.uploadProgress.defer(2000,this);
45819             },
45820        
45821             failure: function(data) {
45822                 Roo.log('progress url failed ');
45823                 Roo.log(data);
45824             },
45825             scope : this
45826         });
45827            
45828     },
45829     
45830     
45831     run : function()
45832     {
45833         // run get Values on the form, so it syncs any secondary forms.
45834         this.form.getValues();
45835         
45836         var o = this.options;
45837         var method = this.getMethod();
45838         var isPost = method == 'POST';
45839         if(o.clientValidation === false || this.form.isValid()){
45840             
45841             if (this.form.progressUrl) {
45842                 this.form.findField('UPLOAD_IDENTIFIER').setValue(
45843                     (new Date() * 1) + '' + Math.random());
45844                     
45845             } 
45846             
45847             
45848             Roo.Ajax.request(Roo.apply(this.createCallback(), {
45849                 form:this.form.el.dom,
45850                 url:this.getUrl(!isPost),
45851                 method: method,
45852                 params:isPost ? this.getParams() : null,
45853                 isUpload: this.form.fileUpload
45854             }));
45855             
45856             this.uploadProgress();
45857
45858         }else if (o.clientValidation !== false){ // client validation failed
45859             this.failureType = Roo.form.Action.CLIENT_INVALID;
45860             this.form.afterAction(this, false);
45861         }
45862     },
45863
45864     success : function(response)
45865     {
45866         this.uploadComplete= true;
45867         if (this.haveProgress) {
45868             Roo.MessageBox.hide();
45869         }
45870         
45871         
45872         var result = this.processResponse(response);
45873         if(result === true || result.success){
45874             this.form.afterAction(this, true);
45875             return;
45876         }
45877         if(result.errors){
45878             this.form.markInvalid(result.errors);
45879             this.failureType = Roo.form.Action.SERVER_INVALID;
45880         }
45881         this.form.afterAction(this, false);
45882     },
45883     failure : function(response)
45884     {
45885         this.uploadComplete= true;
45886         if (this.haveProgress) {
45887             Roo.MessageBox.hide();
45888         }
45889         
45890         this.response = response;
45891         this.failureType = Roo.form.Action.CONNECT_FAILURE;
45892         this.form.afterAction(this, false);
45893     },
45894     
45895     handleResponse : function(response){
45896         if(this.form.errorReader){
45897             var rs = this.form.errorReader.read(response);
45898             var errors = [];
45899             if(rs.records){
45900                 for(var i = 0, len = rs.records.length; i < len; i++) {
45901                     var r = rs.records[i];
45902                     errors[i] = r.data;
45903                 }
45904             }
45905             if(errors.length < 1){
45906                 errors = null;
45907             }
45908             return {
45909                 success : rs.success,
45910                 errors : errors
45911             };
45912         }
45913         var ret = false;
45914         try {
45915             ret = Roo.decode(response.responseText);
45916         } catch (e) {
45917             ret = {
45918                 success: false,
45919                 errorMsg: "Failed to read server message: " + (response ? response.responseText : ' - no message'),
45920                 errors : []
45921             };
45922         }
45923         return ret;
45924         
45925     }
45926 });
45927
45928
45929 Roo.form.Action.Load = function(form, options){
45930     Roo.form.Action.Load.superclass.constructor.call(this, form, options);
45931     this.reader = this.form.reader;
45932 };
45933
45934 Roo.extend(Roo.form.Action.Load, Roo.form.Action, {
45935     type : 'load',
45936
45937     run : function(){
45938         
45939         Roo.Ajax.request(Roo.apply(
45940                 this.createCallback(), {
45941                     method:this.getMethod(),
45942                     url:this.getUrl(false),
45943                     params:this.getParams()
45944         }));
45945     },
45946
45947     success : function(response){
45948         
45949         var result = this.processResponse(response);
45950         if(result === true || !result.success || !result.data){
45951             this.failureType = Roo.form.Action.LOAD_FAILURE;
45952             this.form.afterAction(this, false);
45953             return;
45954         }
45955         this.form.clearInvalid();
45956         this.form.setValues(result.data);
45957         this.form.afterAction(this, true);
45958     },
45959
45960     handleResponse : function(response){
45961         if(this.form.reader){
45962             var rs = this.form.reader.read(response);
45963             var data = rs.records && rs.records[0] ? rs.records[0].data : null;
45964             return {
45965                 success : rs.success,
45966                 data : data
45967             };
45968         }
45969         return Roo.decode(response.responseText);
45970     }
45971 });
45972
45973 Roo.form.Action.ACTION_TYPES = {
45974     'load' : Roo.form.Action.Load,
45975     'submit' : Roo.form.Action.Submit
45976 };/*
45977  * Based on:
45978  * Ext JS Library 1.1.1
45979  * Copyright(c) 2006-2007, Ext JS, LLC.
45980  *
45981  * Originally Released Under LGPL - original licence link has changed is not relivant.
45982  *
45983  * Fork - LGPL
45984  * <script type="text/javascript">
45985  */
45986  
45987 /**
45988  * @class Roo.form.Layout
45989  * @extends Roo.Component
45990  * Creates a container for layout and rendering of fields in an {@link Roo.form.Form}.
45991  * @constructor
45992  * @param {Object} config Configuration options
45993  */
45994 Roo.form.Layout = function(config){
45995     var xitems = [];
45996     if (config.items) {
45997         xitems = config.items;
45998         delete config.items;
45999     }
46000     Roo.form.Layout.superclass.constructor.call(this, config);
46001     this.stack = [];
46002     Roo.each(xitems, this.addxtype, this);
46003      
46004 };
46005
46006 Roo.extend(Roo.form.Layout, Roo.Component, {
46007     /**
46008      * @cfg {String/Object} autoCreate
46009      * A DomHelper element spec used to autocreate the layout (defaults to {tag: 'div', cls: 'x-form-ct'})
46010      */
46011     /**
46012      * @cfg {String/Object/Function} style
46013      * A style specification string, e.g. "width:100px", or object in the form {width:"100px"}, or
46014      * a function which returns such a specification.
46015      */
46016     /**
46017      * @cfg {String} labelAlign
46018      * Valid values are "left," "top" and "right" (defaults to "left")
46019      */
46020     /**
46021      * @cfg {Number} labelWidth
46022      * Fixed width in pixels of all field labels (defaults to undefined)
46023      */
46024     /**
46025      * @cfg {Boolean} clear
46026      * True to add a clearing element at the end of this layout, equivalent to CSS clear: both (defaults to true)
46027      */
46028     clear : true,
46029     /**
46030      * @cfg {String} labelSeparator
46031      * The separator to use after field labels (defaults to ':')
46032      */
46033     labelSeparator : ':',
46034     /**
46035      * @cfg {Boolean} hideLabels
46036      * True to suppress the display of field labels in this layout (defaults to false)
46037      */
46038     hideLabels : false,
46039
46040     // private
46041     defaultAutoCreate : {tag: 'div', cls: 'x-form-ct'},
46042     
46043     isLayout : true,
46044     
46045     // private
46046     onRender : function(ct, position){
46047         if(this.el){ // from markup
46048             this.el = Roo.get(this.el);
46049         }else {  // generate
46050             var cfg = this.getAutoCreate();
46051             this.el = ct.createChild(cfg, position);
46052         }
46053         if(this.style){
46054             this.el.applyStyles(this.style);
46055         }
46056         if(this.labelAlign){
46057             this.el.addClass('x-form-label-'+this.labelAlign);
46058         }
46059         if(this.hideLabels){
46060             this.labelStyle = "display:none";
46061             this.elementStyle = "padding-left:0;";
46062         }else{
46063             if(typeof this.labelWidth == 'number'){
46064                 this.labelStyle = "width:"+this.labelWidth+"px;";
46065                 this.elementStyle = "padding-left:"+((this.labelWidth+(typeof this.labelPad == 'number' ? this.labelPad : 5))+'px')+";";
46066             }
46067             if(this.labelAlign == 'top'){
46068                 this.labelStyle = "width:auto;";
46069                 this.elementStyle = "padding-left:0;";
46070             }
46071         }
46072         var stack = this.stack;
46073         var slen = stack.length;
46074         if(slen > 0){
46075             if(!this.fieldTpl){
46076                 var t = new Roo.Template(
46077                     '<div class="x-form-item {5}">',
46078                         '<label for="{0}" style="{2}">{1}{4}</label>',
46079                         '<div class="x-form-element" id="x-form-el-{0}" style="{3}">',
46080                         '</div>',
46081                     '</div><div class="x-form-clear-left"></div>'
46082                 );
46083                 t.disableFormats = true;
46084                 t.compile();
46085                 Roo.form.Layout.prototype.fieldTpl = t;
46086             }
46087             for(var i = 0; i < slen; i++) {
46088                 if(stack[i].isFormField){
46089                     this.renderField(stack[i]);
46090                 }else{
46091                     this.renderComponent(stack[i]);
46092                 }
46093             }
46094         }
46095         if(this.clear){
46096             this.el.createChild({cls:'x-form-clear'});
46097         }
46098     },
46099
46100     // private
46101     renderField : function(f){
46102         f.fieldEl = Roo.get(this.fieldTpl.append(this.el, [
46103                f.id, //0
46104                f.fieldLabel, //1
46105                f.labelStyle||this.labelStyle||'', //2
46106                this.elementStyle||'', //3
46107                typeof f.labelSeparator == 'undefined' ? this.labelSeparator : f.labelSeparator, //4
46108                f.itemCls||this.itemCls||''  //5
46109        ], true).getPrevSibling());
46110     },
46111
46112     // private
46113     renderComponent : function(c){
46114         c.render(c.isLayout ? this.el : this.el.createChild());    
46115     },
46116     /**
46117      * Adds a object form elements (using the xtype property as the factory method.)
46118      * Valid xtypes are:  TextField, TextArea .... Button, Layout, FieldSet, Column
46119      * @param {Object} config 
46120      */
46121     addxtype : function(o)
46122     {
46123         // create the lement.
46124         o.form = this.form;
46125         var fe = Roo.factory(o, Roo.form);
46126         this.form.allItems.push(fe);
46127         this.stack.push(fe);
46128         
46129         if (fe.isFormField) {
46130             this.form.items.add(fe);
46131         }
46132          
46133         return fe;
46134     }
46135 });
46136
46137 /**
46138  * @class Roo.form.Column
46139  * @extends Roo.form.Layout
46140  * Creates a column container for layout and rendering of fields in an {@link Roo.form.Form}.
46141  * @constructor
46142  * @param {Object} config Configuration options
46143  */
46144 Roo.form.Column = function(config){
46145     Roo.form.Column.superclass.constructor.call(this, config);
46146 };
46147
46148 Roo.extend(Roo.form.Column, Roo.form.Layout, {
46149     /**
46150      * @cfg {Number/String} width
46151      * The fixed width of the column in pixels or CSS value (defaults to "auto")
46152      */
46153     /**
46154      * @cfg {String/Object} autoCreate
46155      * A DomHelper element spec used to autocreate the column (defaults to {tag: 'div', cls: 'x-form-ct x-form-column'})
46156      */
46157
46158     // private
46159     defaultAutoCreate : {tag: 'div', cls: 'x-form-ct x-form-column'},
46160
46161     // private
46162     onRender : function(ct, position){
46163         Roo.form.Column.superclass.onRender.call(this, ct, position);
46164         if(this.width){
46165             this.el.setWidth(this.width);
46166         }
46167     }
46168 });
46169
46170
46171 /**
46172  * @class Roo.form.Row
46173  * @extends Roo.form.Layout
46174  * Creates a row container for layout and rendering of fields in an {@link Roo.form.Form}.
46175  * @constructor
46176  * @param {Object} config Configuration options
46177  */
46178
46179  
46180 Roo.form.Row = function(config){
46181     Roo.form.Row.superclass.constructor.call(this, config);
46182 };
46183  
46184 Roo.extend(Roo.form.Row, Roo.form.Layout, {
46185       /**
46186      * @cfg {Number/String} width
46187      * The fixed width of the column in pixels or CSS value (defaults to "auto")
46188      */
46189     /**
46190      * @cfg {Number/String} height
46191      * The fixed height of the column in pixels or CSS value (defaults to "auto")
46192      */
46193     defaultAutoCreate : {tag: 'div', cls: 'x-form-ct x-form-row'},
46194     
46195     padWidth : 20,
46196     // private
46197     onRender : function(ct, position){
46198         //console.log('row render');
46199         if(!this.rowTpl){
46200             var t = new Roo.Template(
46201                 '<div class="x-form-item {5}" style="float:left;width:{6}px">',
46202                     '<label for="{0}" style="{2}">{1}{4}</label>',
46203                     '<div class="x-form-element" id="x-form-el-{0}" style="{3}">',
46204                     '</div>',
46205                 '</div>'
46206             );
46207             t.disableFormats = true;
46208             t.compile();
46209             Roo.form.Layout.prototype.rowTpl = t;
46210         }
46211         this.fieldTpl = this.rowTpl;
46212         
46213         //console.log('lw' + this.labelWidth +', la:' + this.labelAlign);
46214         var labelWidth = 100;
46215         
46216         if ((this.labelAlign != 'top')) {
46217             if (typeof this.labelWidth == 'number') {
46218                 labelWidth = this.labelWidth
46219             }
46220             this.padWidth =  20 + labelWidth;
46221             
46222         }
46223         
46224         Roo.form.Column.superclass.onRender.call(this, ct, position);
46225         if(this.width){
46226             this.el.setWidth(this.width);
46227         }
46228         if(this.height){
46229             this.el.setHeight(this.height);
46230         }
46231     },
46232     
46233     // private
46234     renderField : function(f){
46235         f.fieldEl = this.fieldTpl.append(this.el, [
46236                f.id, f.fieldLabel,
46237                f.labelStyle||this.labelStyle||'',
46238                this.elementStyle||'',
46239                typeof f.labelSeparator == 'undefined' ? this.labelSeparator : f.labelSeparator,
46240                f.itemCls||this.itemCls||'',
46241                f.width ? f.width + this.padWidth : 160 + this.padWidth
46242        ],true);
46243     }
46244 });
46245  
46246
46247 /**
46248  * @class Roo.form.FieldSet
46249  * @extends Roo.form.Layout
46250  * Creates a fieldset container for layout and rendering of fields in an {@link Roo.form.Form}.
46251  * @constructor
46252  * @param {Object} config Configuration options
46253  */
46254 Roo.form.FieldSet = function(config){
46255     Roo.form.FieldSet.superclass.constructor.call(this, config);
46256 };
46257
46258 Roo.extend(Roo.form.FieldSet, Roo.form.Layout, {
46259     /**
46260      * @cfg {String} legend
46261      * The text to display as the legend for the FieldSet (defaults to '')
46262      */
46263     /**
46264      * @cfg {String/Object} autoCreate
46265      * A DomHelper element spec used to autocreate the fieldset (defaults to {tag: 'fieldset', cn: {tag:'legend'}})
46266      */
46267
46268     // private
46269     defaultAutoCreate : {tag: 'fieldset', cn: {tag:'legend'}},
46270
46271     // private
46272     onRender : function(ct, position){
46273         Roo.form.FieldSet.superclass.onRender.call(this, ct, position);
46274         if(this.legend){
46275             this.setLegend(this.legend);
46276         }
46277     },
46278
46279     // private
46280     setLegend : function(text){
46281         if(this.rendered){
46282             this.el.child('legend').update(text);
46283         }
46284     }
46285 });/*
46286  * Based on:
46287  * Ext JS Library 1.1.1
46288  * Copyright(c) 2006-2007, Ext JS, LLC.
46289  *
46290  * Originally Released Under LGPL - original licence link has changed is not relivant.
46291  *
46292  * Fork - LGPL
46293  * <script type="text/javascript">
46294  */
46295 /**
46296  * @class Roo.form.VTypes
46297  * Overridable validation definitions. The validations provided are basic and intended to be easily customizable and extended.
46298  * @singleton
46299  */
46300 Roo.form.VTypes = function(){
46301     // closure these in so they are only created once.
46302     var alpha = /^[a-zA-Z_]+$/;
46303     var alphanum = /^[a-zA-Z0-9_]+$/;
46304     var email = /^([\w]+)(.[\w]+)*@([\w-]+\.){1,5}([A-Za-z]){2,4}$/;
46305     var url = /(((https?)|(ftp)):\/\/([\-\w]+\.)+\w{2,3}(\/[%\-\w]+(\.\w{2,})?)*(([\w\-\.\?\\\/+@&#;`~=%!]*)(\.\w{2,})?)*\/?)/i;
46306
46307     // All these messages and functions are configurable
46308     return {
46309         /**
46310          * The function used to validate email addresses
46311          * @param {String} value The email address
46312          */
46313         'email' : function(v){
46314             return email.test(v);
46315         },
46316         /**
46317          * The error text to display when the email validation function returns false
46318          * @type String
46319          */
46320         'emailText' : 'This field should be an e-mail address in the format "user@domain.com"',
46321         /**
46322          * The keystroke filter mask to be applied on email input
46323          * @type RegExp
46324          */
46325         'emailMask' : /[a-z0-9_\.\-@]/i,
46326
46327         /**
46328          * The function used to validate URLs
46329          * @param {String} value The URL
46330          */
46331         'url' : function(v){
46332             return url.test(v);
46333         },
46334         /**
46335          * The error text to display when the url validation function returns false
46336          * @type String
46337          */
46338         'urlText' : 'This field should be a URL in the format "http:/'+'/www.domain.com"',
46339         
46340         /**
46341          * The function used to validate alpha values
46342          * @param {String} value The value
46343          */
46344         'alpha' : function(v){
46345             return alpha.test(v);
46346         },
46347         /**
46348          * The error text to display when the alpha validation function returns false
46349          * @type String
46350          */
46351         'alphaText' : 'This field should only contain letters and _',
46352         /**
46353          * The keystroke filter mask to be applied on alpha input
46354          * @type RegExp
46355          */
46356         'alphaMask' : /[a-z_]/i,
46357
46358         /**
46359          * The function used to validate alphanumeric values
46360          * @param {String} value The value
46361          */
46362         'alphanum' : function(v){
46363             return alphanum.test(v);
46364         },
46365         /**
46366          * The error text to display when the alphanumeric validation function returns false
46367          * @type String
46368          */
46369         'alphanumText' : 'This field should only contain letters, numbers and _',
46370         /**
46371          * The keystroke filter mask to be applied on alphanumeric input
46372          * @type RegExp
46373          */
46374         'alphanumMask' : /[a-z0-9_]/i
46375     };
46376 }();//<script type="text/javascript">
46377
46378 /**
46379  * @class Roo.form.FCKeditor
46380  * @extends Roo.form.TextArea
46381  * Wrapper around the FCKEditor http://www.fckeditor.net
46382  * @constructor
46383  * Creates a new FCKeditor
46384  * @param {Object} config Configuration options
46385  */
46386 Roo.form.FCKeditor = function(config){
46387     Roo.form.FCKeditor.superclass.constructor.call(this, config);
46388     this.addEvents({
46389          /**
46390          * @event editorinit
46391          * Fired when the editor is initialized - you can add extra handlers here..
46392          * @param {FCKeditor} this
46393          * @param {Object} the FCK object.
46394          */
46395         editorinit : true
46396     });
46397     
46398     
46399 };
46400 Roo.form.FCKeditor.editors = { };
46401 Roo.extend(Roo.form.FCKeditor, Roo.form.TextArea,
46402 {
46403     //defaultAutoCreate : {
46404     //    tag : "textarea",style   : "width:100px;height:60px;" ,autocomplete    : "off"
46405     //},
46406     // private
46407     /**
46408      * @cfg {Object} fck options - see fck manual for details.
46409      */
46410     fckconfig : false,
46411     
46412     /**
46413      * @cfg {Object} fck toolbar set (Basic or Default)
46414      */
46415     toolbarSet : 'Basic',
46416     /**
46417      * @cfg {Object} fck BasePath
46418      */ 
46419     basePath : '/fckeditor/',
46420     
46421     
46422     frame : false,
46423     
46424     value : '',
46425     
46426    
46427     onRender : function(ct, position)
46428     {
46429         if(!this.el){
46430             this.defaultAutoCreate = {
46431                 tag: "textarea",
46432                 style:"width:300px;height:60px;",
46433                 autocomplete: "off"
46434             };
46435         }
46436         Roo.form.FCKeditor.superclass.onRender.call(this, ct, position);
46437         /*
46438         if(this.grow){
46439             this.textSizeEl = Roo.DomHelper.append(document.body, {tag: "pre", cls: "x-form-grow-sizer"});
46440             if(this.preventScrollbars){
46441                 this.el.setStyle("overflow", "hidden");
46442             }
46443             this.el.setHeight(this.growMin);
46444         }
46445         */
46446         //console.log('onrender' + this.getId() );
46447         Roo.form.FCKeditor.editors[this.getId()] = this;
46448          
46449
46450         this.replaceTextarea() ;
46451         
46452     },
46453     
46454     getEditor : function() {
46455         return this.fckEditor;
46456     },
46457     /**
46458      * Sets a data value into the field and validates it.  To set the value directly without validation see {@link #setRawValue}.
46459      * @param {Mixed} value The value to set
46460      */
46461     
46462     
46463     setValue : function(value)
46464     {
46465         //console.log('setValue: ' + value);
46466         
46467         if(typeof(value) == 'undefined') { // not sure why this is happending...
46468             return;
46469         }
46470         Roo.form.FCKeditor.superclass.setValue.apply(this,[value]);
46471         
46472         //if(!this.el || !this.getEditor()) {
46473         //    this.value = value;
46474             //this.setValue.defer(100,this,[value]);    
46475         //    return;
46476         //} 
46477         
46478         if(!this.getEditor()) {
46479             return;
46480         }
46481         
46482         this.getEditor().SetData(value);
46483         
46484         //
46485
46486     },
46487
46488     /**
46489      * Returns the normalized data value (undefined or emptyText will be returned as '').  To return the raw value see {@link #getRawValue}.
46490      * @return {Mixed} value The field value
46491      */
46492     getValue : function()
46493     {
46494         
46495         if (this.frame && this.frame.dom.style.display == 'none') {
46496             return Roo.form.FCKeditor.superclass.getValue.call(this);
46497         }
46498         
46499         if(!this.el || !this.getEditor()) {
46500            
46501            // this.getValue.defer(100,this); 
46502             return this.value;
46503         }
46504        
46505         
46506         var value=this.getEditor().GetData();
46507         Roo.form.FCKeditor.superclass.setValue.apply(this,[value]);
46508         return Roo.form.FCKeditor.superclass.getValue.call(this);
46509         
46510
46511     },
46512
46513     /**
46514      * Returns the raw data value which may or may not be a valid, defined value.  To return a normalized value see {@link #getValue}.
46515      * @return {Mixed} value The field value
46516      */
46517     getRawValue : function()
46518     {
46519         if (this.frame && this.frame.dom.style.display == 'none') {
46520             return Roo.form.FCKeditor.superclass.getRawValue.call(this);
46521         }
46522         
46523         if(!this.el || !this.getEditor()) {
46524             //this.getRawValue.defer(100,this); 
46525             return this.value;
46526             return;
46527         }
46528         
46529         
46530         
46531         var value=this.getEditor().GetData();
46532         Roo.form.FCKeditor.superclass.setRawValue.apply(this,[value]);
46533         return Roo.form.FCKeditor.superclass.getRawValue.call(this);
46534          
46535     },
46536     
46537     setSize : function(w,h) {
46538         
46539         
46540         
46541         //if (this.frame && this.frame.dom.style.display == 'none') {
46542         //    Roo.form.FCKeditor.superclass.setSize.apply(this, [w, h]);
46543         //    return;
46544         //}
46545         //if(!this.el || !this.getEditor()) {
46546         //    this.setSize.defer(100,this, [w,h]); 
46547         //    return;
46548         //}
46549         
46550         
46551         
46552         Roo.form.FCKeditor.superclass.setSize.apply(this, [w, h]);
46553         
46554         this.frame.dom.setAttribute('width', w);
46555         this.frame.dom.setAttribute('height', h);
46556         this.frame.setSize(w,h);
46557         
46558     },
46559     
46560     toggleSourceEdit : function(value) {
46561         
46562       
46563          
46564         this.el.dom.style.display = value ? '' : 'none';
46565         this.frame.dom.style.display = value ?  'none' : '';
46566         
46567     },
46568     
46569     
46570     focus: function(tag)
46571     {
46572         if (this.frame.dom.style.display == 'none') {
46573             return Roo.form.FCKeditor.superclass.focus.call(this);
46574         }
46575         if(!this.el || !this.getEditor()) {
46576             this.focus.defer(100,this, [tag]); 
46577             return;
46578         }
46579         
46580         
46581         
46582         
46583         var tgs = this.getEditor().EditorDocument.getElementsByTagName(tag);
46584         this.getEditor().Focus();
46585         if (tgs.length) {
46586             if (!this.getEditor().Selection.GetSelection()) {
46587                 this.focus.defer(100,this, [tag]); 
46588                 return;
46589             }
46590             
46591             
46592             var r = this.getEditor().EditorDocument.createRange();
46593             r.setStart(tgs[0],0);
46594             r.setEnd(tgs[0],0);
46595             this.getEditor().Selection.GetSelection().removeAllRanges();
46596             this.getEditor().Selection.GetSelection().addRange(r);
46597             this.getEditor().Focus();
46598         }
46599         
46600     },
46601     
46602     
46603     
46604     replaceTextarea : function()
46605     {
46606         if ( document.getElementById( this.getId() + '___Frame' ) )
46607             return ;
46608         //if ( !this.checkBrowser || this._isCompatibleBrowser() )
46609         //{
46610             // We must check the elements firstly using the Id and then the name.
46611         var oTextarea = document.getElementById( this.getId() );
46612         
46613         var colElementsByName = document.getElementsByName( this.getId() ) ;
46614          
46615         oTextarea.style.display = 'none' ;
46616
46617         if ( oTextarea.tabIndex ) {            
46618             this.TabIndex = oTextarea.tabIndex ;
46619         }
46620         
46621         this._insertHtmlBefore( this._getConfigHtml(), oTextarea ) ;
46622         this._insertHtmlBefore( this._getIFrameHtml(), oTextarea ) ;
46623         this.frame = Roo.get(this.getId() + '___Frame')
46624     },
46625     
46626     _getConfigHtml : function()
46627     {
46628         var sConfig = '' ;
46629
46630         for ( var o in this.fckconfig ) {
46631             sConfig += sConfig.length > 0  ? '&amp;' : '';
46632             sConfig += encodeURIComponent( o ) + '=' + encodeURIComponent( this.fckconfig[o] ) ;
46633         }
46634
46635         return '<input type="hidden" id="' + this.getId() + '___Config" value="' + sConfig + '" style="display:none" />' ;
46636     },
46637     
46638     
46639     _getIFrameHtml : function()
46640     {
46641         var sFile = 'fckeditor.html' ;
46642         /* no idea what this is about..
46643         try
46644         {
46645             if ( (/fcksource=true/i).test( window.top.location.search ) )
46646                 sFile = 'fckeditor.original.html' ;
46647         }
46648         catch (e) { 
46649         */
46650
46651         var sLink = this.basePath + 'editor/' + sFile + '?InstanceName=' + encodeURIComponent( this.getId() ) ;
46652         sLink += this.toolbarSet ? ( '&amp;Toolbar=' + this.toolbarSet)  : '';
46653         
46654         
46655         var html = '<iframe id="' + this.getId() +
46656             '___Frame" src="' + sLink +
46657             '" width="' + this.width +
46658             '" height="' + this.height + '"' +
46659             (this.tabIndex ?  ' tabindex="' + this.tabIndex + '"' :'' ) +
46660             ' frameborder="0" scrolling="no"></iframe>' ;
46661
46662         return html ;
46663     },
46664     
46665     _insertHtmlBefore : function( html, element )
46666     {
46667         if ( element.insertAdjacentHTML )       {
46668             // IE
46669             element.insertAdjacentHTML( 'beforeBegin', html ) ;
46670         } else { // Gecko
46671             var oRange = document.createRange() ;
46672             oRange.setStartBefore( element ) ;
46673             var oFragment = oRange.createContextualFragment( html );
46674             element.parentNode.insertBefore( oFragment, element ) ;
46675         }
46676     }
46677     
46678     
46679   
46680     
46681     
46682     
46683     
46684
46685 });
46686
46687 //Roo.reg('fckeditor', Roo.form.FCKeditor);
46688
46689 function FCKeditor_OnComplete(editorInstance){
46690     var f = Roo.form.FCKeditor.editors[editorInstance.Name];
46691     f.fckEditor = editorInstance;
46692     //console.log("loaded");
46693     f.fireEvent('editorinit', f, editorInstance);
46694
46695   
46696
46697  
46698
46699
46700
46701
46702
46703
46704
46705
46706
46707
46708
46709
46710
46711
46712
46713 //<script type="text/javascript">
46714 /**
46715  * @class Roo.form.GridField
46716  * @extends Roo.form.Field
46717  * Embed a grid (or editable grid into a form)
46718  * STATUS ALPHA
46719  * 
46720  * This embeds a grid in a form, the value of the field should be the json encoded array of rows
46721  * it needs 
46722  * xgrid.store = Roo.data.Store
46723  * xgrid.store.proxy = Roo.data.MemoryProxy (data = [] )
46724  * xgrid.store.reader = Roo.data.JsonReader 
46725  * 
46726  * 
46727  * @constructor
46728  * Creates a new GridField
46729  * @param {Object} config Configuration options
46730  */
46731 Roo.form.GridField = function(config){
46732     Roo.form.GridField.superclass.constructor.call(this, config);
46733      
46734 };
46735
46736 Roo.extend(Roo.form.GridField, Roo.form.Field,  {
46737     /**
46738      * @cfg {Number} width  - used to restrict width of grid..
46739      */
46740     width : 100,
46741     /**
46742      * @cfg {Number} height - used to restrict height of grid..
46743      */
46744     height : 50,
46745      /**
46746      * @cfg {Object} xgrid (xtype'd description of grid) { xtype : 'Grid', dataSource: .... }
46747          * 
46748          *}
46749      */
46750     xgrid : false, 
46751     /**
46752      * @cfg {String/Object} autoCreate A DomHelper element spec, or true for a default element spec (defaults to
46753      * {tag: "input", type: "checkbox", autocomplete: "off"})
46754      */
46755    // defaultAutoCreate : { tag: 'div' },
46756     defaultAutoCreate : { tag: 'input', type: 'hidden', autocomplete: 'off'},
46757     /**
46758      * @cfg {String} addTitle Text to include for adding a title.
46759      */
46760     addTitle : false,
46761     //
46762     onResize : function(){
46763         Roo.form.Field.superclass.onResize.apply(this, arguments);
46764     },
46765
46766     initEvents : function(){
46767         // Roo.form.Checkbox.superclass.initEvents.call(this);
46768         // has no events...
46769        
46770     },
46771
46772
46773     getResizeEl : function(){
46774         return this.wrap;
46775     },
46776
46777     getPositionEl : function(){
46778         return this.wrap;
46779     },
46780
46781     // private
46782     onRender : function(ct, position){
46783         
46784         this.style = this.style || 'overflow: hidden; border:1px solid #c3daf9;';
46785         var style = this.style;
46786         delete this.style;
46787         
46788         Roo.form.GridField.superclass.onRender.call(this, ct, position);
46789         this.wrap = this.el.wrap({cls: ''}); // not sure why ive done thsi...
46790         this.viewEl = this.wrap.createChild({ tag: 'div' });
46791         if (style) {
46792             this.viewEl.applyStyles(style);
46793         }
46794         if (this.width) {
46795             this.viewEl.setWidth(this.width);
46796         }
46797         if (this.height) {
46798             this.viewEl.setHeight(this.height);
46799         }
46800         //if(this.inputValue !== undefined){
46801         //this.setValue(this.value);
46802         
46803         
46804         this.grid = new Roo.grid[this.xgrid.xtype](this.viewEl, this.xgrid);
46805         
46806         
46807         this.grid.render();
46808         this.grid.getDataSource().on('remove', this.refreshValue, this);
46809         this.grid.getDataSource().on('update', this.refreshValue, this);
46810         this.grid.on('afteredit', this.refreshValue, this);
46811  
46812     },
46813      
46814     
46815     /**
46816      * Sets the value of the item. 
46817      * @param {String} either an object  or a string..
46818      */
46819     setValue : function(v){
46820         //this.value = v;
46821         v = v || []; // empty set..
46822         // this does not seem smart - it really only affects memoryproxy grids..
46823         if (this.grid && this.grid.getDataSource() && typeof(v) != 'undefined') {
46824             var ds = this.grid.getDataSource();
46825             // assumes a json reader..
46826             var data = {}
46827             data[ds.reader.meta.root ] =  typeof(v) == 'string' ? Roo.decode(v) : v;
46828             ds.loadData( data);
46829         }
46830         // clear selection so it does not get stale.
46831         if (this.grid.sm) { 
46832             this.grid.sm.clearSelections();
46833         }
46834         
46835         Roo.form.GridField.superclass.setValue.call(this, v);
46836         this.refreshValue();
46837         // should load data in the grid really....
46838     },
46839     
46840     // private
46841     refreshValue: function() {
46842          var val = [];
46843         this.grid.getDataSource().each(function(r) {
46844             val.push(r.data);
46845         });
46846         this.el.dom.value = Roo.encode(val);
46847     }
46848     
46849      
46850     
46851     
46852 });/*
46853  * Based on:
46854  * Ext JS Library 1.1.1
46855  * Copyright(c) 2006-2007, Ext JS, LLC.
46856  *
46857  * Originally Released Under LGPL - original licence link has changed is not relivant.
46858  *
46859  * Fork - LGPL
46860  * <script type="text/javascript">
46861  */
46862 /**
46863  * @class Roo.form.DisplayField
46864  * @extends Roo.form.Field
46865  * A generic Field to display non-editable data.
46866  * @constructor
46867  * Creates a new Display Field item.
46868  * @param {Object} config Configuration options
46869  */
46870 Roo.form.DisplayField = function(config){
46871     Roo.form.DisplayField.superclass.constructor.call(this, config);
46872     
46873 };
46874
46875 Roo.extend(Roo.form.DisplayField, Roo.form.TextField,  {
46876     inputType:      'hidden',
46877     allowBlank:     true,
46878     readOnly:         true,
46879     
46880  
46881     /**
46882      * @cfg {String} focusClass The CSS class to use when the checkbox receives focus (defaults to undefined)
46883      */
46884     focusClass : undefined,
46885     /**
46886      * @cfg {String} fieldClass The default CSS class for the checkbox (defaults to "x-form-field")
46887      */
46888     fieldClass: 'x-form-field',
46889     
46890      /**
46891      * @cfg {Function} valueRenderer The renderer for the field (so you can reformat output). should return raw HTML
46892      */
46893     valueRenderer: undefined,
46894     
46895     width: 100,
46896     /**
46897      * @cfg {String/Object} autoCreate A DomHelper element spec, or true for a default element spec (defaults to
46898      * {tag: "input", type: "checkbox", autocomplete: "off"})
46899      */
46900      
46901  //   defaultAutoCreate : { tag: 'input', type: 'hidden', autocomplete: 'off'},
46902
46903     onResize : function(){
46904         Roo.form.DisplayField.superclass.onResize.apply(this, arguments);
46905         
46906     },
46907
46908     initEvents : function(){
46909         // Roo.form.Checkbox.superclass.initEvents.call(this);
46910         // has no events...
46911        
46912     },
46913
46914
46915     getResizeEl : function(){
46916         return this.wrap;
46917     },
46918
46919     getPositionEl : function(){
46920         return this.wrap;
46921     },
46922
46923     // private
46924     onRender : function(ct, position){
46925         
46926         Roo.form.DisplayField.superclass.onRender.call(this, ct, position);
46927         //if(this.inputValue !== undefined){
46928         this.wrap = this.el.wrap();
46929         
46930         this.viewEl = this.wrap.createChild({ tag: 'div', cls: 'x-form-displayfield'});
46931         
46932         if (this.bodyStyle) {
46933             this.viewEl.applyStyles(this.bodyStyle);
46934         }
46935         //this.viewEl.setStyle('padding', '2px');
46936         
46937         this.setValue(this.value);
46938         
46939     },
46940 /*
46941     // private
46942     initValue : Roo.emptyFn,
46943
46944   */
46945
46946         // private
46947     onClick : function(){
46948         
46949     },
46950
46951     /**
46952      * Sets the checked state of the checkbox.
46953      * @param {Boolean/String} checked True, 'true', '1', or 'on' to check the checkbox, any other value will uncheck it.
46954      */
46955     setValue : function(v){
46956         this.value = v;
46957         var html = this.valueRenderer ?  this.valueRenderer(v) : String.format('{0}', v);
46958         // this might be called before we have a dom element..
46959         if (!this.viewEl) {
46960             return;
46961         }
46962         this.viewEl.dom.innerHTML = html;
46963         Roo.form.DisplayField.superclass.setValue.call(this, v);
46964
46965     }
46966 });/*
46967  * 
46968  * Licence- LGPL
46969  * 
46970  */
46971
46972 /**
46973  * @class Roo.form.DayPicker
46974  * @extends Roo.form.Field
46975  * A Day picker show [M] [T] [W] ....
46976  * @constructor
46977  * Creates a new Day Picker
46978  * @param {Object} config Configuration options
46979  */
46980 Roo.form.DayPicker= function(config){
46981     Roo.form.DayPicker.superclass.constructor.call(this, config);
46982      
46983 };
46984
46985 Roo.extend(Roo.form.DayPicker, Roo.form.Field,  {
46986     /**
46987      * @cfg {String} focusClass The CSS class to use when the checkbox receives focus (defaults to undefined)
46988      */
46989     focusClass : undefined,
46990     /**
46991      * @cfg {String} fieldClass The default CSS class for the checkbox (defaults to "x-form-field")
46992      */
46993     fieldClass: "x-form-field",
46994    
46995     /**
46996      * @cfg {String/Object} autoCreate A DomHelper element spec, or true for a default element spec (defaults to
46997      * {tag: "input", type: "checkbox", autocomplete: "off"})
46998      */
46999     defaultAutoCreate : { tag: "input", type: 'hidden', autocomplete: "off"},
47000     
47001    
47002     actionMode : 'viewEl', 
47003     //
47004     // private
47005  
47006     inputType : 'hidden',
47007     
47008      
47009     inputElement: false, // real input element?
47010     basedOn: false, // ????
47011     
47012     isFormField: true, // not sure where this is needed!!!!
47013
47014     onResize : function(){
47015         Roo.form.Checkbox.superclass.onResize.apply(this, arguments);
47016         if(!this.boxLabel){
47017             this.el.alignTo(this.wrap, 'c-c');
47018         }
47019     },
47020
47021     initEvents : function(){
47022         Roo.form.Checkbox.superclass.initEvents.call(this);
47023         this.el.on("click", this.onClick,  this);
47024         this.el.on("change", this.onClick,  this);
47025     },
47026
47027
47028     getResizeEl : function(){
47029         return this.wrap;
47030     },
47031
47032     getPositionEl : function(){
47033         return this.wrap;
47034     },
47035
47036     
47037     // private
47038     onRender : function(ct, position){
47039         Roo.form.Checkbox.superclass.onRender.call(this, ct, position);
47040        
47041         this.wrap = this.el.wrap({cls: 'x-form-daypick-item '});
47042         
47043         var r1 = '<table><tr>';
47044         var r2 = '<tr class="x-form-daypick-icons">';
47045         for (var i=0; i < 7; i++) {
47046             r1+= '<td><div>' + Date.dayNames[i].substring(0,3) + '</div></td>';
47047             r2+= '<td><img class="x-menu-item-icon" src="' + Roo.BLANK_IMAGE_URL  +'"></td>';
47048         }
47049         
47050         var viewEl = this.wrap.createChild( r1 + '</tr>' + r2 + '</tr></table>');
47051         viewEl.select('img').on('click', this.onClick, this);
47052         this.viewEl = viewEl;   
47053         
47054         
47055         // this will not work on Chrome!!!
47056         this.el.on('DOMAttrModified', this.setFromHidden,  this); //ff
47057         this.el.on('propertychange', this.setFromHidden,  this);  //ie
47058         
47059         
47060           
47061
47062     },
47063
47064     // private
47065     initValue : Roo.emptyFn,
47066
47067     /**
47068      * Returns the checked state of the checkbox.
47069      * @return {Boolean} True if checked, else false
47070      */
47071     getValue : function(){
47072         return this.el.dom.value;
47073         
47074     },
47075
47076         // private
47077     onClick : function(e){ 
47078         //this.setChecked(!this.checked);
47079         Roo.get(e.target).toggleClass('x-menu-item-checked');
47080         this.refreshValue();
47081         //if(this.el.dom.checked != this.checked){
47082         //    this.setValue(this.el.dom.checked);
47083        // }
47084     },
47085     
47086     // private
47087     refreshValue : function()
47088     {
47089         var val = '';
47090         this.viewEl.select('img',true).each(function(e,i,n)  {
47091             val += e.is(".x-menu-item-checked") ? String(n) : '';
47092         });
47093         this.setValue(val, true);
47094     },
47095
47096     /**
47097      * Sets the checked state of the checkbox.
47098      * On is always based on a string comparison between inputValue and the param.
47099      * @param {Boolean/String} value - the value to set 
47100      * @param {Boolean/String} suppressEvent - whether to suppress the checkchange event.
47101      */
47102     setValue : function(v,suppressEvent){
47103         if (!this.el.dom) {
47104             return;
47105         }
47106         var old = this.el.dom.value ;
47107         this.el.dom.value = v;
47108         if (suppressEvent) {
47109             return ;
47110         }
47111          
47112         // update display..
47113         this.viewEl.select('img',true).each(function(e,i,n)  {
47114             
47115             var on = e.is(".x-menu-item-checked");
47116             var newv = v.indexOf(String(n)) > -1;
47117             if (on != newv) {
47118                 e.toggleClass('x-menu-item-checked');
47119             }
47120             
47121         });
47122         
47123         
47124         this.fireEvent('change', this, v, old);
47125         
47126         
47127     },
47128    
47129     // handle setting of hidden value by some other method!!?!?
47130     setFromHidden: function()
47131     {
47132         if(!this.el){
47133             return;
47134         }
47135         //console.log("SET FROM HIDDEN");
47136         //alert('setFrom hidden');
47137         this.setValue(this.el.dom.value);
47138     },
47139     
47140     onDestroy : function()
47141     {
47142         if(this.viewEl){
47143             Roo.get(this.viewEl).remove();
47144         }
47145          
47146         Roo.form.DayPicker.superclass.onDestroy.call(this);
47147     }
47148
47149 });/*
47150  * RooJS Library 1.1.1
47151  * Copyright(c) 2008-2011  Alan Knowles
47152  *
47153  * License - LGPL
47154  */
47155  
47156
47157 /**
47158  * @class Roo.form.ComboCheck
47159  * @extends Roo.form.ComboBox
47160  * A combobox for multiple select items.
47161  *
47162  * FIXME - could do with a reset button..
47163  * 
47164  * @constructor
47165  * Create a new ComboCheck
47166  * @param {Object} config Configuration options
47167  */
47168 Roo.form.ComboCheck = function(config){
47169     Roo.form.ComboCheck.superclass.constructor.call(this, config);
47170     // should verify some data...
47171     // like
47172     // hiddenName = required..
47173     // displayField = required
47174     // valudField == required
47175     var req= [ 'hiddenName', 'displayField', 'valueField' ];
47176     var _t = this;
47177     Roo.each(req, function(e) {
47178         if ((typeof(_t[e]) == 'undefined' ) || !_t[e].length) {
47179             throw "Roo.form.ComboCheck : missing value for: " + e;
47180         }
47181     });
47182     
47183     
47184 };
47185
47186 Roo.extend(Roo.form.ComboCheck, Roo.form.ComboBox, {
47187      
47188      
47189     editable : false,
47190      
47191     selectedClass: 'x-menu-item-checked', 
47192     
47193     // private
47194     onRender : function(ct, position){
47195         var _t = this;
47196         
47197         
47198         
47199         if(!this.tpl){
47200             var cls = 'x-combo-list';
47201
47202             
47203             this.tpl =  new Roo.Template({
47204                 html :  '<div class="'+cls+'-item x-menu-check-item">' +
47205                    '<img class="x-menu-item-icon" style="margin: 0px;" src="' + Roo.BLANK_IMAGE_URL + '">' + 
47206                    '<span>{' + this.displayField + '}</span>' +
47207                     '</div>' 
47208                 
47209             });
47210         }
47211  
47212         
47213         Roo.form.ComboCheck.superclass.onRender.call(this, ct, position);
47214         this.view.singleSelect = false;
47215         this.view.multiSelect = true;
47216         this.view.toggleSelect = true;
47217         this.pageTb.add(new Roo.Toolbar.Fill(), {
47218             
47219             text: 'Done',
47220             handler: function()
47221             {
47222                 _t.collapse();
47223             }
47224         });
47225     },
47226     
47227     onViewOver : function(e, t){
47228         // do nothing...
47229         return;
47230         
47231     },
47232     
47233     onViewClick : function(doFocus,index){
47234         return;
47235         
47236     },
47237     select: function () {
47238         //Roo.log("SELECT CALLED");
47239     },
47240      
47241     selectByValue : function(xv, scrollIntoView){
47242         var ar = this.getValueArray();
47243         var sels = [];
47244         
47245         Roo.each(ar, function(v) {
47246             if(v === undefined || v === null){
47247                 return;
47248             }
47249             var r = this.findRecord(this.valueField, v);
47250             if(r){
47251                 sels.push(this.store.indexOf(r))
47252                 
47253             }
47254         },this);
47255         this.view.select(sels);
47256         return false;
47257     },
47258     
47259     
47260     
47261     onSelect : function(record, index){
47262        // Roo.log("onselect Called");
47263        // this is only called by the clear button now..
47264         this.view.clearSelections();
47265         this.setValue('[]');
47266         if (this.value != this.valueBefore) {
47267             this.fireEvent('change', this, this.value, this.valueBefore);
47268             this.valueBefore = this.value;
47269         }
47270     },
47271     getValueArray : function()
47272     {
47273         var ar = [] ;
47274         
47275         try {
47276             //Roo.log(this.value);
47277             if (typeof(this.value) == 'undefined') {
47278                 return [];
47279             }
47280             var ar = Roo.decode(this.value);
47281             return  ar instanceof Array ? ar : []; //?? valid?
47282             
47283         } catch(e) {
47284             Roo.log(e + "\nRoo.form.ComboCheck:getValueArray  invalid data:" + this.getValue());
47285             return [];
47286         }
47287          
47288     },
47289     expand : function ()
47290     {
47291         
47292         Roo.form.ComboCheck.superclass.expand.call(this);
47293         this.valueBefore = typeof(this.value) == 'undefined' ? '' : this.value;
47294         //this.valueBefore = typeof(this.valueBefore) == 'undefined' ? '' : this.valueBefore;
47295         
47296
47297     },
47298     
47299     collapse : function(){
47300         Roo.form.ComboCheck.superclass.collapse.call(this);
47301         var sl = this.view.getSelectedIndexes();
47302         var st = this.store;
47303         var nv = [];
47304         var tv = [];
47305         var r;
47306         Roo.each(sl, function(i) {
47307             r = st.getAt(i);
47308             nv.push(r.get(this.valueField));
47309         },this);
47310         this.setValue(Roo.encode(nv));
47311         if (this.value != this.valueBefore) {
47312
47313             this.fireEvent('change', this, this.value, this.valueBefore);
47314             this.valueBefore = this.value;
47315         }
47316         
47317     },
47318     
47319     setValue : function(v){
47320         // Roo.log(v);
47321         this.value = v;
47322         
47323         var vals = this.getValueArray();
47324         var tv = [];
47325         Roo.each(vals, function(k) {
47326             var r = this.findRecord(this.valueField, k);
47327             if(r){
47328                 tv.push(r.data[this.displayField]);
47329             }else if(this.valueNotFoundText !== undefined){
47330                 tv.push( this.valueNotFoundText );
47331             }
47332         },this);
47333        // Roo.log(tv);
47334         
47335         Roo.form.ComboBox.superclass.setValue.call(this, tv.join(', '));
47336         this.hiddenField.value = v;
47337         this.value = v;
47338     }
47339     
47340 });/*
47341  * Based on:
47342  * Ext JS Library 1.1.1
47343  * Copyright(c) 2006-2007, Ext JS, LLC.
47344  *
47345  * Originally Released Under LGPL - original licence link has changed is not relivant.
47346  *
47347  * Fork - LGPL
47348  * <script type="text/javascript">
47349  */
47350  
47351 /**
47352  * @class Roo.form.Signature
47353  * @extends Roo.form.Field
47354  * Signature field.  
47355  * @constructor
47356  * 
47357  * @param {Object} config Configuration options
47358  */
47359
47360 Roo.form.Signature = function(config){
47361     Roo.form.Signature.superclass.constructor.call(this, config);
47362     
47363     this.addEvents({// not in used??
47364          /**
47365          * @event confirm
47366          * Fires when the 'confirm' icon is pressed (add a listener to enable add button)
47367              * @param {Roo.form.Signature} combo This combo box
47368              */
47369         'confirm' : true,
47370         /**
47371          * @event reset
47372          * Fires when the 'edit' icon is pressed (add a listener to enable add button)
47373              * @param {Roo.form.ComboBox} combo This combo box
47374              * @param {Roo.data.Record|false} record The data record returned from the underlying store (or false on nothing selected)
47375              */
47376         'reset' : true
47377     });
47378 };
47379
47380 Roo.extend(Roo.form.Signature, Roo.form.Field,  {
47381     /**
47382      * @cfg {Object} labels Label to use when rendering a form.
47383      * defaults to 
47384      * labels : { 
47385      *      clear : "Clear",
47386      *      confirm : "Confirm"
47387      *  }
47388      */
47389     labels : { 
47390         clear : "Clear",
47391         confirm : "Confirm"
47392     },
47393     /**
47394      * @cfg {Number} width The signature panel width (defaults to 300)
47395      */
47396     width: 300,
47397     /**
47398      * @cfg {Number} height The signature panel height (defaults to 100)
47399      */
47400     height : 100,
47401     /**
47402      * @cfg {Boolean} allowBlank False to validate that the value length > 0 (defaults to false)
47403      */
47404     allowBlank : false,
47405     
47406     //private
47407     // {Object} signPanel The signature SVG panel element (defaults to {})
47408     signPanel : {},
47409     // {Boolean} isMouseDown False to validate that the mouse down event (defaults to false)
47410     isMouseDown : false,
47411     // {Boolean} isConfirmed validate the signature is confirmed or not for submitting form (defaults to false)
47412     isConfirmed : false,
47413     // {String} signatureTmp SVG mapping string (defaults to empty string)
47414     signatureTmp : '',
47415     
47416     
47417     defaultAutoCreate : { // modified by initCompnoent..
47418         tag: "input",
47419         type:"hidden"
47420     },
47421
47422     // private
47423     onRender : function(ct, position){
47424         
47425         Roo.form.Signature.superclass.onRender.call(this, ct, position);
47426         
47427         this.wrap = this.el.wrap({
47428             cls:'x-form-signature-wrap', style : 'width: ' + this.width + 'px', cn:{cls:'x-form-signature'}
47429         });
47430         
47431         this.createToolbar(this);
47432         this.signPanel = this.wrap.createChild({
47433                 tag: 'div',
47434                 style: 'width: ' + this.width + 'px; height: ' + this.height + 'px; border: 0;'
47435             }, this.el
47436         );
47437             
47438         this.svgID = Roo.id();
47439         this.svgEl = this.signPanel.createChild({
47440               xmlns : 'http://www.w3.org/2000/svg',
47441               tag : 'svg',
47442               id : this.svgID + "-svg",
47443               width: this.width,
47444               height: this.height,
47445               viewBox: '0 0 '+this.width+' '+this.height,
47446               cn : [
47447                 {
47448                     tag: "rect",
47449                     id: this.svgID + "-svg-r",
47450                     width: this.width,
47451                     height: this.height,
47452                     fill: "#ffa"
47453                 },
47454                 {
47455                     tag: "line",
47456                     id: this.svgID + "-svg-l",
47457                     x1: "0", // start
47458                     y1: (this.height*0.8), // start set the line in 80% of height
47459                     x2: this.width, // end
47460                     y2: (this.height*0.8), // end set the line in 80% of height
47461                     'stroke': "#666",
47462                     'stroke-width': "1",
47463                     'stroke-dasharray': "3",
47464                     'shape-rendering': "crispEdges",
47465                     'pointer-events': "none"
47466                 },
47467                 {
47468                     tag: "path",
47469                     id: this.svgID + "-svg-p",
47470                     'stroke': "navy",
47471                     'stroke-width': "3",
47472                     'fill': "none",
47473                     'pointer-events': 'none'
47474                 }
47475               ]
47476         });
47477         this.createSVG();
47478         this.svgBox = this.svgEl.dom.getScreenCTM();
47479     },
47480     createSVG : function(){ 
47481         var svg = this.signPanel;
47482         var r = svg.select('#'+ this.svgID + '-svg-r', true).first().dom;
47483         var t = this;
47484
47485         r.addEventListener('mousedown', function(e) { return t.down(e); }, false);
47486         r.addEventListener('mousemove', function(e) { return t.move(e); }, false);
47487         r.addEventListener('mouseup', function(e) { return t.up(e); }, false);
47488         r.addEventListener('mouseout', function(e) { return t.up(e); }, false);
47489         r.addEventListener('touchstart', function(e) { return t.down(e); }, false);
47490         r.addEventListener('touchmove', function(e) { return t.move(e); }, false);
47491         r.addEventListener('touchend', function(e) { return t.up(e); }, false);
47492         
47493     },
47494     isTouchEvent : function(e){
47495         return e.type.match(/^touch/);
47496     },
47497     getCoords : function (e) {
47498         var pt    = this.svgEl.dom.createSVGPoint();
47499         pt.x = e.clientX; 
47500         pt.y = e.clientY;
47501         if (this.isTouchEvent(e)) {
47502             pt.x =  e.targetTouches[0].clientX 
47503             pt.y = e.targetTouches[0].clientY;
47504         }
47505         var a = this.svgEl.dom.getScreenCTM();
47506         var b = a.inverse();
47507         var mx = pt.matrixTransform(b);
47508         return mx.x + ',' + mx.y;
47509     },
47510     //mouse event headler 
47511     down : function (e) {
47512         this.signatureTmp += 'M' + this.getCoords(e) + ' ';
47513         this.signPanel.select('#'+ this.svgID + '-svg-p', true).first().attr('d', this.signatureTmp);
47514         
47515         this.isMouseDown = true;
47516         
47517         e.preventDefault();
47518     },
47519     move : function (e) {
47520         if (this.isMouseDown) {
47521             this.signatureTmp += 'L' + this.getCoords(e) + ' ';
47522             this.signPanel.select('#'+ this.svgID + '-svg-p', true).first().attr( 'd', this.signatureTmp);
47523         }
47524         
47525         e.preventDefault();
47526     },
47527     up : function (e) {
47528         this.isMouseDown = false;
47529         var sp = this.signatureTmp.split(' ');
47530         
47531         if(sp.length > 1){
47532             if(!sp[sp.length-2].match(/^L/)){
47533                 sp.pop();
47534                 sp.pop();
47535                 sp.push("");
47536                 this.signatureTmp = sp.join(" ");
47537             }
47538         }
47539         if(this.getValue() != this.signatureTmp){
47540             this.signPanel.select('#'+ this.svgID + '-svg-r', true).first().attr('fill', '#ffa');
47541             this.isConfirmed = false;
47542         }
47543         e.preventDefault();
47544     },
47545     
47546     /**
47547      * Protected method that will not generally be called directly. It
47548      * is called when the editor creates its toolbar. Override this method if you need to
47549      * add custom toolbar buttons.
47550      * @param {HtmlEditor} editor
47551      */
47552     createToolbar : function(editor){
47553          function btn(id, toggle, handler){
47554             var xid = fid + '-'+ id ;
47555             return {
47556                 id : xid,
47557                 cmd : id,
47558                 cls : 'x-btn-icon x-edit-'+id,
47559                 enableToggle:toggle !== false,
47560                 scope: editor, // was editor...
47561                 handler:handler||editor.relayBtnCmd,
47562                 clickEvent:'mousedown',
47563                 tooltip: etb.buttonTips[id] || undefined, ///tips ???
47564                 tabIndex:-1
47565             };
47566         }
47567         
47568         
47569         var tb = new Roo.Toolbar(editor.wrap.dom.firstChild);
47570         this.tb = tb;
47571         this.tb.add(
47572            {
47573                 cls : ' x-signature-btn x-signature-'+id,
47574                 scope: editor, // was editor...
47575                 handler: this.reset,
47576                 clickEvent:'mousedown',
47577                 text: this.labels.clear
47578             },
47579             {
47580                  xtype : 'Fill',
47581                  xns: Roo.Toolbar
47582             }, 
47583             {
47584                 cls : '  x-signature-btn x-signature-'+id,
47585                 scope: editor, // was editor...
47586                 handler: this.confirmHandler,
47587                 clickEvent:'mousedown',
47588                 text: this.labels.confirm
47589             }
47590         );
47591     
47592     },
47593     //public
47594     /**
47595      * when user is clicked confirm then show this image.....
47596      * 
47597      * @return {String} Image Data URI
47598      */
47599     getImageDataURI : function(){
47600         var svg = this.svgEl.dom.parentNode.innerHTML;
47601         var src = 'data:image/svg+xml;base64,'+window.btoa(svg);
47602         return src; 
47603     },
47604     /**
47605      * 
47606      * @return {Boolean} this.isConfirmed
47607      */
47608     getConfirmed : function(){
47609         return this.isConfirmed;
47610     },
47611     /**
47612      * 
47613      * @return {Number} this.width
47614      */
47615     getWidth : function(){
47616         return this.width;
47617     },
47618     /**
47619      * 
47620      * @return {Number} this.height
47621      */
47622     getHeight : function(){
47623         return this.height;
47624     },
47625     // private
47626     getSignature : function(){
47627         return this.signatureTmp;
47628     },
47629     // private
47630     reset : function(){
47631         this.signatureTmp = '';
47632         this.signPanel.select('#'+ this.svgID + '-svg-r', true).first().attr('fill', '#ffa');
47633         this.signPanel.select('#'+ this.svgID + '-svg-p', true).first().attr( 'd', '');
47634         this.isConfirmed = false;
47635         Roo.form.Signature.superclass.reset.call(this);
47636     },
47637     setSignature : function(s){
47638         this.signatureTmp = s;
47639         this.signPanel.select('#'+ this.svgID + '-svg-r', true).first().attr('fill', '#ffa');
47640         this.signPanel.select('#'+ this.svgID + '-svg-p', true).first().attr( 'd', s);
47641         this.setValue(s);
47642         this.isConfirmed = false;
47643         Roo.form.Signature.superclass.reset.call(this);
47644     }, 
47645     test : function(){
47646 //        Roo.log(this.signPanel.dom.contentWindow.up())
47647     },
47648     //private
47649     setConfirmed : function(){
47650         
47651         
47652         
47653 //        Roo.log(Roo.get(this.signPanel.dom.contentWindow.r).attr('fill', '#cfc'));
47654     },
47655     // private
47656     confirmHandler : function(){
47657         if(!this.getSignature()){
47658             return;
47659         }
47660         
47661         this.signPanel.select('#'+ this.svgID + '-svg-r', true).first().attr('fill', '#cfc');
47662         this.setValue(this.getSignature());
47663         this.isConfirmed = true;
47664         
47665         this.fireEvent('confirm', this);
47666     },
47667     // private
47668     // Subclasses should provide the validation implementation by overriding this
47669     validateValue : function(value){
47670         if(this.allowBlank){
47671             return true;
47672         }
47673         
47674         if(this.isConfirmed){
47675             return true;
47676         }
47677         return false;
47678     }
47679 });/*
47680  * Based on:
47681  * Ext JS Library 1.1.1
47682  * Copyright(c) 2006-2007, Ext JS, LLC.
47683  *
47684  * Originally Released Under LGPL - original licence link has changed is not relivant.
47685  *
47686  * Fork - LGPL
47687  * <script type="text/javascript">
47688  */
47689  
47690
47691 /**
47692  * @class Roo.form.ComboBox
47693  * @extends Roo.form.TriggerField
47694  * A combobox control with support for autocomplete, remote-loading, paging and many other features.
47695  * @constructor
47696  * Create a new ComboBox.
47697  * @param {Object} config Configuration options
47698  */
47699 Roo.form.Select = function(config){
47700     Roo.form.Select.superclass.constructor.call(this, config);
47701      
47702 };
47703
47704 Roo.extend(Roo.form.Select , Roo.form.ComboBox, {
47705     /**
47706      * @cfg {String/HTMLElement/Element} transform The id, DOM node or element of an existing select to convert to a ComboBox
47707      */
47708     /**
47709      * @cfg {Boolean} lazyRender True to prevent the ComboBox from rendering until requested (should always be used when
47710      * rendering into an Roo.Editor, defaults to false)
47711      */
47712     /**
47713      * @cfg {Boolean/Object} autoCreate A DomHelper element spec, or true for a default element spec (defaults to:
47714      * {tag: "input", type: "text", size: "24", autocomplete: "off"})
47715      */
47716     /**
47717      * @cfg {Roo.data.Store} store The data store to which this combo is bound (defaults to undefined)
47718      */
47719     /**
47720      * @cfg {String} title If supplied, a header element is created containing this text and added into the top of
47721      * the dropdown list (defaults to undefined, with no header element)
47722      */
47723
47724      /**
47725      * @cfg {String/Roo.Template} tpl The template to use to render the output
47726      */
47727      
47728     // private
47729     defaultAutoCreate : {tag: "select"  },
47730     /**
47731      * @cfg {Number} listWidth The width in pixels of the dropdown list (defaults to the width of the ComboBox field)
47732      */
47733     listWidth: undefined,
47734     /**
47735      * @cfg {String} displayField The underlying data field name to bind to this CombBox (defaults to undefined if
47736      * mode = 'remote' or 'text' if mode = 'local')
47737      */
47738     displayField: undefined,
47739     /**
47740      * @cfg {String} valueField The underlying data value name to bind to this CombBox (defaults to undefined if
47741      * mode = 'remote' or 'value' if mode = 'local'). 
47742      * Note: use of a valueField requires the user make a selection
47743      * in order for a value to be mapped.
47744      */
47745     valueField: undefined,
47746     
47747     
47748     /**
47749      * @cfg {String} hiddenName If specified, a hidden form field with this name is dynamically generated to store the
47750      * field's data value (defaults to the underlying DOM element's name)
47751      */
47752     hiddenName: undefined,
47753     /**
47754      * @cfg {String} listClass CSS class to apply to the dropdown list element (defaults to '')
47755      */
47756     listClass: '',
47757     /**
47758      * @cfg {String} selectedClass CSS class to apply to the selected item in the dropdown list (defaults to 'x-combo-selected')
47759      */
47760     selectedClass: 'x-combo-selected',
47761     /**
47762      * @cfg {String} triggerClass An additional CSS class used to style the trigger button.  The trigger will always get the
47763      * class 'x-form-trigger' and triggerClass will be <b>appended</b> if specified (defaults to 'x-form-arrow-trigger'
47764      * which displays a downward arrow icon).
47765      */
47766     triggerClass : 'x-form-arrow-trigger',
47767     /**
47768      * @cfg {Boolean/String} shadow True or "sides" for the default effect, "frame" for 4-way shadow, and "drop" for bottom-right
47769      */
47770     shadow:'sides',
47771     /**
47772      * @cfg {String} listAlign A valid anchor position value. See {@link Roo.Element#alignTo} for details on supported
47773      * anchor positions (defaults to 'tl-bl')
47774      */
47775     listAlign: 'tl-bl?',
47776     /**
47777      * @cfg {Number} maxHeight The maximum height in pixels of the dropdown list before scrollbars are shown (defaults to 300)
47778      */
47779     maxHeight: 300,
47780     /**
47781      * @cfg {String} triggerAction The action to execute when the trigger field is activated.  Use 'all' to run the
47782      * query specified by the allQuery config option (defaults to 'query')
47783      */
47784     triggerAction: 'query',
47785     /**
47786      * @cfg {Number} minChars The minimum number of characters the user must type before autocomplete and typeahead activate
47787      * (defaults to 4, does not apply if editable = false)
47788      */
47789     minChars : 4,
47790     /**
47791      * @cfg {Boolean} typeAhead True to populate and autoselect the remainder of the text being typed after a configurable
47792      * delay (typeAheadDelay) if it matches a known value (defaults to false)
47793      */
47794     typeAhead: false,
47795     /**
47796      * @cfg {Number} queryDelay The length of time in milliseconds to delay between the start of typing and sending the
47797      * query to filter the dropdown list (defaults to 500 if mode = 'remote' or 10 if mode = 'local')
47798      */
47799     queryDelay: 500,
47800     /**
47801      * @cfg {Number} pageSize If greater than 0, a paging toolbar is displayed in the footer of the dropdown list and the
47802      * filter queries will execute with page start and limit parameters.  Only applies when mode = 'remote' (defaults to 0)
47803      */
47804     pageSize: 0,
47805     /**
47806      * @cfg {Boolean} selectOnFocus True to select any existing text in the field immediately on focus.  Only applies
47807      * when editable = true (defaults to false)
47808      */
47809     selectOnFocus:false,
47810     /**
47811      * @cfg {String} queryParam Name of the query as it will be passed on the querystring (defaults to 'query')
47812      */
47813     queryParam: 'query',
47814     /**
47815      * @cfg {String} loadingText The text to display in the dropdown list while data is loading.  Only applies
47816      * when mode = 'remote' (defaults to 'Loading...')
47817      */
47818     loadingText: 'Loading...',
47819     /**
47820      * @cfg {Boolean} resizable True to add a resize handle to the bottom of the dropdown list (defaults to false)
47821      */
47822     resizable: false,
47823     /**
47824      * @cfg {Number} handleHeight The height in pixels of the dropdown list resize handle if resizable = true (defaults to 8)
47825      */
47826     handleHeight : 8,
47827     /**
47828      * @cfg {Boolean} editable False to prevent the user from typing text directly into the field, just like a
47829      * traditional select (defaults to true)
47830      */
47831     editable: true,
47832     /**
47833      * @cfg {String} allQuery The text query to send to the server to return all records for the list with no filtering (defaults to '')
47834      */
47835     allQuery: '',
47836     /**
47837      * @cfg {String} mode Set to 'local' if the ComboBox loads local data (defaults to 'remote' which loads from the server)
47838      */
47839     mode: 'remote',
47840     /**
47841      * @cfg {Number} minListWidth The minimum width of the dropdown list in pixels (defaults to 70, will be ignored if
47842      * listWidth has a higher value)
47843      */
47844     minListWidth : 70,
47845     /**
47846      * @cfg {Boolean} forceSelection True to restrict the selected value to one of the values in the list, false to
47847      * allow the user to set arbitrary text into the field (defaults to false)
47848      */
47849     forceSelection:false,
47850     /**
47851      * @cfg {Number} typeAheadDelay The length of time in milliseconds to wait until the typeahead text is displayed
47852      * if typeAhead = true (defaults to 250)
47853      */
47854     typeAheadDelay : 250,
47855     /**
47856      * @cfg {String} valueNotFoundText When using a name/value combo, if the value passed to setValue is not found in
47857      * the store, valueNotFoundText will be displayed as the field text if defined (defaults to undefined)
47858      */
47859     valueNotFoundText : undefined,
47860     
47861     /**
47862      * @cfg {String} defaultValue The value displayed after loading the store.
47863      */
47864     defaultValue: '',
47865     
47866     /**
47867      * @cfg {Boolean} blockFocus Prevents all focus calls, so it can work with things like HTML edtor bar
47868      */
47869     blockFocus : false,
47870     
47871     /**
47872      * @cfg {Boolean} disableClear Disable showing of clear button.
47873      */
47874     disableClear : false,
47875     /**
47876      * @cfg {Boolean} alwaysQuery  Disable caching of results, and always send query
47877      */
47878     alwaysQuery : false,
47879     
47880     //private
47881     addicon : false,
47882     editicon: false,
47883     
47884     // element that contains real text value.. (when hidden is used..)
47885      
47886     // private
47887     onRender : function(ct, position){
47888         Roo.form.Field.prototype.onRender.call(this, ct, position);
47889         
47890         if(this.store){
47891             this.store.on('beforeload', this.onBeforeLoad, this);
47892             this.store.on('load', this.onLoad, this);
47893             this.store.on('loadexception', this.onLoadException, this);
47894             this.store.load({});
47895         }
47896         
47897         
47898         
47899     },
47900
47901     // private
47902     initEvents : function(){
47903         //Roo.form.ComboBox.superclass.initEvents.call(this);
47904  
47905     },
47906
47907     onDestroy : function(){
47908        
47909         if(this.store){
47910             this.store.un('beforeload', this.onBeforeLoad, this);
47911             this.store.un('load', this.onLoad, this);
47912             this.store.un('loadexception', this.onLoadException, this);
47913         }
47914         //Roo.form.ComboBox.superclass.onDestroy.call(this);
47915     },
47916
47917     // private
47918     fireKey : function(e){
47919         if(e.isNavKeyPress() && !this.list.isVisible()){
47920             this.fireEvent("specialkey", this, e);
47921         }
47922     },
47923
47924     // private
47925     onResize: function(w, h){
47926         
47927         return; 
47928     
47929         
47930     },
47931
47932     /**
47933      * Allow or prevent the user from directly editing the field text.  If false is passed,
47934      * the user will only be able to select from the items defined in the dropdown list.  This method
47935      * is the runtime equivalent of setting the 'editable' config option at config time.
47936      * @param {Boolean} value True to allow the user to directly edit the field text
47937      */
47938     setEditable : function(value){
47939          
47940     },
47941
47942     // private
47943     onBeforeLoad : function(){
47944         
47945         Roo.log("Select before load");
47946         return;
47947     
47948         this.innerList.update(this.loadingText ?
47949                '<div class="loading-indicator">'+this.loadingText+'</div>' : '');
47950         //this.restrictHeight();
47951         this.selectedIndex = -1;
47952     },
47953
47954     // private
47955     onLoad : function(){
47956
47957     
47958         var dom = this.el.dom;
47959         dom.innerHTML = '';
47960          var od = dom.ownerDocument;
47961          
47962         if (this.emptyText) {
47963             var op = od.createElement('option');
47964             op.setAttribute('value', '');
47965             op.innerHTML = String.format('{0}', this.emptyText);
47966             dom.appendChild(op);
47967         }
47968         if(this.store.getCount() > 0){
47969            
47970             var vf = this.valueField;
47971             var df = this.displayField;
47972             this.store.data.each(function(r) {
47973                 // which colmsn to use... testing - cdoe / title..
47974                 var op = od.createElement('option');
47975                 op.setAttribute('value', r.data[vf]);
47976                 op.innerHTML = String.format('{0}', r.data[df]);
47977                 dom.appendChild(op);
47978             });
47979             if (typeof(this.defaultValue != 'undefined')) {
47980                 this.setValue(this.defaultValue);
47981             }
47982             
47983              
47984         }else{
47985             //this.onEmptyResults();
47986         }
47987         //this.el.focus();
47988     },
47989     // private
47990     onLoadException : function()
47991     {
47992         dom.innerHTML = '';
47993             
47994         Roo.log("Select on load exception");
47995         return;
47996     
47997         this.collapse();
47998         Roo.log(this.store.reader.jsonData);
47999         if (this.store && typeof(this.store.reader.jsonData.errorMsg) != 'undefined') {
48000             Roo.MessageBox.alert("Error loading",this.store.reader.jsonData.errorMsg);
48001         }
48002         
48003         
48004     },
48005     // private
48006     onTypeAhead : function(){
48007          
48008     },
48009
48010     // private
48011     onSelect : function(record, index){
48012         Roo.log('on select?');
48013         return;
48014         if(this.fireEvent('beforeselect', this, record, index) !== false){
48015             this.setFromData(index > -1 ? record.data : false);
48016             this.collapse();
48017             this.fireEvent('select', this, record, index);
48018         }
48019     },
48020
48021     /**
48022      * Returns the currently selected field value or empty string if no value is set.
48023      * @return {String} value The selected value
48024      */
48025     getValue : function(){
48026         var dom = this.el.dom;
48027         this.value = dom.options[dom.selectedIndex].value;
48028         return this.value;
48029         
48030     },
48031
48032     /**
48033      * Clears any text/value currently set in the field
48034      */
48035     clearValue : function(){
48036         this.value = '';
48037         this.el.dom.selectedIndex = this.emptyText ? 0 : -1;
48038         
48039     },
48040
48041     /**
48042      * Sets the specified value into the field.  If the value finds a match, the corresponding record text
48043      * will be displayed in the field.  If the value does not match the data value of an existing item,
48044      * and the valueNotFoundText config option is defined, it will be displayed as the default field text.
48045      * Otherwise the field will be blank (although the value will still be set).
48046      * @param {String} value The value to match
48047      */
48048     setValue : function(v){
48049         var d = this.el.dom;
48050         for (var i =0; i < d.options.length;i++) {
48051             if (v == d.options[i].value) {
48052                 d.selectedIndex = i;
48053                 this.value = v;
48054                 return;
48055             }
48056         }
48057         this.clearValue();
48058     },
48059     /**
48060      * @property {Object} the last set data for the element
48061      */
48062     
48063     lastData : false,
48064     /**
48065      * Sets the value of the field based on a object which is related to the record format for the store.
48066      * @param {Object} value the value to set as. or false on reset?
48067      */
48068     setFromData : function(o){
48069         Roo.log('setfrom data?');
48070          
48071         
48072         
48073     },
48074     // private
48075     reset : function(){
48076         this.clearValue();
48077     },
48078     // private
48079     findRecord : function(prop, value){
48080         
48081         return false;
48082     
48083         var record;
48084         if(this.store.getCount() > 0){
48085             this.store.each(function(r){
48086                 if(r.data[prop] == value){
48087                     record = r;
48088                     return false;
48089                 }
48090                 return true;
48091             });
48092         }
48093         return record;
48094     },
48095     
48096     getName: function()
48097     {
48098         // returns hidden if it's set..
48099         if (!this.rendered) {return ''};
48100         return !this.hiddenName && this.el.dom.name  ? this.el.dom.name : (this.hiddenName || '');
48101         
48102     },
48103      
48104
48105     
48106
48107     // private
48108     onEmptyResults : function(){
48109         Roo.log('empty results');
48110         //this.collapse();
48111     },
48112
48113     /**
48114      * Returns true if the dropdown list is expanded, else false.
48115      */
48116     isExpanded : function(){
48117         return false;
48118     },
48119
48120     /**
48121      * Select an item in the dropdown list by its data value. This function does NOT cause the select event to fire.
48122      * The store must be loaded and the list expanded for this function to work, otherwise use setValue.
48123      * @param {String} value The data value of the item to select
48124      * @param {Boolean} scrollIntoView False to prevent the dropdown list from autoscrolling to display the
48125      * selected item if it is not currently in view (defaults to true)
48126      * @return {Boolean} True if the value matched an item in the list, else false
48127      */
48128     selectByValue : function(v, scrollIntoView){
48129         Roo.log('select By Value');
48130         return false;
48131     
48132         if(v !== undefined && v !== null){
48133             var r = this.findRecord(this.valueField || this.displayField, v);
48134             if(r){
48135                 this.select(this.store.indexOf(r), scrollIntoView);
48136                 return true;
48137             }
48138         }
48139         return false;
48140     },
48141
48142     /**
48143      * Select an item in the dropdown list by its numeric index in the list. This function does NOT cause the select event to fire.
48144      * The store must be loaded and the list expanded for this function to work, otherwise use setValue.
48145      * @param {Number} index The zero-based index of the list item to select
48146      * @param {Boolean} scrollIntoView False to prevent the dropdown list from autoscrolling to display the
48147      * selected item if it is not currently in view (defaults to true)
48148      */
48149     select : function(index, scrollIntoView){
48150         Roo.log('select ');
48151         return  ;
48152         
48153         this.selectedIndex = index;
48154         this.view.select(index);
48155         if(scrollIntoView !== false){
48156             var el = this.view.getNode(index);
48157             if(el){
48158                 this.innerList.scrollChildIntoView(el, false);
48159             }
48160         }
48161     },
48162
48163       
48164
48165     // private
48166     validateBlur : function(){
48167         
48168         return;
48169         
48170     },
48171
48172     // private
48173     initQuery : function(){
48174         this.doQuery(this.getRawValue());
48175     },
48176
48177     // private
48178     doForce : function(){
48179         if(this.el.dom.value.length > 0){
48180             this.el.dom.value =
48181                 this.lastSelectionText === undefined ? '' : this.lastSelectionText;
48182              
48183         }
48184     },
48185
48186     /**
48187      * Execute a query to filter the dropdown list.  Fires the beforequery event prior to performing the
48188      * query allowing the query action to be canceled if needed.
48189      * @param {String} query The SQL query to execute
48190      * @param {Boolean} forceAll True to force the query to execute even if there are currently fewer characters
48191      * in the field than the minimum specified by the minChars config option.  It also clears any filter previously
48192      * saved in the current store (defaults to false)
48193      */
48194     doQuery : function(q, forceAll){
48195         
48196         Roo.log('doQuery?');
48197         if(q === undefined || q === null){
48198             q = '';
48199         }
48200         var qe = {
48201             query: q,
48202             forceAll: forceAll,
48203             combo: this,
48204             cancel:false
48205         };
48206         if(this.fireEvent('beforequery', qe)===false || qe.cancel){
48207             return false;
48208         }
48209         q = qe.query;
48210         forceAll = qe.forceAll;
48211         if(forceAll === true || (q.length >= this.minChars)){
48212             if(this.lastQuery != q || this.alwaysQuery){
48213                 this.lastQuery = q;
48214                 if(this.mode == 'local'){
48215                     this.selectedIndex = -1;
48216                     if(forceAll){
48217                         this.store.clearFilter();
48218                     }else{
48219                         this.store.filter(this.displayField, q);
48220                     }
48221                     this.onLoad();
48222                 }else{
48223                     this.store.baseParams[this.queryParam] = q;
48224                     this.store.load({
48225                         params: this.getParams(q)
48226                     });
48227                     this.expand();
48228                 }
48229             }else{
48230                 this.selectedIndex = -1;
48231                 this.onLoad();   
48232             }
48233         }
48234     },
48235
48236     // private
48237     getParams : function(q){
48238         var p = {};
48239         //p[this.queryParam] = q;
48240         if(this.pageSize){
48241             p.start = 0;
48242             p.limit = this.pageSize;
48243         }
48244         return p;
48245     },
48246
48247     /**
48248      * Hides the dropdown list if it is currently expanded. Fires the 'collapse' event on completion.
48249      */
48250     collapse : function(){
48251         
48252     },
48253
48254     // private
48255     collapseIf : function(e){
48256         
48257     },
48258
48259     /**
48260      * Expands the dropdown list if it is currently hidden. Fires the 'expand' event on completion.
48261      */
48262     expand : function(){
48263         
48264     } ,
48265
48266     // private
48267      
48268
48269     /** 
48270     * @cfg {Boolean} grow 
48271     * @hide 
48272     */
48273     /** 
48274     * @cfg {Number} growMin 
48275     * @hide 
48276     */
48277     /** 
48278     * @cfg {Number} growMax 
48279     * @hide 
48280     */
48281     /**
48282      * @hide
48283      * @method autoSize
48284      */
48285     
48286     setWidth : function()
48287     {
48288         
48289     },
48290     getResizeEl : function(){
48291         return this.el;
48292     }
48293 });//<script type="text/javasscript">
48294  
48295
48296 /**
48297  * @class Roo.DDView
48298  * A DnD enabled version of Roo.View.
48299  * @param {Element/String} container The Element in which to create the View.
48300  * @param {String} tpl The template string used to create the markup for each element of the View
48301  * @param {Object} config The configuration properties. These include all the config options of
48302  * {@link Roo.View} plus some specific to this class.<br>
48303  * <p>
48304  * Drag/drop is implemented by adding {@link Roo.data.Record}s to the target DDView. If copying is
48305  * not being performed, the original {@link Roo.data.Record} is removed from the source DDView.<br>
48306  * <p>
48307  * The following extra CSS rules are needed to provide insertion point highlighting:<pre><code>
48308 .x-view-drag-insert-above {
48309         border-top:1px dotted #3366cc;
48310 }
48311 .x-view-drag-insert-below {
48312         border-bottom:1px dotted #3366cc;
48313 }
48314 </code></pre>
48315  * 
48316  */
48317  
48318 Roo.DDView = function(container, tpl, config) {
48319     Roo.DDView.superclass.constructor.apply(this, arguments);
48320     this.getEl().setStyle("outline", "0px none");
48321     this.getEl().unselectable();
48322     if (this.dragGroup) {
48323                 this.setDraggable(this.dragGroup.split(","));
48324     }
48325     if (this.dropGroup) {
48326                 this.setDroppable(this.dropGroup.split(","));
48327     }
48328     if (this.deletable) {
48329         this.setDeletable();
48330     }
48331     this.isDirtyFlag = false;
48332         this.addEvents({
48333                 "drop" : true
48334         });
48335 };
48336
48337 Roo.extend(Roo.DDView, Roo.View, {
48338 /**     @cfg {String/Array} dragGroup The ddgroup name(s) for the View's DragZone. */
48339 /**     @cfg {String/Array} dropGroup The ddgroup name(s) for the View's DropZone. */
48340 /**     @cfg {Boolean} copy Causes drag operations to copy nodes rather than move. */
48341 /**     @cfg {Boolean} allowCopy Causes ctrl/drag operations to copy nodes rather than move. */
48342
48343         isFormField: true,
48344
48345         reset: Roo.emptyFn,
48346         
48347         clearInvalid: Roo.form.Field.prototype.clearInvalid,
48348
48349         validate: function() {
48350                 return true;
48351         },
48352         
48353         destroy: function() {
48354                 this.purgeListeners();
48355                 this.getEl.removeAllListeners();
48356                 this.getEl().remove();
48357                 if (this.dragZone) {
48358                         if (this.dragZone.destroy) {
48359                                 this.dragZone.destroy();
48360                         }
48361                 }
48362                 if (this.dropZone) {
48363                         if (this.dropZone.destroy) {
48364                                 this.dropZone.destroy();
48365                         }
48366                 }
48367         },
48368
48369 /**     Allows this class to be an Roo.form.Field so it can be found using {@link Roo.form.BasicForm#findField}. */
48370         getName: function() {
48371                 return this.name;
48372         },
48373
48374 /**     Loads the View from a JSON string representing the Records to put into the Store. */
48375         setValue: function(v) {
48376                 if (!this.store) {
48377                         throw "DDView.setValue(). DDView must be constructed with a valid Store";
48378                 }
48379                 var data = {};
48380                 data[this.store.reader.meta.root] = v ? [].concat(v) : [];
48381                 this.store.proxy = new Roo.data.MemoryProxy(data);
48382                 this.store.load();
48383         },
48384
48385 /**     @return {String} a parenthesised list of the ids of the Records in the View. */
48386         getValue: function() {
48387                 var result = '(';
48388                 this.store.each(function(rec) {
48389                         result += rec.id + ',';
48390                 });
48391                 return result.substr(0, result.length - 1) + ')';
48392         },
48393         
48394         getIds: function() {
48395                 var i = 0, result = new Array(this.store.getCount());
48396                 this.store.each(function(rec) {
48397                         result[i++] = rec.id;
48398                 });
48399                 return result;
48400         },
48401         
48402         isDirty: function() {
48403                 return this.isDirtyFlag;
48404         },
48405
48406 /**
48407  *      Part of the Roo.dd.DropZone interface. If no target node is found, the
48408  *      whole Element becomes the target, and this causes the drop gesture to append.
48409  */
48410     getTargetFromEvent : function(e) {
48411                 var target = e.getTarget();
48412                 while ((target !== null) && (target.parentNode != this.el.dom)) {
48413                 target = target.parentNode;
48414                 }
48415                 if (!target) {
48416                         target = this.el.dom.lastChild || this.el.dom;
48417                 }
48418                 return target;
48419     },
48420
48421 /**
48422  *      Create the drag data which consists of an object which has the property "ddel" as
48423  *      the drag proxy element. 
48424  */
48425     getDragData : function(e) {
48426         var target = this.findItemFromChild(e.getTarget());
48427                 if(target) {
48428                         this.handleSelection(e);
48429                         var selNodes = this.getSelectedNodes();
48430             var dragData = {
48431                 source: this,
48432                 copy: this.copy || (this.allowCopy && e.ctrlKey),
48433                 nodes: selNodes,
48434                 records: []
48435                         };
48436                         var selectedIndices = this.getSelectedIndexes();
48437                         for (var i = 0; i < selectedIndices.length; i++) {
48438                                 dragData.records.push(this.store.getAt(selectedIndices[i]));
48439                         }
48440                         if (selNodes.length == 1) {
48441                                 dragData.ddel = target.cloneNode(true); // the div element
48442                         } else {
48443                                 var div = document.createElement('div'); // create the multi element drag "ghost"
48444                                 div.className = 'multi-proxy';
48445                                 for (var i = 0, len = selNodes.length; i < len; i++) {
48446                                         div.appendChild(selNodes[i].cloneNode(true));
48447                                 }
48448                                 dragData.ddel = div;
48449                         }
48450             //console.log(dragData)
48451             //console.log(dragData.ddel.innerHTML)
48452                         return dragData;
48453                 }
48454         //console.log('nodragData')
48455                 return false;
48456     },
48457     
48458 /**     Specify to which ddGroup items in this DDView may be dragged. */
48459     setDraggable: function(ddGroup) {
48460         if (ddGroup instanceof Array) {
48461                 Roo.each(ddGroup, this.setDraggable, this);
48462                 return;
48463         }
48464         if (this.dragZone) {
48465                 this.dragZone.addToGroup(ddGroup);
48466         } else {
48467                         this.dragZone = new Roo.dd.DragZone(this.getEl(), {
48468                                 containerScroll: true,
48469                                 ddGroup: ddGroup 
48470
48471                         });
48472 //                      Draggability implies selection. DragZone's mousedown selects the element.
48473                         if (!this.multiSelect) { this.singleSelect = true; }
48474
48475 //                      Wire the DragZone's handlers up to methods in *this*
48476                         this.dragZone.getDragData = this.getDragData.createDelegate(this);
48477                 }
48478     },
48479
48480 /**     Specify from which ddGroup this DDView accepts drops. */
48481     setDroppable: function(ddGroup) {
48482         if (ddGroup instanceof Array) {
48483                 Roo.each(ddGroup, this.setDroppable, this);
48484                 return;
48485         }
48486         if (this.dropZone) {
48487                 this.dropZone.addToGroup(ddGroup);
48488         } else {
48489                         this.dropZone = new Roo.dd.DropZone(this.getEl(), {
48490                                 containerScroll: true,
48491                                 ddGroup: ddGroup
48492                         });
48493
48494 //                      Wire the DropZone's handlers up to methods in *this*
48495                         this.dropZone.getTargetFromEvent = this.getTargetFromEvent.createDelegate(this);
48496                         this.dropZone.onNodeEnter = this.onNodeEnter.createDelegate(this);
48497                         this.dropZone.onNodeOver = this.onNodeOver.createDelegate(this);
48498                         this.dropZone.onNodeOut = this.onNodeOut.createDelegate(this);
48499                         this.dropZone.onNodeDrop = this.onNodeDrop.createDelegate(this);
48500                 }
48501     },
48502
48503 /**     Decide whether to drop above or below a View node. */
48504     getDropPoint : function(e, n, dd){
48505         if (n == this.el.dom) { return "above"; }
48506                 var t = Roo.lib.Dom.getY(n), b = t + n.offsetHeight;
48507                 var c = t + (b - t) / 2;
48508                 var y = Roo.lib.Event.getPageY(e);
48509                 if(y <= c) {
48510                         return "above";
48511                 }else{
48512                         return "below";
48513                 }
48514     },
48515
48516     onNodeEnter : function(n, dd, e, data){
48517                 return false;
48518     },
48519     
48520     onNodeOver : function(n, dd, e, data){
48521                 var pt = this.getDropPoint(e, n, dd);
48522                 // set the insert point style on the target node
48523                 var dragElClass = this.dropNotAllowed;
48524                 if (pt) {
48525                         var targetElClass;
48526                         if (pt == "above"){
48527                                 dragElClass = n.previousSibling ? "x-tree-drop-ok-between" : "x-tree-drop-ok-above";
48528                                 targetElClass = "x-view-drag-insert-above";
48529                         } else {
48530                                 dragElClass = n.nextSibling ? "x-tree-drop-ok-between" : "x-tree-drop-ok-below";
48531                                 targetElClass = "x-view-drag-insert-below";
48532                         }
48533                         if (this.lastInsertClass != targetElClass){
48534                                 Roo.fly(n).replaceClass(this.lastInsertClass, targetElClass);
48535                                 this.lastInsertClass = targetElClass;
48536                         }
48537                 }
48538                 return dragElClass;
48539         },
48540
48541     onNodeOut : function(n, dd, e, data){
48542                 this.removeDropIndicators(n);
48543     },
48544
48545     onNodeDrop : function(n, dd, e, data){
48546         if (this.fireEvent("drop", this, n, dd, e, data) === false) {
48547                 return false;
48548         }
48549         var pt = this.getDropPoint(e, n, dd);
48550                 var insertAt = (n == this.el.dom) ? this.nodes.length : n.nodeIndex;
48551                 if (pt == "below") { insertAt++; }
48552                 for (var i = 0; i < data.records.length; i++) {
48553                         var r = data.records[i];
48554                         var dup = this.store.getById(r.id);
48555                         if (dup && (dd != this.dragZone)) {
48556                                 Roo.fly(this.getNode(this.store.indexOf(dup))).frame("red", 1);
48557                         } else {
48558                                 if (data.copy) {
48559                                         this.store.insert(insertAt++, r.copy());
48560                                 } else {
48561                                         data.source.isDirtyFlag = true;
48562                                         r.store.remove(r);
48563                                         this.store.insert(insertAt++, r);
48564                                 }
48565                                 this.isDirtyFlag = true;
48566                         }
48567                 }
48568                 this.dragZone.cachedTarget = null;
48569                 return true;
48570     },
48571
48572     removeDropIndicators : function(n){
48573                 if(n){
48574                         Roo.fly(n).removeClass([
48575                                 "x-view-drag-insert-above",
48576                                 "x-view-drag-insert-below"]);
48577                         this.lastInsertClass = "_noclass";
48578                 }
48579     },
48580
48581 /**
48582  *      Utility method. Add a delete option to the DDView's context menu.
48583  *      @param {String} imageUrl The URL of the "delete" icon image.
48584  */
48585         setDeletable: function(imageUrl) {
48586                 if (!this.singleSelect && !this.multiSelect) {
48587                         this.singleSelect = true;
48588                 }
48589                 var c = this.getContextMenu();
48590                 this.contextMenu.on("itemclick", function(item) {
48591                         switch (item.id) {
48592                                 case "delete":
48593                                         this.remove(this.getSelectedIndexes());
48594                                         break;
48595                         }
48596                 }, this);
48597                 this.contextMenu.add({
48598                         icon: imageUrl,
48599                         id: "delete",
48600                         text: 'Delete'
48601                 });
48602         },
48603         
48604 /**     Return the context menu for this DDView. */
48605         getContextMenu: function() {
48606                 if (!this.contextMenu) {
48607 //                      Create the View's context menu
48608                         this.contextMenu = new Roo.menu.Menu({
48609                                 id: this.id + "-contextmenu"
48610                         });
48611                         this.el.on("contextmenu", this.showContextMenu, this);
48612                 }
48613                 return this.contextMenu;
48614         },
48615         
48616         disableContextMenu: function() {
48617                 if (this.contextMenu) {
48618                         this.el.un("contextmenu", this.showContextMenu, this);
48619                 }
48620         },
48621
48622         showContextMenu: function(e, item) {
48623         item = this.findItemFromChild(e.getTarget());
48624                 if (item) {
48625                         e.stopEvent();
48626                         this.select(this.getNode(item), this.multiSelect && e.ctrlKey, true);
48627                         this.contextMenu.showAt(e.getXY());
48628             }
48629     },
48630
48631 /**
48632  *      Remove {@link Roo.data.Record}s at the specified indices.
48633  *      @param {Array/Number} selectedIndices The index (or Array of indices) of Records to remove.
48634  */
48635     remove: function(selectedIndices) {
48636                 selectedIndices = [].concat(selectedIndices);
48637                 for (var i = 0; i < selectedIndices.length; i++) {
48638                         var rec = this.store.getAt(selectedIndices[i]);
48639                         this.store.remove(rec);
48640                 }
48641     },
48642
48643 /**
48644  *      Double click fires the event, but also, if this is draggable, and there is only one other
48645  *      related DropZone, it transfers the selected node.
48646  */
48647     onDblClick : function(e){
48648         var item = this.findItemFromChild(e.getTarget());
48649         if(item){
48650             if (this.fireEvent("dblclick", this, this.indexOf(item), item, e) === false) {
48651                 return false;
48652             }
48653             if (this.dragGroup) {
48654                     var targets = Roo.dd.DragDropMgr.getRelated(this.dragZone, true);
48655                     while (targets.indexOf(this.dropZone) > -1) {
48656                             targets.remove(this.dropZone);
48657                                 }
48658                     if (targets.length == 1) {
48659                                         this.dragZone.cachedTarget = null;
48660                         var el = Roo.get(targets[0].getEl());
48661                         var box = el.getBox(true);
48662                         targets[0].onNodeDrop(el.dom, {
48663                                 target: el.dom,
48664                                 xy: [box.x, box.y + box.height - 1]
48665                         }, null, this.getDragData(e));
48666                     }
48667                 }
48668         }
48669     },
48670     
48671     handleSelection: function(e) {
48672                 this.dragZone.cachedTarget = null;
48673         var item = this.findItemFromChild(e.getTarget());
48674         if (!item) {
48675                 this.clearSelections(true);
48676                 return;
48677         }
48678                 if (item && (this.multiSelect || this.singleSelect)){
48679                         if(this.multiSelect && e.shiftKey && (!e.ctrlKey) && this.lastSelection){
48680                                 this.select(this.getNodes(this.indexOf(this.lastSelection), item.nodeIndex), false);
48681                         }else if (this.isSelected(this.getNode(item)) && e.ctrlKey){
48682                                 this.unselect(item);
48683                         } else {
48684                                 this.select(item, this.multiSelect && e.ctrlKey);
48685                                 this.lastSelection = item;
48686                         }
48687                 }
48688     },
48689
48690     onItemClick : function(item, index, e){
48691                 if(this.fireEvent("beforeclick", this, index, item, e) === false){
48692                         return false;
48693                 }
48694                 return true;
48695     },
48696
48697     unselect : function(nodeInfo, suppressEvent){
48698                 var node = this.getNode(nodeInfo);
48699                 if(node && this.isSelected(node)){
48700                         if(this.fireEvent("beforeselect", this, node, this.selections) !== false){
48701                                 Roo.fly(node).removeClass(this.selectedClass);
48702                                 this.selections.remove(node);
48703                                 if(!suppressEvent){
48704                                         this.fireEvent("selectionchange", this, this.selections);
48705                                 }
48706                         }
48707                 }
48708     }
48709 });
48710 /*
48711  * Based on:
48712  * Ext JS Library 1.1.1
48713  * Copyright(c) 2006-2007, Ext JS, LLC.
48714  *
48715  * Originally Released Under LGPL - original licence link has changed is not relivant.
48716  *
48717  * Fork - LGPL
48718  * <script type="text/javascript">
48719  */
48720  
48721 /**
48722  * @class Roo.LayoutManager
48723  * @extends Roo.util.Observable
48724  * Base class for layout managers.
48725  */
48726 Roo.LayoutManager = function(container, config){
48727     Roo.LayoutManager.superclass.constructor.call(this);
48728     this.el = Roo.get(container);
48729     // ie scrollbar fix
48730     if(this.el.dom == document.body && Roo.isIE && !config.allowScroll){
48731         document.body.scroll = "no";
48732     }else if(this.el.dom != document.body && this.el.getStyle('position') == 'static'){
48733         this.el.position('relative');
48734     }
48735     this.id = this.el.id;
48736     this.el.addClass("x-layout-container");
48737     /** false to disable window resize monitoring @type Boolean */
48738     this.monitorWindowResize = true;
48739     this.regions = {};
48740     this.addEvents({
48741         /**
48742          * @event layout
48743          * Fires when a layout is performed. 
48744          * @param {Roo.LayoutManager} this
48745          */
48746         "layout" : true,
48747         /**
48748          * @event regionresized
48749          * Fires when the user resizes a region. 
48750          * @param {Roo.LayoutRegion} region The resized region
48751          * @param {Number} newSize The new size (width for east/west, height for north/south)
48752          */
48753         "regionresized" : true,
48754         /**
48755          * @event regioncollapsed
48756          * Fires when a region is collapsed. 
48757          * @param {Roo.LayoutRegion} region The collapsed region
48758          */
48759         "regioncollapsed" : true,
48760         /**
48761          * @event regionexpanded
48762          * Fires when a region is expanded.  
48763          * @param {Roo.LayoutRegion} region The expanded region
48764          */
48765         "regionexpanded" : true
48766     });
48767     this.updating = false;
48768     Roo.EventManager.onWindowResize(this.onWindowResize, this, true);
48769 };
48770
48771 Roo.extend(Roo.LayoutManager, Roo.util.Observable, {
48772     /**
48773      * Returns true if this layout is currently being updated
48774      * @return {Boolean}
48775      */
48776     isUpdating : function(){
48777         return this.updating; 
48778     },
48779     
48780     /**
48781      * Suspend the LayoutManager from doing auto-layouts while
48782      * making multiple add or remove calls
48783      */
48784     beginUpdate : function(){
48785         this.updating = true;    
48786     },
48787     
48788     /**
48789      * Restore auto-layouts and optionally disable the manager from performing a layout
48790      * @param {Boolean} noLayout true to disable a layout update 
48791      */
48792     endUpdate : function(noLayout){
48793         this.updating = false;
48794         if(!noLayout){
48795             this.layout();
48796         }    
48797     },
48798     
48799     layout: function(){
48800         
48801     },
48802     
48803     onRegionResized : function(region, newSize){
48804         this.fireEvent("regionresized", region, newSize);
48805         this.layout();
48806     },
48807     
48808     onRegionCollapsed : function(region){
48809         this.fireEvent("regioncollapsed", region);
48810     },
48811     
48812     onRegionExpanded : function(region){
48813         this.fireEvent("regionexpanded", region);
48814     },
48815         
48816     /**
48817      * Returns the size of the current view. This method normalizes document.body and element embedded layouts and
48818      * performs box-model adjustments.
48819      * @return {Object} The size as an object {width: (the width), height: (the height)}
48820      */
48821     getViewSize : function(){
48822         var size;
48823         if(this.el.dom != document.body){
48824             size = this.el.getSize();
48825         }else{
48826             size = {width: Roo.lib.Dom.getViewWidth(), height: Roo.lib.Dom.getViewHeight()};
48827         }
48828         size.width -= this.el.getBorderWidth("lr")-this.el.getPadding("lr");
48829         size.height -= this.el.getBorderWidth("tb")-this.el.getPadding("tb");
48830         return size;
48831     },
48832     
48833     /**
48834      * Returns the Element this layout is bound to.
48835      * @return {Roo.Element}
48836      */
48837     getEl : function(){
48838         return this.el;
48839     },
48840     
48841     /**
48842      * Returns the specified region.
48843      * @param {String} target The region key ('center', 'north', 'south', 'east' or 'west')
48844      * @return {Roo.LayoutRegion}
48845      */
48846     getRegion : function(target){
48847         return this.regions[target.toLowerCase()];
48848     },
48849     
48850     onWindowResize : function(){
48851         if(this.monitorWindowResize){
48852             this.layout();
48853         }
48854     }
48855 });/*
48856  * Based on:
48857  * Ext JS Library 1.1.1
48858  * Copyright(c) 2006-2007, Ext JS, LLC.
48859  *
48860  * Originally Released Under LGPL - original licence link has changed is not relivant.
48861  *
48862  * Fork - LGPL
48863  * <script type="text/javascript">
48864  */
48865 /**
48866  * @class Roo.BorderLayout
48867  * @extends Roo.LayoutManager
48868  * This class represents a common layout manager used in desktop applications. For screenshots and more details,
48869  * please see: <br><br>
48870  * <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>
48871  * <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>
48872  * Example:
48873  <pre><code>
48874  var layout = new Roo.BorderLayout(document.body, {
48875     north: {
48876         initialSize: 25,
48877         titlebar: false
48878     },
48879     west: {
48880         split:true,
48881         initialSize: 200,
48882         minSize: 175,
48883         maxSize: 400,
48884         titlebar: true,
48885         collapsible: true
48886     },
48887     east: {
48888         split:true,
48889         initialSize: 202,
48890         minSize: 175,
48891         maxSize: 400,
48892         titlebar: true,
48893         collapsible: true
48894     },
48895     south: {
48896         split:true,
48897         initialSize: 100,
48898         minSize: 100,
48899         maxSize: 200,
48900         titlebar: true,
48901         collapsible: true
48902     },
48903     center: {
48904         titlebar: true,
48905         autoScroll:true,
48906         resizeTabs: true,
48907         minTabWidth: 50,
48908         preferredTabWidth: 150
48909     }
48910 });
48911
48912 // shorthand
48913 var CP = Roo.ContentPanel;
48914
48915 layout.beginUpdate();
48916 layout.add("north", new CP("north", "North"));
48917 layout.add("south", new CP("south", {title: "South", closable: true}));
48918 layout.add("west", new CP("west", {title: "West"}));
48919 layout.add("east", new CP("autoTabs", {title: "Auto Tabs", closable: true}));
48920 layout.add("center", new CP("center1", {title: "Close Me", closable: true}));
48921 layout.add("center", new CP("center2", {title: "Center Panel", closable: false}));
48922 layout.getRegion("center").showPanel("center1");
48923 layout.endUpdate();
48924 </code></pre>
48925
48926 <b>The container the layout is rendered into can be either the body element or any other element.
48927 If it is not the body element, the container needs to either be an absolute positioned element,
48928 or you will need to add "position:relative" to the css of the container.  You will also need to specify
48929 the container size if it is not the body element.</b>
48930
48931 * @constructor
48932 * Create a new BorderLayout
48933 * @param {String/HTMLElement/Element} container The container this layout is bound to
48934 * @param {Object} config Configuration options
48935  */
48936 Roo.BorderLayout = function(container, config){
48937     config = config || {};
48938     Roo.BorderLayout.superclass.constructor.call(this, container, config);
48939     this.factory = config.factory || Roo.BorderLayout.RegionFactory;
48940     for(var i = 0, len = this.factory.validRegions.length; i < len; i++) {
48941         var target = this.factory.validRegions[i];
48942         if(config[target]){
48943             this.addRegion(target, config[target]);
48944         }
48945     }
48946 };
48947
48948 Roo.extend(Roo.BorderLayout, Roo.LayoutManager, {
48949     /**
48950      * Creates and adds a new region if it doesn't already exist.
48951      * @param {String} target The target region key (north, south, east, west or center).
48952      * @param {Object} config The regions config object
48953      * @return {BorderLayoutRegion} The new region
48954      */
48955     addRegion : function(target, config){
48956         if(!this.regions[target]){
48957             var r = this.factory.create(target, this, config);
48958             this.bindRegion(target, r);
48959         }
48960         return this.regions[target];
48961     },
48962
48963     // private (kinda)
48964     bindRegion : function(name, r){
48965         this.regions[name] = r;
48966         r.on("visibilitychange", this.layout, this);
48967         r.on("paneladded", this.layout, this);
48968         r.on("panelremoved", this.layout, this);
48969         r.on("invalidated", this.layout, this);
48970         r.on("resized", this.onRegionResized, this);
48971         r.on("collapsed", this.onRegionCollapsed, this);
48972         r.on("expanded", this.onRegionExpanded, this);
48973     },
48974
48975     /**
48976      * Performs a layout update.
48977      */
48978     layout : function(){
48979         if(this.updating) return;
48980         var size = this.getViewSize();
48981         var w = size.width;
48982         var h = size.height;
48983         var centerW = w;
48984         var centerH = h;
48985         var centerY = 0;
48986         var centerX = 0;
48987         //var x = 0, y = 0;
48988
48989         var rs = this.regions;
48990         var north = rs["north"];
48991         var south = rs["south"]; 
48992         var west = rs["west"];
48993         var east = rs["east"];
48994         var center = rs["center"];
48995         //if(this.hideOnLayout){ // not supported anymore
48996             //c.el.setStyle("display", "none");
48997         //}
48998         if(north && north.isVisible()){
48999             var b = north.getBox();
49000             var m = north.getMargins();
49001             b.width = w - (m.left+m.right);
49002             b.x = m.left;
49003             b.y = m.top;
49004             centerY = b.height + b.y + m.bottom;
49005             centerH -= centerY;
49006             north.updateBox(this.safeBox(b));
49007         }
49008         if(south && south.isVisible()){
49009             var b = south.getBox();
49010             var m = south.getMargins();
49011             b.width = w - (m.left+m.right);
49012             b.x = m.left;
49013             var totalHeight = (b.height + m.top + m.bottom);
49014             b.y = h - totalHeight + m.top;
49015             centerH -= totalHeight;
49016             south.updateBox(this.safeBox(b));
49017         }
49018         if(west && west.isVisible()){
49019             var b = west.getBox();
49020             var m = west.getMargins();
49021             b.height = centerH - (m.top+m.bottom);
49022             b.x = m.left;
49023             b.y = centerY + m.top;
49024             var totalWidth = (b.width + m.left + m.right);
49025             centerX += totalWidth;
49026             centerW -= totalWidth;
49027             west.updateBox(this.safeBox(b));
49028         }
49029         if(east && east.isVisible()){
49030             var b = east.getBox();
49031             var m = east.getMargins();
49032             b.height = centerH - (m.top+m.bottom);
49033             var totalWidth = (b.width + m.left + m.right);
49034             b.x = w - totalWidth + m.left;
49035             b.y = centerY + m.top;
49036             centerW -= totalWidth;
49037             east.updateBox(this.safeBox(b));
49038         }
49039         if(center){
49040             var m = center.getMargins();
49041             var centerBox = {
49042                 x: centerX + m.left,
49043                 y: centerY + m.top,
49044                 width: centerW - (m.left+m.right),
49045                 height: centerH - (m.top+m.bottom)
49046             };
49047             //if(this.hideOnLayout){
49048                 //center.el.setStyle("display", "block");
49049             //}
49050             center.updateBox(this.safeBox(centerBox));
49051         }
49052         this.el.repaint();
49053         this.fireEvent("layout", this);
49054     },
49055
49056     // private
49057     safeBox : function(box){
49058         box.width = Math.max(0, box.width);
49059         box.height = Math.max(0, box.height);
49060         return box;
49061     },
49062
49063     /**
49064      * Adds a ContentPanel (or subclass) to this layout.
49065      * @param {String} target The target region key (north, south, east, west or center).
49066      * @param {Roo.ContentPanel} panel The panel to add
49067      * @return {Roo.ContentPanel} The added panel
49068      */
49069     add : function(target, panel){
49070          
49071         target = target.toLowerCase();
49072         return this.regions[target].add(panel);
49073     },
49074
49075     /**
49076      * Remove a ContentPanel (or subclass) to this layout.
49077      * @param {String} target The target region key (north, south, east, west or center).
49078      * @param {Number/String/Roo.ContentPanel} panel The index, id or panel to remove
49079      * @return {Roo.ContentPanel} The removed panel
49080      */
49081     remove : function(target, panel){
49082         target = target.toLowerCase();
49083         return this.regions[target].remove(panel);
49084     },
49085
49086     /**
49087      * Searches all regions for a panel with the specified id
49088      * @param {String} panelId
49089      * @return {Roo.ContentPanel} The panel or null if it wasn't found
49090      */
49091     findPanel : function(panelId){
49092         var rs = this.regions;
49093         for(var target in rs){
49094             if(typeof rs[target] != "function"){
49095                 var p = rs[target].getPanel(panelId);
49096                 if(p){
49097                     return p;
49098                 }
49099             }
49100         }
49101         return null;
49102     },
49103
49104     /**
49105      * Searches all regions for a panel with the specified id and activates (shows) it.
49106      * @param {String/ContentPanel} panelId The panels id or the panel itself
49107      * @return {Roo.ContentPanel} The shown panel or null
49108      */
49109     showPanel : function(panelId) {
49110       var rs = this.regions;
49111       for(var target in rs){
49112          var r = rs[target];
49113          if(typeof r != "function"){
49114             if(r.hasPanel(panelId)){
49115                return r.showPanel(panelId);
49116             }
49117          }
49118       }
49119       return null;
49120    },
49121
49122    /**
49123      * Restores this layout's state using Roo.state.Manager or the state provided by the passed provider.
49124      * @param {Roo.state.Provider} provider (optional) An alternate state provider
49125      */
49126     restoreState : function(provider){
49127         if(!provider){
49128             provider = Roo.state.Manager;
49129         }
49130         var sm = new Roo.LayoutStateManager();
49131         sm.init(this, provider);
49132     },
49133
49134     /**
49135      * Adds a batch of multiple ContentPanels dynamically by passing a special regions config object.  This config
49136      * object should contain properties for each region to add ContentPanels to, and each property's value should be
49137      * a valid ContentPanel config object.  Example:
49138      * <pre><code>
49139 // Create the main layout
49140 var layout = new Roo.BorderLayout('main-ct', {
49141     west: {
49142         split:true,
49143         minSize: 175,
49144         titlebar: true
49145     },
49146     center: {
49147         title:'Components'
49148     }
49149 }, 'main-ct');
49150
49151 // Create and add multiple ContentPanels at once via configs
49152 layout.batchAdd({
49153    west: {
49154        id: 'source-files',
49155        autoCreate:true,
49156        title:'Ext Source Files',
49157        autoScroll:true,
49158        fitToFrame:true
49159    },
49160    center : {
49161        el: cview,
49162        autoScroll:true,
49163        fitToFrame:true,
49164        toolbar: tb,
49165        resizeEl:'cbody'
49166    }
49167 });
49168 </code></pre>
49169      * @param {Object} regions An object containing ContentPanel configs by region name
49170      */
49171     batchAdd : function(regions){
49172         this.beginUpdate();
49173         for(var rname in regions){
49174             var lr = this.regions[rname];
49175             if(lr){
49176                 this.addTypedPanels(lr, regions[rname]);
49177             }
49178         }
49179         this.endUpdate();
49180     },
49181
49182     // private
49183     addTypedPanels : function(lr, ps){
49184         if(typeof ps == 'string'){
49185             lr.add(new Roo.ContentPanel(ps));
49186         }
49187         else if(ps instanceof Array){
49188             for(var i =0, len = ps.length; i < len; i++){
49189                 this.addTypedPanels(lr, ps[i]);
49190             }
49191         }
49192         else if(!ps.events){ // raw config?
49193             var el = ps.el;
49194             delete ps.el; // prevent conflict
49195             lr.add(new Roo.ContentPanel(el || Roo.id(), ps));
49196         }
49197         else {  // panel object assumed!
49198             lr.add(ps);
49199         }
49200     },
49201     /**
49202      * Adds a xtype elements to the layout.
49203      * <pre><code>
49204
49205 layout.addxtype({
49206        xtype : 'ContentPanel',
49207        region: 'west',
49208        items: [ .... ]
49209    }
49210 );
49211
49212 layout.addxtype({
49213         xtype : 'NestedLayoutPanel',
49214         region: 'west',
49215         layout: {
49216            center: { },
49217            west: { }   
49218         },
49219         items : [ ... list of content panels or nested layout panels.. ]
49220    }
49221 );
49222 </code></pre>
49223      * @param {Object} cfg Xtype definition of item to add.
49224      */
49225     addxtype : function(cfg)
49226     {
49227         // basically accepts a pannel...
49228         // can accept a layout region..!?!?
49229         //Roo.log('Roo.BorderLayout add ' + cfg.xtype)
49230         
49231         if (!cfg.xtype.match(/Panel$/)) {
49232             return false;
49233         }
49234         var ret = false;
49235         
49236         if (typeof(cfg.region) == 'undefined') {
49237             Roo.log("Failed to add Panel, region was not set");
49238             Roo.log(cfg);
49239             return false;
49240         }
49241         var region = cfg.region;
49242         delete cfg.region;
49243         
49244           
49245         var xitems = [];
49246         if (cfg.items) {
49247             xitems = cfg.items;
49248             delete cfg.items;
49249         }
49250         var nb = false;
49251         
49252         switch(cfg.xtype) 
49253         {
49254             case 'ContentPanel':  // ContentPanel (el, cfg)
49255             case 'ScrollPanel':  // ContentPanel (el, cfg)
49256             case 'ViewPanel': 
49257                 if(cfg.autoCreate) {
49258                     ret = new Roo[cfg.xtype](cfg); // new panel!!!!!
49259                 } else {
49260                     var el = this.el.createChild();
49261                     ret = new Roo[cfg.xtype](el, cfg); // new panel!!!!!
49262                 }
49263                 
49264                 this.add(region, ret);
49265                 break;
49266             
49267             
49268             case 'TreePanel': // our new panel!
49269                 cfg.el = this.el.createChild();
49270                 ret = new Roo[cfg.xtype](cfg); // new panel!!!!!
49271                 this.add(region, ret);
49272                 break;
49273             
49274             case 'NestedLayoutPanel': 
49275                 // create a new Layout (which is  a Border Layout...
49276                 var el = this.el.createChild();
49277                 var clayout = cfg.layout;
49278                 delete cfg.layout;
49279                 clayout.items   = clayout.items  || [];
49280                 // replace this exitems with the clayout ones..
49281                 xitems = clayout.items;
49282                  
49283                 
49284                 if (region == 'center' && this.active && this.getRegion('center').panels.length < 1) {
49285                     cfg.background = false;
49286                 }
49287                 var layout = new Roo.BorderLayout(el, clayout);
49288                 
49289                 ret = new Roo[cfg.xtype](layout, cfg); // new panel!!!!!
49290                 //console.log('adding nested layout panel '  + cfg.toSource());
49291                 this.add(region, ret);
49292                 nb = {}; /// find first...
49293                 break;
49294                 
49295             case 'GridPanel': 
49296             
49297                 // needs grid and region
49298                 
49299                 //var el = this.getRegion(region).el.createChild();
49300                 var el = this.el.createChild();
49301                 // create the grid first...
49302                 
49303                 var grid = new Roo.grid[cfg.grid.xtype](el, cfg.grid);
49304                 delete cfg.grid;
49305                 if (region == 'center' && this.active ) {
49306                     cfg.background = false;
49307                 }
49308                 ret = new Roo[cfg.xtype](grid, cfg); // new panel!!!!!
49309                 
49310                 this.add(region, ret);
49311                 if (cfg.background) {
49312                     ret.on('activate', function(gp) {
49313                         if (!gp.grid.rendered) {
49314                             gp.grid.render();
49315                         }
49316                     });
49317                 } else {
49318                     grid.render();
49319                 }
49320                 break;
49321            
49322            
49323            
49324                 
49325                 
49326                 
49327             default:
49328                 if (typeof(Roo[cfg.xtype]) != 'undefined') {
49329                     
49330                     ret = new Roo[cfg.xtype](cfg); // new panel!!!!!
49331                     this.add(region, ret);
49332                 } else {
49333                 
49334                     alert("Can not add '" + cfg.xtype + "' to BorderLayout");
49335                     return null;
49336                 }
49337                 
49338              // GridPanel (grid, cfg)
49339             
49340         }
49341         this.beginUpdate();
49342         // add children..
49343         var region = '';
49344         var abn = {};
49345         Roo.each(xitems, function(i)  {
49346             region = nb && i.region ? i.region : false;
49347             
49348             var add = ret.addxtype(i);
49349            
49350             if (region) {
49351                 nb[region] = nb[region] == undefined ? 0 : nb[region]+1;
49352                 if (!i.background) {
49353                     abn[region] = nb[region] ;
49354                 }
49355             }
49356             
49357         });
49358         this.endUpdate();
49359
49360         // make the last non-background panel active..
49361         //if (nb) { Roo.log(abn); }
49362         if (nb) {
49363             
49364             for(var r in abn) {
49365                 region = this.getRegion(r);
49366                 if (region) {
49367                     // tried using nb[r], but it does not work..
49368                      
49369                     region.showPanel(abn[r]);
49370                    
49371                 }
49372             }
49373         }
49374         return ret;
49375         
49376     }
49377 });
49378
49379 /**
49380  * Shortcut for creating a new BorderLayout object and adding one or more ContentPanels to it in a single step, handling
49381  * the beginUpdate and endUpdate calls internally.  The key to this method is the <b>panels</b> property that can be
49382  * provided with each region config, which allows you to add ContentPanel configs in addition to the region configs
49383  * during creation.  The following code is equivalent to the constructor-based example at the beginning of this class:
49384  * <pre><code>
49385 // shorthand
49386 var CP = Roo.ContentPanel;
49387
49388 var layout = Roo.BorderLayout.create({
49389     north: {
49390         initialSize: 25,
49391         titlebar: false,
49392         panels: [new CP("north", "North")]
49393     },
49394     west: {
49395         split:true,
49396         initialSize: 200,
49397         minSize: 175,
49398         maxSize: 400,
49399         titlebar: true,
49400         collapsible: true,
49401         panels: [new CP("west", {title: "West"})]
49402     },
49403     east: {
49404         split:true,
49405         initialSize: 202,
49406         minSize: 175,
49407         maxSize: 400,
49408         titlebar: true,
49409         collapsible: true,
49410         panels: [new CP("autoTabs", {title: "Auto Tabs", closable: true})]
49411     },
49412     south: {
49413         split:true,
49414         initialSize: 100,
49415         minSize: 100,
49416         maxSize: 200,
49417         titlebar: true,
49418         collapsible: true,
49419         panels: [new CP("south", {title: "South", closable: true})]
49420     },
49421     center: {
49422         titlebar: true,
49423         autoScroll:true,
49424         resizeTabs: true,
49425         minTabWidth: 50,
49426         preferredTabWidth: 150,
49427         panels: [
49428             new CP("center1", {title: "Close Me", closable: true}),
49429             new CP("center2", {title: "Center Panel", closable: false})
49430         ]
49431     }
49432 }, document.body);
49433
49434 layout.getRegion("center").showPanel("center1");
49435 </code></pre>
49436  * @param config
49437  * @param targetEl
49438  */
49439 Roo.BorderLayout.create = function(config, targetEl){
49440     var layout = new Roo.BorderLayout(targetEl || document.body, config);
49441     layout.beginUpdate();
49442     var regions = Roo.BorderLayout.RegionFactory.validRegions;
49443     for(var j = 0, jlen = regions.length; j < jlen; j++){
49444         var lr = regions[j];
49445         if(layout.regions[lr] && config[lr].panels){
49446             var r = layout.regions[lr];
49447             var ps = config[lr].panels;
49448             layout.addTypedPanels(r, ps);
49449         }
49450     }
49451     layout.endUpdate();
49452     return layout;
49453 };
49454
49455 // private
49456 Roo.BorderLayout.RegionFactory = {
49457     // private
49458     validRegions : ["north","south","east","west","center"],
49459
49460     // private
49461     create : function(target, mgr, config){
49462         target = target.toLowerCase();
49463         if(config.lightweight || config.basic){
49464             return new Roo.BasicLayoutRegion(mgr, config, target);
49465         }
49466         switch(target){
49467             case "north":
49468                 return new Roo.NorthLayoutRegion(mgr, config);
49469             case "south":
49470                 return new Roo.SouthLayoutRegion(mgr, config);
49471             case "east":
49472                 return new Roo.EastLayoutRegion(mgr, config);
49473             case "west":
49474                 return new Roo.WestLayoutRegion(mgr, config);
49475             case "center":
49476                 return new Roo.CenterLayoutRegion(mgr, config);
49477         }
49478         throw 'Layout region "'+target+'" not supported.';
49479     }
49480 };/*
49481  * Based on:
49482  * Ext JS Library 1.1.1
49483  * Copyright(c) 2006-2007, Ext JS, LLC.
49484  *
49485  * Originally Released Under LGPL - original licence link has changed is not relivant.
49486  *
49487  * Fork - LGPL
49488  * <script type="text/javascript">
49489  */
49490  
49491 /**
49492  * @class Roo.BasicLayoutRegion
49493  * @extends Roo.util.Observable
49494  * This class represents a lightweight region in a layout manager. This region does not move dom nodes
49495  * and does not have a titlebar, tabs or any other features. All it does is size and position 
49496  * panels. To create a BasicLayoutRegion, add lightweight:true or basic:true to your regions config.
49497  */
49498 Roo.BasicLayoutRegion = function(mgr, config, pos, skipConfig){
49499     this.mgr = mgr;
49500     this.position  = pos;
49501     this.events = {
49502         /**
49503          * @scope Roo.BasicLayoutRegion
49504          */
49505         
49506         /**
49507          * @event beforeremove
49508          * Fires before a panel is removed (or closed). To cancel the removal set "e.cancel = true" on the event argument.
49509          * @param {Roo.LayoutRegion} this
49510          * @param {Roo.ContentPanel} panel The panel
49511          * @param {Object} e The cancel event object
49512          */
49513         "beforeremove" : true,
49514         /**
49515          * @event invalidated
49516          * Fires when the layout for this region is changed.
49517          * @param {Roo.LayoutRegion} this
49518          */
49519         "invalidated" : true,
49520         /**
49521          * @event visibilitychange
49522          * Fires when this region is shown or hidden 
49523          * @param {Roo.LayoutRegion} this
49524          * @param {Boolean} visibility true or false
49525          */
49526         "visibilitychange" : true,
49527         /**
49528          * @event paneladded
49529          * Fires when a panel is added. 
49530          * @param {Roo.LayoutRegion} this
49531          * @param {Roo.ContentPanel} panel The panel
49532          */
49533         "paneladded" : true,
49534         /**
49535          * @event panelremoved
49536          * Fires when a panel is removed. 
49537          * @param {Roo.LayoutRegion} this
49538          * @param {Roo.ContentPanel} panel The panel
49539          */
49540         "panelremoved" : true,
49541         /**
49542          * @event collapsed
49543          * Fires when this region is collapsed.
49544          * @param {Roo.LayoutRegion} this
49545          */
49546         "collapsed" : true,
49547         /**
49548          * @event expanded
49549          * Fires when this region is expanded.
49550          * @param {Roo.LayoutRegion} this
49551          */
49552         "expanded" : true,
49553         /**
49554          * @event slideshow
49555          * Fires when this region is slid into view.
49556          * @param {Roo.LayoutRegion} this
49557          */
49558         "slideshow" : true,
49559         /**
49560          * @event slidehide
49561          * Fires when this region slides out of view. 
49562          * @param {Roo.LayoutRegion} this
49563          */
49564         "slidehide" : true,
49565         /**
49566          * @event panelactivated
49567          * Fires when a panel is activated. 
49568          * @param {Roo.LayoutRegion} this
49569          * @param {Roo.ContentPanel} panel The activated panel
49570          */
49571         "panelactivated" : true,
49572         /**
49573          * @event resized
49574          * Fires when the user resizes this region. 
49575          * @param {Roo.LayoutRegion} this
49576          * @param {Number} newSize The new size (width for east/west, height for north/south)
49577          */
49578         "resized" : true
49579     };
49580     /** A collection of panels in this region. @type Roo.util.MixedCollection */
49581     this.panels = new Roo.util.MixedCollection();
49582     this.panels.getKey = this.getPanelId.createDelegate(this);
49583     this.box = null;
49584     this.activePanel = null;
49585     // ensure listeners are added...
49586     
49587     if (config.listeners || config.events) {
49588         Roo.BasicLayoutRegion.superclass.constructor.call(this, {
49589             listeners : config.listeners || {},
49590             events : config.events || {}
49591         });
49592     }
49593     
49594     if(skipConfig !== true){
49595         this.applyConfig(config);
49596     }
49597 };
49598
49599 Roo.extend(Roo.BasicLayoutRegion, Roo.util.Observable, {
49600     getPanelId : function(p){
49601         return p.getId();
49602     },
49603     
49604     applyConfig : function(config){
49605         this.margins = config.margins || this.margins || {top: 0, left: 0, right:0, bottom: 0};
49606         this.config = config;
49607         
49608     },
49609     
49610     /**
49611      * Resizes the region to the specified size. For vertical regions (west, east) this adjusts 
49612      * the width, for horizontal (north, south) the height.
49613      * @param {Number} newSize The new width or height
49614      */
49615     resizeTo : function(newSize){
49616         var el = this.el ? this.el :
49617                  (this.activePanel ? this.activePanel.getEl() : null);
49618         if(el){
49619             switch(this.position){
49620                 case "east":
49621                 case "west":
49622                     el.setWidth(newSize);
49623                     this.fireEvent("resized", this, newSize);
49624                 break;
49625                 case "north":
49626                 case "south":
49627                     el.setHeight(newSize);
49628                     this.fireEvent("resized", this, newSize);
49629                 break;                
49630             }
49631         }
49632     },
49633     
49634     getBox : function(){
49635         return this.activePanel ? this.activePanel.getEl().getBox(false, true) : null;
49636     },
49637     
49638     getMargins : function(){
49639         return this.margins;
49640     },
49641     
49642     updateBox : function(box){
49643         this.box = box;
49644         var el = this.activePanel.getEl();
49645         el.dom.style.left = box.x + "px";
49646         el.dom.style.top = box.y + "px";
49647         this.activePanel.setSize(box.width, box.height);
49648     },
49649     
49650     /**
49651      * Returns the container element for this region.
49652      * @return {Roo.Element}
49653      */
49654     getEl : function(){
49655         return this.activePanel;
49656     },
49657     
49658     /**
49659      * Returns true if this region is currently visible.
49660      * @return {Boolean}
49661      */
49662     isVisible : function(){
49663         return this.activePanel ? true : false;
49664     },
49665     
49666     setActivePanel : function(panel){
49667         panel = this.getPanel(panel);
49668         if(this.activePanel && this.activePanel != panel){
49669             this.activePanel.setActiveState(false);
49670             this.activePanel.getEl().setLeftTop(-10000,-10000);
49671         }
49672         this.activePanel = panel;
49673         panel.setActiveState(true);
49674         if(this.box){
49675             panel.setSize(this.box.width, this.box.height);
49676         }
49677         this.fireEvent("panelactivated", this, panel);
49678         this.fireEvent("invalidated");
49679     },
49680     
49681     /**
49682      * Show the specified panel.
49683      * @param {Number/String/ContentPanel} panelId The panels index, id or the panel itself
49684      * @return {Roo.ContentPanel} The shown panel or null
49685      */
49686     showPanel : function(panel){
49687         if(panel = this.getPanel(panel)){
49688             this.setActivePanel(panel);
49689         }
49690         return panel;
49691     },
49692     
49693     /**
49694      * Get the active panel for this region.
49695      * @return {Roo.ContentPanel} The active panel or null
49696      */
49697     getActivePanel : function(){
49698         return this.activePanel;
49699     },
49700     
49701     /**
49702      * Add the passed ContentPanel(s)
49703      * @param {ContentPanel...} panel The ContentPanel(s) to add (you can pass more than one)
49704      * @return {Roo.ContentPanel} The panel added (if only one was added)
49705      */
49706     add : function(panel){
49707         if(arguments.length > 1){
49708             for(var i = 0, len = arguments.length; i < len; i++) {
49709                 this.add(arguments[i]);
49710             }
49711             return null;
49712         }
49713         if(this.hasPanel(panel)){
49714             this.showPanel(panel);
49715             return panel;
49716         }
49717         var el = panel.getEl();
49718         if(el.dom.parentNode != this.mgr.el.dom){
49719             this.mgr.el.dom.appendChild(el.dom);
49720         }
49721         if(panel.setRegion){
49722             panel.setRegion(this);
49723         }
49724         this.panels.add(panel);
49725         el.setStyle("position", "absolute");
49726         if(!panel.background){
49727             this.setActivePanel(panel);
49728             if(this.config.initialSize && this.panels.getCount()==1){
49729                 this.resizeTo(this.config.initialSize);
49730             }
49731         }
49732         this.fireEvent("paneladded", this, panel);
49733         return panel;
49734     },
49735     
49736     /**
49737      * Returns true if the panel is in this region.
49738      * @param {Number/String/ContentPanel} panel The panels index, id or the panel itself
49739      * @return {Boolean}
49740      */
49741     hasPanel : function(panel){
49742         if(typeof panel == "object"){ // must be panel obj
49743             panel = panel.getId();
49744         }
49745         return this.getPanel(panel) ? true : false;
49746     },
49747     
49748     /**
49749      * Removes the specified panel. If preservePanel is not true (either here or in the config), the panel is destroyed.
49750      * @param {Number/String/ContentPanel} panel The panels index, id or the panel itself
49751      * @param {Boolean} preservePanel Overrides the config preservePanel option
49752      * @return {Roo.ContentPanel} The panel that was removed
49753      */
49754     remove : function(panel, preservePanel){
49755         panel = this.getPanel(panel);
49756         if(!panel){
49757             return null;
49758         }
49759         var e = {};
49760         this.fireEvent("beforeremove", this, panel, e);
49761         if(e.cancel === true){
49762             return null;
49763         }
49764         var panelId = panel.getId();
49765         this.panels.removeKey(panelId);
49766         return panel;
49767     },
49768     
49769     /**
49770      * Returns the panel specified or null if it's not in this region.
49771      * @param {Number/String/ContentPanel} panel The panels index, id or the panel itself
49772      * @return {Roo.ContentPanel}
49773      */
49774     getPanel : function(id){
49775         if(typeof id == "object"){ // must be panel obj
49776             return id;
49777         }
49778         return this.panels.get(id);
49779     },
49780     
49781     /**
49782      * Returns this regions position (north/south/east/west/center).
49783      * @return {String} 
49784      */
49785     getPosition: function(){
49786         return this.position;    
49787     }
49788 });/*
49789  * Based on:
49790  * Ext JS Library 1.1.1
49791  * Copyright(c) 2006-2007, Ext JS, LLC.
49792  *
49793  * Originally Released Under LGPL - original licence link has changed is not relivant.
49794  *
49795  * Fork - LGPL
49796  * <script type="text/javascript">
49797  */
49798  
49799 /**
49800  * @class Roo.LayoutRegion
49801  * @extends Roo.BasicLayoutRegion
49802  * This class represents a region in a layout manager.
49803  * @cfg {Boolean}   collapsible     False to disable collapsing (defaults to true)
49804  * @cfg {Boolean}   collapsed       True to set the initial display to collapsed (defaults to false)
49805  * @cfg {Boolean}   floatable       False to disable floating (defaults to true)
49806  * @cfg {Object}    margins         Margins for the element (defaults to {top: 0, left: 0, right:0, bottom: 0})
49807  * @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})
49808  * @cfg {String}    tabPosition     "top" or "bottom" (defaults to "bottom")
49809  * @cfg {String}    collapsedTitle  Optional string message to display in the collapsed block of a north or south region
49810  * @cfg {Boolean}   alwaysShowTabs  True to always display tabs even when there is only 1 panel (defaults to false)
49811  * @cfg {Boolean}   autoScroll      True to enable overflow scrolling (defaults to false)
49812  * @cfg {Boolean}   titlebar        True to display a title bar (defaults to true)
49813  * @cfg {String}    title           The title for the region (overrides panel titles)
49814  * @cfg {Boolean}   animate         True to animate expand/collapse (defaults to false)
49815  * @cfg {Boolean}   autoHide        False to disable auto hiding when the mouse leaves the "floated" region (defaults to true)
49816  * @cfg {Boolean}   preservePanels  True to preserve removed panels so they can be readded later (defaults to false)
49817  * @cfg {Boolean}   closeOnTab      True to place the close icon on the tabs instead of the region titlebar (defaults to false)
49818  * @cfg {Boolean}   hideTabs        True to hide the tab strip (defaults to false)
49819  * @cfg {Boolean}   resizeTabs      True to enable automatic tab resizing. This will resize the tabs so they are all the same size and fit within
49820  *                      the space available, similar to FireFox 1.5 tabs (defaults to false)
49821  * @cfg {Number}    minTabWidth     The minimum tab width (defaults to 40)
49822  * @cfg {Number}    preferredTabWidth The preferred tab width (defaults to 150)
49823  * @cfg {Boolean}   showPin         True to show a pin button
49824  * @cfg {Boolean}   hidden          True to start the region hidden (defaults to false)
49825  * @cfg {Boolean}   hideWhenEmpty   True to hide the region when it has no panels
49826  * @cfg {Boolean}   disableTabTips  True to disable tab tooltips
49827  * @cfg {Number}    width           For East/West panels
49828  * @cfg {Number}    height          For North/South panels
49829  * @cfg {Boolean}   split           To show the splitter
49830  * @cfg {Boolean}   toolbar         xtype configuration for a toolbar - shows on right of tabbar
49831  */
49832 Roo.LayoutRegion = function(mgr, config, pos){
49833     Roo.LayoutRegion.superclass.constructor.call(this, mgr, config, pos, true);
49834     var dh = Roo.DomHelper;
49835     /** This region's container element 
49836     * @type Roo.Element */
49837     this.el = dh.append(mgr.el.dom, {tag: "div", cls: "x-layout-panel x-layout-panel-" + this.position}, true);
49838     /** This region's title element 
49839     * @type Roo.Element */
49840
49841     this.titleEl = dh.append(this.el.dom, {tag: "div", unselectable: "on", cls: "x-unselectable x-layout-panel-hd x-layout-title-"+this.position, children:[
49842         {tag: "span", cls: "x-unselectable x-layout-panel-hd-text", unselectable: "on", html: "&#160;"},
49843         {tag: "div", cls: "x-unselectable x-layout-panel-hd-tools", unselectable: "on"}
49844     ]}, true);
49845     this.titleEl.enableDisplayMode();
49846     /** This region's title text element 
49847     * @type HTMLElement */
49848     this.titleTextEl = this.titleEl.dom.firstChild;
49849     this.tools = Roo.get(this.titleEl.dom.childNodes[1], true);
49850     this.closeBtn = this.createTool(this.tools.dom, "x-layout-close");
49851     this.closeBtn.enableDisplayMode();
49852     this.closeBtn.on("click", this.closeClicked, this);
49853     this.closeBtn.hide();
49854
49855     this.createBody(config);
49856     this.visible = true;
49857     this.collapsed = false;
49858
49859     if(config.hideWhenEmpty){
49860         this.hide();
49861         this.on("paneladded", this.validateVisibility, this);
49862         this.on("panelremoved", this.validateVisibility, this);
49863     }
49864     this.applyConfig(config);
49865 };
49866
49867 Roo.extend(Roo.LayoutRegion, Roo.BasicLayoutRegion, {
49868
49869     createBody : function(){
49870         /** This region's body element 
49871         * @type Roo.Element */
49872         this.bodyEl = this.el.createChild({tag: "div", cls: "x-layout-panel-body"});
49873     },
49874
49875     applyConfig : function(c){
49876         if(c.collapsible && this.position != "center" && !this.collapsedEl){
49877             var dh = Roo.DomHelper;
49878             if(c.titlebar !== false){
49879                 this.collapseBtn = this.createTool(this.tools.dom, "x-layout-collapse-"+this.position);
49880                 this.collapseBtn.on("click", this.collapse, this);
49881                 this.collapseBtn.enableDisplayMode();
49882
49883                 if(c.showPin === true || this.showPin){
49884                     this.stickBtn = this.createTool(this.tools.dom, "x-layout-stick");
49885                     this.stickBtn.enableDisplayMode();
49886                     this.stickBtn.on("click", this.expand, this);
49887                     this.stickBtn.hide();
49888                 }
49889             }
49890             /** This region's collapsed element
49891             * @type Roo.Element */
49892             this.collapsedEl = dh.append(this.mgr.el.dom, {cls: "x-layout-collapsed x-layout-collapsed-"+this.position, children:[
49893                 {cls: "x-layout-collapsed-tools", children:[{cls: "x-layout-ctools-inner"}]}
49894             ]}, true);
49895             if(c.floatable !== false){
49896                this.collapsedEl.addClassOnOver("x-layout-collapsed-over");
49897                this.collapsedEl.on("click", this.collapseClick, this);
49898             }
49899
49900             if(c.collapsedTitle && (this.position == "north" || this.position== "south")) {
49901                 this.collapsedTitleTextEl = dh.append(this.collapsedEl.dom, {tag: "div", cls: "x-unselectable x-layout-panel-hd-text",
49902                    id: "message", unselectable: "on", style:{"float":"left"}});
49903                this.collapsedTitleTextEl.innerHTML = c.collapsedTitle;
49904              }
49905             this.expandBtn = this.createTool(this.collapsedEl.dom.firstChild.firstChild, "x-layout-expand-"+this.position);
49906             this.expandBtn.on("click", this.expand, this);
49907         }
49908         if(this.collapseBtn){
49909             this.collapseBtn.setVisible(c.collapsible == true);
49910         }
49911         this.cmargins = c.cmargins || this.cmargins ||
49912                          (this.position == "west" || this.position == "east" ?
49913                              {top: 0, left: 2, right:2, bottom: 0} :
49914                              {top: 2, left: 0, right:0, bottom: 2});
49915         this.margins = c.margins || this.margins || {top: 0, left: 0, right:0, bottom: 0};
49916         this.bottomTabs = c.tabPosition != "top";
49917         this.autoScroll = c.autoScroll || false;
49918         if(this.autoScroll){
49919             this.bodyEl.setStyle("overflow", "auto");
49920         }else{
49921             this.bodyEl.setStyle("overflow", "hidden");
49922         }
49923         //if(c.titlebar !== false){
49924             if((!c.titlebar && !c.title) || c.titlebar === false){
49925                 this.titleEl.hide();
49926             }else{
49927                 this.titleEl.show();
49928                 if(c.title){
49929                     this.titleTextEl.innerHTML = c.title;
49930                 }
49931             }
49932         //}
49933         this.duration = c.duration || .30;
49934         this.slideDuration = c.slideDuration || .45;
49935         this.config = c;
49936         if(c.collapsed){
49937             this.collapse(true);
49938         }
49939         if(c.hidden){
49940             this.hide();
49941         }
49942     },
49943     /**
49944      * Returns true if this region is currently visible.
49945      * @return {Boolean}
49946      */
49947     isVisible : function(){
49948         return this.visible;
49949     },
49950
49951     /**
49952      * Updates the title for collapsed north/south regions (used with {@link #collapsedTitle} config option)
49953      * @param {String} title (optional) The title text (accepts HTML markup, defaults to the numeric character reference for a non-breaking space, "&amp;#160;")
49954      */
49955     setCollapsedTitle : function(title){
49956         title = title || "&#160;";
49957         if(this.collapsedTitleTextEl){
49958             this.collapsedTitleTextEl.innerHTML = title;
49959         }
49960     },
49961
49962     getBox : function(){
49963         var b;
49964         if(!this.collapsed){
49965             b = this.el.getBox(false, true);
49966         }else{
49967             b = this.collapsedEl.getBox(false, true);
49968         }
49969         return b;
49970     },
49971
49972     getMargins : function(){
49973         return this.collapsed ? this.cmargins : this.margins;
49974     },
49975
49976     highlight : function(){
49977         this.el.addClass("x-layout-panel-dragover");
49978     },
49979
49980     unhighlight : function(){
49981         this.el.removeClass("x-layout-panel-dragover");
49982     },
49983
49984     updateBox : function(box){
49985         this.box = box;
49986         if(!this.collapsed){
49987             this.el.dom.style.left = box.x + "px";
49988             this.el.dom.style.top = box.y + "px";
49989             this.updateBody(box.width, box.height);
49990         }else{
49991             this.collapsedEl.dom.style.left = box.x + "px";
49992             this.collapsedEl.dom.style.top = box.y + "px";
49993             this.collapsedEl.setSize(box.width, box.height);
49994         }
49995         if(this.tabs){
49996             this.tabs.autoSizeTabs();
49997         }
49998     },
49999
50000     updateBody : function(w, h){
50001         if(w !== null){
50002             this.el.setWidth(w);
50003             w -= this.el.getBorderWidth("rl");
50004             if(this.config.adjustments){
50005                 w += this.config.adjustments[0];
50006             }
50007         }
50008         if(h !== null){
50009             this.el.setHeight(h);
50010             h = this.titleEl && this.titleEl.isDisplayed() ? h - (this.titleEl.getHeight()||0) : h;
50011             h -= this.el.getBorderWidth("tb");
50012             if(this.config.adjustments){
50013                 h += this.config.adjustments[1];
50014             }
50015             this.bodyEl.setHeight(h);
50016             if(this.tabs){
50017                 h = this.tabs.syncHeight(h);
50018             }
50019         }
50020         if(this.panelSize){
50021             w = w !== null ? w : this.panelSize.width;
50022             h = h !== null ? h : this.panelSize.height;
50023         }
50024         if(this.activePanel){
50025             var el = this.activePanel.getEl();
50026             w = w !== null ? w : el.getWidth();
50027             h = h !== null ? h : el.getHeight();
50028             this.panelSize = {width: w, height: h};
50029             this.activePanel.setSize(w, h);
50030         }
50031         if(Roo.isIE && this.tabs){
50032             this.tabs.el.repaint();
50033         }
50034     },
50035
50036     /**
50037      * Returns the container element for this region.
50038      * @return {Roo.Element}
50039      */
50040     getEl : function(){
50041         return this.el;
50042     },
50043
50044     /**
50045      * Hides this region.
50046      */
50047     hide : function(){
50048         if(!this.collapsed){
50049             this.el.dom.style.left = "-2000px";
50050             this.el.hide();
50051         }else{
50052             this.collapsedEl.dom.style.left = "-2000px";
50053             this.collapsedEl.hide();
50054         }
50055         this.visible = false;
50056         this.fireEvent("visibilitychange", this, false);
50057     },
50058
50059     /**
50060      * Shows this region if it was previously hidden.
50061      */
50062     show : function(){
50063         if(!this.collapsed){
50064             this.el.show();
50065         }else{
50066             this.collapsedEl.show();
50067         }
50068         this.visible = true;
50069         this.fireEvent("visibilitychange", this, true);
50070     },
50071
50072     closeClicked : function(){
50073         if(this.activePanel){
50074             this.remove(this.activePanel);
50075         }
50076     },
50077
50078     collapseClick : function(e){
50079         if(this.isSlid){
50080            e.stopPropagation();
50081            this.slideIn();
50082         }else{
50083            e.stopPropagation();
50084            this.slideOut();
50085         }
50086     },
50087
50088     /**
50089      * Collapses this region.
50090      * @param {Boolean} skipAnim (optional) true to collapse the element without animation (if animate is true)
50091      */
50092     collapse : function(skipAnim){
50093         if(this.collapsed) return;
50094         this.collapsed = true;
50095         if(this.split){
50096             this.split.el.hide();
50097         }
50098         if(this.config.animate && skipAnim !== true){
50099             this.fireEvent("invalidated", this);
50100             this.animateCollapse();
50101         }else{
50102             this.el.setLocation(-20000,-20000);
50103             this.el.hide();
50104             this.collapsedEl.show();
50105             this.fireEvent("collapsed", this);
50106             this.fireEvent("invalidated", this);
50107         }
50108     },
50109
50110     animateCollapse : function(){
50111         // overridden
50112     },
50113
50114     /**
50115      * Expands this region if it was previously collapsed.
50116      * @param {Roo.EventObject} e The event that triggered the expand (or null if calling manually)
50117      * @param {Boolean} skipAnim (optional) true to expand the element without animation (if animate is true)
50118      */
50119     expand : function(e, skipAnim){
50120         if(e) e.stopPropagation();
50121         if(!this.collapsed || this.el.hasActiveFx()) return;
50122         if(this.isSlid){
50123             this.afterSlideIn();
50124             skipAnim = true;
50125         }
50126         this.collapsed = false;
50127         if(this.config.animate && skipAnim !== true){
50128             this.animateExpand();
50129         }else{
50130             this.el.show();
50131             if(this.split){
50132                 this.split.el.show();
50133             }
50134             this.collapsedEl.setLocation(-2000,-2000);
50135             this.collapsedEl.hide();
50136             this.fireEvent("invalidated", this);
50137             this.fireEvent("expanded", this);
50138         }
50139     },
50140
50141     animateExpand : function(){
50142         // overridden
50143     },
50144
50145     initTabs : function()
50146     {
50147         this.bodyEl.setStyle("overflow", "hidden");
50148         var ts = new Roo.TabPanel(
50149                 this.bodyEl.dom,
50150                 {
50151                     tabPosition: this.bottomTabs ? 'bottom' : 'top',
50152                     disableTooltips: this.config.disableTabTips,
50153                     toolbar : this.config.toolbar
50154                 }
50155         );
50156         if(this.config.hideTabs){
50157             ts.stripWrap.setDisplayed(false);
50158         }
50159         this.tabs = ts;
50160         ts.resizeTabs = this.config.resizeTabs === true;
50161         ts.minTabWidth = this.config.minTabWidth || 40;
50162         ts.maxTabWidth = this.config.maxTabWidth || 250;
50163         ts.preferredTabWidth = this.config.preferredTabWidth || 150;
50164         ts.monitorResize = false;
50165         ts.bodyEl.setStyle("overflow", this.config.autoScroll ? "auto" : "hidden");
50166         ts.bodyEl.addClass('x-layout-tabs-body');
50167         this.panels.each(this.initPanelAsTab, this);
50168     },
50169
50170     initPanelAsTab : function(panel){
50171         var ti = this.tabs.addTab(panel.getEl().id, panel.getTitle(), null,
50172                     this.config.closeOnTab && panel.isClosable());
50173         if(panel.tabTip !== undefined){
50174             ti.setTooltip(panel.tabTip);
50175         }
50176         ti.on("activate", function(){
50177               this.setActivePanel(panel);
50178         }, this);
50179         if(this.config.closeOnTab){
50180             ti.on("beforeclose", function(t, e){
50181                 e.cancel = true;
50182                 this.remove(panel);
50183             }, this);
50184         }
50185         return ti;
50186     },
50187
50188     updatePanelTitle : function(panel, title){
50189         if(this.activePanel == panel){
50190             this.updateTitle(title);
50191         }
50192         if(this.tabs){
50193             var ti = this.tabs.getTab(panel.getEl().id);
50194             ti.setText(title);
50195             if(panel.tabTip !== undefined){
50196                 ti.setTooltip(panel.tabTip);
50197             }
50198         }
50199     },
50200
50201     updateTitle : function(title){
50202         if(this.titleTextEl && !this.config.title){
50203             this.titleTextEl.innerHTML = (typeof title != "undefined" && title.length > 0 ? title : "&#160;");
50204         }
50205     },
50206
50207     setActivePanel : function(panel){
50208         panel = this.getPanel(panel);
50209         if(this.activePanel && this.activePanel != panel){
50210             this.activePanel.setActiveState(false);
50211         }
50212         this.activePanel = panel;
50213         panel.setActiveState(true);
50214         if(this.panelSize){
50215             panel.setSize(this.panelSize.width, this.panelSize.height);
50216         }
50217         if(this.closeBtn){
50218             this.closeBtn.setVisible(!this.config.closeOnTab && !this.isSlid && panel.isClosable());
50219         }
50220         this.updateTitle(panel.getTitle());
50221         if(this.tabs){
50222             this.fireEvent("invalidated", this);
50223         }
50224         this.fireEvent("panelactivated", this, panel);
50225     },
50226
50227     /**
50228      * Shows the specified panel.
50229      * @param {Number/String/ContentPanel} panelId The panel's index, id or the panel itself
50230      * @return {Roo.ContentPanel} The shown panel, or null if a panel could not be found from panelId
50231      */
50232     showPanel : function(panel){
50233         if(panel = this.getPanel(panel)){
50234             if(this.tabs){
50235                 var tab = this.tabs.getTab(panel.getEl().id);
50236                 if(tab.isHidden()){
50237                     this.tabs.unhideTab(tab.id);
50238                 }
50239                 tab.activate();
50240             }else{
50241                 this.setActivePanel(panel);
50242             }
50243         }
50244         return panel;
50245     },
50246
50247     /**
50248      * Get the active panel for this region.
50249      * @return {Roo.ContentPanel} The active panel or null
50250      */
50251     getActivePanel : function(){
50252         return this.activePanel;
50253     },
50254
50255     validateVisibility : function(){
50256         if(this.panels.getCount() < 1){
50257             this.updateTitle("&#160;");
50258             this.closeBtn.hide();
50259             this.hide();
50260         }else{
50261             if(!this.isVisible()){
50262                 this.show();
50263             }
50264         }
50265     },
50266
50267     /**
50268      * Adds the passed ContentPanel(s) to this region.
50269      * @param {ContentPanel...} panel The ContentPanel(s) to add (you can pass more than one)
50270      * @return {Roo.ContentPanel} The panel added (if only one was added; null otherwise)
50271      */
50272     add : function(panel){
50273         if(arguments.length > 1){
50274             for(var i = 0, len = arguments.length; i < len; i++) {
50275                 this.add(arguments[i]);
50276             }
50277             return null;
50278         }
50279         if(this.hasPanel(panel)){
50280             this.showPanel(panel);
50281             return panel;
50282         }
50283         panel.setRegion(this);
50284         this.panels.add(panel);
50285         if(this.panels.getCount() == 1 && !this.config.alwaysShowTabs){
50286             this.bodyEl.dom.appendChild(panel.getEl().dom);
50287             if(panel.background !== true){
50288                 this.setActivePanel(panel);
50289             }
50290             this.fireEvent("paneladded", this, panel);
50291             return panel;
50292         }
50293         if(!this.tabs){
50294             this.initTabs();
50295         }else{
50296             this.initPanelAsTab(panel);
50297         }
50298         if(panel.background !== true){
50299             this.tabs.activate(panel.getEl().id);
50300         }
50301         this.fireEvent("paneladded", this, panel);
50302         return panel;
50303     },
50304
50305     /**
50306      * Hides the tab for the specified panel.
50307      * @param {Number/String/ContentPanel} panel The panel's index, id or the panel itself
50308      */
50309     hidePanel : function(panel){
50310         if(this.tabs && (panel = this.getPanel(panel))){
50311             this.tabs.hideTab(panel.getEl().id);
50312         }
50313     },
50314
50315     /**
50316      * Unhides the tab for a previously hidden panel.
50317      * @param {Number/String/ContentPanel} panel The panel's index, id or the panel itself
50318      */
50319     unhidePanel : function(panel){
50320         if(this.tabs && (panel = this.getPanel(panel))){
50321             this.tabs.unhideTab(panel.getEl().id);
50322         }
50323     },
50324
50325     clearPanels : function(){
50326         while(this.panels.getCount() > 0){
50327              this.remove(this.panels.first());
50328         }
50329     },
50330
50331     /**
50332      * Removes the specified panel. If preservePanel is not true (either here or in the config), the panel is destroyed.
50333      * @param {Number/String/ContentPanel} panel The panel's index, id or the panel itself
50334      * @param {Boolean} preservePanel Overrides the config preservePanel option
50335      * @return {Roo.ContentPanel} The panel that was removed
50336      */
50337     remove : function(panel, preservePanel){
50338         panel = this.getPanel(panel);
50339         if(!panel){
50340             return null;
50341         }
50342         var e = {};
50343         this.fireEvent("beforeremove", this, panel, e);
50344         if(e.cancel === true){
50345             return null;
50346         }
50347         preservePanel = (typeof preservePanel != "undefined" ? preservePanel : (this.config.preservePanels === true || panel.preserve === true));
50348         var panelId = panel.getId();
50349         this.panels.removeKey(panelId);
50350         if(preservePanel){
50351             document.body.appendChild(panel.getEl().dom);
50352         }
50353         if(this.tabs){
50354             this.tabs.removeTab(panel.getEl().id);
50355         }else if (!preservePanel){
50356             this.bodyEl.dom.removeChild(panel.getEl().dom);
50357         }
50358         if(this.panels.getCount() == 1 && this.tabs && !this.config.alwaysShowTabs){
50359             var p = this.panels.first();
50360             var tempEl = document.createElement("div"); // temp holder to keep IE from deleting the node
50361             tempEl.appendChild(p.getEl().dom);
50362             this.bodyEl.update("");
50363             this.bodyEl.dom.appendChild(p.getEl().dom);
50364             tempEl = null;
50365             this.updateTitle(p.getTitle());
50366             this.tabs = null;
50367             this.bodyEl.setStyle("overflow", this.config.autoScroll ? "auto" : "hidden");
50368             this.setActivePanel(p);
50369         }
50370         panel.setRegion(null);
50371         if(this.activePanel == panel){
50372             this.activePanel = null;
50373         }
50374         if(this.config.autoDestroy !== false && preservePanel !== true){
50375             try{panel.destroy();}catch(e){}
50376         }
50377         this.fireEvent("panelremoved", this, panel);
50378         return panel;
50379     },
50380
50381     /**
50382      * Returns the TabPanel component used by this region
50383      * @return {Roo.TabPanel}
50384      */
50385     getTabs : function(){
50386         return this.tabs;
50387     },
50388
50389     createTool : function(parentEl, className){
50390         var btn = Roo.DomHelper.append(parentEl, {tag: "div", cls: "x-layout-tools-button",
50391             children: [{tag: "div", cls: "x-layout-tools-button-inner " + className, html: "&#160;"}]}, true);
50392         btn.addClassOnOver("x-layout-tools-button-over");
50393         return btn;
50394     }
50395 });/*
50396  * Based on:
50397  * Ext JS Library 1.1.1
50398  * Copyright(c) 2006-2007, Ext JS, LLC.
50399  *
50400  * Originally Released Under LGPL - original licence link has changed is not relivant.
50401  *
50402  * Fork - LGPL
50403  * <script type="text/javascript">
50404  */
50405  
50406
50407
50408 /**
50409  * @class Roo.SplitLayoutRegion
50410  * @extends Roo.LayoutRegion
50411  * Adds a splitbar and other (private) useful functionality to a {@link Roo.LayoutRegion}.
50412  */
50413 Roo.SplitLayoutRegion = function(mgr, config, pos, cursor){
50414     this.cursor = cursor;
50415     Roo.SplitLayoutRegion.superclass.constructor.call(this, mgr, config, pos);
50416 };
50417
50418 Roo.extend(Roo.SplitLayoutRegion, Roo.LayoutRegion, {
50419     splitTip : "Drag to resize.",
50420     collapsibleSplitTip : "Drag to resize. Double click to hide.",
50421     useSplitTips : false,
50422
50423     applyConfig : function(config){
50424         Roo.SplitLayoutRegion.superclass.applyConfig.call(this, config);
50425         if(config.split){
50426             if(!this.split){
50427                 var splitEl = Roo.DomHelper.append(this.mgr.el.dom, 
50428                         {tag: "div", id: this.el.id + "-split", cls: "x-layout-split x-layout-split-"+this.position, html: "&#160;"});
50429                 /** The SplitBar for this region 
50430                 * @type Roo.SplitBar */
50431                 this.split = new Roo.SplitBar(splitEl, this.el, this.orientation);
50432                 this.split.on("moved", this.onSplitMove, this);
50433                 this.split.useShim = config.useShim === true;
50434                 this.split.getMaximumSize = this[this.position == 'north' || this.position == 'south' ? 'getVMaxSize' : 'getHMaxSize'].createDelegate(this);
50435                 if(this.useSplitTips){
50436                     this.split.el.dom.title = config.collapsible ? this.collapsibleSplitTip : this.splitTip;
50437                 }
50438                 if(config.collapsible){
50439                     this.split.el.on("dblclick", this.collapse,  this);
50440                 }
50441             }
50442             if(typeof config.minSize != "undefined"){
50443                 this.split.minSize = config.minSize;
50444             }
50445             if(typeof config.maxSize != "undefined"){
50446                 this.split.maxSize = config.maxSize;
50447             }
50448             if(config.hideWhenEmpty || config.hidden || config.collapsed){
50449                 this.hideSplitter();
50450             }
50451         }
50452     },
50453
50454     getHMaxSize : function(){
50455          var cmax = this.config.maxSize || 10000;
50456          var center = this.mgr.getRegion("center");
50457          return Math.min(cmax, (this.el.getWidth()+center.getEl().getWidth())-center.getMinWidth());
50458     },
50459
50460     getVMaxSize : function(){
50461          var cmax = this.config.maxSize || 10000;
50462          var center = this.mgr.getRegion("center");
50463          return Math.min(cmax, (this.el.getHeight()+center.getEl().getHeight())-center.getMinHeight());
50464     },
50465
50466     onSplitMove : function(split, newSize){
50467         this.fireEvent("resized", this, newSize);
50468     },
50469     
50470     /** 
50471      * Returns the {@link Roo.SplitBar} for this region.
50472      * @return {Roo.SplitBar}
50473      */
50474     getSplitBar : function(){
50475         return this.split;
50476     },
50477     
50478     hide : function(){
50479         this.hideSplitter();
50480         Roo.SplitLayoutRegion.superclass.hide.call(this);
50481     },
50482
50483     hideSplitter : function(){
50484         if(this.split){
50485             this.split.el.setLocation(-2000,-2000);
50486             this.split.el.hide();
50487         }
50488     },
50489
50490     show : function(){
50491         if(this.split){
50492             this.split.el.show();
50493         }
50494         Roo.SplitLayoutRegion.superclass.show.call(this);
50495     },
50496     
50497     beforeSlide: function(){
50498         if(Roo.isGecko){// firefox overflow auto bug workaround
50499             this.bodyEl.clip();
50500             if(this.tabs) this.tabs.bodyEl.clip();
50501             if(this.activePanel){
50502                 this.activePanel.getEl().clip();
50503                 
50504                 if(this.activePanel.beforeSlide){
50505                     this.activePanel.beforeSlide();
50506                 }
50507             }
50508         }
50509     },
50510     
50511     afterSlide : function(){
50512         if(Roo.isGecko){// firefox overflow auto bug workaround
50513             this.bodyEl.unclip();
50514             if(this.tabs) this.tabs.bodyEl.unclip();
50515             if(this.activePanel){
50516                 this.activePanel.getEl().unclip();
50517                 if(this.activePanel.afterSlide){
50518                     this.activePanel.afterSlide();
50519                 }
50520             }
50521         }
50522     },
50523
50524     initAutoHide : function(){
50525         if(this.autoHide !== false){
50526             if(!this.autoHideHd){
50527                 var st = new Roo.util.DelayedTask(this.slideIn, this);
50528                 this.autoHideHd = {
50529                     "mouseout": function(e){
50530                         if(!e.within(this.el, true)){
50531                             st.delay(500);
50532                         }
50533                     },
50534                     "mouseover" : function(e){
50535                         st.cancel();
50536                     },
50537                     scope : this
50538                 };
50539             }
50540             this.el.on(this.autoHideHd);
50541         }
50542     },
50543
50544     clearAutoHide : function(){
50545         if(this.autoHide !== false){
50546             this.el.un("mouseout", this.autoHideHd.mouseout);
50547             this.el.un("mouseover", this.autoHideHd.mouseover);
50548         }
50549     },
50550
50551     clearMonitor : function(){
50552         Roo.get(document).un("click", this.slideInIf, this);
50553     },
50554
50555     // these names are backwards but not changed for compat
50556     slideOut : function(){
50557         if(this.isSlid || this.el.hasActiveFx()){
50558             return;
50559         }
50560         this.isSlid = true;
50561         if(this.collapseBtn){
50562             this.collapseBtn.hide();
50563         }
50564         this.closeBtnState = this.closeBtn.getStyle('display');
50565         this.closeBtn.hide();
50566         if(this.stickBtn){
50567             this.stickBtn.show();
50568         }
50569         this.el.show();
50570         this.el.alignTo(this.collapsedEl, this.getCollapseAnchor());
50571         this.beforeSlide();
50572         this.el.setStyle("z-index", 10001);
50573         this.el.slideIn(this.getSlideAnchor(), {
50574             callback: function(){
50575                 this.afterSlide();
50576                 this.initAutoHide();
50577                 Roo.get(document).on("click", this.slideInIf, this);
50578                 this.fireEvent("slideshow", this);
50579             },
50580             scope: this,
50581             block: true
50582         });
50583     },
50584
50585     afterSlideIn : function(){
50586         this.clearAutoHide();
50587         this.isSlid = false;
50588         this.clearMonitor();
50589         this.el.setStyle("z-index", "");
50590         if(this.collapseBtn){
50591             this.collapseBtn.show();
50592         }
50593         this.closeBtn.setStyle('display', this.closeBtnState);
50594         if(this.stickBtn){
50595             this.stickBtn.hide();
50596         }
50597         this.fireEvent("slidehide", this);
50598     },
50599
50600     slideIn : function(cb){
50601         if(!this.isSlid || this.el.hasActiveFx()){
50602             Roo.callback(cb);
50603             return;
50604         }
50605         this.isSlid = false;
50606         this.beforeSlide();
50607         this.el.slideOut(this.getSlideAnchor(), {
50608             callback: function(){
50609                 this.el.setLeftTop(-10000, -10000);
50610                 this.afterSlide();
50611                 this.afterSlideIn();
50612                 Roo.callback(cb);
50613             },
50614             scope: this,
50615             block: true
50616         });
50617     },
50618     
50619     slideInIf : function(e){
50620         if(!e.within(this.el)){
50621             this.slideIn();
50622         }
50623     },
50624
50625     animateCollapse : function(){
50626         this.beforeSlide();
50627         this.el.setStyle("z-index", 20000);
50628         var anchor = this.getSlideAnchor();
50629         this.el.slideOut(anchor, {
50630             callback : function(){
50631                 this.el.setStyle("z-index", "");
50632                 this.collapsedEl.slideIn(anchor, {duration:.3});
50633                 this.afterSlide();
50634                 this.el.setLocation(-10000,-10000);
50635                 this.el.hide();
50636                 this.fireEvent("collapsed", this);
50637             },
50638             scope: this,
50639             block: true
50640         });
50641     },
50642
50643     animateExpand : function(){
50644         this.beforeSlide();
50645         this.el.alignTo(this.collapsedEl, this.getCollapseAnchor(), this.getExpandAdj());
50646         this.el.setStyle("z-index", 20000);
50647         this.collapsedEl.hide({
50648             duration:.1
50649         });
50650         this.el.slideIn(this.getSlideAnchor(), {
50651             callback : function(){
50652                 this.el.setStyle("z-index", "");
50653                 this.afterSlide();
50654                 if(this.split){
50655                     this.split.el.show();
50656                 }
50657                 this.fireEvent("invalidated", this);
50658                 this.fireEvent("expanded", this);
50659             },
50660             scope: this,
50661             block: true
50662         });
50663     },
50664
50665     anchors : {
50666         "west" : "left",
50667         "east" : "right",
50668         "north" : "top",
50669         "south" : "bottom"
50670     },
50671
50672     sanchors : {
50673         "west" : "l",
50674         "east" : "r",
50675         "north" : "t",
50676         "south" : "b"
50677     },
50678
50679     canchors : {
50680         "west" : "tl-tr",
50681         "east" : "tr-tl",
50682         "north" : "tl-bl",
50683         "south" : "bl-tl"
50684     },
50685
50686     getAnchor : function(){
50687         return this.anchors[this.position];
50688     },
50689
50690     getCollapseAnchor : function(){
50691         return this.canchors[this.position];
50692     },
50693
50694     getSlideAnchor : function(){
50695         return this.sanchors[this.position];
50696     },
50697
50698     getAlignAdj : function(){
50699         var cm = this.cmargins;
50700         switch(this.position){
50701             case "west":
50702                 return [0, 0];
50703             break;
50704             case "east":
50705                 return [0, 0];
50706             break;
50707             case "north":
50708                 return [0, 0];
50709             break;
50710             case "south":
50711                 return [0, 0];
50712             break;
50713         }
50714     },
50715
50716     getExpandAdj : function(){
50717         var c = this.collapsedEl, cm = this.cmargins;
50718         switch(this.position){
50719             case "west":
50720                 return [-(cm.right+c.getWidth()+cm.left), 0];
50721             break;
50722             case "east":
50723                 return [cm.right+c.getWidth()+cm.left, 0];
50724             break;
50725             case "north":
50726                 return [0, -(cm.top+cm.bottom+c.getHeight())];
50727             break;
50728             case "south":
50729                 return [0, cm.top+cm.bottom+c.getHeight()];
50730             break;
50731         }
50732     }
50733 });/*
50734  * Based on:
50735  * Ext JS Library 1.1.1
50736  * Copyright(c) 2006-2007, Ext JS, LLC.
50737  *
50738  * Originally Released Under LGPL - original licence link has changed is not relivant.
50739  *
50740  * Fork - LGPL
50741  * <script type="text/javascript">
50742  */
50743 /*
50744  * These classes are private internal classes
50745  */
50746 Roo.CenterLayoutRegion = function(mgr, config){
50747     Roo.LayoutRegion.call(this, mgr, config, "center");
50748     this.visible = true;
50749     this.minWidth = config.minWidth || 20;
50750     this.minHeight = config.minHeight || 20;
50751 };
50752
50753 Roo.extend(Roo.CenterLayoutRegion, Roo.LayoutRegion, {
50754     hide : function(){
50755         // center panel can't be hidden
50756     },
50757     
50758     show : function(){
50759         // center panel can't be hidden
50760     },
50761     
50762     getMinWidth: function(){
50763         return this.minWidth;
50764     },
50765     
50766     getMinHeight: function(){
50767         return this.minHeight;
50768     }
50769 });
50770
50771
50772 Roo.NorthLayoutRegion = function(mgr, config){
50773     Roo.LayoutRegion.call(this, mgr, config, "north", "n-resize");
50774     if(this.split){
50775         this.split.placement = Roo.SplitBar.TOP;
50776         this.split.orientation = Roo.SplitBar.VERTICAL;
50777         this.split.el.addClass("x-layout-split-v");
50778     }
50779     var size = config.initialSize || config.height;
50780     if(typeof size != "undefined"){
50781         this.el.setHeight(size);
50782     }
50783 };
50784 Roo.extend(Roo.NorthLayoutRegion, Roo.SplitLayoutRegion, {
50785     orientation: Roo.SplitBar.VERTICAL,
50786     getBox : function(){
50787         if(this.collapsed){
50788             return this.collapsedEl.getBox();
50789         }
50790         var box = this.el.getBox();
50791         if(this.split){
50792             box.height += this.split.el.getHeight();
50793         }
50794         return box;
50795     },
50796     
50797     updateBox : function(box){
50798         if(this.split && !this.collapsed){
50799             box.height -= this.split.el.getHeight();
50800             this.split.el.setLeft(box.x);
50801             this.split.el.setTop(box.y+box.height);
50802             this.split.el.setWidth(box.width);
50803         }
50804         if(this.collapsed){
50805             this.updateBody(box.width, null);
50806         }
50807         Roo.LayoutRegion.prototype.updateBox.call(this, box);
50808     }
50809 });
50810
50811 Roo.SouthLayoutRegion = function(mgr, config){
50812     Roo.SplitLayoutRegion.call(this, mgr, config, "south", "s-resize");
50813     if(this.split){
50814         this.split.placement = Roo.SplitBar.BOTTOM;
50815         this.split.orientation = Roo.SplitBar.VERTICAL;
50816         this.split.el.addClass("x-layout-split-v");
50817     }
50818     var size = config.initialSize || config.height;
50819     if(typeof size != "undefined"){
50820         this.el.setHeight(size);
50821     }
50822 };
50823 Roo.extend(Roo.SouthLayoutRegion, Roo.SplitLayoutRegion, {
50824     orientation: Roo.SplitBar.VERTICAL,
50825     getBox : function(){
50826         if(this.collapsed){
50827             return this.collapsedEl.getBox();
50828         }
50829         var box = this.el.getBox();
50830         if(this.split){
50831             var sh = this.split.el.getHeight();
50832             box.height += sh;
50833             box.y -= sh;
50834         }
50835         return box;
50836     },
50837     
50838     updateBox : function(box){
50839         if(this.split && !this.collapsed){
50840             var sh = this.split.el.getHeight();
50841             box.height -= sh;
50842             box.y += sh;
50843             this.split.el.setLeft(box.x);
50844             this.split.el.setTop(box.y-sh);
50845             this.split.el.setWidth(box.width);
50846         }
50847         if(this.collapsed){
50848             this.updateBody(box.width, null);
50849         }
50850         Roo.LayoutRegion.prototype.updateBox.call(this, box);
50851     }
50852 });
50853
50854 Roo.EastLayoutRegion = function(mgr, config){
50855     Roo.SplitLayoutRegion.call(this, mgr, config, "east", "e-resize");
50856     if(this.split){
50857         this.split.placement = Roo.SplitBar.RIGHT;
50858         this.split.orientation = Roo.SplitBar.HORIZONTAL;
50859         this.split.el.addClass("x-layout-split-h");
50860     }
50861     var size = config.initialSize || config.width;
50862     if(typeof size != "undefined"){
50863         this.el.setWidth(size);
50864     }
50865 };
50866 Roo.extend(Roo.EastLayoutRegion, Roo.SplitLayoutRegion, {
50867     orientation: Roo.SplitBar.HORIZONTAL,
50868     getBox : function(){
50869         if(this.collapsed){
50870             return this.collapsedEl.getBox();
50871         }
50872         var box = this.el.getBox();
50873         if(this.split){
50874             var sw = this.split.el.getWidth();
50875             box.width += sw;
50876             box.x -= sw;
50877         }
50878         return box;
50879     },
50880
50881     updateBox : function(box){
50882         if(this.split && !this.collapsed){
50883             var sw = this.split.el.getWidth();
50884             box.width -= sw;
50885             this.split.el.setLeft(box.x);
50886             this.split.el.setTop(box.y);
50887             this.split.el.setHeight(box.height);
50888             box.x += sw;
50889         }
50890         if(this.collapsed){
50891             this.updateBody(null, box.height);
50892         }
50893         Roo.LayoutRegion.prototype.updateBox.call(this, box);
50894     }
50895 });
50896
50897 Roo.WestLayoutRegion = function(mgr, config){
50898     Roo.SplitLayoutRegion.call(this, mgr, config, "west", "w-resize");
50899     if(this.split){
50900         this.split.placement = Roo.SplitBar.LEFT;
50901         this.split.orientation = Roo.SplitBar.HORIZONTAL;
50902         this.split.el.addClass("x-layout-split-h");
50903     }
50904     var size = config.initialSize || config.width;
50905     if(typeof size != "undefined"){
50906         this.el.setWidth(size);
50907     }
50908 };
50909 Roo.extend(Roo.WestLayoutRegion, Roo.SplitLayoutRegion, {
50910     orientation: Roo.SplitBar.HORIZONTAL,
50911     getBox : function(){
50912         if(this.collapsed){
50913             return this.collapsedEl.getBox();
50914         }
50915         var box = this.el.getBox();
50916         if(this.split){
50917             box.width += this.split.el.getWidth();
50918         }
50919         return box;
50920     },
50921     
50922     updateBox : function(box){
50923         if(this.split && !this.collapsed){
50924             var sw = this.split.el.getWidth();
50925             box.width -= sw;
50926             this.split.el.setLeft(box.x+box.width);
50927             this.split.el.setTop(box.y);
50928             this.split.el.setHeight(box.height);
50929         }
50930         if(this.collapsed){
50931             this.updateBody(null, box.height);
50932         }
50933         Roo.LayoutRegion.prototype.updateBox.call(this, box);
50934     }
50935 });
50936 /*
50937  * Based on:
50938  * Ext JS Library 1.1.1
50939  * Copyright(c) 2006-2007, Ext JS, LLC.
50940  *
50941  * Originally Released Under LGPL - original licence link has changed is not relivant.
50942  *
50943  * Fork - LGPL
50944  * <script type="text/javascript">
50945  */
50946  
50947  
50948 /*
50949  * Private internal class for reading and applying state
50950  */
50951 Roo.LayoutStateManager = function(layout){
50952      // default empty state
50953      this.state = {
50954         north: {},
50955         south: {},
50956         east: {},
50957         west: {}       
50958     };
50959 };
50960
50961 Roo.LayoutStateManager.prototype = {
50962     init : function(layout, provider){
50963         this.provider = provider;
50964         var state = provider.get(layout.id+"-layout-state");
50965         if(state){
50966             var wasUpdating = layout.isUpdating();
50967             if(!wasUpdating){
50968                 layout.beginUpdate();
50969             }
50970             for(var key in state){
50971                 if(typeof state[key] != "function"){
50972                     var rstate = state[key];
50973                     var r = layout.getRegion(key);
50974                     if(r && rstate){
50975                         if(rstate.size){
50976                             r.resizeTo(rstate.size);
50977                         }
50978                         if(rstate.collapsed == true){
50979                             r.collapse(true);
50980                         }else{
50981                             r.expand(null, true);
50982                         }
50983                     }
50984                 }
50985             }
50986             if(!wasUpdating){
50987                 layout.endUpdate();
50988             }
50989             this.state = state; 
50990         }
50991         this.layout = layout;
50992         layout.on("regionresized", this.onRegionResized, this);
50993         layout.on("regioncollapsed", this.onRegionCollapsed, this);
50994         layout.on("regionexpanded", this.onRegionExpanded, this);
50995     },
50996     
50997     storeState : function(){
50998         this.provider.set(this.layout.id+"-layout-state", this.state);
50999     },
51000     
51001     onRegionResized : function(region, newSize){
51002         this.state[region.getPosition()].size = newSize;
51003         this.storeState();
51004     },
51005     
51006     onRegionCollapsed : function(region){
51007         this.state[region.getPosition()].collapsed = true;
51008         this.storeState();
51009     },
51010     
51011     onRegionExpanded : function(region){
51012         this.state[region.getPosition()].collapsed = false;
51013         this.storeState();
51014     }
51015 };/*
51016  * Based on:
51017  * Ext JS Library 1.1.1
51018  * Copyright(c) 2006-2007, Ext JS, LLC.
51019  *
51020  * Originally Released Under LGPL - original licence link has changed is not relivant.
51021  *
51022  * Fork - LGPL
51023  * <script type="text/javascript">
51024  */
51025 /**
51026  * @class Roo.ContentPanel
51027  * @extends Roo.util.Observable
51028  * A basic ContentPanel element.
51029  * @cfg {Boolean}   fitToFrame    True for this panel to adjust its size to fit when the region resizes  (defaults to false)
51030  * @cfg {Boolean}   fitContainer   When using {@link #fitToFrame} and {@link #resizeEl}, you can also fit the parent container  (defaults to false)
51031  * @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
51032  * @cfg {Boolean}   closable      True if the panel can be closed/removed
51033  * @cfg {Boolean}   background    True if the panel should not be activated when it is added (defaults to false)
51034  * @cfg {String/HTMLElement/Element} resizeEl An element to resize if {@link #fitToFrame} is true (instead of this panel's element)
51035  * @cfg {Toolbar}   toolbar       A toolbar for this panel
51036  * @cfg {Boolean} autoScroll    True to scroll overflow in this panel (use with {@link #fitToFrame})
51037  * @cfg {String} title          The title for this panel
51038  * @cfg {Array} adjustments     Values to <b>add</b> to the width/height when doing a {@link #fitToFrame} (default is [0, 0])
51039  * @cfg {String} url            Calls {@link #setUrl} with this value
51040  * @cfg {String} region         (center|north|south|east|west) which region to put this panel on (when used with xtype constructors)
51041  * @cfg {String/Object} params  When used with {@link #url}, calls {@link #setUrl} with this value
51042  * @cfg {Boolean} loadOnce      When used with {@link #url}, calls {@link #setUrl} with this value
51043  * @cfg {String}    content        Raw content to fill content panel with (uses setContent on construction.)
51044
51045  * @constructor
51046  * Create a new ContentPanel.
51047  * @param {String/HTMLElement/Roo.Element} el The container element for this panel
51048  * @param {String/Object} config A string to set only the title or a config object
51049  * @param {String} content (optional) Set the HTML content for this panel
51050  * @param {String} region (optional) Used by xtype constructors to add to regions. (values center,east,west,south,north)
51051  */
51052 Roo.ContentPanel = function(el, config, content){
51053     
51054      
51055     /*
51056     if(el.autoCreate || el.xtype){ // xtype is available if this is called from factory
51057         config = el;
51058         el = Roo.id();
51059     }
51060     if (config && config.parentLayout) { 
51061         el = config.parentLayout.el.createChild(); 
51062     }
51063     */
51064     if(el.autoCreate){ // xtype is available if this is called from factory
51065         config = el;
51066         el = Roo.id();
51067     }
51068     this.el = Roo.get(el);
51069     if(!this.el && config && config.autoCreate){
51070         if(typeof config.autoCreate == "object"){
51071             if(!config.autoCreate.id){
51072                 config.autoCreate.id = config.id||el;
51073             }
51074             this.el = Roo.DomHelper.append(document.body,
51075                         config.autoCreate, true);
51076         }else{
51077             this.el = Roo.DomHelper.append(document.body,
51078                         {tag: "div", cls: "x-layout-inactive-content", id: config.id||el}, true);
51079         }
51080     }
51081     this.closable = false;
51082     this.loaded = false;
51083     this.active = false;
51084     if(typeof config == "string"){
51085         this.title = config;
51086     }else{
51087         Roo.apply(this, config);
51088     }
51089     
51090     if (this.toolbar && !this.toolbar.el && this.toolbar.xtype) {
51091         this.wrapEl = this.el.wrap();
51092         this.toolbar.container = this.el.insertSibling(false, 'before');
51093         this.toolbar = new Roo.Toolbar(this.toolbar);
51094     }
51095     
51096     // xtype created footer. - not sure if will work as we normally have to render first..
51097     if (this.footer && !this.footer.el && this.footer.xtype) {
51098         if (!this.wrapEl) {
51099             this.wrapEl = this.el.wrap();
51100         }
51101     
51102         this.footer.container = this.wrapEl.createChild();
51103          
51104         this.footer = Roo.factory(this.footer, Roo);
51105         
51106     }
51107     
51108     if(this.resizeEl){
51109         this.resizeEl = Roo.get(this.resizeEl, true);
51110     }else{
51111         this.resizeEl = this.el;
51112     }
51113     // handle view.xtype
51114     
51115  
51116     
51117     
51118     this.addEvents({
51119         /**
51120          * @event activate
51121          * Fires when this panel is activated. 
51122          * @param {Roo.ContentPanel} this
51123          */
51124         "activate" : true,
51125         /**
51126          * @event deactivate
51127          * Fires when this panel is activated. 
51128          * @param {Roo.ContentPanel} this
51129          */
51130         "deactivate" : true,
51131
51132         /**
51133          * @event resize
51134          * Fires when this panel is resized if fitToFrame is true.
51135          * @param {Roo.ContentPanel} this
51136          * @param {Number} width The width after any component adjustments
51137          * @param {Number} height The height after any component adjustments
51138          */
51139         "resize" : true,
51140         
51141          /**
51142          * @event render
51143          * Fires when this tab is created
51144          * @param {Roo.ContentPanel} this
51145          */
51146         "render" : true
51147         
51148         
51149         
51150     });
51151     
51152
51153     
51154     
51155     if(this.autoScroll){
51156         this.resizeEl.setStyle("overflow", "auto");
51157     } else {
51158         // fix randome scrolling
51159         this.el.on('scroll', function() {
51160             Roo.log('fix random scolling');
51161             this.scrollTo('top',0); 
51162         });
51163     }
51164     content = content || this.content;
51165     if(content){
51166         this.setContent(content);
51167     }
51168     if(config && config.url){
51169         this.setUrl(this.url, this.params, this.loadOnce);
51170     }
51171     
51172     
51173     
51174     Roo.ContentPanel.superclass.constructor.call(this);
51175     
51176     if (this.view && typeof(this.view.xtype) != 'undefined') {
51177         this.view.el = this.el.appendChild(document.createElement("div"));
51178         this.view = Roo.factory(this.view); 
51179         this.view.render  &&  this.view.render(false, '');  
51180     }
51181     
51182     
51183     this.fireEvent('render', this);
51184 };
51185
51186 Roo.extend(Roo.ContentPanel, Roo.util.Observable, {
51187     tabTip:'',
51188     setRegion : function(region){
51189         this.region = region;
51190         if(region){
51191            this.el.replaceClass("x-layout-inactive-content", "x-layout-active-content");
51192         }else{
51193            this.el.replaceClass("x-layout-active-content", "x-layout-inactive-content");
51194         } 
51195     },
51196     
51197     /**
51198      * Returns the toolbar for this Panel if one was configured. 
51199      * @return {Roo.Toolbar} 
51200      */
51201     getToolbar : function(){
51202         return this.toolbar;
51203     },
51204     
51205     setActiveState : function(active){
51206         this.active = active;
51207         if(!active){
51208             this.fireEvent("deactivate", this);
51209         }else{
51210             this.fireEvent("activate", this);
51211         }
51212     },
51213     /**
51214      * Updates this panel's element
51215      * @param {String} content The new content
51216      * @param {Boolean} loadScripts (optional) true to look for and process scripts
51217     */
51218     setContent : function(content, loadScripts){
51219         this.el.update(content, loadScripts);
51220     },
51221
51222     ignoreResize : function(w, h){
51223         if(this.lastSize && this.lastSize.width == w && this.lastSize.height == h){
51224             return true;
51225         }else{
51226             this.lastSize = {width: w, height: h};
51227             return false;
51228         }
51229     },
51230     /**
51231      * Get the {@link Roo.UpdateManager} for this panel. Enables you to perform Ajax updates.
51232      * @return {Roo.UpdateManager} The UpdateManager
51233      */
51234     getUpdateManager : function(){
51235         return this.el.getUpdateManager();
51236     },
51237      /**
51238      * Loads this content panel immediately with content from XHR. Note: to delay loading until the panel is activated, use {@link #setUrl}.
51239      * @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:
51240 <pre><code>
51241 panel.load({
51242     url: "your-url.php",
51243     params: {param1: "foo", param2: "bar"}, // or a URL encoded string
51244     callback: yourFunction,
51245     scope: yourObject, //(optional scope)
51246     discardUrl: false,
51247     nocache: false,
51248     text: "Loading...",
51249     timeout: 30,
51250     scripts: false
51251 });
51252 </code></pre>
51253      * The only required property is <i>url</i>. The optional properties <i>nocache</i>, <i>text</i> and <i>scripts</i>
51254      * 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.
51255      * @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}
51256      * @param {Function} callback (optional) Callback when transaction is complete -- called with signature (oElement, bSuccess, oResponse)
51257      * @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.
51258      * @return {Roo.ContentPanel} this
51259      */
51260     load : function(){
51261         var um = this.el.getUpdateManager();
51262         um.update.apply(um, arguments);
51263         return this;
51264     },
51265
51266
51267     /**
51268      * 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.
51269      * @param {String/Function} url The URL to load the content from or a function to call to get the URL
51270      * @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)
51271      * @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)
51272      * @return {Roo.UpdateManager} The UpdateManager
51273      */
51274     setUrl : function(url, params, loadOnce){
51275         if(this.refreshDelegate){
51276             this.removeListener("activate", this.refreshDelegate);
51277         }
51278         this.refreshDelegate = this._handleRefresh.createDelegate(this, [url, params, loadOnce]);
51279         this.on("activate", this.refreshDelegate);
51280         return this.el.getUpdateManager();
51281     },
51282     
51283     _handleRefresh : function(url, params, loadOnce){
51284         if(!loadOnce || !this.loaded){
51285             var updater = this.el.getUpdateManager();
51286             updater.update(url, params, this._setLoaded.createDelegate(this));
51287         }
51288     },
51289     
51290     _setLoaded : function(){
51291         this.loaded = true;
51292     }, 
51293     
51294     /**
51295      * Returns this panel's id
51296      * @return {String} 
51297      */
51298     getId : function(){
51299         return this.el.id;
51300     },
51301     
51302     /** 
51303      * Returns this panel's element - used by regiosn to add.
51304      * @return {Roo.Element} 
51305      */
51306     getEl : function(){
51307         return this.wrapEl || this.el;
51308     },
51309     
51310     adjustForComponents : function(width, height)
51311     {
51312         //Roo.log('adjustForComponents ');
51313         if(this.resizeEl != this.el){
51314             width -= this.el.getFrameWidth('lr');
51315             height -= this.el.getFrameWidth('tb');
51316         }
51317         if(this.toolbar){
51318             var te = this.toolbar.getEl();
51319             height -= te.getHeight();
51320             te.setWidth(width);
51321         }
51322         if(this.footer){
51323             var te = this.footer.getEl();
51324             Roo.log("footer:" + te.getHeight());
51325             
51326             height -= te.getHeight();
51327             te.setWidth(width);
51328         }
51329         
51330         
51331         if(this.adjustments){
51332             width += this.adjustments[0];
51333             height += this.adjustments[1];
51334         }
51335         return {"width": width, "height": height};
51336     },
51337     
51338     setSize : function(width, height){
51339         if(this.fitToFrame && !this.ignoreResize(width, height)){
51340             if(this.fitContainer && this.resizeEl != this.el){
51341                 this.el.setSize(width, height);
51342             }
51343             var size = this.adjustForComponents(width, height);
51344             this.resizeEl.setSize(this.autoWidth ? "auto" : size.width, this.autoHeight ? "auto" : size.height);
51345             this.fireEvent('resize', this, size.width, size.height);
51346         }
51347     },
51348     
51349     /**
51350      * Returns this panel's title
51351      * @return {String} 
51352      */
51353     getTitle : function(){
51354         return this.title;
51355     },
51356     
51357     /**
51358      * Set this panel's title
51359      * @param {String} title
51360      */
51361     setTitle : function(title){
51362         this.title = title;
51363         if(this.region){
51364             this.region.updatePanelTitle(this, title);
51365         }
51366     },
51367     
51368     /**
51369      * Returns true is this panel was configured to be closable
51370      * @return {Boolean} 
51371      */
51372     isClosable : function(){
51373         return this.closable;
51374     },
51375     
51376     beforeSlide : function(){
51377         this.el.clip();
51378         this.resizeEl.clip();
51379     },
51380     
51381     afterSlide : function(){
51382         this.el.unclip();
51383         this.resizeEl.unclip();
51384     },
51385     
51386     /**
51387      *   Force a content refresh from the URL specified in the {@link #setUrl} method.
51388      *   Will fail silently if the {@link #setUrl} method has not been called.
51389      *   This does not activate the panel, just updates its content.
51390      */
51391     refresh : function(){
51392         if(this.refreshDelegate){
51393            this.loaded = false;
51394            this.refreshDelegate();
51395         }
51396     },
51397     
51398     /**
51399      * Destroys this panel
51400      */
51401     destroy : function(){
51402         this.el.removeAllListeners();
51403         var tempEl = document.createElement("span");
51404         tempEl.appendChild(this.el.dom);
51405         tempEl.innerHTML = "";
51406         this.el.remove();
51407         this.el = null;
51408     },
51409     
51410     /**
51411      * form - if the content panel contains a form - this is a reference to it.
51412      * @type {Roo.form.Form}
51413      */
51414     form : false,
51415     /**
51416      * view - if the content panel contains a view (Roo.DatePicker / Roo.View / Roo.JsonView)
51417      *    This contains a reference to it.
51418      * @type {Roo.View}
51419      */
51420     view : false,
51421     
51422       /**
51423      * Adds a xtype elements to the panel - currently only supports Forms, View, JsonView.
51424      * <pre><code>
51425
51426 layout.addxtype({
51427        xtype : 'Form',
51428        items: [ .... ]
51429    }
51430 );
51431
51432 </code></pre>
51433      * @param {Object} cfg Xtype definition of item to add.
51434      */
51435     
51436     addxtype : function(cfg) {
51437         // add form..
51438         if (cfg.xtype.match(/^Form$/)) {
51439             
51440             var el;
51441             //if (this.footer) {
51442             //    el = this.footer.container.insertSibling(false, 'before');
51443             //} else {
51444                 el = this.el.createChild();
51445             //}
51446
51447             this.form = new  Roo.form.Form(cfg);
51448             
51449             
51450             if ( this.form.allItems.length) this.form.render(el.dom);
51451             return this.form;
51452         }
51453         // should only have one of theses..
51454         if ([ 'View', 'JsonView', 'DatePicker'].indexOf(cfg.xtype) > -1) {
51455             // views.. should not be just added - used named prop 'view''
51456             
51457             cfg.el = this.el.appendChild(document.createElement("div"));
51458             // factory?
51459             
51460             var ret = new Roo.factory(cfg);
51461              
51462              ret.render && ret.render(false, ''); // render blank..
51463             this.view = ret;
51464             return ret;
51465         }
51466         return false;
51467     }
51468 });
51469
51470 /**
51471  * @class Roo.GridPanel
51472  * @extends Roo.ContentPanel
51473  * @constructor
51474  * Create a new GridPanel.
51475  * @param {Roo.grid.Grid} grid The grid for this panel
51476  * @param {String/Object} config A string to set only the panel's title, or a config object
51477  */
51478 Roo.GridPanel = function(grid, config){
51479     
51480   
51481     this.wrapper = Roo.DomHelper.append(document.body, // wrapper for IE7 strict & safari scroll issue
51482         {tag: "div", cls: "x-layout-grid-wrapper x-layout-inactive-content"}, true);
51483         
51484     this.wrapper.dom.appendChild(grid.getGridEl().dom);
51485     
51486     Roo.GridPanel.superclass.constructor.call(this, this.wrapper, config);
51487     
51488     if(this.toolbar){
51489         this.toolbar.el.insertBefore(this.wrapper.dom.firstChild);
51490     }
51491     // xtype created footer. - not sure if will work as we normally have to render first..
51492     if (this.footer && !this.footer.el && this.footer.xtype) {
51493         
51494         this.footer.container = this.grid.getView().getFooterPanel(true);
51495         this.footer.dataSource = this.grid.dataSource;
51496         this.footer = Roo.factory(this.footer, Roo);
51497         
51498     }
51499     
51500     grid.monitorWindowResize = false; // turn off autosizing
51501     grid.autoHeight = false;
51502     grid.autoWidth = false;
51503     this.grid = grid;
51504     this.grid.getGridEl().replaceClass("x-layout-inactive-content", "x-layout-component-panel");
51505 };
51506
51507 Roo.extend(Roo.GridPanel, Roo.ContentPanel, {
51508     getId : function(){
51509         return this.grid.id;
51510     },
51511     
51512     /**
51513      * Returns the grid for this panel
51514      * @return {Roo.grid.Grid} 
51515      */
51516     getGrid : function(){
51517         return this.grid;    
51518     },
51519     
51520     setSize : function(width, height){
51521         if(!this.ignoreResize(width, height)){
51522             var grid = this.grid;
51523             var size = this.adjustForComponents(width, height);
51524             grid.getGridEl().setSize(size.width, size.height);
51525             grid.autoSize();
51526         }
51527     },
51528     
51529     beforeSlide : function(){
51530         this.grid.getView().scroller.clip();
51531     },
51532     
51533     afterSlide : function(){
51534         this.grid.getView().scroller.unclip();
51535     },
51536     
51537     destroy : function(){
51538         this.grid.destroy();
51539         delete this.grid;
51540         Roo.GridPanel.superclass.destroy.call(this); 
51541     }
51542 });
51543
51544
51545 /**
51546  * @class Roo.NestedLayoutPanel
51547  * @extends Roo.ContentPanel
51548  * @constructor
51549  * Create a new NestedLayoutPanel.
51550  * 
51551  * 
51552  * @param {Roo.BorderLayout} layout The layout for this panel
51553  * @param {String/Object} config A string to set only the title or a config object
51554  */
51555 Roo.NestedLayoutPanel = function(layout, config)
51556 {
51557     // construct with only one argument..
51558     /* FIXME - implement nicer consturctors
51559     if (layout.layout) {
51560         config = layout;
51561         layout = config.layout;
51562         delete config.layout;
51563     }
51564     if (layout.xtype && !layout.getEl) {
51565         // then layout needs constructing..
51566         layout = Roo.factory(layout, Roo);
51567     }
51568     */
51569     
51570     
51571     Roo.NestedLayoutPanel.superclass.constructor.call(this, layout.getEl(), config);
51572     
51573     layout.monitorWindowResize = false; // turn off autosizing
51574     this.layout = layout;
51575     this.layout.getEl().addClass("x-layout-nested-layout");
51576     
51577     
51578     
51579     
51580 };
51581
51582 Roo.extend(Roo.NestedLayoutPanel, Roo.ContentPanel, {
51583
51584     setSize : function(width, height){
51585         if(!this.ignoreResize(width, height)){
51586             var size = this.adjustForComponents(width, height);
51587             var el = this.layout.getEl();
51588             el.setSize(size.width, size.height);
51589             var touch = el.dom.offsetWidth;
51590             this.layout.layout();
51591             // ie requires a double layout on the first pass
51592             if(Roo.isIE && !this.initialized){
51593                 this.initialized = true;
51594                 this.layout.layout();
51595             }
51596         }
51597     },
51598     
51599     // activate all subpanels if not currently active..
51600     
51601     setActiveState : function(active){
51602         this.active = active;
51603         if(!active){
51604             this.fireEvent("deactivate", this);
51605             return;
51606         }
51607         
51608         this.fireEvent("activate", this);
51609         // not sure if this should happen before or after..
51610         if (!this.layout) {
51611             return; // should not happen..
51612         }
51613         var reg = false;
51614         for (var r in this.layout.regions) {
51615             reg = this.layout.getRegion(r);
51616             if (reg.getActivePanel()) {
51617                 //reg.showPanel(reg.getActivePanel()); // force it to activate.. 
51618                 reg.setActivePanel(reg.getActivePanel());
51619                 continue;
51620             }
51621             if (!reg.panels.length) {
51622                 continue;
51623             }
51624             reg.showPanel(reg.getPanel(0));
51625         }
51626         
51627         
51628         
51629         
51630     },
51631     
51632     /**
51633      * Returns the nested BorderLayout for this panel
51634      * @return {Roo.BorderLayout} 
51635      */
51636     getLayout : function(){
51637         return this.layout;
51638     },
51639     
51640      /**
51641      * Adds a xtype elements to the layout of the nested panel
51642      * <pre><code>
51643
51644 panel.addxtype({
51645        xtype : 'ContentPanel',
51646        region: 'west',
51647        items: [ .... ]
51648    }
51649 );
51650
51651 panel.addxtype({
51652         xtype : 'NestedLayoutPanel',
51653         region: 'west',
51654         layout: {
51655            center: { },
51656            west: { }   
51657         },
51658         items : [ ... list of content panels or nested layout panels.. ]
51659    }
51660 );
51661 </code></pre>
51662      * @param {Object} cfg Xtype definition of item to add.
51663      */
51664     addxtype : function(cfg) {
51665         return this.layout.addxtype(cfg);
51666     
51667     }
51668 });
51669
51670 Roo.ScrollPanel = function(el, config, content){
51671     config = config || {};
51672     config.fitToFrame = true;
51673     Roo.ScrollPanel.superclass.constructor.call(this, el, config, content);
51674     
51675     this.el.dom.style.overflow = "hidden";
51676     var wrap = this.el.wrap({cls: "x-scroller x-layout-inactive-content"});
51677     this.el.removeClass("x-layout-inactive-content");
51678     this.el.on("mousewheel", this.onWheel, this);
51679
51680     var up = wrap.createChild({cls: "x-scroller-up", html: "&#160;"}, this.el.dom);
51681     var down = wrap.createChild({cls: "x-scroller-down", html: "&#160;"});
51682     up.unselectable(); down.unselectable();
51683     up.on("click", this.scrollUp, this);
51684     down.on("click", this.scrollDown, this);
51685     up.addClassOnOver("x-scroller-btn-over");
51686     down.addClassOnOver("x-scroller-btn-over");
51687     up.addClassOnClick("x-scroller-btn-click");
51688     down.addClassOnClick("x-scroller-btn-click");
51689     this.adjustments = [0, -(up.getHeight() + down.getHeight())];
51690
51691     this.resizeEl = this.el;
51692     this.el = wrap; this.up = up; this.down = down;
51693 };
51694
51695 Roo.extend(Roo.ScrollPanel, Roo.ContentPanel, {
51696     increment : 100,
51697     wheelIncrement : 5,
51698     scrollUp : function(){
51699         this.resizeEl.scroll("up", this.increment, {callback: this.afterScroll, scope: this});
51700     },
51701
51702     scrollDown : function(){
51703         this.resizeEl.scroll("down", this.increment, {callback: this.afterScroll, scope: this});
51704     },
51705
51706     afterScroll : function(){
51707         var el = this.resizeEl;
51708         var t = el.dom.scrollTop, h = el.dom.scrollHeight, ch = el.dom.clientHeight;
51709         this.up[t == 0 ? "addClass" : "removeClass"]("x-scroller-btn-disabled");
51710         this.down[h - t <= ch ? "addClass" : "removeClass"]("x-scroller-btn-disabled");
51711     },
51712
51713     setSize : function(){
51714         Roo.ScrollPanel.superclass.setSize.apply(this, arguments);
51715         this.afterScroll();
51716     },
51717
51718     onWheel : function(e){
51719         var d = e.getWheelDelta();
51720         this.resizeEl.dom.scrollTop -= (d*this.wheelIncrement);
51721         this.afterScroll();
51722         e.stopEvent();
51723     },
51724
51725     setContent : function(content, loadScripts){
51726         this.resizeEl.update(content, loadScripts);
51727     }
51728
51729 });
51730
51731
51732
51733
51734
51735
51736
51737
51738
51739 /**
51740  * @class Roo.TreePanel
51741  * @extends Roo.ContentPanel
51742  * @constructor
51743  * Create a new TreePanel. - defaults to fit/scoll contents.
51744  * @param {String/Object} config A string to set only the panel's title, or a config object
51745  * @cfg {Roo.tree.TreePanel} tree The tree TreePanel, with config etc.
51746  */
51747 Roo.TreePanel = function(config){
51748     var el = config.el;
51749     var tree = config.tree;
51750     delete config.tree; 
51751     delete config.el; // hopefull!
51752     
51753     // wrapper for IE7 strict & safari scroll issue
51754     
51755     var treeEl = el.createChild();
51756     config.resizeEl = treeEl;
51757     
51758     
51759     
51760     Roo.TreePanel.superclass.constructor.call(this, el, config);
51761  
51762  
51763     this.tree = new Roo.tree.TreePanel(treeEl , tree);
51764     //console.log(tree);
51765     this.on('activate', function()
51766     {
51767         if (this.tree.rendered) {
51768             return;
51769         }
51770         //console.log('render tree');
51771         this.tree.render();
51772     });
51773     // this should not be needed.. - it's actually the 'el' that resizes?
51774     // actuall it breaks the containerScroll - dragging nodes auto scroll at top
51775     
51776     //this.on('resize',  function (cp, w, h) {
51777     //        this.tree.innerCt.setWidth(w);
51778     //        this.tree.innerCt.setHeight(h);
51779     //        //this.tree.innerCt.setStyle('overflow-y', 'auto');
51780     //});
51781
51782         
51783     
51784 };
51785
51786 Roo.extend(Roo.TreePanel, Roo.ContentPanel, {   
51787     fitToFrame : true,
51788     autoScroll : true
51789 });
51790
51791
51792
51793
51794
51795
51796
51797
51798
51799
51800
51801 /*
51802  * Based on:
51803  * Ext JS Library 1.1.1
51804  * Copyright(c) 2006-2007, Ext JS, LLC.
51805  *
51806  * Originally Released Under LGPL - original licence link has changed is not relivant.
51807  *
51808  * Fork - LGPL
51809  * <script type="text/javascript">
51810  */
51811  
51812
51813 /**
51814  * @class Roo.ReaderLayout
51815  * @extends Roo.BorderLayout
51816  * This is a pre-built layout that represents a classic, 5-pane application.  It consists of a header, a primary
51817  * center region containing two nested regions (a top one for a list view and one for item preview below),
51818  * and regions on either side that can be used for navigation, application commands, informational displays, etc.
51819  * The setup and configuration work exactly the same as it does for a {@link Roo.BorderLayout} - this class simply
51820  * expedites the setup of the overall layout and regions for this common application style.
51821  * Example:
51822  <pre><code>
51823 var reader = new Roo.ReaderLayout();
51824 var CP = Roo.ContentPanel;  // shortcut for adding
51825
51826 reader.beginUpdate();
51827 reader.add("north", new CP("north", "North"));
51828 reader.add("west", new CP("west", {title: "West"}));
51829 reader.add("east", new CP("east", {title: "East"}));
51830
51831 reader.regions.listView.add(new CP("listView", "List"));
51832 reader.regions.preview.add(new CP("preview", "Preview"));
51833 reader.endUpdate();
51834 </code></pre>
51835 * @constructor
51836 * Create a new ReaderLayout
51837 * @param {Object} config Configuration options
51838 * @param {String/HTMLElement/Element} container (optional) The container this layout is bound to (defaults to
51839 * document.body if omitted)
51840 */
51841 Roo.ReaderLayout = function(config, renderTo){
51842     var c = config || {size:{}};
51843     Roo.ReaderLayout.superclass.constructor.call(this, renderTo || document.body, {
51844         north: c.north !== false ? Roo.apply({
51845             split:false,
51846             initialSize: 32,
51847             titlebar: false
51848         }, c.north) : false,
51849         west: c.west !== false ? Roo.apply({
51850             split:true,
51851             initialSize: 200,
51852             minSize: 175,
51853             maxSize: 400,
51854             titlebar: true,
51855             collapsible: true,
51856             animate: true,
51857             margins:{left:5,right:0,bottom:5,top:5},
51858             cmargins:{left:5,right:5,bottom:5,top:5}
51859         }, c.west) : false,
51860         east: c.east !== false ? Roo.apply({
51861             split:true,
51862             initialSize: 200,
51863             minSize: 175,
51864             maxSize: 400,
51865             titlebar: true,
51866             collapsible: true,
51867             animate: true,
51868             margins:{left:0,right:5,bottom:5,top:5},
51869             cmargins:{left:5,right:5,bottom:5,top:5}
51870         }, c.east) : false,
51871         center: Roo.apply({
51872             tabPosition: 'top',
51873             autoScroll:false,
51874             closeOnTab: true,
51875             titlebar:false,
51876             margins:{left:c.west!==false ? 0 : 5,right:c.east!==false ? 0 : 5,bottom:5,top:2}
51877         }, c.center)
51878     });
51879
51880     this.el.addClass('x-reader');
51881
51882     this.beginUpdate();
51883
51884     var inner = new Roo.BorderLayout(Roo.get(document.body).createChild(), {
51885         south: c.preview !== false ? Roo.apply({
51886             split:true,
51887             initialSize: 200,
51888             minSize: 100,
51889             autoScroll:true,
51890             collapsible:true,
51891             titlebar: true,
51892             cmargins:{top:5,left:0, right:0, bottom:0}
51893         }, c.preview) : false,
51894         center: Roo.apply({
51895             autoScroll:false,
51896             titlebar:false,
51897             minHeight:200
51898         }, c.listView)
51899     });
51900     this.add('center', new Roo.NestedLayoutPanel(inner,
51901             Roo.apply({title: c.mainTitle || '',tabTip:''},c.innerPanelCfg)));
51902
51903     this.endUpdate();
51904
51905     this.regions.preview = inner.getRegion('south');
51906     this.regions.listView = inner.getRegion('center');
51907 };
51908
51909 Roo.extend(Roo.ReaderLayout, Roo.BorderLayout);/*
51910  * Based on:
51911  * Ext JS Library 1.1.1
51912  * Copyright(c) 2006-2007, Ext JS, LLC.
51913  *
51914  * Originally Released Under LGPL - original licence link has changed is not relivant.
51915  *
51916  * Fork - LGPL
51917  * <script type="text/javascript">
51918  */
51919  
51920 /**
51921  * @class Roo.grid.Grid
51922  * @extends Roo.util.Observable
51923  * This class represents the primary interface of a component based grid control.
51924  * <br><br>Usage:<pre><code>
51925  var grid = new Roo.grid.Grid("my-container-id", {
51926      ds: myDataStore,
51927      cm: myColModel,
51928      selModel: mySelectionModel,
51929      autoSizeColumns: true,
51930      monitorWindowResize: false,
51931      trackMouseOver: true
51932  });
51933  // set any options
51934  grid.render();
51935  * </code></pre>
51936  * <b>Common Problems:</b><br/>
51937  * - Grid does not resize properly when going smaller: Setting overflow hidden on the container
51938  * element will correct this<br/>
51939  * - If you get el.style[camel]= NaNpx or -2px or something related, be certain you have given your container element
51940  * dimensions. The grid adapts to your container's size, if your container has no size defined then the results
51941  * are unpredictable.<br/>
51942  * - Do not render the grid into an element with display:none. Try using visibility:hidden. Otherwise there is no way for the
51943  * grid to calculate dimensions/offsets.<br/>
51944   * @constructor
51945  * @param {String/HTMLElement/Roo.Element} container The element into which this grid will be rendered -
51946  * The container MUST have some type of size defined for the grid to fill. The container will be
51947  * automatically set to position relative if it isn't already.
51948  * @param {Object} config A config object that sets properties on this grid.
51949  */
51950 Roo.grid.Grid = function(container, config){
51951         // initialize the container
51952         this.container = Roo.get(container);
51953         this.container.update("");
51954         this.container.setStyle("overflow", "hidden");
51955     this.container.addClass('x-grid-container');
51956
51957     this.id = this.container.id;
51958
51959     Roo.apply(this, config);
51960     // check and correct shorthanded configs
51961     if(this.ds){
51962         this.dataSource = this.ds;
51963         delete this.ds;
51964     }
51965     if(this.cm){
51966         this.colModel = this.cm;
51967         delete this.cm;
51968     }
51969     if(this.sm){
51970         this.selModel = this.sm;
51971         delete this.sm;
51972     }
51973
51974     if (this.selModel) {
51975         this.selModel = Roo.factory(this.selModel, Roo.grid);
51976         this.sm = this.selModel;
51977         this.sm.xmodule = this.xmodule || false;
51978     }
51979     if (typeof(this.colModel.config) == 'undefined') {
51980         this.colModel = new Roo.grid.ColumnModel(this.colModel);
51981         this.cm = this.colModel;
51982         this.cm.xmodule = this.xmodule || false;
51983     }
51984     if (this.dataSource) {
51985         this.dataSource= Roo.factory(this.dataSource, Roo.data);
51986         this.ds = this.dataSource;
51987         this.ds.xmodule = this.xmodule || false;
51988          
51989     }
51990     
51991     
51992     
51993     if(this.width){
51994         this.container.setWidth(this.width);
51995     }
51996
51997     if(this.height){
51998         this.container.setHeight(this.height);
51999     }
52000     /** @private */
52001         this.addEvents({
52002         // raw events
52003         /**
52004          * @event click
52005          * The raw click event for the entire grid.
52006          * @param {Roo.EventObject} e
52007          */
52008         "click" : true,
52009         /**
52010          * @event dblclick
52011          * The raw dblclick event for the entire grid.
52012          * @param {Roo.EventObject} e
52013          */
52014         "dblclick" : true,
52015         /**
52016          * @event contextmenu
52017          * The raw contextmenu event for the entire grid.
52018          * @param {Roo.EventObject} e
52019          */
52020         "contextmenu" : true,
52021         /**
52022          * @event mousedown
52023          * The raw mousedown event for the entire grid.
52024          * @param {Roo.EventObject} e
52025          */
52026         "mousedown" : true,
52027         /**
52028          * @event mouseup
52029          * The raw mouseup event for the entire grid.
52030          * @param {Roo.EventObject} e
52031          */
52032         "mouseup" : true,
52033         /**
52034          * @event mouseover
52035          * The raw mouseover event for the entire grid.
52036          * @param {Roo.EventObject} e
52037          */
52038         "mouseover" : true,
52039         /**
52040          * @event mouseout
52041          * The raw mouseout event for the entire grid.
52042          * @param {Roo.EventObject} e
52043          */
52044         "mouseout" : true,
52045         /**
52046          * @event keypress
52047          * The raw keypress event for the entire grid.
52048          * @param {Roo.EventObject} e
52049          */
52050         "keypress" : true,
52051         /**
52052          * @event keydown
52053          * The raw keydown event for the entire grid.
52054          * @param {Roo.EventObject} e
52055          */
52056         "keydown" : true,
52057
52058         // custom events
52059
52060         /**
52061          * @event cellclick
52062          * Fires when a cell is clicked
52063          * @param {Grid} this
52064          * @param {Number} rowIndex
52065          * @param {Number} columnIndex
52066          * @param {Roo.EventObject} e
52067          */
52068         "cellclick" : true,
52069         /**
52070          * @event celldblclick
52071          * Fires when a cell is double clicked
52072          * @param {Grid} this
52073          * @param {Number} rowIndex
52074          * @param {Number} columnIndex
52075          * @param {Roo.EventObject} e
52076          */
52077         "celldblclick" : true,
52078         /**
52079          * @event rowclick
52080          * Fires when a row is clicked
52081          * @param {Grid} this
52082          * @param {Number} rowIndex
52083          * @param {Roo.EventObject} e
52084          */
52085         "rowclick" : true,
52086         /**
52087          * @event rowdblclick
52088          * Fires when a row is double clicked
52089          * @param {Grid} this
52090          * @param {Number} rowIndex
52091          * @param {Roo.EventObject} e
52092          */
52093         "rowdblclick" : true,
52094         /**
52095          * @event headerclick
52096          * Fires when a header is clicked
52097          * @param {Grid} this
52098          * @param {Number} columnIndex
52099          * @param {Roo.EventObject} e
52100          */
52101         "headerclick" : true,
52102         /**
52103          * @event headerdblclick
52104          * Fires when a header cell is double clicked
52105          * @param {Grid} this
52106          * @param {Number} columnIndex
52107          * @param {Roo.EventObject} e
52108          */
52109         "headerdblclick" : true,
52110         /**
52111          * @event rowcontextmenu
52112          * Fires when a row is right clicked
52113          * @param {Grid} this
52114          * @param {Number} rowIndex
52115          * @param {Roo.EventObject} e
52116          */
52117         "rowcontextmenu" : true,
52118         /**
52119          * @event cellcontextmenu
52120          * Fires when a cell is right clicked
52121          * @param {Grid} this
52122          * @param {Number} rowIndex
52123          * @param {Number} cellIndex
52124          * @param {Roo.EventObject} e
52125          */
52126          "cellcontextmenu" : true,
52127         /**
52128          * @event headercontextmenu
52129          * Fires when a header is right clicked
52130          * @param {Grid} this
52131          * @param {Number} columnIndex
52132          * @param {Roo.EventObject} e
52133          */
52134         "headercontextmenu" : true,
52135         /**
52136          * @event bodyscroll
52137          * Fires when the body element is scrolled
52138          * @param {Number} scrollLeft
52139          * @param {Number} scrollTop
52140          */
52141         "bodyscroll" : true,
52142         /**
52143          * @event columnresize
52144          * Fires when the user resizes a column
52145          * @param {Number} columnIndex
52146          * @param {Number} newSize
52147          */
52148         "columnresize" : true,
52149         /**
52150          * @event columnmove
52151          * Fires when the user moves a column
52152          * @param {Number} oldIndex
52153          * @param {Number} newIndex
52154          */
52155         "columnmove" : true,
52156         /**
52157          * @event startdrag
52158          * Fires when row(s) start being dragged
52159          * @param {Grid} this
52160          * @param {Roo.GridDD} dd The drag drop object
52161          * @param {event} e The raw browser event
52162          */
52163         "startdrag" : true,
52164         /**
52165          * @event enddrag
52166          * Fires when a drag operation is complete
52167          * @param {Grid} this
52168          * @param {Roo.GridDD} dd The drag drop object
52169          * @param {event} e The raw browser event
52170          */
52171         "enddrag" : true,
52172         /**
52173          * @event dragdrop
52174          * Fires when dragged row(s) are dropped on a valid DD target
52175          * @param {Grid} this
52176          * @param {Roo.GridDD} dd The drag drop object
52177          * @param {String} targetId The target drag drop object
52178          * @param {event} e The raw browser event
52179          */
52180         "dragdrop" : true,
52181         /**
52182          * @event dragover
52183          * Fires while row(s) are being dragged. "targetId" is the id of the Yahoo.util.DD object the selected rows are being dragged over.
52184          * @param {Grid} this
52185          * @param {Roo.GridDD} dd The drag drop object
52186          * @param {String} targetId The target drag drop object
52187          * @param {event} e The raw browser event
52188          */
52189         "dragover" : true,
52190         /**
52191          * @event dragenter
52192          *  Fires when the dragged row(s) first cross another DD target while being dragged
52193          * @param {Grid} this
52194          * @param {Roo.GridDD} dd The drag drop object
52195          * @param {String} targetId The target drag drop object
52196          * @param {event} e The raw browser event
52197          */
52198         "dragenter" : true,
52199         /**
52200          * @event dragout
52201          * Fires when the dragged row(s) leave another DD target while being dragged
52202          * @param {Grid} this
52203          * @param {Roo.GridDD} dd The drag drop object
52204          * @param {String} targetId The target drag drop object
52205          * @param {event} e The raw browser event
52206          */
52207         "dragout" : true,
52208         /**
52209          * @event rowclass
52210          * Fires when a row is rendered, so you can change add a style to it.
52211          * @param {GridView} gridview   The grid view
52212          * @param {Object} rowcfg   contains record  rowIndex and rowClass - set rowClass to add a style.
52213          */
52214         'rowclass' : true,
52215
52216         /**
52217          * @event render
52218          * Fires when the grid is rendered
52219          * @param {Grid} grid
52220          */
52221         'render' : true
52222     });
52223
52224     Roo.grid.Grid.superclass.constructor.call(this);
52225 };
52226 Roo.extend(Roo.grid.Grid, Roo.util.Observable, {
52227     
52228     /**
52229      * @cfg {String} ddGroup - drag drop group.
52230      */
52231
52232     /**
52233      * @cfg {Number} minColumnWidth The minimum width a column can be resized to. Default is 25.
52234      */
52235     minColumnWidth : 25,
52236
52237     /**
52238      * @cfg {Boolean} autoSizeColumns True to automatically resize the columns to fit their content
52239      * <b>on initial render.</b> It is more efficient to explicitly size the columns
52240      * through the ColumnModel's {@link Roo.grid.ColumnModel#width} config option.  Default is false.
52241      */
52242     autoSizeColumns : false,
52243
52244     /**
52245      * @cfg {Boolean} autoSizeHeaders True to measure headers with column data when auto sizing columns. Default is true.
52246      */
52247     autoSizeHeaders : true,
52248
52249     /**
52250      * @cfg {Boolean} monitorWindowResize True to autoSize the grid when the window resizes. Default is true.
52251      */
52252     monitorWindowResize : true,
52253
52254     /**
52255      * @cfg {Boolean} maxRowsToMeasure If autoSizeColumns is on, maxRowsToMeasure can be used to limit the number of
52256      * rows measured to get a columns size. Default is 0 (all rows).
52257      */
52258     maxRowsToMeasure : 0,
52259
52260     /**
52261      * @cfg {Boolean} trackMouseOver True to highlight rows when the mouse is over. Default is true.
52262      */
52263     trackMouseOver : true,
52264
52265     /**
52266     * @cfg {Boolean} enableDrag  True to enable drag of rows. Default is false. (double check if this is needed?)
52267     */
52268     
52269     /**
52270     * @cfg {Boolean} enableDragDrop True to enable drag and drop of rows. Default is false.
52271     */
52272     enableDragDrop : false,
52273     
52274     /**
52275     * @cfg {Boolean} enableColumnMove True to enable drag and drop reorder of columns. Default is true.
52276     */
52277     enableColumnMove : true,
52278     
52279     /**
52280     * @cfg {Boolean} enableColumnHide True to enable hiding of columns with the header context menu. Default is true.
52281     */
52282     enableColumnHide : true,
52283     
52284     /**
52285     * @cfg {Boolean} enableRowHeightSync True to manually sync row heights across locked and not locked rows. Default is false.
52286     */
52287     enableRowHeightSync : false,
52288     
52289     /**
52290     * @cfg {Boolean} stripeRows True to stripe the rows.  Default is true.
52291     */
52292     stripeRows : true,
52293     
52294     /**
52295     * @cfg {Boolean} autoHeight True to fit the height of the grid container to the height of the data. Default is false.
52296     */
52297     autoHeight : false,
52298
52299     /**
52300      * @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.
52301      */
52302     autoExpandColumn : false,
52303
52304     /**
52305     * @cfg {Number} autoExpandMin The minimum width the autoExpandColumn can have (if enabled).
52306     * Default is 50.
52307     */
52308     autoExpandMin : 50,
52309
52310     /**
52311     * @cfg {Number} autoExpandMax The maximum width the autoExpandColumn can have (if enabled). Default is 1000.
52312     */
52313     autoExpandMax : 1000,
52314
52315     /**
52316     * @cfg {Object} view The {@link Roo.grid.GridView} used by the grid. This can be set before a call to render().
52317     */
52318     view : null,
52319
52320     /**
52321     * @cfg {Object} loadMask An {@link Roo.LoadMask} config or true to mask the grid while loading. Default is false.
52322     */
52323     loadMask : false,
52324     /**
52325     * @cfg {Roo.dd.DropTarget} dropTarget An {@link Roo.dd.DropTarget} config
52326     */
52327     dropTarget: false,
52328     
52329    
52330     
52331     // private
52332     rendered : false,
52333
52334     /**
52335     * @cfg {Boolean} autoWidth True to set the grid's width to the default total width of the grid's columns instead
52336     * of a fixed width. Default is false.
52337     */
52338     /**
52339     * @cfg {Number} maxHeight Sets the maximum height of the grid - ignored if autoHeight is not on.
52340     */
52341     /**
52342      * Called once after all setup has been completed and the grid is ready to be rendered.
52343      * @return {Roo.grid.Grid} this
52344      */
52345     render : function()
52346     {
52347         var c = this.container;
52348         // try to detect autoHeight/width mode
52349         if((!c.dom.offsetHeight || c.dom.offsetHeight < 20) || c.getStyle("height") == "auto"){
52350             this.autoHeight = true;
52351         }
52352         var view = this.getView();
52353         view.init(this);
52354
52355         c.on("click", this.onClick, this);
52356         c.on("dblclick", this.onDblClick, this);
52357         c.on("contextmenu", this.onContextMenu, this);
52358         c.on("keydown", this.onKeyDown, this);
52359         if (Roo.isTouch) {
52360             c.on("touchstart", this.onTouchStart, this);
52361         }
52362
52363         this.relayEvents(c, ["mousedown","mouseup","mouseover","mouseout","keypress"]);
52364
52365         this.getSelectionModel().init(this);
52366
52367         view.render();
52368
52369         if(this.loadMask){
52370             this.loadMask = new Roo.LoadMask(this.container,
52371                     Roo.apply({store:this.dataSource}, this.loadMask));
52372         }
52373         
52374         
52375         if (this.toolbar && this.toolbar.xtype) {
52376             this.toolbar.container = this.getView().getHeaderPanel(true);
52377             this.toolbar = new Roo.Toolbar(this.toolbar);
52378         }
52379         if (this.footer && this.footer.xtype) {
52380             this.footer.dataSource = this.getDataSource();
52381             this.footer.container = this.getView().getFooterPanel(true);
52382             this.footer = Roo.factory(this.footer, Roo);
52383         }
52384         if (this.dropTarget && this.dropTarget.xtype) {
52385             delete this.dropTarget.xtype;
52386             this.dropTarget =  new Roo.dd.DropTarget(this.getView().mainBody, this.dropTarget);
52387         }
52388         
52389         
52390         this.rendered = true;
52391         this.fireEvent('render', this);
52392         return this;
52393     },
52394
52395         /**
52396          * Reconfigures the grid to use a different Store and Column Model.
52397          * The View will be bound to the new objects and refreshed.
52398          * @param {Roo.data.Store} dataSource The new {@link Roo.data.Store} object
52399          * @param {Roo.grid.ColumnModel} The new {@link Roo.grid.ColumnModel} object
52400          */
52401     reconfigure : function(dataSource, colModel){
52402         if(this.loadMask){
52403             this.loadMask.destroy();
52404             this.loadMask = new Roo.LoadMask(this.container,
52405                     Roo.apply({store:dataSource}, this.loadMask));
52406         }
52407         this.view.bind(dataSource, colModel);
52408         this.dataSource = dataSource;
52409         this.colModel = colModel;
52410         this.view.refresh(true);
52411     },
52412
52413     // private
52414     onKeyDown : function(e){
52415         this.fireEvent("keydown", e);
52416     },
52417
52418     /**
52419      * Destroy this grid.
52420      * @param {Boolean} removeEl True to remove the element
52421      */
52422     destroy : function(removeEl, keepListeners){
52423         if(this.loadMask){
52424             this.loadMask.destroy();
52425         }
52426         var c = this.container;
52427         c.removeAllListeners();
52428         this.view.destroy();
52429         this.colModel.purgeListeners();
52430         if(!keepListeners){
52431             this.purgeListeners();
52432         }
52433         c.update("");
52434         if(removeEl === true){
52435             c.remove();
52436         }
52437     },
52438
52439     // private
52440     processEvent : function(name, e){
52441         // does this fire select???
52442         Roo.log('grid:processEvent '  + name);
52443         
52444         if (name != 'touchstart' ) {
52445             this.fireEvent(name, e);    
52446         }
52447         
52448         var t = e.getTarget();
52449         var v = this.view;
52450         var header = v.findHeaderIndex(t);
52451         if(header !== false){
52452             var ename = name == 'touchstart' ? 'click' : name;
52453              
52454             this.fireEvent("header" + ename, this, header, e);
52455         }else{
52456             var row = v.findRowIndex(t);
52457             var cell = v.findCellIndex(t);
52458             if (name == 'touchstart') {
52459                 // first touch is always a click.
52460                 // hopefull this happens after selection is updated.?
52461                 name = false;
52462                 
52463                 if (typeof(this.selModel.getSelectedCell) != 'undefined') {
52464                     var cs = this.selModel.getSelectedCell();
52465                     if (row == cs[0] && cell == cs[1]){
52466                         name = 'dblclick';
52467                     }
52468                 }
52469                 if (typeof(this.selModel.getSelections) != 'undefined') {
52470                     var cs = this.selModel.getSelections();
52471                     var ds = this.dataSource;
52472                     if (cs.length == 1 && ds.getAt(row) == cs[0]){
52473                         name = 'dblclick';
52474                     }
52475                 }
52476                 if (!name) {
52477                     return;
52478                 }
52479             }
52480             
52481             
52482             if(row !== false){
52483                 this.fireEvent("row" + name, this, row, e);
52484                 if(cell !== false){
52485                     this.fireEvent("cell" + name, this, row, cell, e);
52486                 }
52487             }
52488         }
52489     },
52490
52491     // private
52492     onClick : function(e){
52493         this.processEvent("click", e);
52494     },
52495    // private
52496     onTouchStart : function(e){
52497         this.processEvent("touchstart", e);
52498     },
52499
52500     // private
52501     onContextMenu : function(e, t){
52502         this.processEvent("contextmenu", e);
52503     },
52504
52505     // private
52506     onDblClick : function(e){
52507         this.processEvent("dblclick", e);
52508     },
52509
52510     // private
52511     walkCells : function(row, col, step, fn, scope){
52512         var cm = this.colModel, clen = cm.getColumnCount();
52513         var ds = this.dataSource, rlen = ds.getCount(), first = true;
52514         if(step < 0){
52515             if(col < 0){
52516                 row--;
52517                 first = false;
52518             }
52519             while(row >= 0){
52520                 if(!first){
52521                     col = clen-1;
52522                 }
52523                 first = false;
52524                 while(col >= 0){
52525                     if(fn.call(scope || this, row, col, cm) === true){
52526                         return [row, col];
52527                     }
52528                     col--;
52529                 }
52530                 row--;
52531             }
52532         } else {
52533             if(col >= clen){
52534                 row++;
52535                 first = false;
52536             }
52537             while(row < rlen){
52538                 if(!first){
52539                     col = 0;
52540                 }
52541                 first = false;
52542                 while(col < clen){
52543                     if(fn.call(scope || this, row, col, cm) === true){
52544                         return [row, col];
52545                     }
52546                     col++;
52547                 }
52548                 row++;
52549             }
52550         }
52551         return null;
52552     },
52553
52554     // private
52555     getSelections : function(){
52556         return this.selModel.getSelections();
52557     },
52558
52559     /**
52560      * Causes the grid to manually recalculate its dimensions. Generally this is done automatically,
52561      * but if manual update is required this method will initiate it.
52562      */
52563     autoSize : function(){
52564         if(this.rendered){
52565             this.view.layout();
52566             if(this.view.adjustForScroll){
52567                 this.view.adjustForScroll();
52568             }
52569         }
52570     },
52571
52572     /**
52573      * Returns the grid's underlying element.
52574      * @return {Element} The element
52575      */
52576     getGridEl : function(){
52577         return this.container;
52578     },
52579
52580     // private for compatibility, overridden by editor grid
52581     stopEditing : function(){},
52582
52583     /**
52584      * Returns the grid's SelectionModel.
52585      * @return {SelectionModel}
52586      */
52587     getSelectionModel : function(){
52588         if(!this.selModel){
52589             this.selModel = new Roo.grid.RowSelectionModel();
52590         }
52591         return this.selModel;
52592     },
52593
52594     /**
52595      * Returns the grid's DataSource.
52596      * @return {DataSource}
52597      */
52598     getDataSource : function(){
52599         return this.dataSource;
52600     },
52601
52602     /**
52603      * Returns the grid's ColumnModel.
52604      * @return {ColumnModel}
52605      */
52606     getColumnModel : function(){
52607         return this.colModel;
52608     },
52609
52610     /**
52611      * Returns the grid's GridView object.
52612      * @return {GridView}
52613      */
52614     getView : function(){
52615         if(!this.view){
52616             this.view = new Roo.grid.GridView(this.viewConfig);
52617         }
52618         return this.view;
52619     },
52620     /**
52621      * Called to get grid's drag proxy text, by default returns this.ddText.
52622      * @return {String}
52623      */
52624     getDragDropText : function(){
52625         var count = this.selModel.getCount();
52626         return String.format(this.ddText, count, count == 1 ? '' : 's');
52627     }
52628 });
52629 /**
52630  * Configures the text is the drag proxy (defaults to "%0 selected row(s)").
52631  * %0 is replaced with the number of selected rows.
52632  * @type String
52633  */
52634 Roo.grid.Grid.prototype.ddText = "{0} selected row{1}";/*
52635  * Based on:
52636  * Ext JS Library 1.1.1
52637  * Copyright(c) 2006-2007, Ext JS, LLC.
52638  *
52639  * Originally Released Under LGPL - original licence link has changed is not relivant.
52640  *
52641  * Fork - LGPL
52642  * <script type="text/javascript">
52643  */
52644  
52645 Roo.grid.AbstractGridView = function(){
52646         this.grid = null;
52647         
52648         this.events = {
52649             "beforerowremoved" : true,
52650             "beforerowsinserted" : true,
52651             "beforerefresh" : true,
52652             "rowremoved" : true,
52653             "rowsinserted" : true,
52654             "rowupdated" : true,
52655             "refresh" : true
52656         };
52657     Roo.grid.AbstractGridView.superclass.constructor.call(this);
52658 };
52659
52660 Roo.extend(Roo.grid.AbstractGridView, Roo.util.Observable, {
52661     rowClass : "x-grid-row",
52662     cellClass : "x-grid-cell",
52663     tdClass : "x-grid-td",
52664     hdClass : "x-grid-hd",
52665     splitClass : "x-grid-hd-split",
52666     
52667     init: function(grid){
52668         this.grid = grid;
52669                 var cid = this.grid.getGridEl().id;
52670         this.colSelector = "#" + cid + " ." + this.cellClass + "-";
52671         this.tdSelector = "#" + cid + " ." + this.tdClass + "-";
52672         this.hdSelector = "#" + cid + " ." + this.hdClass + "-";
52673         this.splitSelector = "#" + cid + " ." + this.splitClass + "-";
52674         },
52675         
52676     getColumnRenderers : function(){
52677         var renderers = [];
52678         var cm = this.grid.colModel;
52679         var colCount = cm.getColumnCount();
52680         for(var i = 0; i < colCount; i++){
52681             renderers[i] = cm.getRenderer(i);
52682         }
52683         return renderers;
52684     },
52685     
52686     getColumnIds : function(){
52687         var ids = [];
52688         var cm = this.grid.colModel;
52689         var colCount = cm.getColumnCount();
52690         for(var i = 0; i < colCount; i++){
52691             ids[i] = cm.getColumnId(i);
52692         }
52693         return ids;
52694     },
52695     
52696     getDataIndexes : function(){
52697         if(!this.indexMap){
52698             this.indexMap = this.buildIndexMap();
52699         }
52700         return this.indexMap.colToData;
52701     },
52702     
52703     getColumnIndexByDataIndex : function(dataIndex){
52704         if(!this.indexMap){
52705             this.indexMap = this.buildIndexMap();
52706         }
52707         return this.indexMap.dataToCol[dataIndex];
52708     },
52709     
52710     /**
52711      * Set a css style for a column dynamically. 
52712      * @param {Number} colIndex The index of the column
52713      * @param {String} name The css property name
52714      * @param {String} value The css value
52715      */
52716     setCSSStyle : function(colIndex, name, value){
52717         var selector = "#" + this.grid.id + " .x-grid-col-" + colIndex;
52718         Roo.util.CSS.updateRule(selector, name, value);
52719     },
52720     
52721     generateRules : function(cm){
52722         var ruleBuf = [], rulesId = this.grid.id + '-cssrules';
52723         Roo.util.CSS.removeStyleSheet(rulesId);
52724         for(var i = 0, len = cm.getColumnCount(); i < len; i++){
52725             var cid = cm.getColumnId(i);
52726             ruleBuf.push(this.colSelector, cid, " {\n", cm.config[i].css, "}\n",
52727                          this.tdSelector, cid, " {\n}\n",
52728                          this.hdSelector, cid, " {\n}\n",
52729                          this.splitSelector, cid, " {\n}\n");
52730         }
52731         return Roo.util.CSS.createStyleSheet(ruleBuf.join(""), rulesId);
52732     }
52733 });/*
52734  * Based on:
52735  * Ext JS Library 1.1.1
52736  * Copyright(c) 2006-2007, Ext JS, LLC.
52737  *
52738  * Originally Released Under LGPL - original licence link has changed is not relivant.
52739  *
52740  * Fork - LGPL
52741  * <script type="text/javascript">
52742  */
52743
52744 // private
52745 // This is a support class used internally by the Grid components
52746 Roo.grid.HeaderDragZone = function(grid, hd, hd2){
52747     this.grid = grid;
52748     this.view = grid.getView();
52749     this.ddGroup = "gridHeader" + this.grid.getGridEl().id;
52750     Roo.grid.HeaderDragZone.superclass.constructor.call(this, hd);
52751     if(hd2){
52752         this.setHandleElId(Roo.id(hd));
52753         this.setOuterHandleElId(Roo.id(hd2));
52754     }
52755     this.scroll = false;
52756 };
52757 Roo.extend(Roo.grid.HeaderDragZone, Roo.dd.DragZone, {
52758     maxDragWidth: 120,
52759     getDragData : function(e){
52760         var t = Roo.lib.Event.getTarget(e);
52761         var h = this.view.findHeaderCell(t);
52762         if(h){
52763             return {ddel: h.firstChild, header:h};
52764         }
52765         return false;
52766     },
52767
52768     onInitDrag : function(e){
52769         this.view.headersDisabled = true;
52770         var clone = this.dragData.ddel.cloneNode(true);
52771         clone.id = Roo.id();
52772         clone.style.width = Math.min(this.dragData.header.offsetWidth,this.maxDragWidth) + "px";
52773         this.proxy.update(clone);
52774         return true;
52775     },
52776
52777     afterValidDrop : function(){
52778         var v = this.view;
52779         setTimeout(function(){
52780             v.headersDisabled = false;
52781         }, 50);
52782     },
52783
52784     afterInvalidDrop : function(){
52785         var v = this.view;
52786         setTimeout(function(){
52787             v.headersDisabled = false;
52788         }, 50);
52789     }
52790 });
52791 /*
52792  * Based on:
52793  * Ext JS Library 1.1.1
52794  * Copyright(c) 2006-2007, Ext JS, LLC.
52795  *
52796  * Originally Released Under LGPL - original licence link has changed is not relivant.
52797  *
52798  * Fork - LGPL
52799  * <script type="text/javascript">
52800  */
52801 // private
52802 // This is a support class used internally by the Grid components
52803 Roo.grid.HeaderDropZone = function(grid, hd, hd2){
52804     this.grid = grid;
52805     this.view = grid.getView();
52806     // split the proxies so they don't interfere with mouse events
52807     this.proxyTop = Roo.DomHelper.append(document.body, {
52808         cls:"col-move-top", html:"&#160;"
52809     }, true);
52810     this.proxyBottom = Roo.DomHelper.append(document.body, {
52811         cls:"col-move-bottom", html:"&#160;"
52812     }, true);
52813     this.proxyTop.hide = this.proxyBottom.hide = function(){
52814         this.setLeftTop(-100,-100);
52815         this.setStyle("visibility", "hidden");
52816     };
52817     this.ddGroup = "gridHeader" + this.grid.getGridEl().id;
52818     // temporarily disabled
52819     //Roo.dd.ScrollManager.register(this.view.scroller.dom);
52820     Roo.grid.HeaderDropZone.superclass.constructor.call(this, grid.getGridEl().dom);
52821 };
52822 Roo.extend(Roo.grid.HeaderDropZone, Roo.dd.DropZone, {
52823     proxyOffsets : [-4, -9],
52824     fly: Roo.Element.fly,
52825
52826     getTargetFromEvent : function(e){
52827         var t = Roo.lib.Event.getTarget(e);
52828         var cindex = this.view.findCellIndex(t);
52829         if(cindex !== false){
52830             return this.view.getHeaderCell(cindex);
52831         }
52832         return null;
52833     },
52834
52835     nextVisible : function(h){
52836         var v = this.view, cm = this.grid.colModel;
52837         h = h.nextSibling;
52838         while(h){
52839             if(!cm.isHidden(v.getCellIndex(h))){
52840                 return h;
52841             }
52842             h = h.nextSibling;
52843         }
52844         return null;
52845     },
52846
52847     prevVisible : function(h){
52848         var v = this.view, cm = this.grid.colModel;
52849         h = h.prevSibling;
52850         while(h){
52851             if(!cm.isHidden(v.getCellIndex(h))){
52852                 return h;
52853             }
52854             h = h.prevSibling;
52855         }
52856         return null;
52857     },
52858
52859     positionIndicator : function(h, n, e){
52860         var x = Roo.lib.Event.getPageX(e);
52861         var r = Roo.lib.Dom.getRegion(n.firstChild);
52862         var px, pt, py = r.top + this.proxyOffsets[1];
52863         if((r.right - x) <= (r.right-r.left)/2){
52864             px = r.right+this.view.borderWidth;
52865             pt = "after";
52866         }else{
52867             px = r.left;
52868             pt = "before";
52869         }
52870         var oldIndex = this.view.getCellIndex(h);
52871         var newIndex = this.view.getCellIndex(n);
52872
52873         if(this.grid.colModel.isFixed(newIndex)){
52874             return false;
52875         }
52876
52877         var locked = this.grid.colModel.isLocked(newIndex);
52878
52879         if(pt == "after"){
52880             newIndex++;
52881         }
52882         if(oldIndex < newIndex){
52883             newIndex--;
52884         }
52885         if(oldIndex == newIndex && (locked == this.grid.colModel.isLocked(oldIndex))){
52886             return false;
52887         }
52888         px +=  this.proxyOffsets[0];
52889         this.proxyTop.setLeftTop(px, py);
52890         this.proxyTop.show();
52891         if(!this.bottomOffset){
52892             this.bottomOffset = this.view.mainHd.getHeight();
52893         }
52894         this.proxyBottom.setLeftTop(px, py+this.proxyTop.dom.offsetHeight+this.bottomOffset);
52895         this.proxyBottom.show();
52896         return pt;
52897     },
52898
52899     onNodeEnter : function(n, dd, e, data){
52900         if(data.header != n){
52901             this.positionIndicator(data.header, n, e);
52902         }
52903     },
52904
52905     onNodeOver : function(n, dd, e, data){
52906         var result = false;
52907         if(data.header != n){
52908             result = this.positionIndicator(data.header, n, e);
52909         }
52910         if(!result){
52911             this.proxyTop.hide();
52912             this.proxyBottom.hide();
52913         }
52914         return result ? this.dropAllowed : this.dropNotAllowed;
52915     },
52916
52917     onNodeOut : function(n, dd, e, data){
52918         this.proxyTop.hide();
52919         this.proxyBottom.hide();
52920     },
52921
52922     onNodeDrop : function(n, dd, e, data){
52923         var h = data.header;
52924         if(h != n){
52925             var cm = this.grid.colModel;
52926             var x = Roo.lib.Event.getPageX(e);
52927             var r = Roo.lib.Dom.getRegion(n.firstChild);
52928             var pt = (r.right - x) <= ((r.right-r.left)/2) ? "after" : "before";
52929             var oldIndex = this.view.getCellIndex(h);
52930             var newIndex = this.view.getCellIndex(n);
52931             var locked = cm.isLocked(newIndex);
52932             if(pt == "after"){
52933                 newIndex++;
52934             }
52935             if(oldIndex < newIndex){
52936                 newIndex--;
52937             }
52938             if(oldIndex == newIndex && (locked == cm.isLocked(oldIndex))){
52939                 return false;
52940             }
52941             cm.setLocked(oldIndex, locked, true);
52942             cm.moveColumn(oldIndex, newIndex);
52943             this.grid.fireEvent("columnmove", oldIndex, newIndex);
52944             return true;
52945         }
52946         return false;
52947     }
52948 });
52949 /*
52950  * Based on:
52951  * Ext JS Library 1.1.1
52952  * Copyright(c) 2006-2007, Ext JS, LLC.
52953  *
52954  * Originally Released Under LGPL - original licence link has changed is not relivant.
52955  *
52956  * Fork - LGPL
52957  * <script type="text/javascript">
52958  */
52959   
52960 /**
52961  * @class Roo.grid.GridView
52962  * @extends Roo.util.Observable
52963  *
52964  * @constructor
52965  * @param {Object} config
52966  */
52967 Roo.grid.GridView = function(config){
52968     Roo.grid.GridView.superclass.constructor.call(this);
52969     this.el = null;
52970
52971     Roo.apply(this, config);
52972 };
52973
52974 Roo.extend(Roo.grid.GridView, Roo.grid.AbstractGridView, {
52975
52976     unselectable :  'unselectable="on"',
52977     unselectableCls :  'x-unselectable',
52978     
52979     
52980     rowClass : "x-grid-row",
52981
52982     cellClass : "x-grid-col",
52983
52984     tdClass : "x-grid-td",
52985
52986     hdClass : "x-grid-hd",
52987
52988     splitClass : "x-grid-split",
52989
52990     sortClasses : ["sort-asc", "sort-desc"],
52991
52992     enableMoveAnim : false,
52993
52994     hlColor: "C3DAF9",
52995
52996     dh : Roo.DomHelper,
52997
52998     fly : Roo.Element.fly,
52999
53000     css : Roo.util.CSS,
53001
53002     borderWidth: 1,
53003
53004     splitOffset: 3,
53005
53006     scrollIncrement : 22,
53007
53008     cellRE: /(?:.*?)x-grid-(?:hd|cell|csplit)-(?:[\d]+)-([\d]+)(?:.*?)/,
53009
53010     findRE: /\s?(?:x-grid-hd|x-grid-col|x-grid-csplit)\s/,
53011
53012     bind : function(ds, cm){
53013         if(this.ds){
53014             this.ds.un("load", this.onLoad, this);
53015             this.ds.un("datachanged", this.onDataChange, this);
53016             this.ds.un("add", this.onAdd, this);
53017             this.ds.un("remove", this.onRemove, this);
53018             this.ds.un("update", this.onUpdate, this);
53019             this.ds.un("clear", this.onClear, this);
53020         }
53021         if(ds){
53022             ds.on("load", this.onLoad, this);
53023             ds.on("datachanged", this.onDataChange, this);
53024             ds.on("add", this.onAdd, this);
53025             ds.on("remove", this.onRemove, this);
53026             ds.on("update", this.onUpdate, this);
53027             ds.on("clear", this.onClear, this);
53028         }
53029         this.ds = ds;
53030
53031         if(this.cm){
53032             this.cm.un("widthchange", this.onColWidthChange, this);
53033             this.cm.un("headerchange", this.onHeaderChange, this);
53034             this.cm.un("hiddenchange", this.onHiddenChange, this);
53035             this.cm.un("columnmoved", this.onColumnMove, this);
53036             this.cm.un("columnlockchange", this.onColumnLock, this);
53037         }
53038         if(cm){
53039             this.generateRules(cm);
53040             cm.on("widthchange", this.onColWidthChange, this);
53041             cm.on("headerchange", this.onHeaderChange, this);
53042             cm.on("hiddenchange", this.onHiddenChange, this);
53043             cm.on("columnmoved", this.onColumnMove, this);
53044             cm.on("columnlockchange", this.onColumnLock, this);
53045         }
53046         this.cm = cm;
53047     },
53048
53049     init: function(grid){
53050         Roo.grid.GridView.superclass.init.call(this, grid);
53051
53052         this.bind(grid.dataSource, grid.colModel);
53053
53054         grid.on("headerclick", this.handleHeaderClick, this);
53055
53056         if(grid.trackMouseOver){
53057             grid.on("mouseover", this.onRowOver, this);
53058             grid.on("mouseout", this.onRowOut, this);
53059         }
53060         grid.cancelTextSelection = function(){};
53061         this.gridId = grid.id;
53062
53063         var tpls = this.templates || {};
53064
53065         if(!tpls.master){
53066             tpls.master = new Roo.Template(
53067                '<div class="x-grid" hidefocus="true">',
53068                 '<a href="#" class="x-grid-focus" tabIndex="-1"></a>',
53069                   '<div class="x-grid-topbar"></div>',
53070                   '<div class="x-grid-scroller"><div></div></div>',
53071                   '<div class="x-grid-locked">',
53072                       '<div class="x-grid-header">{lockedHeader}</div>',
53073                       '<div class="x-grid-body">{lockedBody}</div>',
53074                   "</div>",
53075                   '<div class="x-grid-viewport">',
53076                       '<div class="x-grid-header">{header}</div>',
53077                       '<div class="x-grid-body">{body}</div>',
53078                   "</div>",
53079                   '<div class="x-grid-bottombar"></div>',
53080                  
53081                   '<div class="x-grid-resize-proxy">&#160;</div>',
53082                "</div>"
53083             );
53084             tpls.master.disableformats = true;
53085         }
53086
53087         if(!tpls.header){
53088             tpls.header = new Roo.Template(
53089                '<table border="0" cellspacing="0" cellpadding="0">',
53090                '<tbody><tr class="x-grid-hd-row">{cells}</tr></tbody>',
53091                "</table>{splits}"
53092             );
53093             tpls.header.disableformats = true;
53094         }
53095         tpls.header.compile();
53096
53097         if(!tpls.hcell){
53098             tpls.hcell = new Roo.Template(
53099                 '<td class="x-grid-hd x-grid-td-{id} {cellId}"><div title="{title}" class="x-grid-hd-inner x-grid-hd-{id}">',
53100                 '<div class="x-grid-hd-text ' + this.unselectableCls +  '" ' + this.unselectable +'>{value}<img class="x-grid-sort-icon" src="', Roo.BLANK_IMAGE_URL, '" /></div>',
53101                 "</div></td>"
53102              );
53103              tpls.hcell.disableFormats = true;
53104         }
53105         tpls.hcell.compile();
53106
53107         if(!tpls.hsplit){
53108             tpls.hsplit = new Roo.Template('<div class="x-grid-split {splitId} x-grid-split-{id}" style="{style} ' +
53109                                             this.unselectableCls +  '" ' + this.unselectable +'>&#160;</div>');
53110             tpls.hsplit.disableFormats = true;
53111         }
53112         tpls.hsplit.compile();
53113
53114         if(!tpls.body){
53115             tpls.body = new Roo.Template(
53116                '<table border="0" cellspacing="0" cellpadding="0">',
53117                "<tbody>{rows}</tbody>",
53118                "</table>"
53119             );
53120             tpls.body.disableFormats = true;
53121         }
53122         tpls.body.compile();
53123
53124         if(!tpls.row){
53125             tpls.row = new Roo.Template('<tr class="x-grid-row {alt}">{cells}</tr>');
53126             tpls.row.disableFormats = true;
53127         }
53128         tpls.row.compile();
53129
53130         if(!tpls.cell){
53131             tpls.cell = new Roo.Template(
53132                 '<td class="x-grid-col x-grid-td-{id} {cellId} {css}" tabIndex="0">',
53133                 '<div class="x-grid-col-{id} x-grid-cell-inner"><div class="x-grid-cell-text ' +
53134                     this.unselectableCls +  '" ' + this.unselectable +'" {attr}>{value}</div></div>',
53135                 "</td>"
53136             );
53137             tpls.cell.disableFormats = true;
53138         }
53139         tpls.cell.compile();
53140
53141         this.templates = tpls;
53142     },
53143
53144     // remap these for backwards compat
53145     onColWidthChange : function(){
53146         this.updateColumns.apply(this, arguments);
53147     },
53148     onHeaderChange : function(){
53149         this.updateHeaders.apply(this, arguments);
53150     }, 
53151     onHiddenChange : function(){
53152         this.handleHiddenChange.apply(this, arguments);
53153     },
53154     onColumnMove : function(){
53155         this.handleColumnMove.apply(this, arguments);
53156     },
53157     onColumnLock : function(){
53158         this.handleLockChange.apply(this, arguments);
53159     },
53160
53161     onDataChange : function(){
53162         this.refresh();
53163         this.updateHeaderSortState();
53164     },
53165
53166     onClear : function(){
53167         this.refresh();
53168     },
53169
53170     onUpdate : function(ds, record){
53171         this.refreshRow(record);
53172     },
53173
53174     refreshRow : function(record){
53175         var ds = this.ds, index;
53176         if(typeof record == 'number'){
53177             index = record;
53178             record = ds.getAt(index);
53179         }else{
53180             index = ds.indexOf(record);
53181         }
53182         this.insertRows(ds, index, index, true);
53183         this.onRemove(ds, record, index+1, true);
53184         this.syncRowHeights(index, index);
53185         this.layout();
53186         this.fireEvent("rowupdated", this, index, record);
53187     },
53188
53189     onAdd : function(ds, records, index){
53190         this.insertRows(ds, index, index + (records.length-1));
53191     },
53192
53193     onRemove : function(ds, record, index, isUpdate){
53194         if(isUpdate !== true){
53195             this.fireEvent("beforerowremoved", this, index, record);
53196         }
53197         var bt = this.getBodyTable(), lt = this.getLockedTable();
53198         if(bt.rows[index]){
53199             bt.firstChild.removeChild(bt.rows[index]);
53200         }
53201         if(lt.rows[index]){
53202             lt.firstChild.removeChild(lt.rows[index]);
53203         }
53204         if(isUpdate !== true){
53205             this.stripeRows(index);
53206             this.syncRowHeights(index, index);
53207             this.layout();
53208             this.fireEvent("rowremoved", this, index, record);
53209         }
53210     },
53211
53212     onLoad : function(){
53213         this.scrollToTop();
53214     },
53215
53216     /**
53217      * Scrolls the grid to the top
53218      */
53219     scrollToTop : function(){
53220         if(this.scroller){
53221             this.scroller.dom.scrollTop = 0;
53222             this.syncScroll();
53223         }
53224     },
53225
53226     /**
53227      * Gets a panel in the header of the grid that can be used for toolbars etc.
53228      * After modifying the contents of this panel a call to grid.autoSize() may be
53229      * required to register any changes in size.
53230      * @param {Boolean} doShow By default the header is hidden. Pass true to show the panel
53231      * @return Roo.Element
53232      */
53233     getHeaderPanel : function(doShow){
53234         if(doShow){
53235             this.headerPanel.show();
53236         }
53237         return this.headerPanel;
53238     },
53239
53240     /**
53241      * Gets a panel in the footer of the grid that can be used for toolbars etc.
53242      * After modifying the contents of this panel a call to grid.autoSize() may be
53243      * required to register any changes in size.
53244      * @param {Boolean} doShow By default the footer is hidden. Pass true to show the panel
53245      * @return Roo.Element
53246      */
53247     getFooterPanel : function(doShow){
53248         if(doShow){
53249             this.footerPanel.show();
53250         }
53251         return this.footerPanel;
53252     },
53253
53254     initElements : function(){
53255         var E = Roo.Element;
53256         var el = this.grid.getGridEl().dom.firstChild;
53257         var cs = el.childNodes;
53258
53259         this.el = new E(el);
53260         
53261          this.focusEl = new E(el.firstChild);
53262         this.focusEl.swallowEvent("click", true);
53263         
53264         this.headerPanel = new E(cs[1]);
53265         this.headerPanel.enableDisplayMode("block");
53266
53267         this.scroller = new E(cs[2]);
53268         this.scrollSizer = new E(this.scroller.dom.firstChild);
53269
53270         this.lockedWrap = new E(cs[3]);
53271         this.lockedHd = new E(this.lockedWrap.dom.firstChild);
53272         this.lockedBody = new E(this.lockedWrap.dom.childNodes[1]);
53273
53274         this.mainWrap = new E(cs[4]);
53275         this.mainHd = new E(this.mainWrap.dom.firstChild);
53276         this.mainBody = new E(this.mainWrap.dom.childNodes[1]);
53277
53278         this.footerPanel = new E(cs[5]);
53279         this.footerPanel.enableDisplayMode("block");
53280
53281         this.resizeProxy = new E(cs[6]);
53282
53283         this.headerSelector = String.format(
53284            '#{0} td.x-grid-hd, #{1} td.x-grid-hd',
53285            this.lockedHd.id, this.mainHd.id
53286         );
53287
53288         this.splitterSelector = String.format(
53289            '#{0} div.x-grid-split, #{1} div.x-grid-split',
53290            this.idToCssName(this.lockedHd.id), this.idToCssName(this.mainHd.id)
53291         );
53292     },
53293     idToCssName : function(s)
53294     {
53295         return s.replace(/[^a-z0-9]+/ig, '-');
53296     },
53297
53298     getHeaderCell : function(index){
53299         return Roo.DomQuery.select(this.headerSelector)[index];
53300     },
53301
53302     getHeaderCellMeasure : function(index){
53303         return this.getHeaderCell(index).firstChild;
53304     },
53305
53306     getHeaderCellText : function(index){
53307         return this.getHeaderCell(index).firstChild.firstChild;
53308     },
53309
53310     getLockedTable : function(){
53311         return this.lockedBody.dom.firstChild;
53312     },
53313
53314     getBodyTable : function(){
53315         return this.mainBody.dom.firstChild;
53316     },
53317
53318     getLockedRow : function(index){
53319         return this.getLockedTable().rows[index];
53320     },
53321
53322     getRow : function(index){
53323         return this.getBodyTable().rows[index];
53324     },
53325
53326     getRowComposite : function(index){
53327         if(!this.rowEl){
53328             this.rowEl = new Roo.CompositeElementLite();
53329         }
53330         var els = [], lrow, mrow;
53331         if(lrow = this.getLockedRow(index)){
53332             els.push(lrow);
53333         }
53334         if(mrow = this.getRow(index)){
53335             els.push(mrow);
53336         }
53337         this.rowEl.elements = els;
53338         return this.rowEl;
53339     },
53340     /**
53341      * Gets the 'td' of the cell
53342      * 
53343      * @param {Integer} rowIndex row to select
53344      * @param {Integer} colIndex column to select
53345      * 
53346      * @return {Object} 
53347      */
53348     getCell : function(rowIndex, colIndex){
53349         var locked = this.cm.getLockedCount();
53350         var source;
53351         if(colIndex < locked){
53352             source = this.lockedBody.dom.firstChild;
53353         }else{
53354             source = this.mainBody.dom.firstChild;
53355             colIndex -= locked;
53356         }
53357         return source.rows[rowIndex].childNodes[colIndex];
53358     },
53359
53360     getCellText : function(rowIndex, colIndex){
53361         return this.getCell(rowIndex, colIndex).firstChild.firstChild;
53362     },
53363
53364     getCellBox : function(cell){
53365         var b = this.fly(cell).getBox();
53366         if(Roo.isOpera){ // opera fails to report the Y
53367             b.y = cell.offsetTop + this.mainBody.getY();
53368         }
53369         return b;
53370     },
53371
53372     getCellIndex : function(cell){
53373         var id = String(cell.className).match(this.cellRE);
53374         if(id){
53375             return parseInt(id[1], 10);
53376         }
53377         return 0;
53378     },
53379
53380     findHeaderIndex : function(n){
53381         var r = Roo.fly(n).findParent("td." + this.hdClass, 6);
53382         return r ? this.getCellIndex(r) : false;
53383     },
53384
53385     findHeaderCell : function(n){
53386         var r = Roo.fly(n).findParent("td." + this.hdClass, 6);
53387         return r ? r : false;
53388     },
53389
53390     findRowIndex : function(n){
53391         if(!n){
53392             return false;
53393         }
53394         var r = Roo.fly(n).findParent("tr." + this.rowClass, 6);
53395         return r ? r.rowIndex : false;
53396     },
53397
53398     findCellIndex : function(node){
53399         var stop = this.el.dom;
53400         while(node && node != stop){
53401             if(this.findRE.test(node.className)){
53402                 return this.getCellIndex(node);
53403             }
53404             node = node.parentNode;
53405         }
53406         return false;
53407     },
53408
53409     getColumnId : function(index){
53410         return this.cm.getColumnId(index);
53411     },
53412
53413     getSplitters : function()
53414     {
53415         if(this.splitterSelector){
53416            return Roo.DomQuery.select(this.splitterSelector);
53417         }else{
53418             return null;
53419       }
53420     },
53421
53422     getSplitter : function(index){
53423         return this.getSplitters()[index];
53424     },
53425
53426     onRowOver : function(e, t){
53427         var row;
53428         if((row = this.findRowIndex(t)) !== false){
53429             this.getRowComposite(row).addClass("x-grid-row-over");
53430         }
53431     },
53432
53433     onRowOut : function(e, t){
53434         var row;
53435         if((row = this.findRowIndex(t)) !== false && row !== this.findRowIndex(e.getRelatedTarget())){
53436             this.getRowComposite(row).removeClass("x-grid-row-over");
53437         }
53438     },
53439
53440     renderHeaders : function(){
53441         var cm = this.cm;
53442         var ct = this.templates.hcell, ht = this.templates.header, st = this.templates.hsplit;
53443         var cb = [], lb = [], sb = [], lsb = [], p = {};
53444         for(var i = 0, len = cm.getColumnCount(); i < len; i++){
53445             p.cellId = "x-grid-hd-0-" + i;
53446             p.splitId = "x-grid-csplit-0-" + i;
53447             p.id = cm.getColumnId(i);
53448             p.title = cm.getColumnTooltip(i) || "";
53449             p.value = cm.getColumnHeader(i) || "";
53450             p.style = (this.grid.enableColumnResize === false || !cm.isResizable(i) || cm.isFixed(i)) ? 'cursor:default' : '';
53451             if(!cm.isLocked(i)){
53452                 cb[cb.length] = ct.apply(p);
53453                 sb[sb.length] = st.apply(p);
53454             }else{
53455                 lb[lb.length] = ct.apply(p);
53456                 lsb[lsb.length] = st.apply(p);
53457             }
53458         }
53459         return [ht.apply({cells: lb.join(""), splits:lsb.join("")}),
53460                 ht.apply({cells: cb.join(""), splits:sb.join("")})];
53461     },
53462
53463     updateHeaders : function(){
53464         var html = this.renderHeaders();
53465         this.lockedHd.update(html[0]);
53466         this.mainHd.update(html[1]);
53467     },
53468
53469     /**
53470      * Focuses the specified row.
53471      * @param {Number} row The row index
53472      */
53473     focusRow : function(row)
53474     {
53475         //Roo.log('GridView.focusRow');
53476         var x = this.scroller.dom.scrollLeft;
53477         this.focusCell(row, 0, false);
53478         this.scroller.dom.scrollLeft = x;
53479     },
53480
53481     /**
53482      * Focuses the specified cell.
53483      * @param {Number} row The row index
53484      * @param {Number} col The column index
53485      * @param {Boolean} hscroll false to disable horizontal scrolling
53486      */
53487     focusCell : function(row, col, hscroll)
53488     {
53489         //Roo.log('GridView.focusCell');
53490         var el = this.ensureVisible(row, col, hscroll);
53491         this.focusEl.alignTo(el, "tl-tl");
53492         if(Roo.isGecko){
53493             this.focusEl.focus();
53494         }else{
53495             this.focusEl.focus.defer(1, this.focusEl);
53496         }
53497     },
53498
53499     /**
53500      * Scrolls the specified cell into view
53501      * @param {Number} row The row index
53502      * @param {Number} col The column index
53503      * @param {Boolean} hscroll false to disable horizontal scrolling
53504      */
53505     ensureVisible : function(row, col, hscroll)
53506     {
53507         //Roo.log('GridView.ensureVisible,' + row + ',' + col);
53508         //return null; //disable for testing.
53509         if(typeof row != "number"){
53510             row = row.rowIndex;
53511         }
53512         if(row < 0 && row >= this.ds.getCount()){
53513             return  null;
53514         }
53515         col = (col !== undefined ? col : 0);
53516         var cm = this.grid.colModel;
53517         while(cm.isHidden(col)){
53518             col++;
53519         }
53520
53521         var el = this.getCell(row, col);
53522         if(!el){
53523             return null;
53524         }
53525         var c = this.scroller.dom;
53526
53527         var ctop = parseInt(el.offsetTop, 10);
53528         var cleft = parseInt(el.offsetLeft, 10);
53529         var cbot = ctop + el.offsetHeight;
53530         var cright = cleft + el.offsetWidth;
53531         
53532         var ch = c.clientHeight - this.mainHd.dom.offsetHeight;
53533         var stop = parseInt(c.scrollTop, 10);
53534         var sleft = parseInt(c.scrollLeft, 10);
53535         var sbot = stop + ch;
53536         var sright = sleft + c.clientWidth;
53537         /*
53538         Roo.log('GridView.ensureVisible:' +
53539                 ' ctop:' + ctop +
53540                 ' c.clientHeight:' + c.clientHeight +
53541                 ' this.mainHd.dom.offsetHeight:' + this.mainHd.dom.offsetHeight +
53542                 ' stop:' + stop +
53543                 ' cbot:' + cbot +
53544                 ' sbot:' + sbot +
53545                 ' ch:' + ch  
53546                 );
53547         */
53548         if(ctop < stop){
53549              c.scrollTop = ctop;
53550             //Roo.log("set scrolltop to ctop DISABLE?");
53551         }else if(cbot > sbot){
53552             //Roo.log("set scrolltop to cbot-ch");
53553             c.scrollTop = cbot-ch;
53554         }
53555         
53556         if(hscroll !== false){
53557             if(cleft < sleft){
53558                 c.scrollLeft = cleft;
53559             }else if(cright > sright){
53560                 c.scrollLeft = cright-c.clientWidth;
53561             }
53562         }
53563          
53564         return el;
53565     },
53566
53567     updateColumns : function(){
53568         this.grid.stopEditing();
53569         var cm = this.grid.colModel, colIds = this.getColumnIds();
53570         //var totalWidth = cm.getTotalWidth();
53571         var pos = 0;
53572         for(var i = 0, len = cm.getColumnCount(); i < len; i++){
53573             //if(cm.isHidden(i)) continue;
53574             var w = cm.getColumnWidth(i);
53575             this.css.updateRule(this.colSelector+this.idToCssName(colIds[i]), "width", (w - this.borderWidth) + "px");
53576             this.css.updateRule(this.hdSelector+this.idToCssName(colIds[i]), "width", (w - this.borderWidth) + "px");
53577         }
53578         this.updateSplitters();
53579     },
53580
53581     generateRules : function(cm){
53582         var ruleBuf = [], rulesId = this.idToCssName(this.grid.id)+ '-cssrules';
53583         Roo.util.CSS.removeStyleSheet(rulesId);
53584         for(var i = 0, len = cm.getColumnCount(); i < len; i++){
53585             var cid = cm.getColumnId(i);
53586             var align = '';
53587             if(cm.config[i].align){
53588                 align = 'text-align:'+cm.config[i].align+';';
53589             }
53590             var hidden = '';
53591             if(cm.isHidden(i)){
53592                 hidden = 'display:none;';
53593             }
53594             var width = "width:" + (cm.getColumnWidth(i) - this.borderWidth) + "px;";
53595             ruleBuf.push(
53596                     this.colSelector, cid, " {\n", cm.config[i].css, align, width, "\n}\n",
53597                     this.hdSelector, cid, " {\n", align, width, "}\n",
53598                     this.tdSelector, cid, " {\n",hidden,"\n}\n",
53599                     this.splitSelector, cid, " {\n", hidden , "\n}\n");
53600         }
53601         return Roo.util.CSS.createStyleSheet(ruleBuf.join(""), rulesId);
53602     },
53603
53604     updateSplitters : function(){
53605         var cm = this.cm, s = this.getSplitters();
53606         if(s){ // splitters not created yet
53607             var pos = 0, locked = true;
53608             for(var i = 0, len = cm.getColumnCount(); i < len; i++){
53609                 if(cm.isHidden(i)) continue;
53610                 var w = cm.getColumnWidth(i); // make sure it's a number
53611                 if(!cm.isLocked(i) && locked){
53612                     pos = 0;
53613                     locked = false;
53614                 }
53615                 pos += w;
53616                 s[i].style.left = (pos-this.splitOffset) + "px";
53617             }
53618         }
53619     },
53620
53621     handleHiddenChange : function(colModel, colIndex, hidden){
53622         if(hidden){
53623             this.hideColumn(colIndex);
53624         }else{
53625             this.unhideColumn(colIndex);
53626         }
53627     },
53628
53629     hideColumn : function(colIndex){
53630         var cid = this.getColumnId(colIndex);
53631         this.css.updateRule(this.tdSelector+this.idToCssName(cid), "display", "none");
53632         this.css.updateRule(this.splitSelector+this.idToCssName(cid), "display", "none");
53633         if(Roo.isSafari){
53634             this.updateHeaders();
53635         }
53636         this.updateSplitters();
53637         this.layout();
53638     },
53639
53640     unhideColumn : function(colIndex){
53641         var cid = this.getColumnId(colIndex);
53642         this.css.updateRule(this.tdSelector+this.idToCssName(cid), "display", "");
53643         this.css.updateRule(this.splitSelector+this.idToCssName(cid), "display", "");
53644
53645         if(Roo.isSafari){
53646             this.updateHeaders();
53647         }
53648         this.updateSplitters();
53649         this.layout();
53650     },
53651
53652     insertRows : function(dm, firstRow, lastRow, isUpdate){
53653         if(firstRow == 0 && lastRow == dm.getCount()-1){
53654             this.refresh();
53655         }else{
53656             if(!isUpdate){
53657                 this.fireEvent("beforerowsinserted", this, firstRow, lastRow);
53658             }
53659             var s = this.getScrollState();
53660             var markup = this.renderRows(firstRow, lastRow);
53661             this.bufferRows(markup[0], this.getLockedTable(), firstRow);
53662             this.bufferRows(markup[1], this.getBodyTable(), firstRow);
53663             this.restoreScroll(s);
53664             if(!isUpdate){
53665                 this.fireEvent("rowsinserted", this, firstRow, lastRow);
53666                 this.syncRowHeights(firstRow, lastRow);
53667                 this.stripeRows(firstRow);
53668                 this.layout();
53669             }
53670         }
53671     },
53672
53673     bufferRows : function(markup, target, index){
53674         var before = null, trows = target.rows, tbody = target.tBodies[0];
53675         if(index < trows.length){
53676             before = trows[index];
53677         }
53678         var b = document.createElement("div");
53679         b.innerHTML = "<table><tbody>"+markup+"</tbody></table>";
53680         var rows = b.firstChild.rows;
53681         for(var i = 0, len = rows.length; i < len; i++){
53682             if(before){
53683                 tbody.insertBefore(rows[0], before);
53684             }else{
53685                 tbody.appendChild(rows[0]);
53686             }
53687         }
53688         b.innerHTML = "";
53689         b = null;
53690     },
53691
53692     deleteRows : function(dm, firstRow, lastRow){
53693         if(dm.getRowCount()<1){
53694             this.fireEvent("beforerefresh", this);
53695             this.mainBody.update("");
53696             this.lockedBody.update("");
53697             this.fireEvent("refresh", this);
53698         }else{
53699             this.fireEvent("beforerowsdeleted", this, firstRow, lastRow);
53700             var bt = this.getBodyTable();
53701             var tbody = bt.firstChild;
53702             var rows = bt.rows;
53703             for(var rowIndex = firstRow; rowIndex <= lastRow; rowIndex++){
53704                 tbody.removeChild(rows[firstRow]);
53705             }
53706             this.stripeRows(firstRow);
53707             this.fireEvent("rowsdeleted", this, firstRow, lastRow);
53708         }
53709     },
53710
53711     updateRows : function(dataSource, firstRow, lastRow){
53712         var s = this.getScrollState();
53713         this.refresh();
53714         this.restoreScroll(s);
53715     },
53716
53717     handleSort : function(dataSource, sortColumnIndex, sortDir, noRefresh){
53718         if(!noRefresh){
53719            this.refresh();
53720         }
53721         this.updateHeaderSortState();
53722     },
53723
53724     getScrollState : function(){
53725         
53726         var sb = this.scroller.dom;
53727         return {left: sb.scrollLeft, top: sb.scrollTop};
53728     },
53729
53730     stripeRows : function(startRow){
53731         if(!this.grid.stripeRows || this.ds.getCount() < 1){
53732             return;
53733         }
53734         startRow = startRow || 0;
53735         var rows = this.getBodyTable().rows;
53736         var lrows = this.getLockedTable().rows;
53737         var cls = ' x-grid-row-alt ';
53738         for(var i = startRow, len = rows.length; i < len; i++){
53739             var row = rows[i], lrow = lrows[i];
53740             var isAlt = ((i+1) % 2 == 0);
53741             var hasAlt = (' '+row.className + ' ').indexOf(cls) != -1;
53742             if(isAlt == hasAlt){
53743                 continue;
53744             }
53745             if(isAlt){
53746                 row.className += " x-grid-row-alt";
53747             }else{
53748                 row.className = row.className.replace("x-grid-row-alt", "");
53749             }
53750             if(lrow){
53751                 lrow.className = row.className;
53752             }
53753         }
53754     },
53755
53756     restoreScroll : function(state){
53757         //Roo.log('GridView.restoreScroll');
53758         var sb = this.scroller.dom;
53759         sb.scrollLeft = state.left;
53760         sb.scrollTop = state.top;
53761         this.syncScroll();
53762     },
53763
53764     syncScroll : function(){
53765         //Roo.log('GridView.syncScroll');
53766         var sb = this.scroller.dom;
53767         var sh = this.mainHd.dom;
53768         var bs = this.mainBody.dom;
53769         var lv = this.lockedBody.dom;
53770         sh.scrollLeft = bs.scrollLeft = sb.scrollLeft;
53771         lv.scrollTop = bs.scrollTop = sb.scrollTop;
53772     },
53773
53774     handleScroll : function(e){
53775         this.syncScroll();
53776         var sb = this.scroller.dom;
53777         this.grid.fireEvent("bodyscroll", sb.scrollLeft, sb.scrollTop);
53778         e.stopEvent();
53779     },
53780
53781     handleWheel : function(e){
53782         var d = e.getWheelDelta();
53783         this.scroller.dom.scrollTop -= d*22;
53784         // set this here to prevent jumpy scrolling on large tables
53785         this.lockedBody.dom.scrollTop = this.mainBody.dom.scrollTop = this.scroller.dom.scrollTop;
53786         e.stopEvent();
53787     },
53788
53789     renderRows : function(startRow, endRow){
53790         // pull in all the crap needed to render rows
53791         var g = this.grid, cm = g.colModel, ds = g.dataSource, stripe = g.stripeRows;
53792         var colCount = cm.getColumnCount();
53793
53794         if(ds.getCount() < 1){
53795             return ["", ""];
53796         }
53797
53798         // build a map for all the columns
53799         var cs = [];
53800         for(var i = 0; i < colCount; i++){
53801             var name = cm.getDataIndex(i);
53802             cs[i] = {
53803                 name : typeof name == 'undefined' ? ds.fields.get(i).name : name,
53804                 renderer : cm.getRenderer(i),
53805                 id : cm.getColumnId(i),
53806                 locked : cm.isLocked(i)
53807             };
53808         }
53809
53810         startRow = startRow || 0;
53811         endRow = typeof endRow == "undefined"? ds.getCount()-1 : endRow;
53812
53813         // records to render
53814         var rs = ds.getRange(startRow, endRow);
53815
53816         return this.doRender(cs, rs, ds, startRow, colCount, stripe);
53817     },
53818
53819     // As much as I hate to duplicate code, this was branched because FireFox really hates
53820     // [].join("") on strings. The performance difference was substantial enough to
53821     // branch this function
53822     doRender : Roo.isGecko ?
53823             function(cs, rs, ds, startRow, colCount, stripe){
53824                 var ts = this.templates, ct = ts.cell, rt = ts.row;
53825                 // buffers
53826                 var buf = "", lbuf = "", cb, lcb, c, p = {}, rp = {}, r, rowIndex;
53827                 
53828                 var hasListener = this.grid.hasListener('rowclass');
53829                 var rowcfg = {};
53830                 for(var j = 0, len = rs.length; j < len; j++){
53831                     r = rs[j]; cb = ""; lcb = ""; rowIndex = (j+startRow);
53832                     for(var i = 0; i < colCount; i++){
53833                         c = cs[i];
53834                         p.cellId = "x-grid-cell-" + rowIndex + "-" + i;
53835                         p.id = c.id;
53836                         p.css = p.attr = "";
53837                         p.value = c.renderer(r.data[c.name], p, r, rowIndex, i, ds);
53838                         if(p.value == undefined || p.value === "") p.value = "&#160;";
53839                         if(r.dirty && typeof r.modified[c.name] !== 'undefined'){
53840                             p.css += p.css ? ' x-grid-dirty-cell' : 'x-grid-dirty-cell';
53841                         }
53842                         var markup = ct.apply(p);
53843                         if(!c.locked){
53844                             cb+= markup;
53845                         }else{
53846                             lcb+= markup;
53847                         }
53848                     }
53849                     var alt = [];
53850                     if(stripe && ((rowIndex+1) % 2 == 0)){
53851                         alt.push("x-grid-row-alt")
53852                     }
53853                     if(r.dirty){
53854                         alt.push(  " x-grid-dirty-row");
53855                     }
53856                     rp.cells = lcb;
53857                     if(this.getRowClass){
53858                         alt.push(this.getRowClass(r, rowIndex));
53859                     }
53860                     if (hasListener) {
53861                         rowcfg = {
53862                              
53863                             record: r,
53864                             rowIndex : rowIndex,
53865                             rowClass : ''
53866                         }
53867                         this.grid.fireEvent('rowclass', this, rowcfg);
53868                         alt.push(rowcfg.rowClass);
53869                     }
53870                     rp.alt = alt.join(" ");
53871                     lbuf+= rt.apply(rp);
53872                     rp.cells = cb;
53873                     buf+=  rt.apply(rp);
53874                 }
53875                 return [lbuf, buf];
53876             } :
53877             function(cs, rs, ds, startRow, colCount, stripe){
53878                 var ts = this.templates, ct = ts.cell, rt = ts.row;
53879                 // buffers
53880                 var buf = [], lbuf = [], cb, lcb, c, p = {}, rp = {}, r, rowIndex;
53881                 var hasListener = this.grid.hasListener('rowclass');
53882  
53883                 var rowcfg = {};
53884                 for(var j = 0, len = rs.length; j < len; j++){
53885                     r = rs[j]; cb = []; lcb = []; rowIndex = (j+startRow);
53886                     for(var i = 0; i < colCount; i++){
53887                         c = cs[i];
53888                         p.cellId = "x-grid-cell-" + rowIndex + "-" + i;
53889                         p.id = c.id;
53890                         p.css = p.attr = "";
53891                         p.value = c.renderer(r.data[c.name], p, r, rowIndex, i, ds);
53892                         if(p.value == undefined || p.value === "") p.value = "&#160;";
53893                         if(r.dirty && typeof r.modified[c.name] !== 'undefined'){
53894                             p.css += p.css ? ' x-grid-dirty-cell' : 'x-grid-dirty-cell';
53895                         }
53896                         
53897                         var markup = ct.apply(p);
53898                         if(!c.locked){
53899                             cb[cb.length] = markup;
53900                         }else{
53901                             lcb[lcb.length] = markup;
53902                         }
53903                     }
53904                     var alt = [];
53905                     if(stripe && ((rowIndex+1) % 2 == 0)){
53906                         alt.push( "x-grid-row-alt");
53907                     }
53908                     if(r.dirty){
53909                         alt.push(" x-grid-dirty-row");
53910                     }
53911                     rp.cells = lcb;
53912                     if(this.getRowClass){
53913                         alt.push( this.getRowClass(r, rowIndex));
53914                     }
53915                     if (hasListener) {
53916                         rowcfg = {
53917                              
53918                             record: r,
53919                             rowIndex : rowIndex,
53920                             rowClass : ''
53921                         }
53922                         this.grid.fireEvent('rowclass', this, rowcfg);
53923                         alt.push(rowcfg.rowClass);
53924                     }
53925                     rp.alt = alt.join(" ");
53926                     rp.cells = lcb.join("");
53927                     lbuf[lbuf.length] = rt.apply(rp);
53928                     rp.cells = cb.join("");
53929                     buf[buf.length] =  rt.apply(rp);
53930                 }
53931                 return [lbuf.join(""), buf.join("")];
53932             },
53933
53934     renderBody : function(){
53935         var markup = this.renderRows();
53936         var bt = this.templates.body;
53937         return [bt.apply({rows: markup[0]}), bt.apply({rows: markup[1]})];
53938     },
53939
53940     /**
53941      * Refreshes the grid
53942      * @param {Boolean} headersToo
53943      */
53944     refresh : function(headersToo){
53945         this.fireEvent("beforerefresh", this);
53946         this.grid.stopEditing();
53947         var result = this.renderBody();
53948         this.lockedBody.update(result[0]);
53949         this.mainBody.update(result[1]);
53950         if(headersToo === true){
53951             this.updateHeaders();
53952             this.updateColumns();
53953             this.updateSplitters();
53954             this.updateHeaderSortState();
53955         }
53956         this.syncRowHeights();
53957         this.layout();
53958         this.fireEvent("refresh", this);
53959     },
53960
53961     handleColumnMove : function(cm, oldIndex, newIndex){
53962         this.indexMap = null;
53963         var s = this.getScrollState();
53964         this.refresh(true);
53965         this.restoreScroll(s);
53966         this.afterMove(newIndex);
53967     },
53968
53969     afterMove : function(colIndex){
53970         if(this.enableMoveAnim && Roo.enableFx){
53971             this.fly(this.getHeaderCell(colIndex).firstChild).highlight(this.hlColor);
53972         }
53973         // if multisort - fix sortOrder, and reload..
53974         if (this.grid.dataSource.multiSort) {
53975             // the we can call sort again..
53976             var dm = this.grid.dataSource;
53977             var cm = this.grid.colModel;
53978             var so = [];
53979             for(var i = 0; i < cm.config.length; i++ ) {
53980                 
53981                 if ((typeof(dm.sortToggle[cm.config[i].dataIndex]) == 'undefined')) {
53982                     continue; // dont' bother, it's not in sort list or being set.
53983                 }
53984                 
53985                 so.push(cm.config[i].dataIndex);
53986             };
53987             dm.sortOrder = so;
53988             dm.load(dm.lastOptions);
53989             
53990             
53991         }
53992         
53993     },
53994
53995     updateCell : function(dm, rowIndex, dataIndex){
53996         var colIndex = this.getColumnIndexByDataIndex(dataIndex);
53997         if(typeof colIndex == "undefined"){ // not present in grid
53998             return;
53999         }
54000         var cm = this.grid.colModel;
54001         var cell = this.getCell(rowIndex, colIndex);
54002         var cellText = this.getCellText(rowIndex, colIndex);
54003
54004         var p = {
54005             cellId : "x-grid-cell-" + rowIndex + "-" + colIndex,
54006             id : cm.getColumnId(colIndex),
54007             css: colIndex == cm.getColumnCount()-1 ? "x-grid-col-last" : ""
54008         };
54009         var renderer = cm.getRenderer(colIndex);
54010         var val = renderer(dm.getValueAt(rowIndex, dataIndex), p, rowIndex, colIndex, dm);
54011         if(typeof val == "undefined" || val === "") val = "&#160;";
54012         cellText.innerHTML = val;
54013         cell.className = this.cellClass + " " + this.idToCssName(p.cellId) + " " + p.css;
54014         this.syncRowHeights(rowIndex, rowIndex);
54015     },
54016
54017     calcColumnWidth : function(colIndex, maxRowsToMeasure){
54018         var maxWidth = 0;
54019         if(this.grid.autoSizeHeaders){
54020             var h = this.getHeaderCellMeasure(colIndex);
54021             maxWidth = Math.max(maxWidth, h.scrollWidth);
54022         }
54023         var tb, index;
54024         if(this.cm.isLocked(colIndex)){
54025             tb = this.getLockedTable();
54026             index = colIndex;
54027         }else{
54028             tb = this.getBodyTable();
54029             index = colIndex - this.cm.getLockedCount();
54030         }
54031         if(tb && tb.rows){
54032             var rows = tb.rows;
54033             var stopIndex = Math.min(maxRowsToMeasure || rows.length, rows.length);
54034             for(var i = 0; i < stopIndex; i++){
54035                 var cell = rows[i].childNodes[index].firstChild;
54036                 maxWidth = Math.max(maxWidth, cell.scrollWidth);
54037             }
54038         }
54039         return maxWidth + /*margin for error in IE*/ 5;
54040     },
54041     /**
54042      * Autofit a column to its content.
54043      * @param {Number} colIndex
54044      * @param {Boolean} forceMinSize true to force the column to go smaller if possible
54045      */
54046      autoSizeColumn : function(colIndex, forceMinSize, suppressEvent){
54047          if(this.cm.isHidden(colIndex)){
54048              return; // can't calc a hidden column
54049          }
54050         if(forceMinSize){
54051             var cid = this.cm.getColumnId(colIndex);
54052             this.css.updateRule(this.colSelector +this.idToCssName( cid), "width", this.grid.minColumnWidth + "px");
54053            if(this.grid.autoSizeHeaders){
54054                this.css.updateRule(this.hdSelector + this.idToCssName(cid), "width", this.grid.minColumnWidth + "px");
54055            }
54056         }
54057         var newWidth = this.calcColumnWidth(colIndex);
54058         this.cm.setColumnWidth(colIndex,
54059             Math.max(this.grid.minColumnWidth, newWidth), suppressEvent);
54060         if(!suppressEvent){
54061             this.grid.fireEvent("columnresize", colIndex, newWidth);
54062         }
54063     },
54064
54065     /**
54066      * Autofits all columns to their content and then expands to fit any extra space in the grid
54067      */
54068      autoSizeColumns : function(){
54069         var cm = this.grid.colModel;
54070         var colCount = cm.getColumnCount();
54071         for(var i = 0; i < colCount; i++){
54072             this.autoSizeColumn(i, true, true);
54073         }
54074         if(cm.getTotalWidth() < this.scroller.dom.clientWidth){
54075             this.fitColumns();
54076         }else{
54077             this.updateColumns();
54078             this.layout();
54079         }
54080     },
54081
54082     /**
54083      * Autofits all columns to the grid's width proportionate with their current size
54084      * @param {Boolean} reserveScrollSpace Reserve space for a scrollbar
54085      */
54086     fitColumns : function(reserveScrollSpace){
54087         var cm = this.grid.colModel;
54088         var colCount = cm.getColumnCount();
54089         var cols = [];
54090         var width = 0;
54091         var i, w;
54092         for (i = 0; i < colCount; i++){
54093             if(!cm.isHidden(i) && !cm.isFixed(i)){
54094                 w = cm.getColumnWidth(i);
54095                 cols.push(i);
54096                 cols.push(w);
54097                 width += w;
54098             }
54099         }
54100         var avail = Math.min(this.scroller.dom.clientWidth, this.el.getWidth());
54101         if(reserveScrollSpace){
54102             avail -= 17;
54103         }
54104         var frac = (avail - cm.getTotalWidth())/width;
54105         while (cols.length){
54106             w = cols.pop();
54107             i = cols.pop();
54108             cm.setColumnWidth(i, Math.floor(w + w*frac), true);
54109         }
54110         this.updateColumns();
54111         this.layout();
54112     },
54113
54114     onRowSelect : function(rowIndex){
54115         var row = this.getRowComposite(rowIndex);
54116         row.addClass("x-grid-row-selected");
54117     },
54118
54119     onRowDeselect : function(rowIndex){
54120         var row = this.getRowComposite(rowIndex);
54121         row.removeClass("x-grid-row-selected");
54122     },
54123
54124     onCellSelect : function(row, col){
54125         var cell = this.getCell(row, col);
54126         if(cell){
54127             Roo.fly(cell).addClass("x-grid-cell-selected");
54128         }
54129     },
54130
54131     onCellDeselect : function(row, col){
54132         var cell = this.getCell(row, col);
54133         if(cell){
54134             Roo.fly(cell).removeClass("x-grid-cell-selected");
54135         }
54136     },
54137
54138     updateHeaderSortState : function(){
54139         
54140         // sort state can be single { field: xxx, direction : yyy}
54141         // or   { xxx=>ASC , yyy : DESC ..... }
54142         
54143         var mstate = {};
54144         if (!this.ds.multiSort) { 
54145             var state = this.ds.getSortState();
54146             if(!state){
54147                 return;
54148             }
54149             mstate[state.field] = state.direction;
54150             // FIXME... - this is not used here.. but might be elsewhere..
54151             this.sortState = state;
54152             
54153         } else {
54154             mstate = this.ds.sortToggle;
54155         }
54156         //remove existing sort classes..
54157         
54158         var sc = this.sortClasses;
54159         var hds = this.el.select(this.headerSelector).removeClass(sc);
54160         
54161         for(var f in mstate) {
54162         
54163             var sortColumn = this.cm.findColumnIndex(f);
54164             
54165             if(sortColumn != -1){
54166                 var sortDir = mstate[f];        
54167                 hds.item(sortColumn).addClass(sc[sortDir == "DESC" ? 1 : 0]);
54168             }
54169         }
54170         
54171          
54172         
54173     },
54174
54175
54176     handleHeaderClick : function(g, index,e){
54177         
54178         Roo.log("header click");
54179         
54180         if (Roo.isTouch) {
54181             // touch events on header are handled by context
54182             this.handleHdCtx(g,index,e);
54183             return;
54184         }
54185         
54186         
54187         if(this.headersDisabled){
54188             return;
54189         }
54190         var dm = g.dataSource, cm = g.colModel;
54191         if(!cm.isSortable(index)){
54192             return;
54193         }
54194         g.stopEditing();
54195         
54196         if (dm.multiSort) {
54197             // update the sortOrder
54198             var so = [];
54199             for(var i = 0; i < cm.config.length; i++ ) {
54200                 
54201                 if ((typeof(dm.sortToggle[cm.config[i].dataIndex]) == 'undefined') && (index != i)) {
54202                     continue; // dont' bother, it's not in sort list or being set.
54203                 }
54204                 
54205                 so.push(cm.config[i].dataIndex);
54206             };
54207             dm.sortOrder = so;
54208         }
54209         
54210         
54211         dm.sort(cm.getDataIndex(index));
54212     },
54213
54214
54215     destroy : function(){
54216         if(this.colMenu){
54217             this.colMenu.removeAll();
54218             Roo.menu.MenuMgr.unregister(this.colMenu);
54219             this.colMenu.getEl().remove();
54220             delete this.colMenu;
54221         }
54222         if(this.hmenu){
54223             this.hmenu.removeAll();
54224             Roo.menu.MenuMgr.unregister(this.hmenu);
54225             this.hmenu.getEl().remove();
54226             delete this.hmenu;
54227         }
54228         if(this.grid.enableColumnMove){
54229             var dds = Roo.dd.DDM.ids['gridHeader' + this.grid.getGridEl().id];
54230             if(dds){
54231                 for(var dd in dds){
54232                     if(!dds[dd].config.isTarget && dds[dd].dragElId){
54233                         var elid = dds[dd].dragElId;
54234                         dds[dd].unreg();
54235                         Roo.get(elid).remove();
54236                     } else if(dds[dd].config.isTarget){
54237                         dds[dd].proxyTop.remove();
54238                         dds[dd].proxyBottom.remove();
54239                         dds[dd].unreg();
54240                     }
54241                     if(Roo.dd.DDM.locationCache[dd]){
54242                         delete Roo.dd.DDM.locationCache[dd];
54243                     }
54244                 }
54245                 delete Roo.dd.DDM.ids['gridHeader' + this.grid.getGridEl().id];
54246             }
54247         }
54248         Roo.util.CSS.removeStyleSheet(this.idToCssName(this.grid.id) + '-cssrules');
54249         this.bind(null, null);
54250         Roo.EventManager.removeResizeListener(this.onWindowResize, this);
54251     },
54252
54253     handleLockChange : function(){
54254         this.refresh(true);
54255     },
54256
54257     onDenyColumnLock : function(){
54258
54259     },
54260
54261     onDenyColumnHide : function(){
54262
54263     },
54264
54265     handleHdMenuClick : function(item){
54266         var index = this.hdCtxIndex;
54267         var cm = this.cm, ds = this.ds;
54268         switch(item.id){
54269             case "asc":
54270                 ds.sort(cm.getDataIndex(index), "ASC");
54271                 break;
54272             case "desc":
54273                 ds.sort(cm.getDataIndex(index), "DESC");
54274                 break;
54275             case "lock":
54276                 var lc = cm.getLockedCount();
54277                 if(cm.getColumnCount(true) <= lc+1){
54278                     this.onDenyColumnLock();
54279                     return;
54280                 }
54281                 if(lc != index){
54282                     cm.setLocked(index, true, true);
54283                     cm.moveColumn(index, lc);
54284                     this.grid.fireEvent("columnmove", index, lc);
54285                 }else{
54286                     cm.setLocked(index, true);
54287                 }
54288             break;
54289             case "unlock":
54290                 var lc = cm.getLockedCount();
54291                 if((lc-1) != index){
54292                     cm.setLocked(index, false, true);
54293                     cm.moveColumn(index, lc-1);
54294                     this.grid.fireEvent("columnmove", index, lc-1);
54295                 }else{
54296                     cm.setLocked(index, false);
54297                 }
54298             break;
54299             case 'wider': // used to expand cols on touch..
54300             case 'narrow':
54301                 var cw = cm.getColumnWidth(index);
54302                 cw += (item.id == 'wider' ? 1 : -1) * 50;
54303                 cw = Math.max(0, cw);
54304                 cw = Math.min(cw,4000);
54305                 cm.setColumnWidth(index, cw);
54306                 break;
54307                 
54308             default:
54309                 index = cm.getIndexById(item.id.substr(4));
54310                 if(index != -1){
54311                     if(item.checked && cm.getColumnCount(true) <= 1){
54312                         this.onDenyColumnHide();
54313                         return false;
54314                     }
54315                     cm.setHidden(index, item.checked);
54316                 }
54317         }
54318         return true;
54319     },
54320
54321     beforeColMenuShow : function(){
54322         var cm = this.cm,  colCount = cm.getColumnCount();
54323         this.colMenu.removeAll();
54324         for(var i = 0; i < colCount; i++){
54325             this.colMenu.add(new Roo.menu.CheckItem({
54326                 id: "col-"+cm.getColumnId(i),
54327                 text: cm.getColumnHeader(i),
54328                 checked: !cm.isHidden(i),
54329                 hideOnClick:false
54330             }));
54331         }
54332     },
54333
54334     handleHdCtx : function(g, index, e){
54335         e.stopEvent();
54336         var hd = this.getHeaderCell(index);
54337         this.hdCtxIndex = index;
54338         var ms = this.hmenu.items, cm = this.cm;
54339         ms.get("asc").setDisabled(!cm.isSortable(index));
54340         ms.get("desc").setDisabled(!cm.isSortable(index));
54341         if(this.grid.enableColLock !== false){
54342             ms.get("lock").setDisabled(cm.isLocked(index));
54343             ms.get("unlock").setDisabled(!cm.isLocked(index));
54344         }
54345         this.hmenu.show(hd, "tl-bl");
54346     },
54347
54348     handleHdOver : function(e){
54349         var hd = this.findHeaderCell(e.getTarget());
54350         if(hd && !this.headersDisabled){
54351             if(this.grid.colModel.isSortable(this.getCellIndex(hd))){
54352                this.fly(hd).addClass("x-grid-hd-over");
54353             }
54354         }
54355     },
54356
54357     handleHdOut : function(e){
54358         var hd = this.findHeaderCell(e.getTarget());
54359         if(hd){
54360             this.fly(hd).removeClass("x-grid-hd-over");
54361         }
54362     },
54363
54364     handleSplitDblClick : function(e, t){
54365         var i = this.getCellIndex(t);
54366         if(this.grid.enableColumnResize !== false && this.cm.isResizable(i) && !this.cm.isFixed(i)){
54367             this.autoSizeColumn(i, true);
54368             this.layout();
54369         }
54370     },
54371
54372     render : function(){
54373
54374         var cm = this.cm;
54375         var colCount = cm.getColumnCount();
54376
54377         if(this.grid.monitorWindowResize === true){
54378             Roo.EventManager.onWindowResize(this.onWindowResize, this, true);
54379         }
54380         var header = this.renderHeaders();
54381         var body = this.templates.body.apply({rows:""});
54382         var html = this.templates.master.apply({
54383             lockedBody: body,
54384             body: body,
54385             lockedHeader: header[0],
54386             header: header[1]
54387         });
54388
54389         //this.updateColumns();
54390
54391         this.grid.getGridEl().dom.innerHTML = html;
54392
54393         this.initElements();
54394         
54395         // a kludge to fix the random scolling effect in webkit
54396         this.el.on("scroll", function() {
54397             this.el.dom.scrollTop=0; // hopefully not recursive..
54398         },this);
54399
54400         this.scroller.on("scroll", this.handleScroll, this);
54401         this.lockedBody.on("mousewheel", this.handleWheel, this);
54402         this.mainBody.on("mousewheel", this.handleWheel, this);
54403
54404         this.mainHd.on("mouseover", this.handleHdOver, this);
54405         this.mainHd.on("mouseout", this.handleHdOut, this);
54406         this.mainHd.on("dblclick", this.handleSplitDblClick, this,
54407                 {delegate: "."+this.splitClass});
54408
54409         this.lockedHd.on("mouseover", this.handleHdOver, this);
54410         this.lockedHd.on("mouseout", this.handleHdOut, this);
54411         this.lockedHd.on("dblclick", this.handleSplitDblClick, this,
54412                 {delegate: "."+this.splitClass});
54413
54414         if(this.grid.enableColumnResize !== false && Roo.grid.SplitDragZone){
54415             new Roo.grid.SplitDragZone(this.grid, this.lockedHd.dom, this.mainHd.dom);
54416         }
54417
54418         this.updateSplitters();
54419
54420         if(this.grid.enableColumnMove && Roo.grid.HeaderDragZone){
54421             new Roo.grid.HeaderDragZone(this.grid, this.lockedHd.dom, this.mainHd.dom);
54422             new Roo.grid.HeaderDropZone(this.grid, this.lockedHd.dom, this.mainHd.dom);
54423         }
54424
54425         if(this.grid.enableCtxMenu !== false && Roo.menu.Menu){
54426             this.hmenu = new Roo.menu.Menu({id: this.grid.id + "-hctx"});
54427             this.hmenu.add(
54428                 {id:"asc", text: this.sortAscText, cls: "xg-hmenu-sort-asc"},
54429                 {id:"desc", text: this.sortDescText, cls: "xg-hmenu-sort-desc"}
54430             );
54431             if(this.grid.enableColLock !== false){
54432                 this.hmenu.add('-',
54433                     {id:"lock", text: this.lockText, cls: "xg-hmenu-lock"},
54434                     {id:"unlock", text: this.unlockText, cls: "xg-hmenu-unlock"}
54435                 );
54436             }
54437             if (Roo.isTouch) {
54438                  this.hmenu.add('-',
54439                     {id:"wider", text: this.columnsWiderText},
54440                     {id:"narrow", text: this.columnsNarrowText }
54441                 );
54442                 
54443                  
54444             }
54445             
54446             if(this.grid.enableColumnHide !== false){
54447
54448                 this.colMenu = new Roo.menu.Menu({id:this.grid.id + "-hcols-menu"});
54449                 this.colMenu.on("beforeshow", this.beforeColMenuShow, this);
54450                 this.colMenu.on("itemclick", this.handleHdMenuClick, this);
54451
54452                 this.hmenu.add('-',
54453                     {id:"columns", text: this.columnsText, menu: this.colMenu}
54454                 );
54455             }
54456             this.hmenu.on("itemclick", this.handleHdMenuClick, this);
54457
54458             this.grid.on("headercontextmenu", this.handleHdCtx, this);
54459         }
54460
54461         if((this.grid.enableDragDrop || this.grid.enableDrag) && Roo.grid.GridDragZone){
54462             this.dd = new Roo.grid.GridDragZone(this.grid, {
54463                 ddGroup : this.grid.ddGroup || 'GridDD'
54464             });
54465             
54466         }
54467
54468         /*
54469         for(var i = 0; i < colCount; i++){
54470             if(cm.isHidden(i)){
54471                 this.hideColumn(i);
54472             }
54473             if(cm.config[i].align){
54474                 this.css.updateRule(this.colSelector + i, "textAlign", cm.config[i].align);
54475                 this.css.updateRule(this.hdSelector + i, "textAlign", cm.config[i].align);
54476             }
54477         }*/
54478         
54479         this.updateHeaderSortState();
54480
54481         this.beforeInitialResize();
54482         this.layout(true);
54483
54484         // two part rendering gives faster view to the user
54485         this.renderPhase2.defer(1, this);
54486     },
54487
54488     renderPhase2 : function(){
54489         // render the rows now
54490         this.refresh();
54491         if(this.grid.autoSizeColumns){
54492             this.autoSizeColumns();
54493         }
54494     },
54495
54496     beforeInitialResize : function(){
54497
54498     },
54499
54500     onColumnSplitterMoved : function(i, w){
54501         this.userResized = true;
54502         var cm = this.grid.colModel;
54503         cm.setColumnWidth(i, w, true);
54504         var cid = cm.getColumnId(i);
54505         this.css.updateRule(this.colSelector + this.idToCssName(cid), "width", (w-this.borderWidth) + "px");
54506         this.css.updateRule(this.hdSelector + this.idToCssName(cid), "width", (w-this.borderWidth) + "px");
54507         this.updateSplitters();
54508         this.layout();
54509         this.grid.fireEvent("columnresize", i, w);
54510     },
54511
54512     syncRowHeights : function(startIndex, endIndex){
54513         if(this.grid.enableRowHeightSync === true && this.cm.getLockedCount() > 0){
54514             startIndex = startIndex || 0;
54515             var mrows = this.getBodyTable().rows;
54516             var lrows = this.getLockedTable().rows;
54517             var len = mrows.length-1;
54518             endIndex = Math.min(endIndex || len, len);
54519             for(var i = startIndex; i <= endIndex; i++){
54520                 var m = mrows[i], l = lrows[i];
54521                 var h = Math.max(m.offsetHeight, l.offsetHeight);
54522                 m.style.height = l.style.height = h + "px";
54523             }
54524         }
54525     },
54526
54527     layout : function(initialRender, is2ndPass){
54528         var g = this.grid;
54529         var auto = g.autoHeight;
54530         var scrollOffset = 16;
54531         var c = g.getGridEl(), cm = this.cm,
54532                 expandCol = g.autoExpandColumn,
54533                 gv = this;
54534         //c.beginMeasure();
54535
54536         if(!c.dom.offsetWidth){ // display:none?
54537             if(initialRender){
54538                 this.lockedWrap.show();
54539                 this.mainWrap.show();
54540             }
54541             return;
54542         }
54543
54544         var hasLock = this.cm.isLocked(0);
54545
54546         var tbh = this.headerPanel.getHeight();
54547         var bbh = this.footerPanel.getHeight();
54548
54549         if(auto){
54550             var ch = this.getBodyTable().offsetHeight + tbh + bbh + this.mainHd.getHeight();
54551             var newHeight = ch + c.getBorderWidth("tb");
54552             if(g.maxHeight){
54553                 newHeight = Math.min(g.maxHeight, newHeight);
54554             }
54555             c.setHeight(newHeight);
54556         }
54557
54558         if(g.autoWidth){
54559             c.setWidth(cm.getTotalWidth()+c.getBorderWidth('lr'));
54560         }
54561
54562         var s = this.scroller;
54563
54564         var csize = c.getSize(true);
54565
54566         this.el.setSize(csize.width, csize.height);
54567
54568         this.headerPanel.setWidth(csize.width);
54569         this.footerPanel.setWidth(csize.width);
54570
54571         var hdHeight = this.mainHd.getHeight();
54572         var vw = csize.width;
54573         var vh = csize.height - (tbh + bbh);
54574
54575         s.setSize(vw, vh);
54576
54577         var bt = this.getBodyTable();
54578         var ltWidth = hasLock ?
54579                       Math.max(this.getLockedTable().offsetWidth, this.lockedHd.dom.firstChild.offsetWidth) : 0;
54580
54581         var scrollHeight = bt.offsetHeight;
54582         var scrollWidth = ltWidth + bt.offsetWidth;
54583         var vscroll = false, hscroll = false;
54584
54585         this.scrollSizer.setSize(scrollWidth, scrollHeight+hdHeight);
54586
54587         var lw = this.lockedWrap, mw = this.mainWrap;
54588         var lb = this.lockedBody, mb = this.mainBody;
54589
54590         setTimeout(function(){
54591             var t = s.dom.offsetTop;
54592             var w = s.dom.clientWidth,
54593                 h = s.dom.clientHeight;
54594
54595             lw.setTop(t);
54596             lw.setSize(ltWidth, h);
54597
54598             mw.setLeftTop(ltWidth, t);
54599             mw.setSize(w-ltWidth, h);
54600
54601             lb.setHeight(h-hdHeight);
54602             mb.setHeight(h-hdHeight);
54603
54604             if(is2ndPass !== true && !gv.userResized && expandCol){
54605                 // high speed resize without full column calculation
54606                 
54607                 var ci = cm.getIndexById(expandCol);
54608                 if (ci < 0) {
54609                     ci = cm.findColumnIndex(expandCol);
54610                 }
54611                 ci = Math.max(0, ci); // make sure it's got at least the first col.
54612                 var expandId = cm.getColumnId(ci);
54613                 var  tw = cm.getTotalWidth(false);
54614                 var currentWidth = cm.getColumnWidth(ci);
54615                 var cw = Math.min(Math.max(((w-tw)+currentWidth-2)-/*scrollbar*/(w <= s.dom.offsetWidth ? 0 : 18), g.autoExpandMin), g.autoExpandMax);
54616                 if(currentWidth != cw){
54617                     cm.setColumnWidth(ci, cw, true);
54618                     gv.css.updateRule(gv.colSelector+gv.idToCssName(expandId), "width", (cw - gv.borderWidth) + "px");
54619                     gv.css.updateRule(gv.hdSelector+gv.idToCssName(expandId), "width", (cw - gv.borderWidth) + "px");
54620                     gv.updateSplitters();
54621                     gv.layout(false, true);
54622                 }
54623             }
54624
54625             if(initialRender){
54626                 lw.show();
54627                 mw.show();
54628             }
54629             //c.endMeasure();
54630         }, 10);
54631     },
54632
54633     onWindowResize : function(){
54634         if(!this.grid.monitorWindowResize || this.grid.autoHeight){
54635             return;
54636         }
54637         this.layout();
54638     },
54639
54640     appendFooter : function(parentEl){
54641         return null;
54642     },
54643
54644     sortAscText : "Sort Ascending",
54645     sortDescText : "Sort Descending",
54646     lockText : "Lock Column",
54647     unlockText : "Unlock Column",
54648     columnsText : "Columns",
54649  
54650     columnsWiderText : "Wider",
54651     columnsNarrowText : "Thinner"
54652 });
54653
54654
54655 Roo.grid.GridView.ColumnDragZone = function(grid, hd){
54656     Roo.grid.GridView.ColumnDragZone.superclass.constructor.call(this, grid, hd, null);
54657     this.proxy.el.addClass('x-grid3-col-dd');
54658 };
54659
54660 Roo.extend(Roo.grid.GridView.ColumnDragZone, Roo.grid.HeaderDragZone, {
54661     handleMouseDown : function(e){
54662
54663     },
54664
54665     callHandleMouseDown : function(e){
54666         Roo.grid.GridView.ColumnDragZone.superclass.handleMouseDown.call(this, e);
54667     }
54668 });
54669 /*
54670  * Based on:
54671  * Ext JS Library 1.1.1
54672  * Copyright(c) 2006-2007, Ext JS, LLC.
54673  *
54674  * Originally Released Under LGPL - original licence link has changed is not relivant.
54675  *
54676  * Fork - LGPL
54677  * <script type="text/javascript">
54678  */
54679  
54680 // private
54681 // This is a support class used internally by the Grid components
54682 Roo.grid.SplitDragZone = function(grid, hd, hd2){
54683     this.grid = grid;
54684     this.view = grid.getView();
54685     this.proxy = this.view.resizeProxy;
54686     Roo.grid.SplitDragZone.superclass.constructor.call(this, hd,
54687         "gridSplitters" + this.grid.getGridEl().id, {
54688         dragElId : Roo.id(this.proxy.dom), resizeFrame:false
54689     });
54690     this.setHandleElId(Roo.id(hd));
54691     this.setOuterHandleElId(Roo.id(hd2));
54692     this.scroll = false;
54693 };
54694 Roo.extend(Roo.grid.SplitDragZone, Roo.dd.DDProxy, {
54695     fly: Roo.Element.fly,
54696
54697     b4StartDrag : function(x, y){
54698         this.view.headersDisabled = true;
54699         this.proxy.setHeight(this.view.mainWrap.getHeight());
54700         var w = this.cm.getColumnWidth(this.cellIndex);
54701         var minw = Math.max(w-this.grid.minColumnWidth, 0);
54702         this.resetConstraints();
54703         this.setXConstraint(minw, 1000);
54704         this.setYConstraint(0, 0);
54705         this.minX = x - minw;
54706         this.maxX = x + 1000;
54707         this.startPos = x;
54708         Roo.dd.DDProxy.prototype.b4StartDrag.call(this, x, y);
54709     },
54710
54711
54712     handleMouseDown : function(e){
54713         ev = Roo.EventObject.setEvent(e);
54714         var t = this.fly(ev.getTarget());
54715         if(t.hasClass("x-grid-split")){
54716             this.cellIndex = this.view.getCellIndex(t.dom);
54717             this.split = t.dom;
54718             this.cm = this.grid.colModel;
54719             if(this.cm.isResizable(this.cellIndex) && !this.cm.isFixed(this.cellIndex)){
54720                 Roo.grid.SplitDragZone.superclass.handleMouseDown.apply(this, arguments);
54721             }
54722         }
54723     },
54724
54725     endDrag : function(e){
54726         this.view.headersDisabled = false;
54727         var endX = Math.max(this.minX, Roo.lib.Event.getPageX(e));
54728         var diff = endX - this.startPos;
54729         this.view.onColumnSplitterMoved(this.cellIndex, this.cm.getColumnWidth(this.cellIndex)+diff);
54730     },
54731
54732     autoOffset : function(){
54733         this.setDelta(0,0);
54734     }
54735 });/*
54736  * Based on:
54737  * Ext JS Library 1.1.1
54738  * Copyright(c) 2006-2007, Ext JS, LLC.
54739  *
54740  * Originally Released Under LGPL - original licence link has changed is not relivant.
54741  *
54742  * Fork - LGPL
54743  * <script type="text/javascript">
54744  */
54745  
54746 // private
54747 // This is a support class used internally by the Grid components
54748 Roo.grid.GridDragZone = function(grid, config){
54749     this.view = grid.getView();
54750     Roo.grid.GridDragZone.superclass.constructor.call(this, this.view.mainBody.dom, config);
54751     if(this.view.lockedBody){
54752         this.setHandleElId(Roo.id(this.view.mainBody.dom));
54753         this.setOuterHandleElId(Roo.id(this.view.lockedBody.dom));
54754     }
54755     this.scroll = false;
54756     this.grid = grid;
54757     this.ddel = document.createElement('div');
54758     this.ddel.className = 'x-grid-dd-wrap';
54759 };
54760
54761 Roo.extend(Roo.grid.GridDragZone, Roo.dd.DragZone, {
54762     ddGroup : "GridDD",
54763
54764     getDragData : function(e){
54765         var t = Roo.lib.Event.getTarget(e);
54766         var rowIndex = this.view.findRowIndex(t);
54767         var sm = this.grid.selModel;
54768             
54769         //Roo.log(rowIndex);
54770         
54771         if (sm.getSelectedCell) {
54772             // cell selection..
54773             if (!sm.getSelectedCell()) {
54774                 return false;
54775             }
54776             if (rowIndex != sm.getSelectedCell()[0]) {
54777                 return false;
54778             }
54779         
54780         }
54781         
54782         if(rowIndex !== false){
54783             
54784             // if editorgrid.. 
54785             
54786             
54787             //Roo.log([ sm.getSelectedCell() ? sm.getSelectedCell()[0] : 'NO' , rowIndex ]);
54788                
54789             //if(!sm.isSelected(rowIndex) || e.hasModifier()){
54790               //  
54791             //}
54792             if (e.hasModifier()){
54793                 sm.handleMouseDown(e, t); // non modifier buttons are handled by row select.
54794             }
54795             
54796             Roo.log("getDragData");
54797             
54798             return {
54799                 grid: this.grid,
54800                 ddel: this.ddel,
54801                 rowIndex: rowIndex,
54802                 selections:sm.getSelections ? sm.getSelections() : (
54803                     sm.getSelectedCell() ? [ this.grid.ds.getAt(sm.getSelectedCell()[0]) ] : []
54804                 )
54805             };
54806         }
54807         return false;
54808     },
54809
54810     onInitDrag : function(e){
54811         var data = this.dragData;
54812         this.ddel.innerHTML = this.grid.getDragDropText();
54813         this.proxy.update(this.ddel);
54814         // fire start drag?
54815     },
54816
54817     afterRepair : function(){
54818         this.dragging = false;
54819     },
54820
54821     getRepairXY : function(e, data){
54822         return false;
54823     },
54824
54825     onEndDrag : function(data, e){
54826         // fire end drag?
54827     },
54828
54829     onValidDrop : function(dd, e, id){
54830         // fire drag drop?
54831         this.hideProxy();
54832     },
54833
54834     beforeInvalidDrop : function(e, id){
54835
54836     }
54837 });/*
54838  * Based on:
54839  * Ext JS Library 1.1.1
54840  * Copyright(c) 2006-2007, Ext JS, LLC.
54841  *
54842  * Originally Released Under LGPL - original licence link has changed is not relivant.
54843  *
54844  * Fork - LGPL
54845  * <script type="text/javascript">
54846  */
54847  
54848
54849 /**
54850  * @class Roo.grid.ColumnModel
54851  * @extends Roo.util.Observable
54852  * This is the default implementation of a ColumnModel used by the Grid. It defines
54853  * the columns in the grid.
54854  * <br>Usage:<br>
54855  <pre><code>
54856  var colModel = new Roo.grid.ColumnModel([
54857         {header: "Ticker", width: 60, sortable: true, locked: true},
54858         {header: "Company Name", width: 150, sortable: true},
54859         {header: "Market Cap.", width: 100, sortable: true},
54860         {header: "$ Sales", width: 100, sortable: true, renderer: money},
54861         {header: "Employees", width: 100, sortable: true, resizable: false}
54862  ]);
54863  </code></pre>
54864  * <p>
54865  
54866  * The config options listed for this class are options which may appear in each
54867  * individual column definition.
54868  * <br/>RooJS Fix - column id's are not sequential but use Roo.id() - fixes bugs with layouts.
54869  * @constructor
54870  * @param {Object} config An Array of column config objects. See this class's
54871  * config objects for details.
54872 */
54873 Roo.grid.ColumnModel = function(config){
54874         /**
54875      * The config passed into the constructor
54876      */
54877     this.config = config;
54878     this.lookup = {};
54879
54880     // if no id, create one
54881     // if the column does not have a dataIndex mapping,
54882     // map it to the order it is in the config
54883     for(var i = 0, len = config.length; i < len; i++){
54884         var c = config[i];
54885         if(typeof c.dataIndex == "undefined"){
54886             c.dataIndex = i;
54887         }
54888         if(typeof c.renderer == "string"){
54889             c.renderer = Roo.util.Format[c.renderer];
54890         }
54891         if(typeof c.id == "undefined"){
54892             c.id = Roo.id();
54893         }
54894         if(c.editor && c.editor.xtype){
54895             c.editor  = Roo.factory(c.editor, Roo.grid);
54896         }
54897         if(c.editor && c.editor.isFormField){
54898             c.editor = new Roo.grid.GridEditor(c.editor);
54899         }
54900         this.lookup[c.id] = c;
54901     }
54902
54903     /**
54904      * The width of columns which have no width specified (defaults to 100)
54905      * @type Number
54906      */
54907     this.defaultWidth = 100;
54908
54909     /**
54910      * Default sortable of columns which have no sortable specified (defaults to false)
54911      * @type Boolean
54912      */
54913     this.defaultSortable = false;
54914
54915     this.addEvents({
54916         /**
54917              * @event widthchange
54918              * Fires when the width of a column changes.
54919              * @param {ColumnModel} this
54920              * @param {Number} columnIndex The column index
54921              * @param {Number} newWidth The new width
54922              */
54923             "widthchange": true,
54924         /**
54925              * @event headerchange
54926              * Fires when the text of a header changes.
54927              * @param {ColumnModel} this
54928              * @param {Number} columnIndex The column index
54929              * @param {Number} newText The new header text
54930              */
54931             "headerchange": true,
54932         /**
54933              * @event hiddenchange
54934              * Fires when a column is hidden or "unhidden".
54935              * @param {ColumnModel} this
54936              * @param {Number} columnIndex The column index
54937              * @param {Boolean} hidden true if hidden, false otherwise
54938              */
54939             "hiddenchange": true,
54940             /**
54941          * @event columnmoved
54942          * Fires when a column is moved.
54943          * @param {ColumnModel} this
54944          * @param {Number} oldIndex
54945          * @param {Number} newIndex
54946          */
54947         "columnmoved" : true,
54948         /**
54949          * @event columlockchange
54950          * Fires when a column's locked state is changed
54951          * @param {ColumnModel} this
54952          * @param {Number} colIndex
54953          * @param {Boolean} locked true if locked
54954          */
54955         "columnlockchange" : true
54956     });
54957     Roo.grid.ColumnModel.superclass.constructor.call(this);
54958 };
54959 Roo.extend(Roo.grid.ColumnModel, Roo.util.Observable, {
54960     /**
54961      * @cfg {String} header The header text to display in the Grid view.
54962      */
54963     /**
54964      * @cfg {String} dataIndex (Optional) The name of the field in the grid's {@link Roo.data.Store}'s
54965      * {@link Roo.data.Record} definition from which to draw the column's value. If not
54966      * specified, the column's index is used as an index into the Record's data Array.
54967      */
54968     /**
54969      * @cfg {Number} width (Optional) The initial width in pixels of the column. Using this
54970      * instead of {@link Roo.grid.Grid#autoSizeColumns} is more efficient.
54971      */
54972     /**
54973      * @cfg {Boolean} sortable (Optional) True if sorting is to be allowed on this column.
54974      * Defaults to the value of the {@link #defaultSortable} property.
54975      * Whether local/remote sorting is used is specified in {@link Roo.data.Store#remoteSort}.
54976      */
54977     /**
54978      * @cfg {Boolean} locked (Optional) True to lock the column in place while scrolling the Grid.  Defaults to false.
54979      */
54980     /**
54981      * @cfg {Boolean} fixed (Optional) True if the column width cannot be changed.  Defaults to false.
54982      */
54983     /**
54984      * @cfg {Boolean} resizable (Optional) False to disable column resizing. Defaults to true.
54985      */
54986     /**
54987      * @cfg {Boolean} hidden (Optional) True to hide the column. Defaults to false.
54988      */
54989     /**
54990      * @cfg {Function} renderer (Optional) A function used to generate HTML markup for a cell
54991      * given the cell's data value. See {@link #setRenderer}. If not specified, the
54992      * default renderer uses the raw data value. If an object is returned (bootstrap only)
54993      * then it is treated as a Roo Component object instance, and it is rendered after the initial row is rendered
54994      */
54995        /**
54996      * @cfg {Roo.grid.GridEditor} editor (Optional) For grid editors - returns the grid editor 
54997      */
54998     /**
54999      * @cfg {String} align (Optional) Set the CSS text-align property of the column.  Defaults to undefined.
55000      */
55001
55002     /**
55003      * Returns the id of the column at the specified index.
55004      * @param {Number} index The column index
55005      * @return {String} the id
55006      */
55007     getColumnId : function(index){
55008         return this.config[index].id;
55009     },
55010
55011     /**
55012      * Returns the column for a specified id.
55013      * @param {String} id The column id
55014      * @return {Object} the column
55015      */
55016     getColumnById : function(id){
55017         return this.lookup[id];
55018     },
55019
55020     
55021     /**
55022      * Returns the column for a specified dataIndex.
55023      * @param {String} dataIndex The column dataIndex
55024      * @return {Object|Boolean} the column or false if not found
55025      */
55026     getColumnByDataIndex: function(dataIndex){
55027         var index = this.findColumnIndex(dataIndex);
55028         return index > -1 ? this.config[index] : false;
55029     },
55030     
55031     /**
55032      * Returns the index for a specified column id.
55033      * @param {String} id The column id
55034      * @return {Number} the index, or -1 if not found
55035      */
55036     getIndexById : function(id){
55037         for(var i = 0, len = this.config.length; i < len; i++){
55038             if(this.config[i].id == id){
55039                 return i;
55040             }
55041         }
55042         return -1;
55043     },
55044     
55045     /**
55046      * Returns the index for a specified column dataIndex.
55047      * @param {String} dataIndex The column dataIndex
55048      * @return {Number} the index, or -1 if not found
55049      */
55050     
55051     findColumnIndex : function(dataIndex){
55052         for(var i = 0, len = this.config.length; i < len; i++){
55053             if(this.config[i].dataIndex == dataIndex){
55054                 return i;
55055             }
55056         }
55057         return -1;
55058     },
55059     
55060     
55061     moveColumn : function(oldIndex, newIndex){
55062         var c = this.config[oldIndex];
55063         this.config.splice(oldIndex, 1);
55064         this.config.splice(newIndex, 0, c);
55065         this.dataMap = null;
55066         this.fireEvent("columnmoved", this, oldIndex, newIndex);
55067     },
55068
55069     isLocked : function(colIndex){
55070         return this.config[colIndex].locked === true;
55071     },
55072
55073     setLocked : function(colIndex, value, suppressEvent){
55074         if(this.isLocked(colIndex) == value){
55075             return;
55076         }
55077         this.config[colIndex].locked = value;
55078         if(!suppressEvent){
55079             this.fireEvent("columnlockchange", this, colIndex, value);
55080         }
55081     },
55082
55083     getTotalLockedWidth : function(){
55084         var totalWidth = 0;
55085         for(var i = 0; i < this.config.length; i++){
55086             if(this.isLocked(i) && !this.isHidden(i)){
55087                 this.totalWidth += this.getColumnWidth(i);
55088             }
55089         }
55090         return totalWidth;
55091     },
55092
55093     getLockedCount : function(){
55094         for(var i = 0, len = this.config.length; i < len; i++){
55095             if(!this.isLocked(i)){
55096                 return i;
55097             }
55098         }
55099     },
55100
55101     /**
55102      * Returns the number of columns.
55103      * @return {Number}
55104      */
55105     getColumnCount : function(visibleOnly){
55106         if(visibleOnly === true){
55107             var c = 0;
55108             for(var i = 0, len = this.config.length; i < len; i++){
55109                 if(!this.isHidden(i)){
55110                     c++;
55111                 }
55112             }
55113             return c;
55114         }
55115         return this.config.length;
55116     },
55117
55118     /**
55119      * Returns the column configs that return true by the passed function that is called with (columnConfig, index)
55120      * @param {Function} fn
55121      * @param {Object} scope (optional)
55122      * @return {Array} result
55123      */
55124     getColumnsBy : function(fn, scope){
55125         var r = [];
55126         for(var i = 0, len = this.config.length; i < len; i++){
55127             var c = this.config[i];
55128             if(fn.call(scope||this, c, i) === true){
55129                 r[r.length] = c;
55130             }
55131         }
55132         return r;
55133     },
55134
55135     /**
55136      * Returns true if the specified column is sortable.
55137      * @param {Number} col The column index
55138      * @return {Boolean}
55139      */
55140     isSortable : function(col){
55141         if(typeof this.config[col].sortable == "undefined"){
55142             return this.defaultSortable;
55143         }
55144         return this.config[col].sortable;
55145     },
55146
55147     /**
55148      * Returns the rendering (formatting) function defined for the column.
55149      * @param {Number} col The column index.
55150      * @return {Function} The function used to render the cell. See {@link #setRenderer}.
55151      */
55152     getRenderer : function(col){
55153         if(!this.config[col].renderer){
55154             return Roo.grid.ColumnModel.defaultRenderer;
55155         }
55156         return this.config[col].renderer;
55157     },
55158
55159     /**
55160      * Sets the rendering (formatting) function for a column.
55161      * @param {Number} col The column index
55162      * @param {Function} fn The function to use to process the cell's raw data
55163      * to return HTML markup for the grid view. The render function is called with
55164      * the following parameters:<ul>
55165      * <li>Data value.</li>
55166      * <li>Cell metadata. An object in which you may set the following attributes:<ul>
55167      * <li>css A CSS style string to apply to the table cell.</li>
55168      * <li>attr An HTML attribute definition string to apply to the data container element <i>within</i> the table cell.</li></ul>
55169      * <li>The {@link Roo.data.Record} from which the data was extracted.</li>
55170      * <li>Row index</li>
55171      * <li>Column index</li>
55172      * <li>The {@link Roo.data.Store} object from which the Record was extracted</li></ul>
55173      */
55174     setRenderer : function(col, fn){
55175         this.config[col].renderer = fn;
55176     },
55177
55178     /**
55179      * Returns the width for the specified column.
55180      * @param {Number} col The column index
55181      * @return {Number}
55182      */
55183     getColumnWidth : function(col){
55184         return this.config[col].width * 1 || this.defaultWidth;
55185     },
55186
55187     /**
55188      * Sets the width for a column.
55189      * @param {Number} col The column index
55190      * @param {Number} width The new width
55191      */
55192     setColumnWidth : function(col, width, suppressEvent){
55193         this.config[col].width = width;
55194         this.totalWidth = null;
55195         if(!suppressEvent){
55196              this.fireEvent("widthchange", this, col, width);
55197         }
55198     },
55199
55200     /**
55201      * Returns the total width of all columns.
55202      * @param {Boolean} includeHidden True to include hidden column widths
55203      * @return {Number}
55204      */
55205     getTotalWidth : function(includeHidden){
55206         if(!this.totalWidth){
55207             this.totalWidth = 0;
55208             for(var i = 0, len = this.config.length; i < len; i++){
55209                 if(includeHidden || !this.isHidden(i)){
55210                     this.totalWidth += this.getColumnWidth(i);
55211                 }
55212             }
55213         }
55214         return this.totalWidth;
55215     },
55216
55217     /**
55218      * Returns the header for the specified column.
55219      * @param {Number} col The column index
55220      * @return {String}
55221      */
55222     getColumnHeader : function(col){
55223         return this.config[col].header;
55224     },
55225
55226     /**
55227      * Sets the header for a column.
55228      * @param {Number} col The column index
55229      * @param {String} header The new header
55230      */
55231     setColumnHeader : function(col, header){
55232         this.config[col].header = header;
55233         this.fireEvent("headerchange", this, col, header);
55234     },
55235
55236     /**
55237      * Returns the tooltip for the specified column.
55238      * @param {Number} col The column index
55239      * @return {String}
55240      */
55241     getColumnTooltip : function(col){
55242             return this.config[col].tooltip;
55243     },
55244     /**
55245      * Sets the tooltip for a column.
55246      * @param {Number} col The column index
55247      * @param {String} tooltip The new tooltip
55248      */
55249     setColumnTooltip : function(col, tooltip){
55250             this.config[col].tooltip = tooltip;
55251     },
55252
55253     /**
55254      * Returns the dataIndex for the specified column.
55255      * @param {Number} col The column index
55256      * @return {Number}
55257      */
55258     getDataIndex : function(col){
55259         return this.config[col].dataIndex;
55260     },
55261
55262     /**
55263      * Sets the dataIndex for a column.
55264      * @param {Number} col The column index
55265      * @param {Number} dataIndex The new dataIndex
55266      */
55267     setDataIndex : function(col, dataIndex){
55268         this.config[col].dataIndex = dataIndex;
55269     },
55270
55271     
55272     
55273     /**
55274      * Returns true if the cell is editable.
55275      * @param {Number} colIndex The column index
55276      * @param {Number} rowIndex The row index
55277      * @return {Boolean}
55278      */
55279     isCellEditable : function(colIndex, rowIndex){
55280         return (this.config[colIndex].editable || (typeof this.config[colIndex].editable == "undefined" && this.config[colIndex].editor)) ? true : false;
55281     },
55282
55283     /**
55284      * Returns the editor defined for the cell/column.
55285      * return false or null to disable editing.
55286      * @param {Number} colIndex The column index
55287      * @param {Number} rowIndex The row index
55288      * @return {Object}
55289      */
55290     getCellEditor : function(colIndex, rowIndex){
55291         return this.config[colIndex].editor;
55292     },
55293
55294     /**
55295      * Sets if a column is editable.
55296      * @param {Number} col The column index
55297      * @param {Boolean} editable True if the column is editable
55298      */
55299     setEditable : function(col, editable){
55300         this.config[col].editable = editable;
55301     },
55302
55303
55304     /**
55305      * Returns true if the column is hidden.
55306      * @param {Number} colIndex The column index
55307      * @return {Boolean}
55308      */
55309     isHidden : function(colIndex){
55310         return this.config[colIndex].hidden;
55311     },
55312
55313
55314     /**
55315      * Returns true if the column width cannot be changed
55316      */
55317     isFixed : function(colIndex){
55318         return this.config[colIndex].fixed;
55319     },
55320
55321     /**
55322      * Returns true if the column can be resized
55323      * @return {Boolean}
55324      */
55325     isResizable : function(colIndex){
55326         return colIndex >= 0 && this.config[colIndex].resizable !== false && this.config[colIndex].fixed !== true;
55327     },
55328     /**
55329      * Sets if a column is hidden.
55330      * @param {Number} colIndex The column index
55331      * @param {Boolean} hidden True if the column is hidden
55332      */
55333     setHidden : function(colIndex, hidden){
55334         this.config[colIndex].hidden = hidden;
55335         this.totalWidth = null;
55336         this.fireEvent("hiddenchange", this, colIndex, hidden);
55337     },
55338
55339     /**
55340      * Sets the editor for a column.
55341      * @param {Number} col The column index
55342      * @param {Object} editor The editor object
55343      */
55344     setEditor : function(col, editor){
55345         this.config[col].editor = editor;
55346     }
55347 });
55348
55349 Roo.grid.ColumnModel.defaultRenderer = function(value){
55350         if(typeof value == "string" && value.length < 1){
55351             return "&#160;";
55352         }
55353         return value;
55354 };
55355
55356 // Alias for backwards compatibility
55357 Roo.grid.DefaultColumnModel = Roo.grid.ColumnModel;
55358 /*
55359  * Based on:
55360  * Ext JS Library 1.1.1
55361  * Copyright(c) 2006-2007, Ext JS, LLC.
55362  *
55363  * Originally Released Under LGPL - original licence link has changed is not relivant.
55364  *
55365  * Fork - LGPL
55366  * <script type="text/javascript">
55367  */
55368
55369 /**
55370  * @class Roo.grid.AbstractSelectionModel
55371  * @extends Roo.util.Observable
55372  * Abstract base class for grid SelectionModels.  It provides the interface that should be
55373  * implemented by descendant classes.  This class should not be directly instantiated.
55374  * @constructor
55375  */
55376 Roo.grid.AbstractSelectionModel = function(){
55377     this.locked = false;
55378     Roo.grid.AbstractSelectionModel.superclass.constructor.call(this);
55379 };
55380
55381 Roo.extend(Roo.grid.AbstractSelectionModel, Roo.util.Observable,  {
55382     /** @ignore Called by the grid automatically. Do not call directly. */
55383     init : function(grid){
55384         this.grid = grid;
55385         this.initEvents();
55386     },
55387
55388     /**
55389      * Locks the selections.
55390      */
55391     lock : function(){
55392         this.locked = true;
55393     },
55394
55395     /**
55396      * Unlocks the selections.
55397      */
55398     unlock : function(){
55399         this.locked = false;
55400     },
55401
55402     /**
55403      * Returns true if the selections are locked.
55404      * @return {Boolean}
55405      */
55406     isLocked : function(){
55407         return this.locked;
55408     }
55409 });/*
55410  * Based on:
55411  * Ext JS Library 1.1.1
55412  * Copyright(c) 2006-2007, Ext JS, LLC.
55413  *
55414  * Originally Released Under LGPL - original licence link has changed is not relivant.
55415  *
55416  * Fork - LGPL
55417  * <script type="text/javascript">
55418  */
55419 /**
55420  * @extends Roo.grid.AbstractSelectionModel
55421  * @class Roo.grid.RowSelectionModel
55422  * The default SelectionModel used by {@link Roo.grid.Grid}.
55423  * It supports multiple selections and keyboard selection/navigation. 
55424  * @constructor
55425  * @param {Object} config
55426  */
55427 Roo.grid.RowSelectionModel = function(config){
55428     Roo.apply(this, config);
55429     this.selections = new Roo.util.MixedCollection(false, function(o){
55430         return o.id;
55431     });
55432
55433     this.last = false;
55434     this.lastActive = false;
55435
55436     this.addEvents({
55437         /**
55438              * @event selectionchange
55439              * Fires when the selection changes
55440              * @param {SelectionModel} this
55441              */
55442             "selectionchange" : true,
55443         /**
55444              * @event afterselectionchange
55445              * Fires after the selection changes (eg. by key press or clicking)
55446              * @param {SelectionModel} this
55447              */
55448             "afterselectionchange" : true,
55449         /**
55450              * @event beforerowselect
55451              * Fires when a row is selected being selected, return false to cancel.
55452              * @param {SelectionModel} this
55453              * @param {Number} rowIndex The selected index
55454              * @param {Boolean} keepExisting False if other selections will be cleared
55455              */
55456             "beforerowselect" : true,
55457         /**
55458              * @event rowselect
55459              * Fires when a row is selected.
55460              * @param {SelectionModel} this
55461              * @param {Number} rowIndex The selected index
55462              * @param {Roo.data.Record} r The record
55463              */
55464             "rowselect" : true,
55465         /**
55466              * @event rowdeselect
55467              * Fires when a row is deselected.
55468              * @param {SelectionModel} this
55469              * @param {Number} rowIndex The selected index
55470              */
55471         "rowdeselect" : true
55472     });
55473     Roo.grid.RowSelectionModel.superclass.constructor.call(this);
55474     this.locked = false;
55475 };
55476
55477 Roo.extend(Roo.grid.RowSelectionModel, Roo.grid.AbstractSelectionModel,  {
55478     /**
55479      * @cfg {Boolean} singleSelect
55480      * True to allow selection of only one row at a time (defaults to false)
55481      */
55482     singleSelect : false,
55483
55484     // private
55485     initEvents : function(){
55486
55487         if(!this.grid.enableDragDrop && !this.grid.enableDrag){
55488             this.grid.on("mousedown", this.handleMouseDown, this);
55489         }else{ // allow click to work like normal
55490             this.grid.on("rowclick", this.handleDragableRowClick, this);
55491         }
55492
55493         this.rowNav = new Roo.KeyNav(this.grid.getGridEl(), {
55494             "up" : function(e){
55495                 if(!e.shiftKey){
55496                     this.selectPrevious(e.shiftKey);
55497                 }else if(this.last !== false && this.lastActive !== false){
55498                     var last = this.last;
55499                     this.selectRange(this.last,  this.lastActive-1);
55500                     this.grid.getView().focusRow(this.lastActive);
55501                     if(last !== false){
55502                         this.last = last;
55503                     }
55504                 }else{
55505                     this.selectFirstRow();
55506                 }
55507                 this.fireEvent("afterselectionchange", this);
55508             },
55509             "down" : function(e){
55510                 if(!e.shiftKey){
55511                     this.selectNext(e.shiftKey);
55512                 }else if(this.last !== false && this.lastActive !== false){
55513                     var last = this.last;
55514                     this.selectRange(this.last,  this.lastActive+1);
55515                     this.grid.getView().focusRow(this.lastActive);
55516                     if(last !== false){
55517                         this.last = last;
55518                     }
55519                 }else{
55520                     this.selectFirstRow();
55521                 }
55522                 this.fireEvent("afterselectionchange", this);
55523             },
55524             scope: this
55525         });
55526
55527         var view = this.grid.view;
55528         view.on("refresh", this.onRefresh, this);
55529         view.on("rowupdated", this.onRowUpdated, this);
55530         view.on("rowremoved", this.onRemove, this);
55531     },
55532
55533     // private
55534     onRefresh : function(){
55535         var ds = this.grid.dataSource, i, v = this.grid.view;
55536         var s = this.selections;
55537         s.each(function(r){
55538             if((i = ds.indexOfId(r.id)) != -1){
55539                 v.onRowSelect(i);
55540             }else{
55541                 s.remove(r);
55542             }
55543         });
55544     },
55545
55546     // private
55547     onRemove : function(v, index, r){
55548         this.selections.remove(r);
55549     },
55550
55551     // private
55552     onRowUpdated : function(v, index, r){
55553         if(this.isSelected(r)){
55554             v.onRowSelect(index);
55555         }
55556     },
55557
55558     /**
55559      * Select records.
55560      * @param {Array} records The records to select
55561      * @param {Boolean} keepExisting (optional) True to keep existing selections
55562      */
55563     selectRecords : function(records, keepExisting){
55564         if(!keepExisting){
55565             this.clearSelections();
55566         }
55567         var ds = this.grid.dataSource;
55568         for(var i = 0, len = records.length; i < len; i++){
55569             this.selectRow(ds.indexOf(records[i]), true);
55570         }
55571     },
55572
55573     /**
55574      * Gets the number of selected rows.
55575      * @return {Number}
55576      */
55577     getCount : function(){
55578         return this.selections.length;
55579     },
55580
55581     /**
55582      * Selects the first row in the grid.
55583      */
55584     selectFirstRow : function(){
55585         this.selectRow(0);
55586     },
55587
55588     /**
55589      * Select the last row.
55590      * @param {Boolean} keepExisting (optional) True to keep existing selections
55591      */
55592     selectLastRow : function(keepExisting){
55593         this.selectRow(this.grid.dataSource.getCount() - 1, keepExisting);
55594     },
55595
55596     /**
55597      * Selects the row immediately following the last selected row.
55598      * @param {Boolean} keepExisting (optional) True to keep existing selections
55599      */
55600     selectNext : function(keepExisting){
55601         if(this.last !== false && (this.last+1) < this.grid.dataSource.getCount()){
55602             this.selectRow(this.last+1, keepExisting);
55603             this.grid.getView().focusRow(this.last);
55604         }
55605     },
55606
55607     /**
55608      * Selects the row that precedes the last selected row.
55609      * @param {Boolean} keepExisting (optional) True to keep existing selections
55610      */
55611     selectPrevious : function(keepExisting){
55612         if(this.last){
55613             this.selectRow(this.last-1, keepExisting);
55614             this.grid.getView().focusRow(this.last);
55615         }
55616     },
55617
55618     /**
55619      * Returns the selected records
55620      * @return {Array} Array of selected records
55621      */
55622     getSelections : function(){
55623         return [].concat(this.selections.items);
55624     },
55625
55626     /**
55627      * Returns the first selected record.
55628      * @return {Record}
55629      */
55630     getSelected : function(){
55631         return this.selections.itemAt(0);
55632     },
55633
55634
55635     /**
55636      * Clears all selections.
55637      */
55638     clearSelections : function(fast){
55639         if(this.locked) return;
55640         if(fast !== true){
55641             var ds = this.grid.dataSource;
55642             var s = this.selections;
55643             s.each(function(r){
55644                 this.deselectRow(ds.indexOfId(r.id));
55645             }, this);
55646             s.clear();
55647         }else{
55648             this.selections.clear();
55649         }
55650         this.last = false;
55651     },
55652
55653
55654     /**
55655      * Selects all rows.
55656      */
55657     selectAll : function(){
55658         if(this.locked) return;
55659         this.selections.clear();
55660         for(var i = 0, len = this.grid.dataSource.getCount(); i < len; i++){
55661             this.selectRow(i, true);
55662         }
55663     },
55664
55665     /**
55666      * Returns True if there is a selection.
55667      * @return {Boolean}
55668      */
55669     hasSelection : function(){
55670         return this.selections.length > 0;
55671     },
55672
55673     /**
55674      * Returns True if the specified row is selected.
55675      * @param {Number/Record} record The record or index of the record to check
55676      * @return {Boolean}
55677      */
55678     isSelected : function(index){
55679         var r = typeof index == "number" ? this.grid.dataSource.getAt(index) : index;
55680         return (r && this.selections.key(r.id) ? true : false);
55681     },
55682
55683     /**
55684      * Returns True if the specified record id is selected.
55685      * @param {String} id The id of record to check
55686      * @return {Boolean}
55687      */
55688     isIdSelected : function(id){
55689         return (this.selections.key(id) ? true : false);
55690     },
55691
55692     // private
55693     handleMouseDown : function(e, t){
55694         var view = this.grid.getView(), rowIndex;
55695         if(this.isLocked() || (rowIndex = view.findRowIndex(t)) === false){
55696             return;
55697         };
55698         if(e.shiftKey && this.last !== false){
55699             var last = this.last;
55700             this.selectRange(last, rowIndex, e.ctrlKey);
55701             this.last = last; // reset the last
55702             view.focusRow(rowIndex);
55703         }else{
55704             var isSelected = this.isSelected(rowIndex);
55705             if(e.button !== 0 && isSelected){
55706                 view.focusRow(rowIndex);
55707             }else if(e.ctrlKey && isSelected){
55708                 this.deselectRow(rowIndex);
55709             }else if(!isSelected){
55710                 this.selectRow(rowIndex, e.button === 0 && (e.ctrlKey || e.shiftKey));
55711                 view.focusRow(rowIndex);
55712             }
55713         }
55714         this.fireEvent("afterselectionchange", this);
55715     },
55716     // private
55717     handleDragableRowClick :  function(grid, rowIndex, e) 
55718     {
55719         if(e.button === 0 && !e.shiftKey && !e.ctrlKey) {
55720             this.selectRow(rowIndex, false);
55721             grid.view.focusRow(rowIndex);
55722              this.fireEvent("afterselectionchange", this);
55723         }
55724     },
55725     
55726     /**
55727      * Selects multiple rows.
55728      * @param {Array} rows Array of the indexes of the row to select
55729      * @param {Boolean} keepExisting (optional) True to keep existing selections
55730      */
55731     selectRows : function(rows, keepExisting){
55732         if(!keepExisting){
55733             this.clearSelections();
55734         }
55735         for(var i = 0, len = rows.length; i < len; i++){
55736             this.selectRow(rows[i], true);
55737         }
55738     },
55739
55740     /**
55741      * Selects a range of rows. All rows in between startRow and endRow are also selected.
55742      * @param {Number} startRow The index of the first row in the range
55743      * @param {Number} endRow The index of the last row in the range
55744      * @param {Boolean} keepExisting (optional) True to retain existing selections
55745      */
55746     selectRange : function(startRow, endRow, keepExisting){
55747         if(this.locked) return;
55748         if(!keepExisting){
55749             this.clearSelections();
55750         }
55751         if(startRow <= endRow){
55752             for(var i = startRow; i <= endRow; i++){
55753                 this.selectRow(i, true);
55754             }
55755         }else{
55756             for(var i = startRow; i >= endRow; i--){
55757                 this.selectRow(i, true);
55758             }
55759         }
55760     },
55761
55762     /**
55763      * Deselects a range of rows. All rows in between startRow and endRow are also deselected.
55764      * @param {Number} startRow The index of the first row in the range
55765      * @param {Number} endRow The index of the last row in the range
55766      */
55767     deselectRange : function(startRow, endRow, preventViewNotify){
55768         if(this.locked) return;
55769         for(var i = startRow; i <= endRow; i++){
55770             this.deselectRow(i, preventViewNotify);
55771         }
55772     },
55773
55774     /**
55775      * Selects a row.
55776      * @param {Number} row The index of the row to select
55777      * @param {Boolean} keepExisting (optional) True to keep existing selections
55778      */
55779     selectRow : function(index, keepExisting, preventViewNotify){
55780         if(this.locked || (index < 0 || index >= this.grid.dataSource.getCount())) return;
55781         if(this.fireEvent("beforerowselect", this, index, keepExisting) !== false){
55782             if(!keepExisting || this.singleSelect){
55783                 this.clearSelections();
55784             }
55785             var r = this.grid.dataSource.getAt(index);
55786             this.selections.add(r);
55787             this.last = this.lastActive = index;
55788             if(!preventViewNotify){
55789                 this.grid.getView().onRowSelect(index);
55790             }
55791             this.fireEvent("rowselect", this, index, r);
55792             this.fireEvent("selectionchange", this);
55793         }
55794     },
55795
55796     /**
55797      * Deselects a row.
55798      * @param {Number} row The index of the row to deselect
55799      */
55800     deselectRow : function(index, preventViewNotify){
55801         if(this.locked) return;
55802         if(this.last == index){
55803             this.last = false;
55804         }
55805         if(this.lastActive == index){
55806             this.lastActive = false;
55807         }
55808         var r = this.grid.dataSource.getAt(index);
55809         this.selections.remove(r);
55810         if(!preventViewNotify){
55811             this.grid.getView().onRowDeselect(index);
55812         }
55813         this.fireEvent("rowdeselect", this, index);
55814         this.fireEvent("selectionchange", this);
55815     },
55816
55817     // private
55818     restoreLast : function(){
55819         if(this._last){
55820             this.last = this._last;
55821         }
55822     },
55823
55824     // private
55825     acceptsNav : function(row, col, cm){
55826         return !cm.isHidden(col) && cm.isCellEditable(col, row);
55827     },
55828
55829     // private
55830     onEditorKey : function(field, e){
55831         var k = e.getKey(), newCell, g = this.grid, ed = g.activeEditor;
55832         if(k == e.TAB){
55833             e.stopEvent();
55834             ed.completeEdit();
55835             if(e.shiftKey){
55836                 newCell = g.walkCells(ed.row, ed.col-1, -1, this.acceptsNav, this);
55837             }else{
55838                 newCell = g.walkCells(ed.row, ed.col+1, 1, this.acceptsNav, this);
55839             }
55840         }else if(k == e.ENTER && !e.ctrlKey){
55841             e.stopEvent();
55842             ed.completeEdit();
55843             if(e.shiftKey){
55844                 newCell = g.walkCells(ed.row-1, ed.col, -1, this.acceptsNav, this);
55845             }else{
55846                 newCell = g.walkCells(ed.row+1, ed.col, 1, this.acceptsNav, this);
55847             }
55848         }else if(k == e.ESC){
55849             ed.cancelEdit();
55850         }
55851         if(newCell){
55852             g.startEditing(newCell[0], newCell[1]);
55853         }
55854     }
55855 });/*
55856  * Based on:
55857  * Ext JS Library 1.1.1
55858  * Copyright(c) 2006-2007, Ext JS, LLC.
55859  *
55860  * Originally Released Under LGPL - original licence link has changed is not relivant.
55861  *
55862  * Fork - LGPL
55863  * <script type="text/javascript">
55864  */
55865 /**
55866  * @class Roo.grid.CellSelectionModel
55867  * @extends Roo.grid.AbstractSelectionModel
55868  * This class provides the basic implementation for cell selection in a grid.
55869  * @constructor
55870  * @param {Object} config The object containing the configuration of this model.
55871  * @cfg {Boolean} enter_is_tab Enter behaves the same as tab. (eg. goes to next cell) default: false
55872  */
55873 Roo.grid.CellSelectionModel = function(config){
55874     Roo.apply(this, config);
55875
55876     this.selection = null;
55877
55878     this.addEvents({
55879         /**
55880              * @event beforerowselect
55881              * Fires before a cell is selected.
55882              * @param {SelectionModel} this
55883              * @param {Number} rowIndex The selected row index
55884              * @param {Number} colIndex The selected cell index
55885              */
55886             "beforecellselect" : true,
55887         /**
55888              * @event cellselect
55889              * Fires when a cell is selected.
55890              * @param {SelectionModel} this
55891              * @param {Number} rowIndex The selected row index
55892              * @param {Number} colIndex The selected cell index
55893              */
55894             "cellselect" : true,
55895         /**
55896              * @event selectionchange
55897              * Fires when the active selection changes.
55898              * @param {SelectionModel} this
55899              * @param {Object} selection null for no selection or an object (o) with two properties
55900                 <ul>
55901                 <li>o.record: the record object for the row the selection is in</li>
55902                 <li>o.cell: An array of [rowIndex, columnIndex]</li>
55903                 </ul>
55904              */
55905             "selectionchange" : true,
55906         /**
55907              * @event tabend
55908              * Fires when the tab (or enter) was pressed on the last editable cell
55909              * You can use this to trigger add new row.
55910              * @param {SelectionModel} this
55911              */
55912             "tabend" : true,
55913          /**
55914              * @event beforeeditnext
55915              * Fires before the next editable sell is made active
55916              * You can use this to skip to another cell or fire the tabend
55917              *    if you set cell to false
55918              * @param {Object} eventdata object : { cell : [ row, col ] } 
55919              */
55920             "beforeeditnext" : true
55921     });
55922     Roo.grid.CellSelectionModel.superclass.constructor.call(this);
55923 };
55924
55925 Roo.extend(Roo.grid.CellSelectionModel, Roo.grid.AbstractSelectionModel,  {
55926     
55927     enter_is_tab: false,
55928
55929     /** @ignore */
55930     initEvents : function(){
55931         this.grid.on("mousedown", this.handleMouseDown, this);
55932         this.grid.getGridEl().on(Roo.isIE ? "keydown" : "keypress", this.handleKeyDown, this);
55933         var view = this.grid.view;
55934         view.on("refresh", this.onViewChange, this);
55935         view.on("rowupdated", this.onRowUpdated, this);
55936         view.on("beforerowremoved", this.clearSelections, this);
55937         view.on("beforerowsinserted", this.clearSelections, this);
55938         if(this.grid.isEditor){
55939             this.grid.on("beforeedit", this.beforeEdit,  this);
55940         }
55941     },
55942
55943         //private
55944     beforeEdit : function(e){
55945         this.select(e.row, e.column, false, true, e.record);
55946     },
55947
55948         //private
55949     onRowUpdated : function(v, index, r){
55950         if(this.selection && this.selection.record == r){
55951             v.onCellSelect(index, this.selection.cell[1]);
55952         }
55953     },
55954
55955         //private
55956     onViewChange : function(){
55957         this.clearSelections(true);
55958     },
55959
55960         /**
55961          * Returns the currently selected cell,.
55962          * @return {Array} The selected cell (row, column) or null if none selected.
55963          */
55964     getSelectedCell : function(){
55965         return this.selection ? this.selection.cell : null;
55966     },
55967
55968     /**
55969      * Clears all selections.
55970      * @param {Boolean} true to prevent the gridview from being notified about the change.
55971      */
55972     clearSelections : function(preventNotify){
55973         var s = this.selection;
55974         if(s){
55975             if(preventNotify !== true){
55976                 this.grid.view.onCellDeselect(s.cell[0], s.cell[1]);
55977             }
55978             this.selection = null;
55979             this.fireEvent("selectionchange", this, null);
55980         }
55981     },
55982
55983     /**
55984      * Returns true if there is a selection.
55985      * @return {Boolean}
55986      */
55987     hasSelection : function(){
55988         return this.selection ? true : false;
55989     },
55990
55991     /** @ignore */
55992     handleMouseDown : function(e, t){
55993         var v = this.grid.getView();
55994         if(this.isLocked()){
55995             return;
55996         };
55997         var row = v.findRowIndex(t);
55998         var cell = v.findCellIndex(t);
55999         if(row !== false && cell !== false){
56000             this.select(row, cell);
56001         }
56002     },
56003
56004     /**
56005      * Selects a cell.
56006      * @param {Number} rowIndex
56007      * @param {Number} collIndex
56008      */
56009     select : function(rowIndex, colIndex, preventViewNotify, preventFocus, /*internal*/ r){
56010         if(this.fireEvent("beforecellselect", this, rowIndex, colIndex) !== false){
56011             this.clearSelections();
56012             r = r || this.grid.dataSource.getAt(rowIndex);
56013             this.selection = {
56014                 record : r,
56015                 cell : [rowIndex, colIndex]
56016             };
56017             if(!preventViewNotify){
56018                 var v = this.grid.getView();
56019                 v.onCellSelect(rowIndex, colIndex);
56020                 if(preventFocus !== true){
56021                     v.focusCell(rowIndex, colIndex);
56022                 }
56023             }
56024             this.fireEvent("cellselect", this, rowIndex, colIndex);
56025             this.fireEvent("selectionchange", this, this.selection);
56026         }
56027     },
56028
56029         //private
56030     isSelectable : function(rowIndex, colIndex, cm){
56031         return !cm.isHidden(colIndex);
56032     },
56033
56034     /** @ignore */
56035     handleKeyDown : function(e){
56036         //Roo.log('Cell Sel Model handleKeyDown');
56037         if(!e.isNavKeyPress()){
56038             return;
56039         }
56040         var g = this.grid, s = this.selection;
56041         if(!s){
56042             e.stopEvent();
56043             var cell = g.walkCells(0, 0, 1, this.isSelectable,  this);
56044             if(cell){
56045                 this.select(cell[0], cell[1]);
56046             }
56047             return;
56048         }
56049         var sm = this;
56050         var walk = function(row, col, step){
56051             return g.walkCells(row, col, step, sm.isSelectable,  sm);
56052         };
56053         var k = e.getKey(), r = s.cell[0], c = s.cell[1];
56054         var newCell;
56055
56056       
56057
56058         switch(k){
56059             case e.TAB:
56060                 // handled by onEditorKey
56061                 if (g.isEditor && g.editing) {
56062                     return;
56063                 }
56064                 if(e.shiftKey) {
56065                     newCell = walk(r, c-1, -1);
56066                 } else {
56067                     newCell = walk(r, c+1, 1);
56068                 }
56069                 break;
56070             
56071             case e.DOWN:
56072                newCell = walk(r+1, c, 1);
56073                 break;
56074             
56075             case e.UP:
56076                 newCell = walk(r-1, c, -1);
56077                 break;
56078             
56079             case e.RIGHT:
56080                 newCell = walk(r, c+1, 1);
56081                 break;
56082             
56083             case e.LEFT:
56084                 newCell = walk(r, c-1, -1);
56085                 break;
56086             
56087             case e.ENTER:
56088                 
56089                 if(g.isEditor && !g.editing){
56090                    g.startEditing(r, c);
56091                    e.stopEvent();
56092                    return;
56093                 }
56094                 
56095                 
56096              break;
56097         };
56098         if(newCell){
56099             this.select(newCell[0], newCell[1]);
56100             e.stopEvent();
56101             
56102         }
56103     },
56104
56105     acceptsNav : function(row, col, cm){
56106         return !cm.isHidden(col) && cm.isCellEditable(col, row);
56107     },
56108     /**
56109      * Selects a cell.
56110      * @param {Number} field (not used) - as it's normally used as a listener
56111      * @param {Number} e - event - fake it by using
56112      *
56113      * var e = Roo.EventObjectImpl.prototype;
56114      * e.keyCode = e.TAB
56115      *
56116      * 
56117      */
56118     onEditorKey : function(field, e){
56119         
56120         var k = e.getKey(),
56121             newCell,
56122             g = this.grid,
56123             ed = g.activeEditor,
56124             forward = false;
56125         ///Roo.log('onEditorKey' + k);
56126         
56127         
56128         if (this.enter_is_tab && k == e.ENTER) {
56129             k = e.TAB;
56130         }
56131         
56132         if(k == e.TAB){
56133             if(e.shiftKey){
56134                 newCell = g.walkCells(ed.row, ed.col-1, -1, this.acceptsNav, this);
56135             }else{
56136                 newCell = g.walkCells(ed.row, ed.col+1, 1, this.acceptsNav, this);
56137                 forward = true;
56138             }
56139             
56140             e.stopEvent();
56141             
56142         } else if(k == e.ENTER &&  !e.ctrlKey){
56143             ed.completeEdit();
56144             e.stopEvent();
56145             newCell = g.walkCells(ed.row, ed.col+1, 1, this.acceptsNav, this);
56146         
56147                 } else if(k == e.ESC){
56148             ed.cancelEdit();
56149         }
56150                 
56151         if (newCell) {
56152             var ecall = { cell : newCell, forward : forward };
56153             this.fireEvent('beforeeditnext', ecall );
56154             newCell = ecall.cell;
56155                         forward = ecall.forward;
56156         }
56157                 
56158         if(newCell){
56159             //Roo.log('next cell after edit');
56160             g.startEditing.defer(100, g, [newCell[0], newCell[1]]);
56161         } else if (forward) {
56162             // tabbed past last
56163             this.fireEvent.defer(100, this, ['tabend',this]);
56164         }
56165     }
56166 });/*
56167  * Based on:
56168  * Ext JS Library 1.1.1
56169  * Copyright(c) 2006-2007, Ext JS, LLC.
56170  *
56171  * Originally Released Under LGPL - original licence link has changed is not relivant.
56172  *
56173  * Fork - LGPL
56174  * <script type="text/javascript">
56175  */
56176  
56177 /**
56178  * @class Roo.grid.EditorGrid
56179  * @extends Roo.grid.Grid
56180  * Class for creating and editable grid.
56181  * @param {String/HTMLElement/Roo.Element} container The element into which this grid will be rendered - 
56182  * The container MUST have some type of size defined for the grid to fill. The container will be 
56183  * automatically set to position relative if it isn't already.
56184  * @param {Object} dataSource The data model to bind to
56185  * @param {Object} colModel The column model with info about this grid's columns
56186  */
56187 Roo.grid.EditorGrid = function(container, config){
56188     Roo.grid.EditorGrid.superclass.constructor.call(this, container, config);
56189     this.getGridEl().addClass("xedit-grid");
56190
56191     if(!this.selModel){
56192         this.selModel = new Roo.grid.CellSelectionModel();
56193     }
56194
56195     this.activeEditor = null;
56196
56197         this.addEvents({
56198             /**
56199              * @event beforeedit
56200              * Fires before cell editing is triggered. The edit event object has the following properties <br />
56201              * <ul style="padding:5px;padding-left:16px;">
56202              * <li>grid - This grid</li>
56203              * <li>record - The record being edited</li>
56204              * <li>field - The field name being edited</li>
56205              * <li>value - The value for the field being edited.</li>
56206              * <li>row - The grid row index</li>
56207              * <li>column - The grid column index</li>
56208              * <li>cancel - Set this to true to cancel the edit or return false from your handler.</li>
56209              * </ul>
56210              * @param {Object} e An edit event (see above for description)
56211              */
56212             "beforeedit" : true,
56213             /**
56214              * @event afteredit
56215              * Fires after a cell is edited. <br />
56216              * <ul style="padding:5px;padding-left:16px;">
56217              * <li>grid - This grid</li>
56218              * <li>record - The record being edited</li>
56219              * <li>field - The field name being edited</li>
56220              * <li>value - The value being set</li>
56221              * <li>originalValue - The original value for the field, before the edit.</li>
56222              * <li>row - The grid row index</li>
56223              * <li>column - The grid column index</li>
56224              * </ul>
56225              * @param {Object} e An edit event (see above for description)
56226              */
56227             "afteredit" : true,
56228             /**
56229              * @event validateedit
56230              * Fires after a cell is edited, but before the value is set in the record. 
56231          * You can use this to modify the value being set in the field, Return false
56232              * to cancel the change. The edit event object has the following properties <br />
56233              * <ul style="padding:5px;padding-left:16px;">
56234          * <li>editor - This editor</li>
56235              * <li>grid - This grid</li>
56236              * <li>record - The record being edited</li>
56237              * <li>field - The field name being edited</li>
56238              * <li>value - The value being set</li>
56239              * <li>originalValue - The original value for the field, before the edit.</li>
56240              * <li>row - The grid row index</li>
56241              * <li>column - The grid column index</li>
56242              * <li>cancel - Set this to true to cancel the edit or return false from your handler.</li>
56243              * </ul>
56244              * @param {Object} e An edit event (see above for description)
56245              */
56246             "validateedit" : true
56247         });
56248     this.on("bodyscroll", this.stopEditing,  this);
56249     this.on(this.clicksToEdit == 1 ? "cellclick" : "celldblclick", this.onCellDblClick,  this);
56250 };
56251
56252 Roo.extend(Roo.grid.EditorGrid, Roo.grid.Grid, {
56253     /**
56254      * @cfg {Number} clicksToEdit
56255      * The number of clicks on a cell required to display the cell's editor (defaults to 2)
56256      */
56257     clicksToEdit: 2,
56258
56259     // private
56260     isEditor : true,
56261     // private
56262     trackMouseOver: false, // causes very odd FF errors
56263
56264     onCellDblClick : function(g, row, col){
56265         this.startEditing(row, col);
56266     },
56267
56268     onEditComplete : function(ed, value, startValue){
56269         this.editing = false;
56270         this.activeEditor = null;
56271         ed.un("specialkey", this.selModel.onEditorKey, this.selModel);
56272         var r = ed.record;
56273         var field = this.colModel.getDataIndex(ed.col);
56274         var e = {
56275             grid: this,
56276             record: r,
56277             field: field,
56278             originalValue: startValue,
56279             value: value,
56280             row: ed.row,
56281             column: ed.col,
56282             cancel:false,
56283             editor: ed
56284         };
56285         var cell = Roo.get(this.view.getCell(ed.row,ed.col))
56286         cell.show();
56287           
56288         if(String(value) !== String(startValue)){
56289             
56290             if(this.fireEvent("validateedit", e) !== false && !e.cancel){
56291                 r.set(field, e.value);
56292                 // if we are dealing with a combo box..
56293                 // then we also set the 'name' colum to be the displayField
56294                 if (ed.field.displayField && ed.field.name) {
56295                     r.set(ed.field.name, ed.field.el.dom.value);
56296                 }
56297                 
56298                 delete e.cancel; //?? why!!!
56299                 this.fireEvent("afteredit", e);
56300             }
56301         } else {
56302             this.fireEvent("afteredit", e); // always fire it!
56303         }
56304         this.view.focusCell(ed.row, ed.col);
56305     },
56306
56307     /**
56308      * Starts editing the specified for the specified row/column
56309      * @param {Number} rowIndex
56310      * @param {Number} colIndex
56311      */
56312     startEditing : function(row, col){
56313         this.stopEditing();
56314         if(this.colModel.isCellEditable(col, row)){
56315             this.view.ensureVisible(row, col, true);
56316           
56317             var r = this.dataSource.getAt(row);
56318             var field = this.colModel.getDataIndex(col);
56319             var cell = Roo.get(this.view.getCell(row,col));
56320             var e = {
56321                 grid: this,
56322                 record: r,
56323                 field: field,
56324                 value: r.data[field],
56325                 row: row,
56326                 column: col,
56327                 cancel:false 
56328             };
56329             if(this.fireEvent("beforeedit", e) !== false && !e.cancel){
56330                 this.editing = true;
56331                 var ed = this.colModel.getCellEditor(col, row);
56332                 
56333                 if (!ed) {
56334                     return;
56335                 }
56336                 if(!ed.rendered){
56337                     ed.render(ed.parentEl || document.body);
56338                 }
56339                 ed.field.reset();
56340                
56341                 cell.hide();
56342                 
56343                 (function(){ // complex but required for focus issues in safari, ie and opera
56344                     ed.row = row;
56345                     ed.col = col;
56346                     ed.record = r;
56347                     ed.on("complete",   this.onEditComplete,        this,       {single: true});
56348                     ed.on("specialkey", this.selModel.onEditorKey,  this.selModel);
56349                     this.activeEditor = ed;
56350                     var v = r.data[field];
56351                     ed.startEdit(this.view.getCell(row, col), v);
56352                     // combo's with 'displayField and name set
56353                     if (ed.field.displayField && ed.field.name) {
56354                         ed.field.el.dom.value = r.data[ed.field.name];
56355                     }
56356                     
56357                     
56358                 }).defer(50, this);
56359             }
56360         }
56361     },
56362         
56363     /**
56364      * Stops any active editing
56365      */
56366     stopEditing : function(){
56367         if(this.activeEditor){
56368             this.activeEditor.completeEdit();
56369         }
56370         this.activeEditor = null;
56371     },
56372         
56373          /**
56374      * Called to get grid's drag proxy text, by default returns this.ddText.
56375      * @return {String}
56376      */
56377     getDragDropText : function(){
56378         var count = this.selModel.getSelectedCell() ? 1 : 0;
56379         return String.format(this.ddText, count, count == 1 ? '' : 's');
56380     }
56381         
56382 });/*
56383  * Based on:
56384  * Ext JS Library 1.1.1
56385  * Copyright(c) 2006-2007, Ext JS, LLC.
56386  *
56387  * Originally Released Under LGPL - original licence link has changed is not relivant.
56388  *
56389  * Fork - LGPL
56390  * <script type="text/javascript">
56391  */
56392
56393 // private - not really -- you end up using it !
56394 // This is a support class used internally by the Grid components
56395
56396 /**
56397  * @class Roo.grid.GridEditor
56398  * @extends Roo.Editor
56399  * Class for creating and editable grid elements.
56400  * @param {Object} config any settings (must include field)
56401  */
56402 Roo.grid.GridEditor = function(field, config){
56403     if (!config && field.field) {
56404         config = field;
56405         field = Roo.factory(config.field, Roo.form);
56406     }
56407     Roo.grid.GridEditor.superclass.constructor.call(this, field, config);
56408     field.monitorTab = false;
56409 };
56410
56411 Roo.extend(Roo.grid.GridEditor, Roo.Editor, {
56412     
56413     /**
56414      * @cfg {Roo.form.Field} field Field to wrap (or xtyped)
56415      */
56416     
56417     alignment: "tl-tl",
56418     autoSize: "width",
56419     hideEl : false,
56420     cls: "x-small-editor x-grid-editor",
56421     shim:false,
56422     shadow:"frame"
56423 });/*
56424  * Based on:
56425  * Ext JS Library 1.1.1
56426  * Copyright(c) 2006-2007, Ext JS, LLC.
56427  *
56428  * Originally Released Under LGPL - original licence link has changed is not relivant.
56429  *
56430  * Fork - LGPL
56431  * <script type="text/javascript">
56432  */
56433   
56434
56435   
56436 Roo.grid.PropertyRecord = Roo.data.Record.create([
56437     {name:'name',type:'string'},  'value'
56438 ]);
56439
56440
56441 Roo.grid.PropertyStore = function(grid, source){
56442     this.grid = grid;
56443     this.store = new Roo.data.Store({
56444         recordType : Roo.grid.PropertyRecord
56445     });
56446     this.store.on('update', this.onUpdate,  this);
56447     if(source){
56448         this.setSource(source);
56449     }
56450     Roo.grid.PropertyStore.superclass.constructor.call(this);
56451 };
56452
56453
56454
56455 Roo.extend(Roo.grid.PropertyStore, Roo.util.Observable, {
56456     setSource : function(o){
56457         this.source = o;
56458         this.store.removeAll();
56459         var data = [];
56460         for(var k in o){
56461             if(this.isEditableValue(o[k])){
56462                 data.push(new Roo.grid.PropertyRecord({name: k, value: o[k]}, k));
56463             }
56464         }
56465         this.store.loadRecords({records: data}, {}, true);
56466     },
56467
56468     onUpdate : function(ds, record, type){
56469         if(type == Roo.data.Record.EDIT){
56470             var v = record.data['value'];
56471             var oldValue = record.modified['value'];
56472             if(this.grid.fireEvent('beforepropertychange', this.source, record.id, v, oldValue) !== false){
56473                 this.source[record.id] = v;
56474                 record.commit();
56475                 this.grid.fireEvent('propertychange', this.source, record.id, v, oldValue);
56476             }else{
56477                 record.reject();
56478             }
56479         }
56480     },
56481
56482     getProperty : function(row){
56483        return this.store.getAt(row);
56484     },
56485
56486     isEditableValue: function(val){
56487         if(val && val instanceof Date){
56488             return true;
56489         }else if(typeof val == 'object' || typeof val == 'function'){
56490             return false;
56491         }
56492         return true;
56493     },
56494
56495     setValue : function(prop, value){
56496         this.source[prop] = value;
56497         this.store.getById(prop).set('value', value);
56498     },
56499
56500     getSource : function(){
56501         return this.source;
56502     }
56503 });
56504
56505 Roo.grid.PropertyColumnModel = function(grid, store){
56506     this.grid = grid;
56507     var g = Roo.grid;
56508     g.PropertyColumnModel.superclass.constructor.call(this, [
56509         {header: this.nameText, sortable: true, dataIndex:'name', id: 'name'},
56510         {header: this.valueText, resizable:false, dataIndex: 'value', id: 'value'}
56511     ]);
56512     this.store = store;
56513     this.bselect = Roo.DomHelper.append(document.body, {
56514         tag: 'select', style:'display:none', cls: 'x-grid-editor', children: [
56515             {tag: 'option', value: 'true', html: 'true'},
56516             {tag: 'option', value: 'false', html: 'false'}
56517         ]
56518     });
56519     Roo.id(this.bselect);
56520     var f = Roo.form;
56521     this.editors = {
56522         'date' : new g.GridEditor(new f.DateField({selectOnFocus:true})),
56523         'string' : new g.GridEditor(new f.TextField({selectOnFocus:true})),
56524         'number' : new g.GridEditor(new f.NumberField({selectOnFocus:true, style:'text-align:left;'})),
56525         'int' : new g.GridEditor(new f.NumberField({selectOnFocus:true, allowDecimals:false, style:'text-align:left;'})),
56526         'boolean' : new g.GridEditor(new f.Field({el:this.bselect,selectOnFocus:true}))
56527     };
56528     this.renderCellDelegate = this.renderCell.createDelegate(this);
56529     this.renderPropDelegate = this.renderProp.createDelegate(this);
56530 };
56531
56532 Roo.extend(Roo.grid.PropertyColumnModel, Roo.grid.ColumnModel, {
56533     
56534     
56535     nameText : 'Name',
56536     valueText : 'Value',
56537     
56538     dateFormat : 'm/j/Y',
56539     
56540     
56541     renderDate : function(dateVal){
56542         return dateVal.dateFormat(this.dateFormat);
56543     },
56544
56545     renderBool : function(bVal){
56546         return bVal ? 'true' : 'false';
56547     },
56548
56549     isCellEditable : function(colIndex, rowIndex){
56550         return colIndex == 1;
56551     },
56552
56553     getRenderer : function(col){
56554         return col == 1 ?
56555             this.renderCellDelegate : this.renderPropDelegate;
56556     },
56557
56558     renderProp : function(v){
56559         return this.getPropertyName(v);
56560     },
56561
56562     renderCell : function(val){
56563         var rv = val;
56564         if(val instanceof Date){
56565             rv = this.renderDate(val);
56566         }else if(typeof val == 'boolean'){
56567             rv = this.renderBool(val);
56568         }
56569         return Roo.util.Format.htmlEncode(rv);
56570     },
56571
56572     getPropertyName : function(name){
56573         var pn = this.grid.propertyNames;
56574         return pn && pn[name] ? pn[name] : name;
56575     },
56576
56577     getCellEditor : function(colIndex, rowIndex){
56578         var p = this.store.getProperty(rowIndex);
56579         var n = p.data['name'], val = p.data['value'];
56580         
56581         if(typeof(this.grid.customEditors[n]) == 'string'){
56582             return this.editors[this.grid.customEditors[n]];
56583         }
56584         if(typeof(this.grid.customEditors[n]) != 'undefined'){
56585             return this.grid.customEditors[n];
56586         }
56587         if(val instanceof Date){
56588             return this.editors['date'];
56589         }else if(typeof val == 'number'){
56590             return this.editors['number'];
56591         }else if(typeof val == 'boolean'){
56592             return this.editors['boolean'];
56593         }else{
56594             return this.editors['string'];
56595         }
56596     }
56597 });
56598
56599 /**
56600  * @class Roo.grid.PropertyGrid
56601  * @extends Roo.grid.EditorGrid
56602  * This class represents the  interface of a component based property grid control.
56603  * <br><br>Usage:<pre><code>
56604  var grid = new Roo.grid.PropertyGrid("my-container-id", {
56605       
56606  });
56607  // set any options
56608  grid.render();
56609  * </code></pre>
56610   
56611  * @constructor
56612  * @param {String/HTMLElement/Roo.Element} container The element into which this grid will be rendered -
56613  * The container MUST have some type of size defined for the grid to fill. The container will be
56614  * automatically set to position relative if it isn't already.
56615  * @param {Object} config A config object that sets properties on this grid.
56616  */
56617 Roo.grid.PropertyGrid = function(container, config){
56618     config = config || {};
56619     var store = new Roo.grid.PropertyStore(this);
56620     this.store = store;
56621     var cm = new Roo.grid.PropertyColumnModel(this, store);
56622     store.store.sort('name', 'ASC');
56623     Roo.grid.PropertyGrid.superclass.constructor.call(this, container, Roo.apply({
56624         ds: store.store,
56625         cm: cm,
56626         enableColLock:false,
56627         enableColumnMove:false,
56628         stripeRows:false,
56629         trackMouseOver: false,
56630         clicksToEdit:1
56631     }, config));
56632     this.getGridEl().addClass('x-props-grid');
56633     this.lastEditRow = null;
56634     this.on('columnresize', this.onColumnResize, this);
56635     this.addEvents({
56636          /**
56637              * @event beforepropertychange
56638              * Fires before a property changes (return false to stop?)
56639              * @param {Roo.grid.PropertyGrid} grid property grid? (check could be store)
56640              * @param {String} id Record Id
56641              * @param {String} newval New Value
56642          * @param {String} oldval Old Value
56643              */
56644         "beforepropertychange": true,
56645         /**
56646              * @event propertychange
56647              * Fires after a property changes
56648              * @param {Roo.grid.PropertyGrid} grid property grid? (check could be store)
56649              * @param {String} id Record Id
56650              * @param {String} newval New Value
56651          * @param {String} oldval Old Value
56652              */
56653         "propertychange": true
56654     });
56655     this.customEditors = this.customEditors || {};
56656 };
56657 Roo.extend(Roo.grid.PropertyGrid, Roo.grid.EditorGrid, {
56658     
56659      /**
56660      * @cfg {Object} customEditors map of colnames=> custom editors.
56661      * the custom editor can be one of the standard ones (date|string|number|int|boolean), or a
56662      * grid editor eg. Roo.grid.GridEditor(new Roo.form.TextArea({selectOnFocus:true})),
56663      * false disables editing of the field.
56664          */
56665     
56666       /**
56667      * @cfg {Object} propertyNames map of property Names to their displayed value
56668          */
56669     
56670     render : function(){
56671         Roo.grid.PropertyGrid.superclass.render.call(this);
56672         this.autoSize.defer(100, this);
56673     },
56674
56675     autoSize : function(){
56676         Roo.grid.PropertyGrid.superclass.autoSize.call(this);
56677         if(this.view){
56678             this.view.fitColumns();
56679         }
56680     },
56681
56682     onColumnResize : function(){
56683         this.colModel.setColumnWidth(1, this.container.getWidth(true)-this.colModel.getColumnWidth(0));
56684         this.autoSize();
56685     },
56686     /**
56687      * Sets the data for the Grid
56688      * accepts a Key => Value object of all the elements avaiable.
56689      * @param {Object} data  to appear in grid.
56690      */
56691     setSource : function(source){
56692         this.store.setSource(source);
56693         //this.autoSize();
56694     },
56695     /**
56696      * Gets all the data from the grid.
56697      * @return {Object} data  data stored in grid
56698      */
56699     getSource : function(){
56700         return this.store.getSource();
56701     }
56702 });/*
56703   
56704  * Licence LGPL
56705  
56706  */
56707  
56708 /**
56709  * @class Roo.grid.Calendar
56710  * @extends Roo.util.Grid
56711  * This class extends the Grid to provide a calendar widget
56712  * <br><br>Usage:<pre><code>
56713  var grid = new Roo.grid.Calendar("my-container-id", {
56714      ds: myDataStore,
56715      cm: myColModel,
56716      selModel: mySelectionModel,
56717      autoSizeColumns: true,
56718      monitorWindowResize: false,
56719      trackMouseOver: true
56720      eventstore : real data store..
56721  });
56722  // set any options
56723  grid.render();
56724   
56725   * @constructor
56726  * @param {String/HTMLElement/Roo.Element} container The element into which this grid will be rendered -
56727  * The container MUST have some type of size defined for the grid to fill. The container will be
56728  * automatically set to position relative if it isn't already.
56729  * @param {Object} config A config object that sets properties on this grid.
56730  */
56731 Roo.grid.Calendar = function(container, config){
56732         // initialize the container
56733         this.container = Roo.get(container);
56734         this.container.update("");
56735         this.container.setStyle("overflow", "hidden");
56736     this.container.addClass('x-grid-container');
56737
56738     this.id = this.container.id;
56739
56740     Roo.apply(this, config);
56741     // check and correct shorthanded configs
56742     
56743     var rows = [];
56744     var d =1;
56745     for (var r = 0;r < 6;r++) {
56746         
56747         rows[r]=[];
56748         for (var c =0;c < 7;c++) {
56749             rows[r][c]= '';
56750         }
56751     }
56752     if (this.eventStore) {
56753         this.eventStore= Roo.factory(this.eventStore, Roo.data);
56754         this.eventStore.on('load',this.onLoad, this);
56755         this.eventStore.on('beforeload',this.clearEvents, this);
56756          
56757     }
56758     
56759     this.dataSource = new Roo.data.Store({
56760             proxy: new Roo.data.MemoryProxy(rows),
56761             reader: new Roo.data.ArrayReader({}, [
56762                    'weekday0', 'weekday1', 'weekday2', 'weekday3', 'weekday4', 'weekday5', 'weekday6' ])
56763     });
56764
56765     this.dataSource.load();
56766     this.ds = this.dataSource;
56767     this.ds.xmodule = this.xmodule || false;
56768     
56769     
56770     var cellRender = function(v,x,r)
56771     {
56772         return String.format(
56773             '<div class="fc-day  fc-widget-content"><div>' +
56774                 '<div class="fc-event-container"></div>' +
56775                 '<div class="fc-day-number">{0}</div>'+
56776                 
56777                 '<div class="fc-day-content"><div style="position:relative"></div></div>' +
56778             '</div></div>', v);
56779     
56780     }
56781     
56782     
56783     this.colModel = new Roo.grid.ColumnModel( [
56784         {
56785             xtype: 'ColumnModel',
56786             xns: Roo.grid,
56787             dataIndex : 'weekday0',
56788             header : 'Sunday',
56789             renderer : cellRender
56790         },
56791         {
56792             xtype: 'ColumnModel',
56793             xns: Roo.grid,
56794             dataIndex : 'weekday1',
56795             header : 'Monday',
56796             renderer : cellRender
56797         },
56798         {
56799             xtype: 'ColumnModel',
56800             xns: Roo.grid,
56801             dataIndex : 'weekday2',
56802             header : 'Tuesday',
56803             renderer : cellRender
56804         },
56805         {
56806             xtype: 'ColumnModel',
56807             xns: Roo.grid,
56808             dataIndex : 'weekday3',
56809             header : 'Wednesday',
56810             renderer : cellRender
56811         },
56812         {
56813             xtype: 'ColumnModel',
56814             xns: Roo.grid,
56815             dataIndex : 'weekday4',
56816             header : 'Thursday',
56817             renderer : cellRender
56818         },
56819         {
56820             xtype: 'ColumnModel',
56821             xns: Roo.grid,
56822             dataIndex : 'weekday5',
56823             header : 'Friday',
56824             renderer : cellRender
56825         },
56826         {
56827             xtype: 'ColumnModel',
56828             xns: Roo.grid,
56829             dataIndex : 'weekday6',
56830             header : 'Saturday',
56831             renderer : cellRender
56832         }
56833     ]);
56834     this.cm = this.colModel;
56835     this.cm.xmodule = this.xmodule || false;
56836  
56837         
56838           
56839     //this.selModel = new Roo.grid.CellSelectionModel();
56840     //this.sm = this.selModel;
56841     //this.selModel.init(this);
56842     
56843     
56844     if(this.width){
56845         this.container.setWidth(this.width);
56846     }
56847
56848     if(this.height){
56849         this.container.setHeight(this.height);
56850     }
56851     /** @private */
56852         this.addEvents({
56853         // raw events
56854         /**
56855          * @event click
56856          * The raw click event for the entire grid.
56857          * @param {Roo.EventObject} e
56858          */
56859         "click" : true,
56860         /**
56861          * @event dblclick
56862          * The raw dblclick event for the entire grid.
56863          * @param {Roo.EventObject} e
56864          */
56865         "dblclick" : true,
56866         /**
56867          * @event contextmenu
56868          * The raw contextmenu event for the entire grid.
56869          * @param {Roo.EventObject} e
56870          */
56871         "contextmenu" : true,
56872         /**
56873          * @event mousedown
56874          * The raw mousedown event for the entire grid.
56875          * @param {Roo.EventObject} e
56876          */
56877         "mousedown" : true,
56878         /**
56879          * @event mouseup
56880          * The raw mouseup event for the entire grid.
56881          * @param {Roo.EventObject} e
56882          */
56883         "mouseup" : true,
56884         /**
56885          * @event mouseover
56886          * The raw mouseover event for the entire grid.
56887          * @param {Roo.EventObject} e
56888          */
56889         "mouseover" : true,
56890         /**
56891          * @event mouseout
56892          * The raw mouseout event for the entire grid.
56893          * @param {Roo.EventObject} e
56894          */
56895         "mouseout" : true,
56896         /**
56897          * @event keypress
56898          * The raw keypress event for the entire grid.
56899          * @param {Roo.EventObject} e
56900          */
56901         "keypress" : true,
56902         /**
56903          * @event keydown
56904          * The raw keydown event for the entire grid.
56905          * @param {Roo.EventObject} e
56906          */
56907         "keydown" : true,
56908
56909         // custom events
56910
56911         /**
56912          * @event cellclick
56913          * Fires when a cell is clicked
56914          * @param {Grid} this
56915          * @param {Number} rowIndex
56916          * @param {Number} columnIndex
56917          * @param {Roo.EventObject} e
56918          */
56919         "cellclick" : true,
56920         /**
56921          * @event celldblclick
56922          * Fires when a cell is double clicked
56923          * @param {Grid} this
56924          * @param {Number} rowIndex
56925          * @param {Number} columnIndex
56926          * @param {Roo.EventObject} e
56927          */
56928         "celldblclick" : true,
56929         /**
56930          * @event rowclick
56931          * Fires when a row is clicked
56932          * @param {Grid} this
56933          * @param {Number} rowIndex
56934          * @param {Roo.EventObject} e
56935          */
56936         "rowclick" : true,
56937         /**
56938          * @event rowdblclick
56939          * Fires when a row is double clicked
56940          * @param {Grid} this
56941          * @param {Number} rowIndex
56942          * @param {Roo.EventObject} e
56943          */
56944         "rowdblclick" : true,
56945         /**
56946          * @event headerclick
56947          * Fires when a header is clicked
56948          * @param {Grid} this
56949          * @param {Number} columnIndex
56950          * @param {Roo.EventObject} e
56951          */
56952         "headerclick" : true,
56953         /**
56954          * @event headerdblclick
56955          * Fires when a header cell is double clicked
56956          * @param {Grid} this
56957          * @param {Number} columnIndex
56958          * @param {Roo.EventObject} e
56959          */
56960         "headerdblclick" : true,
56961         /**
56962          * @event rowcontextmenu
56963          * Fires when a row is right clicked
56964          * @param {Grid} this
56965          * @param {Number} rowIndex
56966          * @param {Roo.EventObject} e
56967          */
56968         "rowcontextmenu" : true,
56969         /**
56970          * @event cellcontextmenu
56971          * Fires when a cell is right clicked
56972          * @param {Grid} this
56973          * @param {Number} rowIndex
56974          * @param {Number} cellIndex
56975          * @param {Roo.EventObject} e
56976          */
56977          "cellcontextmenu" : true,
56978         /**
56979          * @event headercontextmenu
56980          * Fires when a header is right clicked
56981          * @param {Grid} this
56982          * @param {Number} columnIndex
56983          * @param {Roo.EventObject} e
56984          */
56985         "headercontextmenu" : true,
56986         /**
56987          * @event bodyscroll
56988          * Fires when the body element is scrolled
56989          * @param {Number} scrollLeft
56990          * @param {Number} scrollTop
56991          */
56992         "bodyscroll" : true,
56993         /**
56994          * @event columnresize
56995          * Fires when the user resizes a column
56996          * @param {Number} columnIndex
56997          * @param {Number} newSize
56998          */
56999         "columnresize" : true,
57000         /**
57001          * @event columnmove
57002          * Fires when the user moves a column
57003          * @param {Number} oldIndex
57004          * @param {Number} newIndex
57005          */
57006         "columnmove" : true,
57007         /**
57008          * @event startdrag
57009          * Fires when row(s) start being dragged
57010          * @param {Grid} this
57011          * @param {Roo.GridDD} dd The drag drop object
57012          * @param {event} e The raw browser event
57013          */
57014         "startdrag" : true,
57015         /**
57016          * @event enddrag
57017          * Fires when a drag operation is complete
57018          * @param {Grid} this
57019          * @param {Roo.GridDD} dd The drag drop object
57020          * @param {event} e The raw browser event
57021          */
57022         "enddrag" : true,
57023         /**
57024          * @event dragdrop
57025          * Fires when dragged row(s) are dropped on a valid DD target
57026          * @param {Grid} this
57027          * @param {Roo.GridDD} dd The drag drop object
57028          * @param {String} targetId The target drag drop object
57029          * @param {event} e The raw browser event
57030          */
57031         "dragdrop" : true,
57032         /**
57033          * @event dragover
57034          * Fires while row(s) are being dragged. "targetId" is the id of the Yahoo.util.DD object the selected rows are being dragged over.
57035          * @param {Grid} this
57036          * @param {Roo.GridDD} dd The drag drop object
57037          * @param {String} targetId The target drag drop object
57038          * @param {event} e The raw browser event
57039          */
57040         "dragover" : true,
57041         /**
57042          * @event dragenter
57043          *  Fires when the dragged row(s) first cross another DD target while being dragged
57044          * @param {Grid} this
57045          * @param {Roo.GridDD} dd The drag drop object
57046          * @param {String} targetId The target drag drop object
57047          * @param {event} e The raw browser event
57048          */
57049         "dragenter" : true,
57050         /**
57051          * @event dragout
57052          * Fires when the dragged row(s) leave another DD target while being dragged
57053          * @param {Grid} this
57054          * @param {Roo.GridDD} dd The drag drop object
57055          * @param {String} targetId The target drag drop object
57056          * @param {event} e The raw browser event
57057          */
57058         "dragout" : true,
57059         /**
57060          * @event rowclass
57061          * Fires when a row is rendered, so you can change add a style to it.
57062          * @param {GridView} gridview   The grid view
57063          * @param {Object} rowcfg   contains record  rowIndex and rowClass - set rowClass to add a style.
57064          */
57065         'rowclass' : true,
57066
57067         /**
57068          * @event render
57069          * Fires when the grid is rendered
57070          * @param {Grid} grid
57071          */
57072         'render' : true,
57073             /**
57074              * @event select
57075              * Fires when a date is selected
57076              * @param {DatePicker} this
57077              * @param {Date} date The selected date
57078              */
57079         'select': true,
57080         /**
57081              * @event monthchange
57082              * Fires when the displayed month changes 
57083              * @param {DatePicker} this
57084              * @param {Date} date The selected month
57085              */
57086         'monthchange': true,
57087         /**
57088              * @event evententer
57089              * Fires when mouse over an event
57090              * @param {Calendar} this
57091              * @param {event} Event
57092              */
57093         'evententer': true,
57094         /**
57095              * @event eventleave
57096              * Fires when the mouse leaves an
57097              * @param {Calendar} this
57098              * @param {event}
57099              */
57100         'eventleave': true,
57101         /**
57102              * @event eventclick
57103              * Fires when the mouse click an
57104              * @param {Calendar} this
57105              * @param {event}
57106              */
57107         'eventclick': true,
57108         /**
57109              * @event eventrender
57110              * Fires before each cell is rendered, so you can modify the contents, like cls / title / qtip
57111              * @param {Calendar} this
57112              * @param {data} data to be modified
57113              */
57114         'eventrender': true
57115         
57116     });
57117
57118     Roo.grid.Grid.superclass.constructor.call(this);
57119     this.on('render', function() {
57120         this.view.el.addClass('x-grid-cal'); 
57121         
57122         (function() { this.setDate(new Date()); }).defer(100,this); //default today..
57123
57124     },this);
57125     
57126     if (!Roo.grid.Calendar.style) {
57127         Roo.grid.Calendar.style = Roo.util.CSS.createStyleSheet({
57128             
57129             
57130             '.x-grid-cal .x-grid-col' :  {
57131                 height: 'auto !important',
57132                 'vertical-align': 'top'
57133             },
57134             '.x-grid-cal  .fc-event-hori' : {
57135                 height: '14px'
57136             }
57137              
57138             
57139         }, Roo.id());
57140     }
57141
57142     
57143     
57144 };
57145 Roo.extend(Roo.grid.Calendar, Roo.grid.Grid, {
57146     /**
57147      * @cfg {Store} eventStore The store that loads events.
57148      */
57149     eventStore : 25,
57150
57151      
57152     activeDate : false,
57153     startDay : 0,
57154     autoWidth : true,
57155     monitorWindowResize : false,
57156
57157     
57158     resizeColumns : function() {
57159         var col = (this.view.el.getWidth() / 7) - 3;
57160         // loop through cols, and setWidth
57161         for(var i =0 ; i < 7 ; i++){
57162             this.cm.setColumnWidth(i, col);
57163         }
57164     },
57165      setDate :function(date) {
57166         
57167         Roo.log('setDate?');
57168         
57169         this.resizeColumns();
57170         var vd = this.activeDate;
57171         this.activeDate = date;
57172 //        if(vd && this.el){
57173 //            var t = date.getTime();
57174 //            if(vd.getMonth() == date.getMonth() && vd.getFullYear() == date.getFullYear()){
57175 //                Roo.log('using add remove');
57176 //                
57177 //                this.fireEvent('monthchange', this, date);
57178 //                
57179 //                this.cells.removeClass("fc-state-highlight");
57180 //                this.cells.each(function(c){
57181 //                   if(c.dateValue == t){
57182 //                       c.addClass("fc-state-highlight");
57183 //                       setTimeout(function(){
57184 //                            try{c.dom.firstChild.focus();}catch(e){}
57185 //                       }, 50);
57186 //                       return false;
57187 //                   }
57188 //                   return true;
57189 //                });
57190 //                return;
57191 //            }
57192 //        }
57193         
57194         var days = date.getDaysInMonth();
57195         
57196         var firstOfMonth = date.getFirstDateOfMonth();
57197         var startingPos = firstOfMonth.getDay()-this.startDay;
57198         
57199         if(startingPos < this.startDay){
57200             startingPos += 7;
57201         }
57202         
57203         var pm = date.add(Date.MONTH, -1);
57204         var prevStart = pm.getDaysInMonth()-startingPos;
57205 //        
57206         
57207         
57208         this.cells = this.view.el.select('.x-grid-row .x-grid-col',true);
57209         
57210         this.textNodes = this.view.el.query('.x-grid-row .x-grid-col .x-grid-cell-text');
57211         //this.cells.addClassOnOver('fc-state-hover');
57212         
57213         var cells = this.cells.elements;
57214         var textEls = this.textNodes;
57215         
57216         //Roo.each(cells, function(cell){
57217         //    cell.removeClass([ 'fc-past', 'fc-other-month', 'fc-future', 'fc-state-highlight', 'fc-state-disabled']);
57218         //});
57219         
57220         days += startingPos;
57221
57222         // convert everything to numbers so it's fast
57223         var day = 86400000;
57224         var d = (new Date(pm.getFullYear(), pm.getMonth(), prevStart)).clearTime();
57225         //Roo.log(d);
57226         //Roo.log(pm);
57227         //Roo.log(prevStart);
57228         
57229         var today = new Date().clearTime().getTime();
57230         var sel = date.clearTime().getTime();
57231         var min = this.minDate ? this.minDate.clearTime() : Number.NEGATIVE_INFINITY;
57232         var max = this.maxDate ? this.maxDate.clearTime() : Number.POSITIVE_INFINITY;
57233         var ddMatch = this.disabledDatesRE;
57234         var ddText = this.disabledDatesText;
57235         var ddays = this.disabledDays ? this.disabledDays.join("") : false;
57236         var ddaysText = this.disabledDaysText;
57237         var format = this.format;
57238         
57239         var setCellClass = function(cal, cell){
57240             
57241             //Roo.log('set Cell Class');
57242             cell.title = "";
57243             var t = d.getTime();
57244             
57245             //Roo.log(d);
57246             
57247             
57248             cell.dateValue = t;
57249             if(t == today){
57250                 cell.className += " fc-today";
57251                 cell.className += " fc-state-highlight";
57252                 cell.title = cal.todayText;
57253             }
57254             if(t == sel){
57255                 // disable highlight in other month..
57256                 cell.className += " fc-state-highlight";
57257                 
57258             }
57259             // disabling
57260             if(t < min) {
57261                 //cell.className = " fc-state-disabled";
57262                 cell.title = cal.minText;
57263                 return;
57264             }
57265             if(t > max) {
57266                 //cell.className = " fc-state-disabled";
57267                 cell.title = cal.maxText;
57268                 return;
57269             }
57270             if(ddays){
57271                 if(ddays.indexOf(d.getDay()) != -1){
57272                     // cell.title = ddaysText;
57273                    // cell.className = " fc-state-disabled";
57274                 }
57275             }
57276             if(ddMatch && format){
57277                 var fvalue = d.dateFormat(format);
57278                 if(ddMatch.test(fvalue)){
57279                     cell.title = ddText.replace("%0", fvalue);
57280                    cell.className = " fc-state-disabled";
57281                 }
57282             }
57283             
57284             if (!cell.initialClassName) {
57285                 cell.initialClassName = cell.dom.className;
57286             }
57287             
57288             cell.dom.className = cell.initialClassName  + ' ' +  cell.className;
57289         };
57290
57291         var i = 0;
57292         
57293         for(; i < startingPos; i++) {
57294             cells[i].dayName =  (++prevStart);
57295             Roo.log(textEls[i]);
57296             d.setDate(d.getDate()+1);
57297             
57298             //cells[i].className = "fc-past fc-other-month";
57299             setCellClass(this, cells[i]);
57300         }
57301         
57302         var intDay = 0;
57303         
57304         for(; i < days; i++){
57305             intDay = i - startingPos + 1;
57306             cells[i].dayName =  (intDay);
57307             d.setDate(d.getDate()+1);
57308             
57309             cells[i].className = ''; // "x-date-active";
57310             setCellClass(this, cells[i]);
57311         }
57312         var extraDays = 0;
57313         
57314         for(; i < 42; i++) {
57315             //textEls[i].innerHTML = (++extraDays);
57316             
57317             d.setDate(d.getDate()+1);
57318             cells[i].dayName = (++extraDays);
57319             cells[i].className = "fc-future fc-other-month";
57320             setCellClass(this, cells[i]);
57321         }
57322         
57323         //this.el.select('.fc-header-title h2',true).update(Date.monthNames[date.getMonth()] + " " + date.getFullYear());
57324         
57325         var totalRows = Math.ceil((date.getDaysInMonth() + date.getFirstDateOfMonth().getDay()) / 7);
57326         
57327         // this will cause all the cells to mis
57328         var rows= [];
57329         var i =0;
57330         for (var r = 0;r < 6;r++) {
57331             for (var c =0;c < 7;c++) {
57332                 this.ds.getAt(r).set('weekday' + c ,cells[i++].dayName );
57333             }    
57334         }
57335         
57336         this.cells = this.view.el.select('.x-grid-row .x-grid-col',true);
57337         for(i=0;i<cells.length;i++) {
57338             
57339             this.cells.elements[i].dayName = cells[i].dayName ;
57340             this.cells.elements[i].className = cells[i].className;
57341             this.cells.elements[i].initialClassName = cells[i].initialClassName ;
57342             this.cells.elements[i].title = cells[i].title ;
57343             this.cells.elements[i].dateValue = cells[i].dateValue ;
57344         }
57345         
57346         
57347         
57348         
57349         //this.el.select('tr.fc-week.fc-prev-last',true).removeClass('fc-last');
57350         //this.el.select('tr.fc-week.fc-next-last',true).addClass('fc-last').show();
57351         
57352         ////if(totalRows != 6){
57353             //this.el.select('tr.fc-week.fc-last',true).removeClass('fc-last').addClass('fc-next-last').hide();
57354            // this.el.select('tr.fc-week.fc-prev-last',true).addClass('fc-last');
57355        // }
57356         
57357         this.fireEvent('monthchange', this, date);
57358         
57359         
57360     },
57361  /**
57362      * Returns the grid's SelectionModel.
57363      * @return {SelectionModel}
57364      */
57365     getSelectionModel : function(){
57366         if(!this.selModel){
57367             this.selModel = new Roo.grid.CellSelectionModel();
57368         }
57369         return this.selModel;
57370     },
57371
57372     load: function() {
57373         this.eventStore.load()
57374         
57375         
57376         
57377     },
57378     
57379     findCell : function(dt) {
57380         dt = dt.clearTime().getTime();
57381         var ret = false;
57382         this.cells.each(function(c){
57383             //Roo.log("check " +c.dateValue + '?=' + dt);
57384             if(c.dateValue == dt){
57385                 ret = c;
57386                 return false;
57387             }
57388             return true;
57389         });
57390         
57391         return ret;
57392     },
57393     
57394     findCells : function(rec) {
57395         var s = rec.data.start_dt.clone().clearTime().getTime();
57396        // Roo.log(s);
57397         var e= rec.data.end_dt.clone().clearTime().getTime();
57398        // Roo.log(e);
57399         var ret = [];
57400         this.cells.each(function(c){
57401              ////Roo.log("check " +c.dateValue + '<' + e + ' > ' + s);
57402             
57403             if(c.dateValue > e){
57404                 return ;
57405             }
57406             if(c.dateValue < s){
57407                 return ;
57408             }
57409             ret.push(c);
57410         });
57411         
57412         return ret;    
57413     },
57414     
57415     findBestRow: function(cells)
57416     {
57417         var ret = 0;
57418         
57419         for (var i =0 ; i < cells.length;i++) {
57420             ret  = Math.max(cells[i].rows || 0,ret);
57421         }
57422         return ret;
57423         
57424     },
57425     
57426     
57427     addItem : function(rec)
57428     {
57429         // look for vertical location slot in
57430         var cells = this.findCells(rec);
57431         
57432         rec.row = this.findBestRow(cells);
57433         
57434         // work out the location.
57435         
57436         var crow = false;
57437         var rows = [];
57438         for(var i =0; i < cells.length; i++) {
57439             if (!crow) {
57440                 crow = {
57441                     start : cells[i],
57442                     end :  cells[i]
57443                 };
57444                 continue;
57445             }
57446             if (crow.start.getY() == cells[i].getY()) {
57447                 // on same row.
57448                 crow.end = cells[i];
57449                 continue;
57450             }
57451             // different row.
57452             rows.push(crow);
57453             crow = {
57454                 start: cells[i],
57455                 end : cells[i]
57456             };
57457             
57458         }
57459         
57460         rows.push(crow);
57461         rec.els = [];
57462         rec.rows = rows;
57463         rec.cells = cells;
57464         for (var i = 0; i < cells.length;i++) {
57465             cells[i].rows = Math.max(cells[i].rows || 0 , rec.row + 1 );
57466             
57467         }
57468         
57469         
57470     },
57471     
57472     clearEvents: function() {
57473         
57474         if (!this.eventStore.getCount()) {
57475             return;
57476         }
57477         // reset number of rows in cells.
57478         Roo.each(this.cells.elements, function(c){
57479             c.rows = 0;
57480         });
57481         
57482         this.eventStore.each(function(e) {
57483             this.clearEvent(e);
57484         },this);
57485         
57486     },
57487     
57488     clearEvent : function(ev)
57489     {
57490         if (ev.els) {
57491             Roo.each(ev.els, function(el) {
57492                 el.un('mouseenter' ,this.onEventEnter, this);
57493                 el.un('mouseleave' ,this.onEventLeave, this);
57494                 el.remove();
57495             },this);
57496             ev.els = [];
57497         }
57498     },
57499     
57500     
57501     renderEvent : function(ev,ctr) {
57502         if (!ctr) {
57503              ctr = this.view.el.select('.fc-event-container',true).first();
57504         }
57505         
57506          
57507         this.clearEvent(ev);
57508             //code
57509        
57510         
57511         
57512         ev.els = [];
57513         var cells = ev.cells;
57514         var rows = ev.rows;
57515         this.fireEvent('eventrender', this, ev);
57516         
57517         for(var i =0; i < rows.length; i++) {
57518             
57519             cls = '';
57520             if (i == 0) {
57521                 cls += ' fc-event-start';
57522             }
57523             if ((i+1) == rows.length) {
57524                 cls += ' fc-event-end';
57525             }
57526             
57527             //Roo.log(ev.data);
57528             // how many rows should it span..
57529             var cg = this.eventTmpl.append(ctr,Roo.apply({
57530                 fccls : cls
57531                 
57532             }, ev.data) , true);
57533             
57534             
57535             cg.on('mouseenter' ,this.onEventEnter, this, ev);
57536             cg.on('mouseleave' ,this.onEventLeave, this, ev);
57537             cg.on('click', this.onEventClick, this, ev);
57538             
57539             ev.els.push(cg);
57540             
57541             var sbox = rows[i].start.select('.fc-day-content',true).first().getBox();
57542             var ebox = rows[i].end.select('.fc-day-content',true).first().getBox();
57543             //Roo.log(cg);
57544              
57545             cg.setXY([sbox.x +2, sbox.y +(ev.row * 20)]);    
57546             cg.setWidth(ebox.right - sbox.x -2);
57547         }
57548     },
57549     
57550     renderEvents: function()
57551     {   
57552         // first make sure there is enough space..
57553         
57554         if (!this.eventTmpl) {
57555             this.eventTmpl = new Roo.Template(
57556                 '<div class="roo-dynamic fc-event fc-event-hori fc-event-draggable ui-draggable {fccls} {cls}"  style="position: absolute" unselectable="on">' +
57557                     '<div class="fc-event-inner">' +
57558                         '<span class="fc-event-time">{time}</span>' +
57559                         '<span class="fc-event-title" qtip="{qtip}">{title}</span>' +
57560                     '</div>' +
57561                     '<div class="ui-resizable-heandle ui-resizable-e">&nbsp;&nbsp;&nbsp;</div>' +
57562                 '</div>'
57563             );
57564                 
57565         }
57566                
57567         
57568         
57569         this.cells.each(function(c) {
57570             //Roo.log(c.select('.fc-day-content div',true).first());
57571             c.select('.fc-day-content div',true).first().setHeight(Math.max(34, (c.rows || 1) * 20));
57572         });
57573         
57574         var ctr = this.view.el.select('.fc-event-container',true).first();
57575         
57576         var cls;
57577         this.eventStore.each(function(ev){
57578             
57579             this.renderEvent(ev);
57580              
57581              
57582         }, this);
57583         this.view.layout();
57584         
57585     },
57586     
57587     onEventEnter: function (e, el,event,d) {
57588         this.fireEvent('evententer', this, el, event);
57589     },
57590     
57591     onEventLeave: function (e, el,event,d) {
57592         this.fireEvent('eventleave', this, el, event);
57593     },
57594     
57595     onEventClick: function (e, el,event,d) {
57596         this.fireEvent('eventclick', this, el, event);
57597     },
57598     
57599     onMonthChange: function () {
57600         this.store.load();
57601     },
57602     
57603     onLoad: function () {
57604         
57605         //Roo.log('calendar onload');
57606 //         
57607         if(this.eventStore.getCount() > 0){
57608             
57609            
57610             
57611             this.eventStore.each(function(d){
57612                 
57613                 
57614                 // FIXME..
57615                 var add =   d.data;
57616                 if (typeof(add.end_dt) == 'undefined')  {
57617                     Roo.log("Missing End time in calendar data: ");
57618                     Roo.log(d);
57619                     return;
57620                 }
57621                 if (typeof(add.start_dt) == 'undefined')  {
57622                     Roo.log("Missing Start time in calendar data: ");
57623                     Roo.log(d);
57624                     return;
57625                 }
57626                 add.start_dt = typeof(add.start_dt) == 'string' ? Date.parseDate(add.start_dt,'Y-m-d H:i:s') : add.start_dt,
57627                 add.end_dt = typeof(add.end_dt) == 'string' ? Date.parseDate(add.end_dt,'Y-m-d H:i:s') : add.end_dt,
57628                 add.id = add.id || d.id;
57629                 add.title = add.title || '??';
57630                 
57631                 this.addItem(d);
57632                 
57633              
57634             },this);
57635         }
57636         
57637         this.renderEvents();
57638     }
57639     
57640
57641 });
57642 /*
57643  grid : {
57644                 xtype: 'Grid',
57645                 xns: Roo.grid,
57646                 listeners : {
57647                     render : function ()
57648                     {
57649                         _this.grid = this;
57650                         
57651                         if (!this.view.el.hasClass('course-timesheet')) {
57652                             this.view.el.addClass('course-timesheet');
57653                         }
57654                         if (this.tsStyle) {
57655                             this.ds.load({});
57656                             return; 
57657                         }
57658                         Roo.log('width');
57659                         Roo.log(_this.grid.view.el.getWidth());
57660                         
57661                         
57662                         this.tsStyle =  Roo.util.CSS.createStyleSheet({
57663                             '.course-timesheet .x-grid-row' : {
57664                                 height: '80px'
57665                             },
57666                             '.x-grid-row td' : {
57667                                 'vertical-align' : 0
57668                             },
57669                             '.course-edit-link' : {
57670                                 'color' : 'blue',
57671                                 'text-overflow' : 'ellipsis',
57672                                 'overflow' : 'hidden',
57673                                 'white-space' : 'nowrap',
57674                                 'cursor' : 'pointer'
57675                             },
57676                             '.sub-link' : {
57677                                 'color' : 'green'
57678                             },
57679                             '.de-act-sup-link' : {
57680                                 'color' : 'purple',
57681                                 'text-decoration' : 'line-through'
57682                             },
57683                             '.de-act-link' : {
57684                                 'color' : 'red',
57685                                 'text-decoration' : 'line-through'
57686                             },
57687                             '.course-timesheet .course-highlight' : {
57688                                 'border-top-style': 'dashed !important',
57689                                 'border-bottom-bottom': 'dashed !important'
57690                             },
57691                             '.course-timesheet .course-item' : {
57692                                 'font-family'   : 'tahoma, arial, helvetica',
57693                                 'font-size'     : '11px',
57694                                 'overflow'      : 'hidden',
57695                                 'padding-left'  : '10px',
57696                                 'padding-right' : '10px',
57697                                 'padding-top' : '10px' 
57698                             }
57699                             
57700                         }, Roo.id());
57701                                 this.ds.load({});
57702                     }
57703                 },
57704                 autoWidth : true,
57705                 monitorWindowResize : false,
57706                 cellrenderer : function(v,x,r)
57707                 {
57708                     return v;
57709                 },
57710                 sm : {
57711                     xtype: 'CellSelectionModel',
57712                     xns: Roo.grid
57713                 },
57714                 dataSource : {
57715                     xtype: 'Store',
57716                     xns: Roo.data,
57717                     listeners : {
57718                         beforeload : function (_self, options)
57719                         {
57720                             options.params = options.params || {};
57721                             options.params._month = _this.monthField.getValue();
57722                             options.params.limit = 9999;
57723                             options.params['sort'] = 'when_dt';    
57724                             options.params['dir'] = 'ASC';    
57725                             this.proxy.loadResponse = this.loadResponse;
57726                             Roo.log("load?");
57727                             //this.addColumns();
57728                         },
57729                         load : function (_self, records, options)
57730                         {
57731                             _this.grid.view.el.select('.course-edit-link', true).on('click', function() {
57732                                 // if you click on the translation.. you can edit it...
57733                                 var el = Roo.get(this);
57734                                 var id = el.dom.getAttribute('data-id');
57735                                 var d = el.dom.getAttribute('data-date');
57736                                 var t = el.dom.getAttribute('data-time');
57737                                 //var id = this.child('span').dom.textContent;
57738                                 
57739                                 //Roo.log(this);
57740                                 Pman.Dialog.CourseCalendar.show({
57741                                     id : id,
57742                                     when_d : d,
57743                                     when_t : t,
57744                                     productitem_active : id ? 1 : 0
57745                                 }, function() {
57746                                     _this.grid.ds.load({});
57747                                 });
57748                            
57749                            });
57750                            
57751                            _this.panel.fireEvent('resize', [ '', '' ]);
57752                         }
57753                     },
57754                     loadResponse : function(o, success, response){
57755                             // this is overridden on before load..
57756                             
57757                             Roo.log("our code?");       
57758                             //Roo.log(success);
57759                             //Roo.log(response)
57760                             delete this.activeRequest;
57761                             if(!success){
57762                                 this.fireEvent("loadexception", this, o, response);
57763                                 o.request.callback.call(o.request.scope, null, o.request.arg, false);
57764                                 return;
57765                             }
57766                             var result;
57767                             try {
57768                                 result = o.reader.read(response);
57769                             }catch(e){
57770                                 Roo.log("load exception?");
57771                                 this.fireEvent("loadexception", this, o, response, e);
57772                                 o.request.callback.call(o.request.scope, null, o.request.arg, false);
57773                                 return;
57774                             }
57775                             Roo.log("ready...");        
57776                             // loop through result.records;
57777                             // and set this.tdate[date] = [] << array of records..
57778                             _this.tdata  = {};
57779                             Roo.each(result.records, function(r){
57780                                 //Roo.log(r.data);
57781                                 if(typeof(_this.tdata[r.data.when_dt.format('j')]) == 'undefined'){
57782                                     _this.tdata[r.data.when_dt.format('j')] = [];
57783                                 }
57784                                 _this.tdata[r.data.when_dt.format('j')].push(r.data);
57785                             });
57786                             
57787                             //Roo.log(_this.tdata);
57788                             
57789                             result.records = [];
57790                             result.totalRecords = 6;
57791                     
57792                             // let's generate some duumy records for the rows.
57793                             //var st = _this.dateField.getValue();
57794                             
57795                             // work out monday..
57796                             //st = st.add(Date.DAY, -1 * st.format('w'));
57797                             
57798                             var date = Date.parseDate(_this.monthField.getValue(), "Y-m-d");
57799                             
57800                             var firstOfMonth = date.getFirstDayOfMonth();
57801                             var days = date.getDaysInMonth();
57802                             var d = 1;
57803                             var firstAdded = false;
57804                             for (var i = 0; i < result.totalRecords ; i++) {
57805                                 //var d= st.add(Date.DAY, i);
57806                                 var row = {};
57807                                 var added = 0;
57808                                 for(var w = 0 ; w < 7 ; w++){
57809                                     if(!firstAdded && firstOfMonth != w){
57810                                         continue;
57811                                     }
57812                                     if(d > days){
57813                                         continue;
57814                                     }
57815                                     firstAdded = true;
57816                                     var dd = (d > 0 && d < 10) ? "0"+d : d;
57817                                     row['weekday'+w] = String.format(
57818                                                     '<span style="font-size: 16px;"><b>{0}</b></span>'+
57819                                                     '<span class="course-edit-link" style="color:blue;" data-id="0" data-date="{1}"> Add New</span>',
57820                                                     d,
57821                                                     date.format('Y-m-')+dd
57822                                                 );
57823                                     added++;
57824                                     if(typeof(_this.tdata[d]) != 'undefined'){
57825                                         Roo.each(_this.tdata[d], function(r){
57826                                             var is_sub = '';
57827                                             var deactive = '';
57828                                             var id = r.id;
57829                                             var desc = (r.productitem_id_descrip) ? r.productitem_id_descrip : '';
57830                                             if(r.parent_id*1>0){
57831                                                 is_sub = (r.productitem_id_visible*1 < 1) ? 'de-act-sup-link' :'sub-link';
57832                                                 id = r.parent_id;
57833                                             }
57834                                             if(r.productitem_id_visible*1 < 1 && r.parent_id*1 < 1){
57835                                                 deactive = 'de-act-link';
57836                                             }
57837                                             
57838                                             row['weekday'+w] += String.format(
57839                                                     '<br /><span class="course-edit-link {3} {4}" qtip="{5}" data-id="{0}">{2} - {1}</span>',
57840                                                     id, //0
57841                                                     r.product_id_name, //1
57842                                                     r.when_dt.format('h:ia'), //2
57843                                                     is_sub, //3
57844                                                     deactive, //4
57845                                                     desc // 5
57846                                             );
57847                                         });
57848                                     }
57849                                     d++;
57850                                 }
57851                                 
57852                                 // only do this if something added..
57853                                 if(added > 0){ 
57854                                     result.records.push(_this.grid.dataSource.reader.newRow(row));
57855                                 }
57856                                 
57857                                 
57858                                 // push it twice. (second one with an hour..
57859                                 
57860                             }
57861                             //Roo.log(result);
57862                             this.fireEvent("load", this, o, o.request.arg);
57863                             o.request.callback.call(o.request.scope, result, o.request.arg, true);
57864                         },
57865                     sortInfo : {field: 'when_dt', direction : 'ASC' },
57866                     proxy : {
57867                         xtype: 'HttpProxy',
57868                         xns: Roo.data,
57869                         method : 'GET',
57870                         url : baseURL + '/Roo/Shop_course.php'
57871                     },
57872                     reader : {
57873                         xtype: 'JsonReader',
57874                         xns: Roo.data,
57875                         id : 'id',
57876                         fields : [
57877                             {
57878                                 'name': 'id',
57879                                 'type': 'int'
57880                             },
57881                             {
57882                                 'name': 'when_dt',
57883                                 'type': 'string'
57884                             },
57885                             {
57886                                 'name': 'end_dt',
57887                                 'type': 'string'
57888                             },
57889                             {
57890                                 'name': 'parent_id',
57891                                 'type': 'int'
57892                             },
57893                             {
57894                                 'name': 'product_id',
57895                                 'type': 'int'
57896                             },
57897                             {
57898                                 'name': 'productitem_id',
57899                                 'type': 'int'
57900                             },
57901                             {
57902                                 'name': 'guid',
57903                                 'type': 'int'
57904                             }
57905                         ]
57906                     }
57907                 },
57908                 toolbar : {
57909                     xtype: 'Toolbar',
57910                     xns: Roo,
57911                     items : [
57912                         {
57913                             xtype: 'Button',
57914                             xns: Roo.Toolbar,
57915                             listeners : {
57916                                 click : function (_self, e)
57917                                 {
57918                                     var sd = Date.parseDate(_this.monthField.getValue(), "Y-m-d");
57919                                     sd.setMonth(sd.getMonth()-1);
57920                                     _this.monthField.setValue(sd.format('Y-m-d'));
57921                                     _this.grid.ds.load({});
57922                                 }
57923                             },
57924                             text : "Back"
57925                         },
57926                         {
57927                             xtype: 'Separator',
57928                             xns: Roo.Toolbar
57929                         },
57930                         {
57931                             xtype: 'MonthField',
57932                             xns: Roo.form,
57933                             listeners : {
57934                                 render : function (_self)
57935                                 {
57936                                     _this.monthField = _self;
57937                                    // _this.monthField.set  today
57938                                 },
57939                                 select : function (combo, date)
57940                                 {
57941                                     _this.grid.ds.load({});
57942                                 }
57943                             },
57944                             value : (function() { return new Date(); })()
57945                         },
57946                         {
57947                             xtype: 'Separator',
57948                             xns: Roo.Toolbar
57949                         },
57950                         {
57951                             xtype: 'TextItem',
57952                             xns: Roo.Toolbar,
57953                             text : "Blue: in-active, green: in-active sup-event, red: de-active, purple: de-active sup-event"
57954                         },
57955                         {
57956                             xtype: 'Fill',
57957                             xns: Roo.Toolbar
57958                         },
57959                         {
57960                             xtype: 'Button',
57961                             xns: Roo.Toolbar,
57962                             listeners : {
57963                                 click : function (_self, e)
57964                                 {
57965                                     var sd = Date.parseDate(_this.monthField.getValue(), "Y-m-d");
57966                                     sd.setMonth(sd.getMonth()+1);
57967                                     _this.monthField.setValue(sd.format('Y-m-d'));
57968                                     _this.grid.ds.load({});
57969                                 }
57970                             },
57971                             text : "Next"
57972                         }
57973                     ]
57974                 },
57975                  
57976             }
57977         };
57978         
57979         *//*
57980  * Based on:
57981  * Ext JS Library 1.1.1
57982  * Copyright(c) 2006-2007, Ext JS, LLC.
57983  *
57984  * Originally Released Under LGPL - original licence link has changed is not relivant.
57985  *
57986  * Fork - LGPL
57987  * <script type="text/javascript">
57988  */
57989  
57990 /**
57991  * @class Roo.LoadMask
57992  * A simple utility class for generically masking elements while loading data.  If the element being masked has
57993  * an underlying {@link Roo.data.Store}, the masking will be automatically synchronized with the store's loading
57994  * process and the mask element will be cached for reuse.  For all other elements, this mask will replace the
57995  * element's UpdateManager load indicator and will be destroyed after the initial load.
57996  * @constructor
57997  * Create a new LoadMask
57998  * @param {String/HTMLElement/Roo.Element} el The element or DOM node, or its id
57999  * @param {Object} config The config object
58000  */
58001 Roo.LoadMask = function(el, config){
58002     this.el = Roo.get(el);
58003     Roo.apply(this, config);
58004     if(this.store){
58005         this.store.on('beforeload', this.onBeforeLoad, this);
58006         this.store.on('load', this.onLoad, this);
58007         this.store.on('loadexception', this.onLoadException, this);
58008         this.removeMask = false;
58009     }else{
58010         var um = this.el.getUpdateManager();
58011         um.showLoadIndicator = false; // disable the default indicator
58012         um.on('beforeupdate', this.onBeforeLoad, this);
58013         um.on('update', this.onLoad, this);
58014         um.on('failure', this.onLoad, this);
58015         this.removeMask = true;
58016     }
58017 };
58018
58019 Roo.LoadMask.prototype = {
58020     /**
58021      * @cfg {Boolean} removeMask
58022      * True to create a single-use mask that is automatically destroyed after loading (useful for page loads),
58023      * False to persist the mask element reference for multiple uses (e.g., for paged data widgets).  Defaults to false.
58024      */
58025     /**
58026      * @cfg {String} msg
58027      * The text to display in a centered loading message box (defaults to 'Loading...')
58028      */
58029     msg : 'Loading...',
58030     /**
58031      * @cfg {String} msgCls
58032      * The CSS class to apply to the loading message element (defaults to "x-mask-loading")
58033      */
58034     msgCls : 'x-mask-loading',
58035
58036     /**
58037      * Read-only. True if the mask is currently disabled so that it will not be displayed (defaults to false)
58038      * @type Boolean
58039      */
58040     disabled: false,
58041
58042     /**
58043      * Disables the mask to prevent it from being displayed
58044      */
58045     disable : function(){
58046        this.disabled = true;
58047     },
58048
58049     /**
58050      * Enables the mask so that it can be displayed
58051      */
58052     enable : function(){
58053         this.disabled = false;
58054     },
58055     
58056     onLoadException : function()
58057     {
58058         Roo.log(arguments);
58059         
58060         if (typeof(arguments[3]) != 'undefined') {
58061             Roo.MessageBox.alert("Error loading",arguments[3]);
58062         } 
58063         /*
58064         try {
58065             if (this.store && typeof(this.store.reader.jsonData.errorMsg) != 'undefined') {
58066                 Roo.MessageBox.alert("Error loading",this.store.reader.jsonData.errorMsg);
58067             }   
58068         } catch(e) {
58069             
58070         }
58071         */
58072     
58073         
58074         
58075         this.el.unmask(this.removeMask);
58076     },
58077     // private
58078     onLoad : function()
58079     {
58080         this.el.unmask(this.removeMask);
58081     },
58082
58083     // private
58084     onBeforeLoad : function(){
58085         if(!this.disabled){
58086             this.el.mask(this.msg, this.msgCls);
58087         }
58088     },
58089
58090     // private
58091     destroy : function(){
58092         if(this.store){
58093             this.store.un('beforeload', this.onBeforeLoad, this);
58094             this.store.un('load', this.onLoad, this);
58095             this.store.un('loadexception', this.onLoadException, this);
58096         }else{
58097             var um = this.el.getUpdateManager();
58098             um.un('beforeupdate', this.onBeforeLoad, this);
58099             um.un('update', this.onLoad, this);
58100             um.un('failure', this.onLoad, this);
58101         }
58102     }
58103 };/*
58104  * Based on:
58105  * Ext JS Library 1.1.1
58106  * Copyright(c) 2006-2007, Ext JS, LLC.
58107  *
58108  * Originally Released Under LGPL - original licence link has changed is not relivant.
58109  *
58110  * Fork - LGPL
58111  * <script type="text/javascript">
58112  */
58113
58114
58115 /**
58116  * @class Roo.XTemplate
58117  * @extends Roo.Template
58118  * Provides a template that can have nested templates for loops or conditionals. The syntax is:
58119 <pre><code>
58120 var t = new Roo.XTemplate(
58121         '&lt;select name="{name}"&gt;',
58122                 '&lt;tpl for="options"&gt;&lt;option value="{value:trim}"&gt;{text:ellipsis(10)}&lt;/option&gt;&lt;/tpl&gt;',
58123         '&lt;/select&gt;'
58124 );
58125  
58126 // then append, applying the master template values
58127  </code></pre>
58128  *
58129  * Supported features:
58130  *
58131  *  Tags:
58132
58133 <pre><code>
58134       {a_variable} - output encoded.
58135       {a_variable.format:("Y-m-d")} - call a method on the variable
58136       {a_variable:raw} - unencoded output
58137       {a_variable:toFixed(1,2)} - Roo.util.Format."toFixed"
58138       {a_variable:this.method_on_template(...)} - call a method on the template object.
58139  
58140 </code></pre>
58141  *  The tpl tag:
58142 <pre><code>
58143         &lt;tpl for="a_variable or condition.."&gt;&lt;/tpl&gt;
58144         &lt;tpl if="a_variable or condition"&gt;&lt;/tpl&gt;
58145         &lt;tpl exec="some javascript"&gt;&lt;/tpl&gt;
58146         &lt;tpl name="named_template"&gt;&lt;/tpl&gt; (experimental)
58147   
58148         &lt;tpl for="."&gt;&lt;/tpl&gt; - just iterate the property..
58149         &lt;tpl for=".."&gt;&lt;/tpl&gt; - iterates with the parent (probably the template) 
58150 </code></pre>
58151  *      
58152  */
58153 Roo.XTemplate = function()
58154 {
58155     Roo.XTemplate.superclass.constructor.apply(this, arguments);
58156     if (this.html) {
58157         this.compile();
58158     }
58159 };
58160
58161
58162 Roo.extend(Roo.XTemplate, Roo.Template, {
58163
58164     /**
58165      * The various sub templates
58166      */
58167     tpls : false,
58168     /**
58169      *
58170      * basic tag replacing syntax
58171      * WORD:WORD()
58172      *
58173      * // you can fake an object call by doing this
58174      *  x.t:(test,tesT) 
58175      * 
58176      */
58177     re : /\{([\w-\.]+)(?:\:([\w\.]*)(?:\((.*?)?\))?)?\}/g,
58178
58179     /**
58180      * compile the template
58181      *
58182      * This is not recursive, so I'm not sure how nested templates are really going to be handled..
58183      *
58184      */
58185     compile: function()
58186     {
58187         var s = this.html;
58188      
58189         s = ['<tpl>', s, '</tpl>'].join('');
58190     
58191         var re     = /<tpl\b[^>]*>((?:(?=([^<]+))\2|<(?!tpl\b[^>]*>))*?)<\/tpl>/,
58192             nameRe = /^<tpl\b[^>]*?for="(.*?)"/,
58193             ifRe   = /^<tpl\b[^>]*?if="(.*?)"/,
58194             execRe = /^<tpl\b[^>]*?exec="(.*?)"/,
58195             namedRe = /^<tpl\b[^>]*?name="(\w+)"/,  // named templates..
58196             m,
58197             id     = 0,
58198             tpls   = [];
58199     
58200         while(true == !!(m = s.match(re))){
58201             var forMatch   = m[0].match(nameRe),
58202                 ifMatch   = m[0].match(ifRe),
58203                 execMatch   = m[0].match(execRe),
58204                 namedMatch   = m[0].match(namedRe),
58205                 
58206                 exp  = null, 
58207                 fn   = null,
58208                 exec = null,
58209                 name = forMatch && forMatch[1] ? forMatch[1] : '';
58210                 
58211             if (ifMatch) {
58212                 // if - puts fn into test..
58213                 exp = ifMatch && ifMatch[1] ? ifMatch[1] : null;
58214                 if(exp){
58215                    fn = new Function('values', 'parent', 'with(values){ return '+(Roo.util.Format.htmlDecode(exp))+'; }');
58216                 }
58217             }
58218             
58219             if (execMatch) {
58220                 // exec - calls a function... returns empty if true is  returned.
58221                 exp = execMatch && execMatch[1] ? execMatch[1] : null;
58222                 if(exp){
58223                    exec = new Function('values', 'parent', 'with(values){ '+(Roo.util.Format.htmlDecode(exp))+'; }');
58224                 }
58225             }
58226             
58227             
58228             if (name) {
58229                 // for = 
58230                 switch(name){
58231                     case '.':  name = new Function('values', 'parent', 'with(values){ return values; }'); break;
58232                     case '..': name = new Function('values', 'parent', 'with(values){ return parent; }'); break;
58233                     default:   name = new Function('values', 'parent', 'with(values){ return '+name+'; }');
58234                 }
58235             }
58236             var uid = namedMatch ? namedMatch[1] : id;
58237             
58238             
58239             tpls.push({
58240                 id:     namedMatch ? namedMatch[1] : id,
58241                 target: name,
58242                 exec:   exec,
58243                 test:   fn,
58244                 body:   m[1] || ''
58245             });
58246             if (namedMatch) {
58247                 s = s.replace(m[0], '');
58248             } else { 
58249                 s = s.replace(m[0], '{xtpl'+ id + '}');
58250             }
58251             ++id;
58252         }
58253         this.tpls = [];
58254         for(var i = tpls.length-1; i >= 0; --i){
58255             this.compileTpl(tpls[i]);
58256             this.tpls[tpls[i].id] = tpls[i];
58257         }
58258         this.master = tpls[tpls.length-1];
58259         return this;
58260     },
58261     /**
58262      * same as applyTemplate, except it's done to one of the subTemplates
58263      * when using named templates, you can do:
58264      *
58265      * var str = pl.applySubTemplate('your-name', values);
58266      *
58267      * 
58268      * @param {Number} id of the template
58269      * @param {Object} values to apply to template
58270      * @param {Object} parent (normaly the instance of this object)
58271      */
58272     applySubTemplate : function(id, values, parent)
58273     {
58274         
58275         
58276         var t = this.tpls[id];
58277         
58278         
58279         try { 
58280             if(t.test && !t.test.call(this, values, parent)){
58281                 return '';
58282             }
58283         } catch(e) {
58284             Roo.log("Xtemplate.applySubTemplate 'test': Exception thrown");
58285             Roo.log(e.toString());
58286             Roo.log(t.test);
58287             return ''
58288         }
58289         try { 
58290             
58291             if(t.exec && t.exec.call(this, values, parent)){
58292                 return '';
58293             }
58294         } catch(e) {
58295             Roo.log("Xtemplate.applySubTemplate 'exec': Exception thrown");
58296             Roo.log(e.toString());
58297             Roo.log(t.exec);
58298             return ''
58299         }
58300         try {
58301             var vs = t.target ? t.target.call(this, values, parent) : values;
58302             parent = t.target ? values : parent;
58303             if(t.target && vs instanceof Array){
58304                 var buf = [];
58305                 for(var i = 0, len = vs.length; i < len; i++){
58306                     buf[buf.length] = t.compiled.call(this, vs[i], parent);
58307                 }
58308                 return buf.join('');
58309             }
58310             return t.compiled.call(this, vs, parent);
58311         } catch (e) {
58312             Roo.log("Xtemplate.applySubTemplate : Exception thrown");
58313             Roo.log(e.toString());
58314             Roo.log(t.compiled);
58315             return '';
58316         }
58317     },
58318
58319     compileTpl : function(tpl)
58320     {
58321         var fm = Roo.util.Format;
58322         var useF = this.disableFormats !== true;
58323         var sep = Roo.isGecko ? "+" : ",";
58324         var undef = function(str) {
58325             Roo.log("Property not found :"  + str);
58326             return '';
58327         };
58328         
58329         var fn = function(m, name, format, args)
58330         {
58331             //Roo.log(arguments);
58332             args = args ? args.replace(/\\'/g,"'") : args;
58333             //["{TEST:(a,b,c)}", "TEST", "", "a,b,c", 0, "{TEST:(a,b,c)}"]
58334             if (typeof(format) == 'undefined') {
58335                 format= 'htmlEncode';
58336             }
58337             if (format == 'raw' ) {
58338                 format = false;
58339             }
58340             
58341             if(name.substr(0, 4) == 'xtpl'){
58342                 return "'"+ sep +'this.applySubTemplate('+name.substr(4)+', values, parent)'+sep+"'";
58343             }
58344             
58345             // build an array of options to determine if value is undefined..
58346             
58347             // basically get 'xxxx.yyyy' then do
58348             // (typeof(xxxx) == 'undefined' || typeof(xxx.yyyy) == 'undefined') ?
58349             //    (function () { Roo.log("Property not found"); return ''; })() :
58350             //    ......
58351             
58352             var udef_ar = [];
58353             var lookfor = '';
58354             Roo.each(name.split('.'), function(st) {
58355                 lookfor += (lookfor.length ? '.': '') + st;
58356                 udef_ar.push(  "(typeof(" + lookfor + ") == 'undefined')"  );
58357             });
58358             
58359             var udef_st = '((' + udef_ar.join(" || ") +") ? undef('" + name + "') : "; // .. needs )
58360             
58361             
58362             if(format && useF){
58363                 
58364                 args = args ? ',' + args : "";
58365                  
58366                 if(format.substr(0, 5) != "this."){
58367                     format = "fm." + format + '(';
58368                 }else{
58369                     format = 'this.call("'+ format.substr(5) + '", ';
58370                     args = ", values";
58371                 }
58372                 
58373                 return "'"+ sep +   udef_st   +    format + name + args + "))"+sep+"'";
58374             }
58375              
58376             if (args.length) {
58377                 // called with xxyx.yuu:(test,test)
58378                 // change to ()
58379                 return "'"+ sep + udef_st  + name + '(' +  args + "))"+sep+"'";
58380             }
58381             // raw.. - :raw modifier..
58382             return "'"+ sep + udef_st  + name + ")"+sep+"'";
58383             
58384         };
58385         var body;
58386         // branched to use + in gecko and [].join() in others
58387         if(Roo.isGecko){
58388             body = "tpl.compiled = function(values, parent){  with(values) { return '" +
58389                    tpl.body.replace(/(\r\n|\n)/g, '\\n').replace(/'/g, "\\'").replace(this.re, fn) +
58390                     "';};};";
58391         }else{
58392             body = ["tpl.compiled = function(values, parent){  with (values) { return ['"];
58393             body.push(tpl.body.replace(/(\r\n|\n)/g,
58394                             '\\n').replace(/'/g, "\\'").replace(this.re, fn));
58395             body.push("'].join('');};};");
58396             body = body.join('');
58397         }
58398         
58399         Roo.debug && Roo.log(body.replace(/\\n/,'\n'));
58400        
58401         /** eval:var:tpl eval:var:fm eval:var:useF eval:var:undef  */
58402         eval(body);
58403         
58404         return this;
58405     },
58406
58407     applyTemplate : function(values){
58408         return this.master.compiled.call(this, values, {});
58409         //var s = this.subs;
58410     },
58411
58412     apply : function(){
58413         return this.applyTemplate.apply(this, arguments);
58414     }
58415
58416  });
58417
58418 Roo.XTemplate.from = function(el){
58419     el = Roo.getDom(el);
58420     return new Roo.XTemplate(el.value || el.innerHTML);
58421 };